From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id AC0D6851E5 for ; Fri, 17 Dec 2021 09:10:39 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 9DE0128898 for ; Fri, 17 Dec 2021 09:10:09 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id A5D1328717 for ; Fri, 17 Dec 2021 09:10:03 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 7F9E445238 for ; Fri, 17 Dec 2021 09:10:03 +0100 (CET) From: Dominik Csapak To: pbs-devel@lists.proxmox.com Date: Fri, 17 Dec 2021 09:10:00 +0100 Message-Id: <20211217081000.1061796-10-d.csapak@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20211217081000.1061796-1-d.csapak@proxmox.com> References: <20211217081000.1061796-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.171 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [mod.rs, config.host, influxdbudp.rs, influxdbhttp.rs, update.host] Subject: [pbs-devel] [PATCH proxmox-backup v3 6/6] api: add metricserver endpoints X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 17 Dec 2021 08:10:39 -0000 but in contrast to pve, we split the api by type of the section config, since we cannot handle multiple types in the updater Signed-off-by: Dominik Csapak --- src/api2/config/metricserver/influxdbhttp.rs | 275 +++++++++++++++++++ src/api2/config/metricserver/influxdbudp.rs | 245 +++++++++++++++++ src/api2/config/metricserver/mod.rs | 16 ++ src/api2/config/mod.rs | 2 + 4 files changed, 538 insertions(+) create mode 100644 src/api2/config/metricserver/influxdbhttp.rs create mode 100644 src/api2/config/metricserver/influxdbudp.rs create mode 100644 src/api2/config/metricserver/mod.rs diff --git a/src/api2/config/metricserver/influxdbhttp.rs b/src/api2/config/metricserver/influxdbhttp.rs new file mode 100644 index 00000000..aea3f7d2 --- /dev/null +++ b/src/api2/config/metricserver/influxdbhttp.rs @@ -0,0 +1,275 @@ +use anyhow::{bail, Error}; +use serde_json::Value; +use serde::{Deserialize, Serialize}; +use hex::FromHex; + +use proxmox_router::{Router, RpcEnvironment, Permission}; +use proxmox_schema::api; + +use pbs_api_types::{ + InfluxDbHttp, InfluxDbHttpUpdater, + PROXMOX_CONFIG_DIGEST_SCHEMA, METRIC_SERVER_ID_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, +}; + +use pbs_config::metrics; + +#[api( + input: { + properties: {}, + }, + returns: { + description: "List of configured InfluxDB http metric servers.", + type: Array, + items: { type: InfluxDbHttp }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false), + }, +)] +/// List configured InfluxDB http metric servers. +pub fn list_influxdb_http_servers( + _param: Value, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result, Error> { + + let (config, digest) = metrics::config()?; + + let mut list: Vec = config.convert_to_typed_array("influxdb-http")?; + + // don't return token via api + for item in list.iter_mut() { + item.token = None; + } + + rpcenv["digest"] = hex::encode(&digest).into(); + + Ok(list) +} + +#[api( + protected: true, + input: { + properties: { + config: { + type: InfluxDbHttp, + flatten: true, + }, + }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Create a new InfluxDB http server configuration +pub fn create_influxdb_http_server(config: InfluxDbHttp) -> Result<(), Error> { + + let _lock = metrics::lock_config()?; + + let (mut metrics, _digest) = metrics::config()?; + + metrics.set_data(&config.name, "influxdb-http", &config)?; + + metrics::save_config(&metrics)?; + + Ok(()) +} + +#[api( + protected: true, + input: { + properties: { + name: { + schema: METRIC_SERVER_ID_SCHEMA, + }, + digest: { + optional: true, + schema: PROXMOX_CONFIG_DIGEST_SCHEMA, + }, + }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Remove a InfluxDB http server configuration +pub fn delete_influxdb_http_server( + name: String, + digest: Option, + _rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let _lock = metrics::lock_config()?; + + let (mut metrics, expected_digest) = metrics::config()?; + + if let Some(ref digest) = digest { + let digest = <[u8; 32]>::from_hex(digest)?; + crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; + } + + if metrics.sections.remove(&name).is_none() { + bail!("name '{}' does not exist.", name); + } + + metrics::save_config(&metrics)?; + + Ok(()) +} + +#[api( + input: { + properties: { + name: { + schema: METRIC_SERVER_ID_SCHEMA, + }, + }, + }, + returns: { type: InfluxDbHttp }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false), + }, +)] +/// Read the InfluxDB http server configuration +pub fn read_influxdb_http_server( + name: String, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result { + + let (metrics, digest) = metrics::config()?; + + let mut config: InfluxDbHttp = metrics.lookup("influxdb-http", &name)?; + + config.token = None; + + rpcenv["digest"] = hex::encode(&digest).into(); + + Ok(config) +} + +#[api()] +#[derive(Serialize, Deserialize)] +#[serde(rename_all="kebab-case")] +/// Deletable property name +pub enum DeletableProperty { + /// Delete the enable property. + Enable, + /// Delete the port property. + Port, + /// Delete the https property. + Https, + /// Delete the token property. + Token, + /// Delete the bucket property. + Bucket, + /// Delete the organization property. + Organization, + /// Delete the max_body_size property. + MaxBodySize, + /// Delete the verify_tls property. + VerifyTls, + /// Delete the comment property. + Comment, +} + +#[api( + protected: true, + input: { + properties: { + name: { + schema: METRIC_SERVER_ID_SCHEMA, + }, + update: { + type: InfluxDbHttpUpdater, + flatten: true, + }, + delete: { + description: "List of properties to delete.", + type: Array, + optional: true, + items: { + type: DeletableProperty, + } + }, + digest: { + optional: true, + schema: PROXMOX_CONFIG_DIGEST_SCHEMA, + }, + }, + }, + returns: { type: InfluxDbHttp }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Update an InfluxDB http server configuration +pub fn update_influxdb_http_server( + name: String, + update: InfluxDbHttpUpdater, + delete: Option>, + digest: Option, + _rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let _lock = metrics::lock_config()?; + + let (mut metrics, expected_digest) = metrics::config()?; + + if let Some(ref digest) = digest { + let digest = <[u8; 32]>::from_hex(digest)?; + crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; + } + + let mut config: InfluxDbHttp = metrics.lookup("influxdb-http", &name)?; + + if let Some(delete) = delete { + for delete_prop in delete { + match delete_prop { + DeletableProperty::Enable => { config.enable = None; }, + DeletableProperty::Port => { config.port = None; }, + DeletableProperty::Https => { config.https = None; }, + DeletableProperty::Token => { config.token = None; }, + DeletableProperty::Bucket => { config.bucket = None; }, + DeletableProperty::Organization => { config.organization = None; }, + DeletableProperty::MaxBodySize => { config.max_body_size = None; }, + DeletableProperty::VerifyTls => { config.verify_tls = None; }, + DeletableProperty::Comment => { config.comment = None; }, + } + } + } + + if let Some(comment) = update.comment { + let comment = comment.trim().to_string(); + if comment.is_empty() { + config.comment = None; + } else { + config.comment = Some(comment); + } + } + + if let Some(host) = update.host { config.host = host; } + + if update.enable.is_some() { config.enable = update.enable; } + if update.port.is_some() { config.port = update.port; } + if update.https.is_some() { config.https = update.https; } + if update.token.is_some() { config.token = update.token; } + if update.bucket.is_some() { config.bucket = update.bucket; } + if update.organization.is_some() { config.organization = update.organization; } + if update.max_body_size.is_some() { config.max_body_size = update.max_body_size; } + if update.verify_tls.is_some() { config.verify_tls = update.verify_tls; } + + metrics.set_data(&name, "influxdb-http", &config)?; + + metrics::save_config(&metrics)?; + + Ok(()) +} + +const ITEM_ROUTER: Router = Router::new() + .get(&API_METHOD_READ_INFLUXDB_HTTP_SERVER) + .put(&API_METHOD_UPDATE_INFLUXDB_HTTP_SERVER) + .delete(&API_METHOD_DELETE_INFLUXDB_HTTP_SERVER); + +pub const ROUTER: Router = Router::new() + .get(&API_METHOD_LIST_INFLUXDB_HTTP_SERVERS) + .post(&API_METHOD_CREATE_INFLUXDB_HTTP_SERVER) + .match_all("name", &ITEM_ROUTER); diff --git a/src/api2/config/metricserver/influxdbudp.rs b/src/api2/config/metricserver/influxdbudp.rs new file mode 100644 index 00000000..2cb91422 --- /dev/null +++ b/src/api2/config/metricserver/influxdbudp.rs @@ -0,0 +1,245 @@ +use anyhow::{bail, Error}; +use serde_json::Value; +use serde::{Deserialize, Serialize}; +use hex::FromHex; + +use proxmox_router::{Router, RpcEnvironment, Permission}; +use proxmox_schema::api; + +use pbs_api_types::{ + InfluxDbUdp, InfluxDbUdpUpdater, + PROXMOX_CONFIG_DIGEST_SCHEMA, METRIC_SERVER_ID_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, +}; + +use pbs_config::metrics; + +#[api( + input: { + properties: {}, + }, + returns: { + description: "List of configured InfluxDB udp metric servers.", + type: Array, + items: { type: InfluxDbUdp }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false), + }, +)] +/// List configured InfluxDB udp metric servers. +pub fn list_influxdb_udp_servers( + _param: Value, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result, Error> { + + let (config, digest) = metrics::config()?; + + let list = config.convert_to_typed_array("influxdb-udp")?; + + rpcenv["digest"] = hex::encode(&digest).into(); + + Ok(list) +} + +#[api( + protected: true, + input: { + properties: { + config: { + type: InfluxDbUdp, + flatten: true, + }, + }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Create a new InfluxDB udp server configuration +pub fn create_influxdb_udp_server(config: InfluxDbUdp) -> Result<(), Error> { + + let _lock = metrics::lock_config()?; + + let (mut metrics, _digest) = metrics::config()?; + + metrics.set_data(&config.name, "influxdb-udp", &config)?; + + metrics::save_config(&metrics)?; + + Ok(()) +} + +#[api( + protected: true, + input: { + properties: { + name: { + schema: METRIC_SERVER_ID_SCHEMA, + }, + digest: { + optional: true, + schema: PROXMOX_CONFIG_DIGEST_SCHEMA, + }, + }, + }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Remove a InfluxDB udp server configuration +pub fn delete_influxdb_udp_server( + name: String, + digest: Option, + _rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let _lock = metrics::lock_config()?; + + let (mut metrics, expected_digest) = metrics::config()?; + + if let Some(ref digest) = digest { + let digest = <[u8; 32]>::from_hex(digest)?; + crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; + } + + if metrics.sections.remove(&name).is_none() { + bail!("name '{}' does not exist.", name); + } + + metrics::save_config(&metrics)?; + + Ok(()) +} + +#[api( + input: { + properties: { + name: { + schema: METRIC_SERVER_ID_SCHEMA, + }, + }, + }, + returns: { type: InfluxDbUdp }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false), + }, +)] +/// Read the InfluxDB udp server configuration +pub fn read_influxdb_udp_server( + name: String, + mut rpcenv: &mut dyn RpcEnvironment, +) -> Result { + + let (metrics, digest) = metrics::config()?; + + let config = metrics.lookup("influxdb-udp", &name)?; + + rpcenv["digest"] = hex::encode(&digest).into(); + + Ok(config) +} + +#[api()] +#[derive(Serialize, Deserialize)] +#[serde(rename_all="kebab-case")] +/// Deletable property name +pub enum DeletableProperty { + /// Delete the enable property. + Enable, + /// Delete the mtu property. + Mtu, + /// Delete the comment property. + Comment, +} + +#[api( + protected: true, + input: { + properties: { + name: { + schema: METRIC_SERVER_ID_SCHEMA, + }, + update: { + type: InfluxDbUdpUpdater, + flatten: true, + }, + delete: { + description: "List of properties to delete.", + type: Array, + optional: true, + items: { + type: DeletableProperty, + } + }, + digest: { + optional: true, + schema: PROXMOX_CONFIG_DIGEST_SCHEMA, + }, + }, + }, + returns: { type: InfluxDbUdp }, + access: { + permission: &Permission::Privilege(&[], PRIV_SYS_MODIFY, false), + }, +)] +/// Update an InfluxDB udp server configuration +pub fn update_influxdb_udp_server( + name: String, + update: InfluxDbUdpUpdater, + delete: Option>, + digest: Option, + _rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + + let _lock = metrics::lock_config()?; + + let (mut metrics, expected_digest) = metrics::config()?; + + if let Some(ref digest) = digest { + let digest = <[u8; 32]>::from_hex(digest)?; + crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; + } + + let mut config: InfluxDbUdp = metrics.lookup("influxdb-udp", &name)?; + + if let Some(delete) = delete { + for delete_prop in delete { + match delete_prop { + DeletableProperty::Enable => { config.enable = None; }, + DeletableProperty::Mtu => { config.mtu = None; }, + DeletableProperty::Comment => { config.comment = None; }, + } + } + } + + if let Some(comment) = update.comment { + let comment = comment.trim().to_string(); + if comment.is_empty() { + config.comment = None; + } else { + config.comment = Some(comment); + } + } + + if let Some(host) = update.host { config.host = host; } + if let Some(port) = update.port { config.port = port; } + + if update.enable.is_some() { config.enable = update.enable; } + if update.mtu.is_some() { config.mtu = update.mtu; } + + metrics.set_data(&name, "influxdb-udp", &config)?; + + metrics::save_config(&metrics)?; + + Ok(()) +} + +const ITEM_ROUTER: Router = Router::new() + .get(&API_METHOD_READ_INFLUXDB_UDP_SERVER) + .put(&API_METHOD_UPDATE_INFLUXDB_UDP_SERVER) + .delete(&API_METHOD_DELETE_INFLUXDB_UDP_SERVER); + +pub const ROUTER: Router = Router::new() + .get(&API_METHOD_LIST_INFLUXDB_UDP_SERVERS) + .post(&API_METHOD_CREATE_INFLUXDB_UDP_SERVER) + .match_all("name", &ITEM_ROUTER); diff --git a/src/api2/config/metricserver/mod.rs b/src/api2/config/metricserver/mod.rs new file mode 100644 index 00000000..cbce34f7 --- /dev/null +++ b/src/api2/config/metricserver/mod.rs @@ -0,0 +1,16 @@ +use proxmox_router::{Router, SubdirMap}; +use proxmox_router::list_subdirs_api_method; +use proxmox_sys::sortable; + +pub mod influxdbudp; +pub mod influxdbhttp; + +#[sortable] +const SUBDIRS: SubdirMap = &sorted!([ + ("influxdb-http", &influxdbhttp::ROUTER), + ("influxdb-udp", &influxdbudp::ROUTER), +]); + +pub const ROUTER: Router = Router::new() + .get(&list_subdirs_api_method!(SUBDIRS)) + .subdirs(SUBDIRS); diff --git a/src/api2/config/mod.rs b/src/api2/config/mod.rs index c256ba64..5de1c28f 100644 --- a/src/api2/config/mod.rs +++ b/src/api2/config/mod.rs @@ -12,6 +12,7 @@ pub mod verify; pub mod drive; pub mod changer; pub mod media_pool; +pub mod metricserver; pub mod tape_encryption_keys; pub mod tape_backup_job; pub mod traffic_control; @@ -23,6 +24,7 @@ const SUBDIRS: SubdirMap = &[ ("datastore", &datastore::ROUTER), ("drive", &drive::ROUTER), ("media-pool", &media_pool::ROUTER), + ("metricserver", &metricserver::ROUTER), ("remote", &remote::ROUTER), ("sync", &sync::ROUTER), ("tape-backup-job", &tape_backup_job::ROUTER), -- 2.30.2