diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7f44a75 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.9-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 8000 + +CMD ["python", "main.py"] diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..4b244ad --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,31 @@ +#vrsion: '3.8' + +services: + owen-exporter: + build: . + container_name: owen-exporter + restart: unless-stopped + environment: + - LOGIN=l_salnikov@internet.ru + - PASSWORD=}v]CT]Q"_g7>4H% + ports: + - "8000:8000" + networks: + - monitoring-net + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + restart: unless-stopped + ports: + - "9090:9090" + volumes: + - ./prometheus:/etc/prometheus + networks: + - monitoring-net + depends_on: + - owen-exporter + +networks: + monitoring-net: + driver: bridge diff --git a/main.py b/main.py new file mode 100755 index 0000000..a7dc95a --- /dev/null +++ b/main.py @@ -0,0 +1,128 @@ +import json +import time +import os +import requests +import logging +from prometheus_client import start_http_server, Gauge +from unidecode import unidecode +from dotenv import load_dotenv + +# Настройка логгирования +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.StreamHandler() + ] +) +logger = logging.getLogger(__name__) + +load_dotenv() + +# Устанавливаем соответствие кириллицы латинице +TRANS_TABLE = str.maketrans({ + "у": "u", "с": "s", "т": "t", "а": "a", "н": "n", "о": "o", "в": "v", "л": "l", + "е": "e", "ч": "ch", "а": "a", "с": "s", "т": "t", "ь": "", "п": "p", "ч": "ch", + "-": "_", ".": "_", "'": "", "`": "", '"': "", "h": "h", "h": "h" # Оставляем 'h' как есть +}) + +def sanitize_metric_name(name): + # Удаляем все недопустимые символы + name = unidecode(name) # Транслитерация сначала + name = name.lower() # Приводим к нижнему регистру + name = name.translate(TRANS_TABLE) # Применяем таблицу замен + + # Дополнительная очистка от недопустимых символов + valid_chars = [] + for c in name: + if c.isalpha() or c.isdigit() or c == '_': + valid_chars.append(c) + elif c in ['-', '.', ' ']: + valid_chars.append('_') + + name = ''.join(valid_chars) + + # Удаляем повторяющиеся подчеркивания + name = '_'.join(filter(None, name.split('_'))) + + # Добавляем префикс, если имя начинается с цифры + if name and name[0].isdigit(): + name = f"metric_{name}" + + # Проверяем, что имя не пустое + if not name: + name = 'unnamed_metric' + + return name + +def get_token(): + login = os.getenv("LOGIN") + password = os.getenv("PASSWORD") + + if not login or not password: + logger.error("Логин или пароль не заданы в .env") + raise ValueError("Логин или пароль не заданы в .env") + + data = {'login': login, 'password': password} + headers = { + "Content-Type": "application/json", + "Accept": "application/json" + } + + try: + req = requests.post("https://api.owencloud.ru/v1/auth/open/", json=data, headers=headers) + req.raise_for_status() + return "Bearer " + req.json()["token"] + except requests.exceptions.RequestException as e: + logger.error(f"Ошибка авторизации: {e}") + return None + +def load_json_data(): + token = get_token() + if not token: + logger.error("Не удалось получить токен. Завершаем работу.") + return [] + + device_id = "459205" + headers = {'Authorization': token} + data = json.dumps({'filter': ''}) + + try: + req = requests.post(f"https://api.owencloud.ru/v1/device/{device_id}", data=data, headers=headers) + req.raise_for_status() + json_response = req.json() + return json_response.get("parameters", []) + except requests.exceptions.RequestException as e: + logger.error(f"Ошибка запроса устройства: {e}") + return [] + +metrics = {} + +def main(): + logger.info("Запуск сервера Prometheus на порту 8000") + start_http_server(8000) + logger.info("Сервер запущен") + + while True: + parameters = load_json_data() + + if parameters: + for param in parameters: + try: + metric_name = sanitize_metric_name(param["name"]) + value = float(param["value"]) + + if metric_name not in metrics: + metrics[metric_name] = Gauge(metric_name, "Описание метрики") + logger.info(f"Создана новая метрика: {metric_name}") + + metrics[metric_name].set(value) + logger.debug(f"Метрика {metric_name} обновлена: {value}") + + except Exception as e: + logger.error(f"Ошибка обработки параметра {param}: {e}") + + time.sleep(60) + +if __name__ == "__main__": + main() diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml new file mode 100644 index 0000000..c7f643f --- /dev/null +++ b/prometheus/prometheus.yml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'owen-exporter' + static_configs: + - targets: ['owen-exporter:8000'] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a8e03e5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +prometheus_client +requests +unidecode +python-dotenv