From 6df073f4de2aeaec02a02dbea27af0001bd97f89 Mon Sep 17 00:00:00 2001 From: Hayk Davtyan <46712946+hayk96@users.noreply.github.com> Date: Sun, 26 May 2024 22:23:26 +0400 Subject: [PATCH] Revert "Add new API metrics-lifecycle-policies (#19) #none" Signed-off-by: hayk96 --- CHANGELOG.md | 8 - docs/examples/docker/docker-compose.yml | 1 - docs/examples/kubernetes/helm/values.yaml | 1 - main.py | 8 +- requirements.txt | 8 +- src/api/v1/api.py | 3 +- src/api/v1/endpoints/policies.py | 356 ---------------------- src/api/v1/endpoints/rules.py | 2 +- src/core/__init__.py | 0 src/core/policies.py | 123 -------- src/models/policy.py | 41 --- src/schemas/policies_create.json | 22 -- src/schemas/policies_update.json | 22 -- src/tasks/__init__.py | 0 src/tasks/policies.py | 63 ---- src/utils/openapi.py | 2 +- src/utils/scheduler.py | 16 - src/utils/settings.py | 13 - 18 files changed, 9 insertions(+), 680 deletions(-) delete mode 100644 src/api/v1/endpoints/policies.py delete mode 100644 src/core/__init__.py delete mode 100644 src/core/policies.py delete mode 100644 src/models/policy.py delete mode 100644 src/schemas/policies_create.json delete mode 100644 src/schemas/policies_update.json delete mode 100644 src/tasks/__init__.py delete mode 100644 src/tasks/policies.py delete mode 100644 src/utils/scheduler.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c187f..fb4f100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,5 @@ # Changelog -## 0.3.0 / 2024-05-26 - -* [ENHANCEMENT] -Introduced a new API `/metrics-lifecycle-policies` for managing metrics lifecycle in the Prometheus ecosystem. This -flexible API allows users to define policies that specify which time-series should be retained and for how long in the -Prometheus TSDB storage. -* [BUGFIX] fixed description of 404 status code of the `DELETE /api/v1/rules` API in Redocli page. - ## 0.2.2 / 2024-05-12 * [REVERT] Reverted schema validation mechanism of rules API. Use local schema validation instead of remote which was introduces in [v0.1.2](https://github.com/hayk96/prometheus-api/releases/tag/v0.1.2). #18 diff --git a/docs/examples/docker/docker-compose.yml b/docs/examples/docker/docker-compose.yml index 3f7423d..197da77 100644 --- a/docs/examples/docker/docker-compose.yml +++ b/docs/examples/docker/docker-compose.yml @@ -11,7 +11,6 @@ services: command: - --config.file=/etc/prometheus/prometheus.yml - --web.enable-lifecycle - - --web.enable-admin-api prometheus-api: image: hayk96/prometheus-api:latest container_name: prometheus-api diff --git a/docs/examples/kubernetes/helm/values.yaml b/docs/examples/kubernetes/helm/values.yaml index 88991ed..65c659a 100644 --- a/docs/examples/kubernetes/helm/values.yaml +++ b/docs/examples/kubernetes/helm/values.yaml @@ -17,7 +17,6 @@ server: readOnly: false extraFlags: - web.enable-lifecycle - - web.enable-admin-api - web.listen-address=:9091 extraVolumeMounts: - name: storage-volume diff --git a/main.py b/main.py index 227562d..d704c83 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,5 @@ from fastapi.middleware.cors import CORSMiddleware from src.utils.arguments import arg_parser -from src.utils.scheduler import schedule from src.api.v1.api import api_router from src.utils.openapi import openapi from src.utils.metrics import metrics @@ -16,9 +15,9 @@ host, port = args.get("web.listen_address").split(":") if not all([settings.check_prom_http_connection(prom_addr), - settings.check_reload_api_status(prom_addr), - settings.check_rules_directory(rule_path), - settings.check_fs_permissions(rule_path)]): + settings.check_reload_api_status(prom_addr), + settings.check_rules_directory(rule_path), + settings.check_fs_permissions(rule_path)]): sys.exit() @@ -57,5 +56,4 @@ def main(): if __name__ == "__main__": - schedule() main() diff --git a/requirements.txt b/requirements.txt index 033d76f..03d5159 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,10 @@ prometheus-fastapi-instrumentator==6.1.0 python-json-logger==2.0.7 email-validator==2.0.0 -APScheduler==3.10.4 -pytimeparse2==1.7.1 -jsonschema==4.17.3 -requests==2.28.2 -pydantic==1.10.7 fastapi==0.109.0 uvicorn==0.21.1 +requests==2.28.2 +pydantic==1.10.7 PyYAML==6.0.1 +jsonschema==4.17.3 httpx==0.24.0 \ No newline at end of file diff --git a/src/api/v1/api.py b/src/api/v1/api.py index 3661281..68b7dad 100644 --- a/src/api/v1/api.py +++ b/src/api/v1/api.py @@ -1,8 +1,7 @@ -from .. v1.endpoints import reverse_proxy, rules, policies, web +from .. v1.endpoints import reverse_proxy, rules, web from fastapi import APIRouter api_router = APIRouter() api_router.include_router(rules.router, prefix="/api/v1") -api_router.include_router(policies.router, prefix="/api/v1") api_router.include_router(web.router, prefix="") api_router.add_route("/{path:path}", reverse_proxy._reverse_proxy, ["GET", "POST", "PUT"]) diff --git a/src/api/v1/endpoints/policies.py b/src/api/v1/endpoints/policies.py deleted file mode 100644 index 73d4618..0000000 --- a/src/api/v1/endpoints/policies.py +++ /dev/null @@ -1,356 +0,0 @@ -from src.models.policy import MetricsLifecyclePolicyCreate, MetricsLifecyclePolicyUpdate -from fastapi import APIRouter, Response, Request, Body, status -from src.core import policies as mlp -from src.utils.log import logger -from typing import Annotated - -router = APIRouter() -policies = mlp.load_policies() - - -@router.get("/metrics-lifecycle-policies/{name}", - name="Get metrics lifecycle policy by name", - description="Returns metrics lifecycle policy that match the provided name parameter", - status_code=status.HTTP_200_OK, - tags=["metrics-lifecycle-policies"], - responses={ - 200: { - "description": "OK", - "content": { - "application/json": { - "example": [ - { - "match": "{__name__=~'go_.+'}", - "keep_for": "7d", - "description": "This metrics lifecycle policy keeps GoLang metrics for 7 days" - } - ] - } - } - }, - 404: { - "description": "Not Found", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Policy not found"} - ] - } - } - } - } - ) -async def get_policy( - name: str, - request: Request, - response: Response, -): - resp = { - 200: { - "status": "success", - "message": "Successfully returned the requested metric lifecycle policy"}, - 404: { - "status": "error", - "message": "Policy not found"}} - response.status_code = 200 if policies.get(name) else 404 - logger.info( - msg=resp[response.status_code]["message"], - extra={ - "status": response.status_code, - "policy_name": name, - "method": request.method, - "request_path": request.url.path}) - return policies.get(name) or resp[response.status_code] - - -@router.get("/metrics-lifecycle-policies", - name="Get all metrics lifecycle policies", - description="Returns all metrics lifecycle policies", - status_code=status.HTTP_200_OK, - tags=["metrics-lifecycle-policies"], - responses={ - 200: { - "description": "OK", - "content": { - "application/json": { - "example": [ - { - "GoLang Policy": { - "match": "{__name__=~'go_.+'}", - "keep_for": "7d", - "message": "This policy keeps GoLang metrics for 7 days" - }, - "Kubernetes Policy": { - "match": "{job='kubernetes-pods'}", - "keep_for": "10d", - "message": "This policy keeps series of job 'kubernetes-pods' for 10 days" - } - } - ] - } - } - } - } - ) -async def get_policies( - request: Request, - response: Response, -): - response.status_code = 200 - logger.info( - msg="Successfully returned all metrics lifecycle policies", - extra={ - "status": response.status_code, - "method": request.method, - "request_path": request.url.path}) - return policies - - -@router.post("/metrics-lifecycle-policies", - name="Create metric lifecycle policy", - description="Creates a new metric lifecycle policy", - status_code=status.HTTP_201_CREATED, - tags=["metrics-lifecycle-policies"], - responses={ - 201: { - "description": "Created", - "content": { - "application/json": { - "example": [ - { - "status": "success", - "policy_name": "GoLang Metrics Policy", - "message": "Policy created successfully" - } - ] - } - } - }, - 400: { - "description": "Bad Request", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Additional properties are not allowed (time was unexpected)" - } - ] - } - } - }, - 409: { - "description": "Conflict", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "The requested policy already exists" - } - ] - } - } - }, - 500: { - "description": "Internal Server Error", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Failed to create metric lifecycle policy" - } - ] - } - } - } - } - ) -async def create( - request: Request, - response: Response, - policy: Annotated[ - MetricsLifecyclePolicyCreate, - Body( - openapi_examples=MetricsLifecyclePolicyCreate._request_body_examples, - ) - ] -): - data = policy.dict() - validation_status, response.status_code, sts, msg = mlp.validate_prom_admin_api() - if validation_status: - validation_status, response.status_code, sts, msg = mlp.validate_policy( - "policies_create.json", data) - if validation_status: - validation_status, response.status_code, sts, msg, val = mlp.validate_duration( - data.get("keep_for")) - if validation_status: - response.status_code, sts, msg = mlp.create_policy( - **policy.dict(), data=policies) - logger.info( - msg=msg, - extra={ - "status": response.status_code, - "policy_name": policy.dict().get("name"), - "method": request.method, - "request_path": request.url.path}) - return { - "status": sts, - "policy_name": policy.dict().get("name"), - "message": msg - } - - -@router.patch("/metrics-lifecycle-policies/{name}", - name="Update metrics lifecycle policy by name", - description="Updates specific metrics lifecycle policy", - status_code=status.HTTP_200_OK, - tags=["metrics-lifecycle-policies"], - responses={ - 200: { - "description": "OK", - "content": { - "application/json": { - "example": [ - { - "status": "success", - "message": "Policy updated successfully" - } - ] - } - } - }, - 400: { - "description": "Bad Request", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Additional properties are not allowed ('name' was unexpected)" - } - ] - } - } - }, - 404: { - "description": "Not Found", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Policy not found" - } - ] - } - } - }, - 500: { - "description": "Internal Server Error", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Failed to create metric lifecycle policy" - } - ] - } - } - } - } - ) -async def update( - name: str, - request: Request, - response: Response, - policy: Annotated[ - MetricsLifecyclePolicyUpdate, - Body( - openapi_examples=MetricsLifecyclePolicyUpdate._request_body_examples, - ) - ] -): - data = policy.dict(exclude_unset=True) - validation_status, response.status_code, sts, msg = mlp.validate_prom_admin_api() - if validation_status: - validation_status, response.status_code, sts, msg = mlp.validate_policy( - "policies_update.json", data) - if validation_status: - validation_status, response.status_code, sts, msg, val = mlp.validate_duration( - data.get("keep_for")) - if validation_status: - response.status_code, sts, msg = mlp.update_policy( - name=name, data=data, policies=policies) - logger.info( - msg=msg, - extra={ - "status": response.status_code, - "policy_name": name, - "method": request.method, - "request_path": request.url.path}) - return { - "status": sts, - "policy_name": name, - "message": msg - } - - -@router.delete("/metrics-lifecycle-policies/{name}", - name="Delete metrics lifecycle policy", - description="Deletes specific metrics lifecycle policy", - status_code=status.HTTP_204_NO_CONTENT, - tags=["metrics-lifecycle-policies"], - responses={ - 204: { - "description": "No Content" - }, - 404: { - "description": "Not Found", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Policy not found" - } - ] - } - } - }, - 500: { - "description": "Internal Server Error", - "content": { - "application/json": { - "example": [ - { - "status": "error", - "message": "Failed to delete metrics lifecycle policy" - } - ] - } - } - } - } - ) -async def delete( - name: str, - request: Request, - response: Response -): - - response.status_code, sts, msg = mlp.delete_policy( - name=name, policies=policies) - logger.info( - msg=msg, - extra={ - "status": response.status_code, - "method": request.method, - "request_path": request.url.path}) - return { - "status": sts, - "message": msg} if response.status_code != 204 else response.status_code diff --git a/src/api/v1/endpoints/rules.py b/src/api/v1/endpoints/rules.py index 20916a7..7546e70 100644 --- a/src/api/v1/endpoints/rules.py +++ b/src/api/v1/endpoints/rules.py @@ -230,7 +230,7 @@ async def update( "description": "No Content", }, 404: { - "description": "Not Found", + "description": "Conflict", "content": { "application/json": { "example": [ diff --git a/src/core/__init__.py b/src/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/policies.py b/src/core/policies.py deleted file mode 100644 index 5f531e7..0000000 --- a/src/core/policies.py +++ /dev/null @@ -1,123 +0,0 @@ -from jsonschema import validate, exceptions -from src.utils.arguments import arg_parser -from src.utils.settings import prom_info -from src.utils.log import logger -from pytimeparse2 import parse -import json - -rule_path = arg_parser().get("rule.path") -prom_addr = arg_parser().get("prom.addr") -policies_data_file = ".policies.json" -prom_storage_retention_human = prom_info( - prom_addr, "/runtimeinfo")["data"]["storageRetention"] - - -def sync_to_file(data) -> None: - """This function saves metrics lifecycle policies - JSON object into file once it's updated""" - global rule_path, policies_data_file - with open(f"{rule_path}/{policies_data_file}", "w") as f: - f.write(json.dumps(data)) - logger.debug( - f"Policy has been updated in file {rule_path}/{policies_data_file}.") - - -def create_policy(name: str, match: str, keep_for: str, - description: str, data: dict) -> tuple[int, str, str]: - """This function creates a new metrics lifecycle policy""" - global rule_path, policies_data_file - if name in data.keys(): - return 409, "error", "The requested policy already exists" - try: - data[name] = { - "match": match, - "keep_for": keep_for, - "description": description - } - sync_to_file(data) - except BaseException as e: - return 500, "error", f"Failed to save policy. {e}" - return 200, "success", "Policy created successfully" - - -def update_policy(name: str, data: dict, - policies: dict) -> tuple[int, str, str]: - """This function updates the existing metrics lifecycle policy""" - global rule_path, policies_data_file - - if name not in policies.keys(): - return 404, "error", "Policy not found" - try: - policies[name].update(data) - sync_to_file(policies) - except BaseException as e: - return 500, "error", f"Failed to update policy. {e}" - return 200, "success", "Policy updated successfully" - - -def delete_policy(name: str, policies: dict) -> tuple[int, str, str]: - """This function deletes a metrics lifecycle policy""" - global rule_path, policies_data_file - - if name not in policies.keys(): - return 404, "error", "Policy not found" - try: - del policies[name] - sync_to_file(policies) - except BaseException as e: - return 500, "error", f"Failed to update policy. {e}" - return 204, "success", "Policy deleted successfully" - - -def load_policies() -> dict: - """This function loads metrics lifecycle policies - object into memory at runtime of the application""" - global rule_path, policies_data_file - policies = dict() - try: - with open(f"{rule_path}/{policies_data_file}", "r") as f: - policies = json.loads(f.read()) - except FileNotFoundError: - logger.debug("No metrics lifecycle policies configured yet") - except BaseException as e: - logger.error(f"Failed to load metrics lifecycle policies. {e}") - finally: - return policies - - -def validate_policy(schema_file, data) -> tuple[bool, int, str, str]: - """ - Validates the rule object provided by - the user against the required schema. - """ - schema_file = f"src/schemas/{schema_file}" - with open(schema_file) as f: - schema = json.load(f) - try: - validate(instance=data, schema=schema) - except exceptions.ValidationError as e: - return False, 400, "error", e.args[0] - return True, 200, "success", "Policy is valid" - - -def validate_duration(val) -> tuple[bool, int, str, str, int]: - """ - This function compares the value of the 'keep_for' - field with the retention time of the Prometheus server - """ - prom_storage_retention_seconds = parse(prom_storage_retention_human) - val_seconds = parse(val) - if val_seconds >= prom_storage_retention_seconds: - return False, 422, "error", f"Invalid duration: 'keep_for' must be less than Prometheus " \ - f"TSDB storage retention time, which is {prom_storage_retention_human}", 0 - return True, 200, "success", "Duration is valid", val_seconds - - -def validate_prom_admin_api() -> tuple[bool, int, str, str]: - prom_admin_api_status = prom_info( - prom_addr, "/flags")["data"]["web.enable-admin-api"] - if prom_admin_api_status == "false": - return False, 500, "error", "Metrics lifecycle policy API requires enabling Prometheus admin APIs. " \ - "For more information on configuration, check out this documentation " \ - "https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-admin-apis" - return True, 200, "success", "Prometheus admin API is enabled" diff --git a/src/models/policy.py b/src/models/policy.py deleted file mode 100644 index 8400917..0000000 --- a/src/models/policy.py +++ /dev/null @@ -1,41 +0,0 @@ -from pydantic import BaseModel, Extra -from typing import Optional - - -class MetricsLifecyclePolicyCreate(BaseModel, extra=Extra.allow): - name: str - match: str - keep_for: str - description: Optional[str] = None - _request_body_examples = { - "GoLang Metrics Lifecycle Policy": { - "description": "Time-series matching with regex will be kept for 7 days", - "value": { - "name": "Example Policy", - "match": "{__name__=~'go_.+'}", - "keep_for": "7d", - "description": "Time-series matching with regex will be kept for 7 days." - } - } - } - - -class MetricsLifecyclePolicyUpdate(BaseModel, extra=Extra.allow): - pattern: Optional[str] - keep_for: Optional[str] - description: Optional[str] - _request_body_examples = { - "Update retention time of series": { - "description": "Updates `keep_for` setting only", - "value": { - "keep_for": "10d" - } - }, - "Update 'match' and 'description' fields": { - "description": "Updates `match` and `description` fields of specific policy", - "value": { - "match": "{__name__='kube_pod_labels', job='kubernetes-service-endpoints'}", - "description": "This policy deletes only 'kube_pod_labels' series of job 'kubernetes-service-endpoints'" - } - } - } diff --git a/src/schemas/policies_create.json b/src/schemas/policies_create.json deleted file mode 100644 index 3e3b733..0000000 --- a/src/schemas/policies_create.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "match": { - "type": "string" - }, - "keep_for": { - "type": "string", - "pattern": "^((([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?|0)$" - }, - "description": { - "type": ["string", "null"] - } - }, - "required": ["name", "match", "keep_for"], - "additionalProperties": false, - "title": "Metrics Lifecycle Policy - Create" -} diff --git a/src/schemas/policies_update.json b/src/schemas/policies_update.json deleted file mode 100644 index a1f7dad..0000000 --- a/src/schemas/policies_update.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "match": { - "type": "string" - }, - "keep_for": { - "type": "string", - "pattern": "^((([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?|0)$" - }, - "description": { - "type": "string" - } - }, - "required": [], - "additionalProperties": false, - "not": { - "required": ["name"] - }, - "title": "Metrics Lifecycle Policy - Update" -} diff --git a/src/tasks/__init__.py b/src/tasks/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tasks/policies.py b/src/tasks/policies.py deleted file mode 100644 index d800655..0000000 --- a/src/tasks/policies.py +++ /dev/null @@ -1,63 +0,0 @@ -from src.core.policies import load_policies -from src.utils.arguments import arg_parser -from src.utils.log import logger -from pytimeparse2 import parse -import requests -import time - -prom_addr = arg_parser().get("prom.addr") - - -def delete_series(policy_name: str, policy: dict) -> None: - """ - This function calls two Prometheus endpoints: - * POST /api/v1/admin/tsdb/delete_series - * POST /api/v1/admin/tsdb/clean_tombstones - User-defined policies passed to this function - perform cleanup based on the specified policy settings. - """ - time_range = time.time() - parse(policy["keep_for"]) - start_time = time.time() - try: - r = requests.post( - f'{prom_addr}/api/v1/admin/tsdb/delete_series?match[]={policy["match"]}&end={time_range}') - except BaseException as e: - logger.error(e, extra={"policy_name": policy_name}) - else: - if r.status_code != 204: - logger.error(f"Failed to delete series, {r.json().get('error')}", extra={ - "status": r.status_code, "policy_name": policy_name}) - return - try: - r = requests.post( - f'{prom_addr}/api/v1/admin/tsdb/clean_tombstones') - except BaseException as e: - logger.error(e, extra={"policy_name": policy_name}) - return - else: - if r.status_code != 204: - logger.error(f"Failed to clean tombstones, {r.json().get('error')}", extra={ - "status": r.status_code, "policy_name": policy_name}) - return - exec_time = float("{:.2f}".format(time.time() - start_time)) - logger.debug("Task cleanup time-series has been successfully completed", - extra={"policy_name": policy_name, "exec_time": exec_time}) - return - - -def task_run_policies(): - """ - This function loops over user-defined metrics lifecycle - policies and executes the cleanup job one by one - """ - policies = load_policies() - if policies: - logger.debug( - f"Found {len(policies)} metrics lifecycle {'policies' if len(policies) > 1 else 'policy'}. " - f"Starting job to cleanup time-series.") - for p in policies: - logger.debug( - "Task cleanup time-series is in progress", extra={ - "policy_name": p, "match": policies[p]["match"], - "keep_for": policies[p]["keep_for"]}) - delete_series(policy_name=p, policy=policies[p]) diff --git a/src/utils/openapi.py b/src/utils/openapi.py index b75645b..411cafc 100644 --- a/src/utils/openapi.py +++ b/src/utils/openapi.py @@ -16,7 +16,7 @@ def openapi(app: FastAPI): "providing additional features and addressing its limitations. " "Running as a sidecar alongside the Prometheus server enables " "users to extend the capabilities of the API.", - version="0.3.0", + version="0.2.2", contact={ "name": "Hayk Davtyan", "url": "https://hayk96.github.io", diff --git a/src/utils/scheduler.py b/src/utils/scheduler.py deleted file mode 100644 index 8c2cbc0..0000000 --- a/src/utils/scheduler.py +++ /dev/null @@ -1,16 +0,0 @@ -from apscheduler.schedulers.background import BackgroundScheduler -from apscheduler.triggers.interval import IntervalTrigger -from src.tasks.policies import task_run_policies -import atexit - - -def schedule(): - scheduler = BackgroundScheduler() - scheduler.start() - scheduler.add_job( - func=task_run_policies, - trigger=IntervalTrigger(minutes=20), - name='Schedule task "cleanup time-series" every 20 minutes', - replace_existing=True - ) - atexit.register(lambda: scheduler.shutdown()) diff --git a/src/utils/settings.py b/src/utils/settings.py index 5300783..b8cdec8 100644 --- a/src/utils/settings.py +++ b/src/utils/settings.py @@ -62,16 +62,3 @@ def check_fs_permissions(prometheus_rules_dir) -> bool: logger.debug( "The application has the necessary permissions to access the rule files directory.") return True - - -def prom_info(prometheus_address, sub_path) -> dict: - """ - Returns various runtime, and configuration information properties - about the Prometheus server based on the sub_path parameter - """ - try: - r = requests.get(f"{prometheus_address}/api/v1/status{sub_path}") - except requests.exceptions.ConnectionError as e: - logger.error(e) - return {} - return r.json()