From 20d0c76e7a5d61892edaf1e7969cbd8d86fea8a0 Mon Sep 17 00:00:00 2001 From: Vincent Stuyck Date: Sat, 20 Dec 2025 16:37:48 +0100 Subject: [PATCH] test and troubleshoot hostconfig --- src/api/folders.rs | 37 +++++++++++++++++++------- src/api/hosts.rs | 28 +++++++++++++++++--- src/main.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/src/api/folders.rs b/src/api/folders.rs index c9a2c64..d378b0a 100644 --- a/src/api/folders.rs +++ b/src/api/folders.rs @@ -3,6 +3,7 @@ use std::fmt::Display; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use serde_json::Value; use crate::api::rules::contactgroups::HostContactGroups; use crate::api::rules::snmp::SnmpCommunity; @@ -39,15 +40,33 @@ pub struct FolderAttributes { /// this attribute is not optional when queried from the api. /// It cannot be filled in when creating a new folder. pub meta_data: Option, - #[serde(flatten, skip_serializing_if = "HashMap::is_empty")] - /// Additional attributes that can be used to categorize hosts. every tag is part of a tag_group - /// NOTE that checkmk always returns taggroups prefixed with "tag_" + // Additional attributes that can be used to categorize hosts. every tag is part of a tag_group + // NOTE that checkmk always returns taggroups prefixed with "tag_" + #[serde(default, flatten, skip_serializing_if = "HashMap::is_empty")] pub tags: HashMap, - // TODO: to be implemented - // #[serde(flatten, default)] - // pub networkscan: NetworkScan, - // #[serde(flatten, default)] - // pub management: ManagementProtocol, + + + // these fields are used to prevent tags from taking them up + #[serde(default)] + management_snmp_community: Option, + #[serde(default, skip_serializing)] + management_address: Value, + #[serde(default, skip_serializing)] + management_protocol: Value, + #[serde(default, skip_serializing)] + management_ipmi_credentials: Value, + #[serde(default, skip_serializing)] + network_scan: Value, + #[serde(default, skip_serializing)] + network_scan_result: Value, + #[serde(default, skip_serializing)] + waiting_for_discovery: Value, + #[serde(default, skip_serializing)] + inventory_failed: Value, + #[serde(default, skip_serializing)] + locked_attributes: Value, + #[serde(default, skip_serializing)] + locked_by: Value, } impl FolderAttributes { @@ -93,7 +112,7 @@ impl FolderAttributes { pub fn add_tag(&mut self, taggroup: impl Display, tagvalue: String) { self.tags - .insert(format!("tag_{taggroup}"), tagvalue.to_string()); + .insert(format!("tag_{taggroup}"), tagvalue); } pub fn with_tag(mut self, taggroup: impl Display, tagvalue: String) -> Self { self.add_tag(taggroup, tagvalue); diff --git a/src/api/hosts.rs b/src/api/hosts.rs index 458f3ff..48ff3ea 100644 --- a/src/api/hosts.rs +++ b/src/api/hosts.rs @@ -1,3 +1,5 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + use serde::{Deserialize, Serialize}; use crate::api::folders::FolderAttributes; @@ -16,13 +18,31 @@ impl Client { #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct HostConfig { pub folder: String, - pub attributes: FolderAttributes, - pub effective_attributes: FolderAttributes, + pub attributes: HostAttributes, + pub effective_attributes: HostAttributes, pub is_cluster: bool, pub is_offline: bool, pub cluster_nodes: Option>, } + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct HostAttributes { + #[serde(default)] + alias: Option, + #[serde(default)] + ipaddress: Option, + #[serde(default)] + ipv6address: Option, + #[serde(default)] + additional_ipv4addresses: Vec, + #[serde(default)] + additional_ipv6addresses: Vec, + + #[serde(flatten, default)] + folder_attributes: FolderAttributes +} + impl DomainExtension for HostConfig { const DOMAIN_TYPE: super::DomainType = super::DomainType::HostConfig; @@ -35,7 +55,7 @@ impl DomainExtension for HostConfig { #[derive(Serialize)] pub struct HostCreationRequest { - pub hostname: String, + pub host_name: String, pub folder: String, pub attributes: FolderAttributes, } @@ -60,7 +80,7 @@ pub struct HostUpdateRequest { impl HostCreationRequest { pub fn new(hostname: String, folder: String, attributes: FolderAttributes) -> Self { HostCreationRequest { - hostname, + host_name: hostname, folder, attributes, } diff --git a/src/main.rs b/src/main.rs index 89bfaca..28e1422 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ async fn main() -> Result<()> { .unwrap(); test_folders(client.clone()).await?; + test_hosts(client.clone()).await?; Ok(()) } @@ -48,11 +49,13 @@ async fn test_folders(client: Client) -> Result<()> { .create(&creq, &()) .await .inspect_err(|e| error!("failed to create folder: {e}"))?; + let folder = folderapi .read(TESTDIRQRY, &()) .await .inspect_err(|e| error!("failed to read folder: {e}"))?; info!("folder config on site: {folder:?}"); + info!("updating folder"); folderapi .update(TESTDIRQRY, &ureq1) @@ -64,6 +67,7 @@ async fn test_folders(client: Client) -> Result<()> { .await .inspect_err(|e| error!("failed to read all folders: {e}")); info!("folder config on site: {folders:?}"); + folderapi .bulk_update(&[FolderBulkUpdateRequest::new(TESTDIRQRY.to_string(), ureq2)]) .await @@ -77,3 +81,64 @@ async fn test_folders(client: Client) -> Result<()> { Ok(()) } + +async fn test_hosts(client: Client) -> Result<()> { + use checkmk_api::api::hosts::*; + use checkmk_api::api::folders::FolderAttributes; + const TESTHOST: &str = "test-host"; + info!("testing hosts"); + + let hostapi = client.host_api(); + let creq = HostCreationRequest::new( + TESTHOST.to_string(), + "~".to_string(), + FolderAttributes::default().with_site("dev".to_string()) + ); + let cquery = HostCreationQuery::new(false); + let rquery = HostReadQuery::new(true); + let ureq1 = HostUpdateRequest::replace( + FolderAttributes::default() + .with_site("dev".to_string()) + .with_label("environment".to_string(), "test".to_string()) + ); + let ureq2 = HostUpdateRequest::update( + FolderAttributes::default().with_label("purpose".to_string(), "testing".to_string()) + ); + + info!("creating test host"); + hostapi + .create(&creq, &cquery) + .await + .inspect_err(|e| error!("failed to create host: {e}"))?; + + let host = hostapi + .read(TESTHOST, &rquery) + .await + .inspect_err(|e| error!("failed to read host: {e}"))?; + info!("host config: {host:#?}"); + + info!("updating host"); + hostapi + .update(TESTHOST, &ureq1) + .await + .inspect_err(|e| error!("failed to update host: {e}"))?; + + let hosts = hostapi + .bulk_read(&HostBulkReadQuery::new().with_effective_attributes(true)) + .await + .inspect_err(|e| error!("failed to read all hosts: {e}")); + info!("all hosts: {hosts:?}"); + + hostapi + .bulk_update(&[HostBulkUpdateRequest::new(TESTHOST.to_string(), ureq2)]) + .await + .inspect_err(|e| error!("failed to do a bulk update of hosts: {e}"))?; + + info!("deleting host"); + hostapi + .delete(TESTHOST, &()) + .await + .inspect_err(|e| error!("failed to delete host: {e}"))?; + + Ok(()) +}