use crate::Client;
use crate::error::Result;
use crate::resource::Resource;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateClusterRequest {
pub name: String,
pub r#type: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tenant: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comments: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateClusterRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#type: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tenant: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comments: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateVirtualMachineRequest {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub site: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cluster: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub device: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub serial: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tenant: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub platform: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_ip4: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_ip6: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vcpus: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comments: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_template: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub local_context_data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateVirtualMachineRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub site: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cluster: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub device: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub serial: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tenant: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub platform: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_ip4: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_ip6: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vcpus: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub comments: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_template: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub local_context_data: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateVmInterfaceRequest {
pub virtual_machine: i32,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bridge: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mtu: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_mac_address: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub untagged_vlan: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tagged_vlans: Option<Vec<i32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub qinq_svlan: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vlan_translation_policy: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vrf: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateVmInterfaceRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub virtual_machine: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bridge: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mtu: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_mac_address: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub untagged_vlan: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tagged_vlans: Option<Vec<i32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub qinq_svlan: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vlan_translation_policy: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub vrf: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateVirtualDiskRequest {
pub virtual_machine: i32,
pub name: String,
pub size: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateVirtualDiskRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub virtual_machine: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<i32>>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ClientConfig;
use httpmock::prelude::*;
use proptest::prelude::*;
use serde_json::{Value, json};
fn mock_client(server: &MockServer) -> Client {
let config = ClientConfig::new(server.base_url(), "test-token");
Client::new(config).unwrap()
}
fn assert_missing(value: &Value, key: &str) {
assert!(value.get(key).is_none(), "expected {} to be omitted", key);
}
fn assert_optional_i32(value: &Value, key: &str, field: &Option<i32>) {
match field {
Some(v) => assert_eq!(value[key], serde_json::json!(*v)),
None => assert_missing(value, key),
}
}
fn assert_optional_string(value: &Value, key: &str, field: &Option<String>) {
match field {
Some(v) => assert_eq!(value[key], serde_json::json!(v)),
None => assert_missing(value, key),
}
}
#[test]
fn serialize_cluster_requests() {
let create = CreateClusterRequest {
name: "cluster".to_string(),
r#type: 1,
group: None,
status: Some("active".to_string()),
tenant: None,
scope_type: None,
scope_id: None,
description: None,
comments: None,
tags: None,
};
let value = serde_json::to_value(&create).unwrap();
assert_eq!(value["name"], "cluster");
assert_eq!(value["type"], 1);
assert_eq!(value["status"], "active");
assert_missing(&value, "group");
let update = UpdateClusterRequest {
name: None,
r#type: None,
group: Some(2),
status: None,
tenant: None,
scope_type: None,
scope_id: None,
description: Some("Updated".to_string()),
comments: None,
tags: None,
};
let value = serde_json::to_value(&update).unwrap();
assert_eq!(value["group"], 2);
assert_eq!(value["description"], "Updated");
assert_missing(&value, "name");
}
#[test]
fn serialize_virtual_machine_requests() {
let create = CreateVirtualMachineRequest {
name: "vm".to_string(),
status: Some("active".to_string()),
site: None,
cluster: Some(1),
device: None,
serial: None,
role: None,
tenant: None,
platform: None,
primary_ip4: None,
primary_ip6: None,
vcpus: Some(2.0),
memory: Some(2048),
disk: Some(40),
description: None,
comments: None,
config_template: None,
local_context_data: None,
tags: None,
};
let value = serde_json::to_value(&create).unwrap();
assert_eq!(value["name"], "vm");
assert_eq!(value["status"], "active");
assert_eq!(value["cluster"], 1);
assert_eq!(value["vcpus"], 2.0);
assert_eq!(value["memory"], 2048);
assert_eq!(value["disk"], 40);
assert_missing(&value, "site");
let update = UpdateVirtualMachineRequest {
name: None,
status: None,
site: None,
cluster: None,
device: None,
serial: None,
role: None,
tenant: None,
platform: None,
primary_ip4: None,
primary_ip6: None,
vcpus: None,
memory: None,
disk: None,
description: Some("Updated".to_string()),
comments: None,
config_template: None,
local_context_data: None,
tags: None,
};
let value = serde_json::to_value(&update).unwrap();
assert_eq!(value["description"], "Updated");
assert_missing(&value, "name");
}
#[test]
fn serialize_vm_interface_requests() {
let create = CreateVmInterfaceRequest {
virtual_machine: 1,
name: "eth0".to_string(),
enabled: Some(true),
parent: None,
bridge: None,
mtu: None,
primary_mac_address: None,
description: None,
mode: None,
untagged_vlan: None,
tagged_vlans: None,
qinq_svlan: None,
vlan_translation_policy: None,
vrf: None,
tags: None,
};
let value = serde_json::to_value(&create).unwrap();
assert_eq!(value["virtual_machine"], 1);
assert_eq!(value["name"], "eth0");
assert_eq!(value["enabled"], true);
assert_missing(&value, "parent");
let update = UpdateVmInterfaceRequest {
virtual_machine: None,
name: None,
enabled: None,
parent: None,
bridge: None,
mtu: Some(1500),
primary_mac_address: None,
description: Some("Updated".to_string()),
mode: None,
untagged_vlan: None,
tagged_vlans: None,
qinq_svlan: None,
vlan_translation_policy: None,
vrf: None,
tags: None,
};
let value = serde_json::to_value(&update).unwrap();
assert_eq!(value["mtu"], 1500);
assert_eq!(value["description"], "Updated");
assert_missing(&value, "name");
}
#[test]
fn serialize_virtual_disk_requests() {
let create = CreateVirtualDiskRequest {
virtual_machine: 1,
name: "root".to_string(),
size: 100,
description: None,
tags: None,
};
let value = serde_json::to_value(&create).unwrap();
assert_eq!(value["virtual_machine"], 1);
assert_eq!(value["name"], "root");
assert_eq!(value["size"], 100);
assert_missing(&value, "description");
let update = UpdateVirtualDiskRequest {
virtual_machine: None,
name: None,
size: Some(200),
description: None,
tags: None,
};
let value = serde_json::to_value(&update).unwrap();
assert_eq!(value["size"], 200);
assert_missing(&value, "name");
}
proptest! {
#[test]
fn prop_virtual_machine_optional_fields(
status in proptest::option::of("[a-z0-9-]{1,12}"),
memory in proptest::option::of(0i32..65536),
) {
let request = CreateVirtualMachineRequest {
name: "vm".to_string(),
status: status.clone(),
site: None,
cluster: None,
device: None,
serial: None,
role: None,
tenant: None,
platform: None,
primary_ip4: None,
primary_ip6: None,
vcpus: None,
memory,
disk: None,
description: None,
comments: None,
config_template: None,
local_context_data: None,
tags: None,
};
let value = serde_json::to_value(&request).unwrap();
assert_optional_string(&value, "status", &status);
assert_optional_i32(&value, "memory", &memory);
}
}
proptest! {
#[test]
fn prop_vm_interface_optional_fields(
mtu in proptest::option::of(0i32..9000),
mode in proptest::option::of("[a-z0-9-]{1,12}"),
) {
let request = CreateVmInterfaceRequest {
virtual_machine: 1,
name: "eth0".to_string(),
enabled: None,
parent: None,
bridge: None,
mtu,
primary_mac_address: None,
description: None,
mode: mode.clone(),
untagged_vlan: None,
tagged_vlans: None,
qinq_svlan: None,
vlan_translation_policy: None,
vrf: None,
tags: None,
};
let value = serde_json::to_value(&request).unwrap();
assert_optional_i32(&value, "mtu", &mtu);
assert_optional_string(&value, "mode", &mode);
}
}
proptest! {
#[test]
fn prop_virtual_disk_optional_fields(
description in proptest::option::of("[a-z0-9-]{0,16}"),
) {
let request = CreateVirtualDiskRequest {
virtual_machine: 1,
name: "root".to_string(),
size: 10,
description: description.clone(),
tags: None,
};
let value = serde_json::to_value(&request).unwrap();
assert_optional_string(&value, "description", &description);
}
}
#[cfg_attr(miri, ignore)]
#[tokio::test]
async fn render_vm_config_uses_expected_path() {
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method(POST)
.path("/api/virtualization/virtual-machines/42/render-config/");
then.status(200).json_body(json!("hostname vm-01"));
});
let client = mock_client(&server);
let result = client.virtualization().render_vm_config(42).await;
assert!(result.is_ok());
mock.assert();
}
}
pub type ClusterGroup = crate::models::ClusterGroup;
pub type ClusterType = crate::models::ClusterType;
pub type Cluster = crate::models::Cluster;
pub type VmInterface = crate::models::VmInterface;
pub type VirtualDisk = crate::models::VirtualDisk;
pub type VirtualMachine = crate::models::VirtualMachineWithConfigContext;
pub type ClusterGroupsApi = Resource<crate::models::ClusterGroup>;
pub type ClusterTypesApi = Resource<crate::models::ClusterType>;
pub type ClustersApi = Resource<crate::models::Cluster>;
pub type VmInterfacesApi = Resource<crate::models::VmInterface>;
pub type VirtualDisksApi = Resource<crate::models::VirtualDisk>;
pub type VirtualMachinesApi = Resource<crate::models::VirtualMachineWithConfigContext>;
#[derive(Clone)]
pub struct VirtualizationApi {
client: Client,
}
impl VirtualizationApi {
pub(crate) fn new(client: Client) -> Self {
Self { client }
}
pub fn cluster_groups(&self) -> ClusterGroupsApi {
Resource::new(self.client.clone(), "virtualization/cluster-groups/")
}
pub fn cluster_types(&self) -> ClusterTypesApi {
Resource::new(self.client.clone(), "virtualization/cluster-types/")
}
pub fn clusters(&self) -> ClustersApi {
Resource::new(self.client.clone(), "virtualization/clusters/")
}
pub fn interfaces(&self) -> VmInterfacesApi {
Resource::new(self.client.clone(), "virtualization/interfaces/")
}
pub fn virtual_disks(&self) -> VirtualDisksApi {
Resource::new(self.client.clone(), "virtualization/virtual-disks/")
}
pub fn virtual_machines(&self) -> VirtualMachinesApi {
Resource::new(self.client.clone(), "virtualization/virtual-machines/")
}
pub async fn render_vm_config(&self, id: u64) -> Result<String> {
self.client
.post(
&format!("virtualization/virtual-machines/{}/render-config/", id),
&(),
)
.await
}
}