diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..82abde1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +/target +Cargo.lock +*.sock +.env diff --git a/.env.example b/.env.example index 721d0f6..59f2dbc 100644 --- a/.env.example +++ b/.env.example @@ -1,15 +1,18 @@ # Template .env for API grabber # PostgreSQL connection [DEPRECATED] +# ------------------------------- DB_HOST = "ip.addr.postgresql.server" DB_USER = "db_user" DB_PASSWORD = "db_user_password" DB_DBNAME = "db_name"1 # Prometheus-Exporter info +# ------------------------------- EXPORTER_URL = "http(s)://ip.ip.ip.ip:port" -# VINTEO Jitter puller (needed to init Jitter native grab) +# VINTEO Jitter puller (needed to init Jitter native grab) +# ------------------------------- VINTEO_URL_BASE = "http(s)://ip.ip.ip.ip:port" VINTEO_ENDPOINT_CONFERENCES = "/api/v1/to/something" VINTEO_ENDPOINT_PARTICIPANTS = "/api/v1/to/something" @@ -20,11 +23,19 @@ VINTEO_API_KEY = "6fe8b0db-62b4-4065-9c1e-441ec4228341.9acec20bd17d7178f332896f8 STATUS_SYSTEM_URL = "http://192.168.2.39:9999/api/input" # eNODE.Monitoring configuration +# ------------------------------- +# eNODE.Monitoring server IP ENODE_MONITORING_IP = "ip.ip.ip.ip" +# eNODE.Monitoring credentials ENODE_MONITORING_LOGIN = "admin_user_enode_monitoring" # admin user is required ENODE_MONITORING_PASSWORD = "admin_password_enode_monitoring" # # admin password is required +# List of target devices ENODE_TARGET_DEVICES = "18, 19" +# to work with unlimit API-Token +ENODE_API_TOKEN = "sssswwwwaaaaffff" +# OPTIONAL SETTINGS +# ------------------------------- # IM configuration for max level of logging info # for example DEBUG, INFO, WARN, ERROR, TRACE IM_LOG_INFO = "INFO" diff --git a/Dockerfile b/Dockerfile index 5215521..48efd4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,11 @@ RUN apt update && apt install -y musl-tools RUN rustup target add x86_64-unknown-linux-musl COPY . . -RUN cargo test + +ENV CARGO_HTTP_DEBUG=true +ENV CARGO_HTTP_TIMEOUT=100 + +RUN cargo test --verbose RUN cargo build --release --target=x86_64-unknown-linux-musl FROM alpine:latest diff --git a/README.md b/README.md index 71c25e4..e9d20e9 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,173 @@ # Интеграционный модуль для проекта "Буревестник ВКС" ## Описание -`integr_mod` - Rust-пакет, предоставляющий функционал интеграционного модуля в проекте "Буревестник ВКС", состоящий из бинарных крейтов для: +`Интеграционный модуль (ИМ)` - Rust-пакет, предоставляющий функционал интеграционного модуля в проекте "Буревестник ВКС", состоящий из бинарных крейтов для: - получение данных через API ВКС - поддержку хранения, валидации и актуализации собственных конфигураций -- предобработку полученных данных и ~~отправку `Системе Мониторинга`~~ сохранение в БД +- предобработку полученных данных и сохранение в БД +- интеграции с `еНОД.Мониторинг` + +## Специфика работы + +На даннный момент предусмотрено два режима работы: + +1) **Нативный** - режим работы, производящий прямой опрос сервиса видео-конференц связи `Vinteo` и соотвествующий процесс `ETL` + +2) **Статичный** - режим работы *"посредник"*, когда все метрические данные ВКС `Vinteo` получаются через `REST-Full API` средства `еНОД.Мониторинг` + +3) **Системный** - аналогичный **статичному** режиму, но метрические данные (заведомо обогащенные нулевым статусом) отправялются не напрямую в модуль `Prometehus exporter`, а в `Статусную модель` + +4) **Vinteo** - особый режим работы, предполагающий сбор определенного набора метрик напрямую с ВКС `Vinteo` механизмом многоэтапного `API-запроса` + +> **Примечание** +По стандарту `ИМ` работает в **НАТИВНОМ** режиме и ожидает конфигурации в формате `.json`, однако приоритетным считается **СТАТИЧНЫЙ** режим. Подробная информация о настройке в пункте `Руководство` ## Руководство -1. Заполнить .env файл или установить переменные окружения в соотвествии с примером в `.env.example` файле -``` toml -# Template .env for API grabber +В данном разделе опсиан алгоритм настройки, сборки и запуска программного модуля `ИМ` -# Prometheus-Exporter info -EXPORTER_URL = "http(s)://ip.ip.ip.ip:port" +### Преднастройка -# eNODE.Monitoring configuration -ENODE_MONITORING_IP = "ip.ip.ip.ip" -# admin user is required -ENODE_MONITORING_LOGIN = "admin_user_enode_monitoring" -# admin password is required -ENODE_MONITORING_PASSWORD = "admin_password_enode_monitoring" +1. Выбор режима работы модуля, который скорректирует принцип настройки: + +| Режим работы | .env | config-api.json | $STATUS_SYSTEM_URL | $EXPORTER_URL | +|---|---|---|---|---| +| Нативный | ❌ | ✅ | ❌ | ❌ | +| Статичный | ✅ | ❌ | ❌❌❌ | ✅ | +| Системный | ✅ | ❌ | ✅ | ❌ | +, где: + +✅ -- следует настроить (предпринять) + +❌ -- игнорируется системой, не стоит настраивать + +❌❌❌ -- **НЕЛЬЗЯ** настраивать (предпринимать), возможны ошибки в работе + +> Режим работы `Vinteo` *не описан* в таблице **намеренно** + +### Настройка режима работы "Нативный" + +Для настройки данного режима необходимо расположить в **активной** директории конфигурационный `config_api.json` файл: + +``` json +{ + "config": [ + { + "id":"zvks", + "login" : "", + "pass" : "", + "api_key" : "6fe8b0db-62b4-4065-9c1e-441ec4228341.9acec20bd17d7178f332896f8c006452877a22b8627d089105ed39c5baef9711", + "period" : "", + "timeout" : "5", + "metrics" : [ + { + "name": "conferences", + "url": "https://demo.vcs.vinteo.dev/api/v1/conferences", + "measure": [ + { "id":"number", "type": "text", "addr": "data.conferences[].number" }, + { "id":"total", "type": "integer", "addr": "data.total" }, + { "id":"participants_total", "type": "integer", "addr": "data.conferences[].participants.total" }, + { "id":"parts_total_in_each", "type": "integer", "addr": "data.conferences[description].participants.total" }, + { "id":"participants_online", "type": "integer", "addr": "data.conferences[].participants.online" } + ] + }, + { + "name": "abonents", + "url": "https://demo.vcs.vinteo.dev/api/v1/accounts", + "measure": [ + { "id":"total", "type": "integer", "addr": "data.total" } + ] + } + ] + } + ] +} ``` + +> **Примечание** +Название конфигурационного файла должно быть как в примере - `config_api.json` + + +### Настройка режима работы "Статичный" + +Для настройки данного режима необходимо пополнить данными о сервере в `.env` файле по примеру: + +``` toml +... +EXPORTER_URL = "http(s)://ip.ip.ip.ip:port" # <--- экспорт данных (обязательно) +# eNODE.Monitoring configuration +ENODE_MONITORING_IP = "ip.ip.ip.ip" +# admin user is required +ENODE_MONITORING_LOGIN = "admin_user_enode_monitoring"# ---> получение данных +# admin password is required +ENODE_MONITORING_PASSWORD = "admin_password_enode_monitoring" +... +``` + +### Настройка режима работы "Системный" + +Для настройки данного режима необходимо пополнить данными о сервере в `.env` файле по примеру: + +``` toml +... +STATUS_SYSTEM_URL = "http(s)://{ip}:{port}/api/input"# <--- экспорт данных +# eNODE.Monitoring configuration +ENODE_MONITORING_IP = "{ip}.{ip}.{ip}.{ip}" +# admin user is required +ENODE_MONITORING_LOGIN = "admin_user_enode_monitoring"# ---> получение данных +# admin password is required +ENODE_MONITORING_PASSWORD = "admin_password_enode_monitoring" +... +``` + +### Настройка режима работы "Vinteo" + +Для работы в данном режиме необходимо установить переменные окружения в соотвествии со списком ниже + +``` toml +... +VINTEO_URL_BASE = "https://demo.vcs.vinteo.dev" +VINTEO_ENDPOINT_CONFERENCES = "/api/v1/conferences" +VINTEO_ENDPOINT_PARTICIPANTS = "/api/v1/participants/" +VINTEO_API_KEY = "00000000000111111111.aaaaaaaaaaaaaaabbbbbbbbbbbbb" +... +``` + +### Настройка экспорта полученных и обработанных данных + +Настройка *точки выхода* для полученных и обработанных метрик определеяется установленными в переменных окружения параметрами, варианта два: + +1) **Экспорт в статусную модель** в рамках механизма сквозного прохода данных в проекте `Буревестник ВКС` + +``` toml +... +STATUS_SYSTEM_URL = "{BASE_URL}/{ROUTE}" +... +``` + +2) **Экспорт в экспортер или иной потребитель данных** + +``` toml +... +EXPORTER_URL = "{BASE_URL}/{ROUTE}" +... +``` +> ### **ОЧЕНЬ ВАЖНОЕ ПРИМЕЧАНИЕ** +> --- +> Одновременное использование `$STATUS_SYSTEM_URL` и `$EXPORTER_URL` **НЕДОПУСТИМО** !! Вариант со ссылкой **на статусную модель** является _по стандарту_ **БОЛЕЕ ПРИОРИТЕТНЫМ**, второй затрется, использовать необходимо только один + 2. Произвести сборку проекта командой : ``` bash cargo build --release ``` 3. Запустить -> Debug версия +> **Debug** версия ``` bash cargo run --bin api-grub ``` или -> Release версия +> **Release** версия ``` bash cargo run --release --bin api-grub ``` @@ -42,6 +176,6 @@ cargo run --release --bin api-grub | Крейт (подмодуль) | Прогресс | |---|---| |`api-grub` | ✅✅✅✅✅✅✅✅✅🛠️ | -|`config-delivery [migrated]` | ❌❌❌❌❌❌❌❌❌❌ | +|`config-delivery` [migrated] | ❌❌❌❌❌❌❌❌❌❌ | |`integrs-structs` | ✅✅✅✅✅✅✅✅✅✅ | |`preproc` [temp-deprecated] | ❌❌❌❌❌❌❌❌❌❌ | (разработка временно остановлена) diff --git a/crates/api-grub/src/jitter.rs b/crates/api-grub/src/jitter.rs index ac76d92..249388d 100644 --- a/crates/api-grub/src/jitter.rs +++ b/crates/api-grub/src/jitter.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, HashSet}, future::Future}; +use std::collections::{HashMap, HashSet}; use integr_structs::api::v3::{PrometheusMetricsExtended, MetricOutputExtended}; use std::sync::Arc; use reqwest::Client; diff --git a/crates/api-grub/src/logger.rs b/crates/api-grub/src/logger.rs index da628c7..d9320fc 100644 --- a/crates/api-grub/src/logger.rs +++ b/crates/api-grub/src/logger.rs @@ -1,6 +1,4 @@ use std::str::FromStr; - -use chrono::Local; use anyhow::Result; use tracing::info; @@ -21,20 +19,6 @@ use tracing::info; /// *depends on* : - /// pub async fn setup_logger() -> Result<()> { - // Builder::new() - // .format(move |buf, record| { - // writeln!( - // buf, - // "|{}| {} [{}] - {}", - // "api-grubber", - // Local::now().format("%d-%m-%Y %H:%M:%S"), - // record.level(), - // record.args(), - // ) - // }) - // .filter(None, LevelFilter::Info) - // .target(env_logger::Target::Stdout) - // .init(); let log_level = std::env::var("IM_LOG_INFO").unwrap_or_else(|_| String::from("INFO")); tracing_subscriber::fmt() diff --git a/crates/api-grub/src/monitoring.rs b/crates/api-grub/src/monitoring.rs index 83fa6d1..e0ad6db 100644 --- a/crates/api-grub/src/monitoring.rs +++ b/crates/api-grub/src/monitoring.rs @@ -79,7 +79,18 @@ pub trait LazyUnzipInstance { impl LazyUnzipInstance for &[MetricInstance] { fn lazy_unzip(&self) -> HashMap { - self.iter().map(|obj| (obj.dola_id.to_string(), MetricMeta::new(&obj.name, &obj.desc, &obj.device, &obj.source))).collect() + self.iter().map( + |obj| + ( + obj.dola_id.to_string(), + MetricMeta::new( + &obj.name, + &obj.desc, + &obj.device, + &obj.source + ) + ) + ).collect() } } @@ -183,6 +194,7 @@ pub struct MonitoringImporter { login : String, password : String, access_token : String, + api_token : String, ts : String, timeout : usize, } @@ -205,6 +217,7 @@ impl MonitoringImporter { login : env::var("ENODE_MONITORING_LOGIN").unwrap_or_else(|_| String::new()), password : env::var("ENODE_MONITORING_PASSWORD").unwrap_or_else(|_| String::new()), access_token : String::new(), + api_token : env::var("ENODE_API_TOKEN").unwrap_or_else(|_| String::new()), ts : String::new(), timeout : std::env::var("IM_CONNECTION_TIMEOUT").unwrap_or_else(|_| "10".to_string()).parse().unwrap_or_else(|_| 10) } @@ -213,7 +226,13 @@ impl MonitoringImporter { /// and can be used to pull and push info to and from CM /// async fn is_valid(&self) -> bool { - !self.ip.is_empty() && !self.login.is_empty() && !self.password.is_empty() + !self.ip.is_empty() && ((!self.login.is_empty() && !self.password.is_empty() ) || !self.api_token.is_empty()) + } + /// Function that checks is current `MonitoringImporter` valid + /// and can be used to pull and push info to and from CM + /// + async fn is_minimal(&self) -> bool { + (self.login.is_empty() || self.password.is_empty()) && !self.api_token.is_empty() } /// A setter of `timestamp` /// @@ -235,38 +254,43 @@ impl MonitoringImporter { #[tracing::instrument(name = "cm_fn_session_start", skip_all)] pub async fn start_session(&mut self) -> anyhow::Result<()> { if !self.is_valid().await { + if self.is_minimal().await { + return Err(Error::msg(format!("Given API-Token is no more actual now ({})", &self.access_token))); + } return Err(Error::msg("Invalid eNODE-Monitoring configuration")); } - let client = Client::new(); - let url = format!("http://{}/e-data-front/auth/login", self.ip); - let fortoken = ForTokenCredentials::new(&self.login, &self.password); - let mut delay = 1; + if !self.api_token.is_empty() { + std::mem::swap(&mut self.access_token, &mut self.api_token); + info!("API-Token that was in the ENODE configuration was set as access-token"); + } else { + let client = Client::new(); + let url = format!("http://{}/e-data-front/auth/login", self.ip); + let fortoken = ForTokenCredentials::new(&self.login, &self.password); + let mut delay = 1; - loop { - let client = client - .post(&url) - .timeout(tokio::time::Duration::from_secs(self.timeout as u64)) - .header("Content-Type", "application/json") - .json(&fortoken); - // let resp = client.send().await?; - if let Ok(resp) = client.send().await { - // let auth = resp.json::().await?; - - match resp.json::().await { - Ok(auth) => { - self.set_ts(&fortoken.ts).await; - self.access_token = auth.access_token.to_owned(); - tracing::trace!("Access key was changed"); - break; - }, - Err(er) => error!("Error with extracting access-key from CM response due to {}", er), + loop { + let client = client + .post(&url) + .timeout(tokio::time::Duration::from_secs(self.timeout as u64)) + .header("Content-Type", "application/json") + .json(&fortoken); + if let Ok(resp) = client.send().await { + match resp.json::().await { + Ok(auth) => { + self.set_ts(&fortoken.ts).await; + self.access_token = auth.access_token.to_owned(); + tracing::trace!("Access key was changed"); + break; + }, + Err(er) => error!("Error with extracting access-key from CM response due to {}", er), + } } + error!("Error while trying to create a new session, waiting {} secs and retrying ...", delay); + tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await; + delay = delay * 2; } - error!("Error while trying to create a new session, waiting {} secs and retrying ...", delay); - tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await; - delay = delay * 2; + info!("Started a new CM session"); } - info!("Started a new CM session"); Ok(()) }