fix: ip assigment index feat: default username
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -4691,8 +4691,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "virt"
|
name = "virt"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://gitlab.com/libvirt/libvirt-rust.git#70394aad4d9597c9ff87c0ada6711ed4f9528991"
|
||||||
checksum = "77a05f77c836efa9be343b5419663cf829d75203b813579993cdd9c44f51767e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"uuid",
|
"uuid",
|
||||||
@ -4702,8 +4701,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "virt-sys"
|
name = "virt-sys"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://gitlab.com/libvirt/libvirt-rust.git#70394aad4d9597c9ff87c0ada6711ed4f9528991"
|
||||||
checksum = "c504e459878f09177f41bf2f8bb3e9a8af4fca7a09e73152fee02535d501601c"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
|
14
Cargo.toml
14
Cargo.toml
@ -7,7 +7,17 @@ edition = "2021"
|
|||||||
name = "api"
|
name = "api"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["mikrotik", "nostr-dm", "nostr-dvm", "proxmox", "lnd", "cloudflare", "revolut", "bitvora"]
|
default = [
|
||||||
|
"mikrotik",
|
||||||
|
"nostr-dm",
|
||||||
|
"nostr-dvm",
|
||||||
|
"proxmox",
|
||||||
|
"lnd",
|
||||||
|
"cloudflare",
|
||||||
|
"revolut",
|
||||||
|
"bitvora",
|
||||||
|
"libvirt"
|
||||||
|
]
|
||||||
mikrotik = ["dep:reqwest"]
|
mikrotik = ["dep:reqwest"]
|
||||||
nostr-dm = ["dep:nostr-sdk"]
|
nostr-dm = ["dep:nostr-sdk"]
|
||||||
nostr-dvm = ["dep:nostr-sdk"]
|
nostr-dvm = ["dep:nostr-sdk"]
|
||||||
@ -53,7 +63,7 @@ ssh2 = { version = "0.9.4", optional = true }
|
|||||||
reqwest = { version = "0.12.8", optional = true }
|
reqwest = { version = "0.12.8", optional = true }
|
||||||
|
|
||||||
#libvirt
|
#libvirt
|
||||||
virt = { version = "0.4.2", optional = true }
|
virt = { git = "https://gitlab.com/libvirt/libvirt-rust.git", optional = true }
|
||||||
|
|
||||||
#lnd
|
#lnd
|
||||||
fedimint-tonic-lnd = { version = "0.2.0", default-features = false, features = ["invoicesrpc"], optional = true }
|
fedimint-tonic-lnd = { version = "0.2.0", default-features = false, features = ["invoicesrpc"], optional = true }
|
||||||
|
4
lnvps_db/migrations/20250328220956_fixes.sql
Normal file
4
lnvps_db/migrations/20250328220956_fixes.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
-- Add migration script here
|
||||||
|
ALTER TABLE vm_ip_assignment DROP KEY ix_vm_ip_assignment_ip;
|
||||||
|
alter table vm_os_image
|
||||||
|
add column default_username varchar(50);
|
@ -206,6 +206,7 @@ pub struct VmOsImage {
|
|||||||
pub release_date: DateTime<Utc>,
|
pub release_date: DateTime<Utc>,
|
||||||
/// URL location of cloud image
|
/// URL location of cloud image
|
||||||
pub url: String,
|
pub url: String,
|
||||||
|
pub default_username: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VmOsImage {
|
impl VmOsImage {
|
||||||
|
@ -455,6 +455,7 @@ pub struct ApiVmOsImage {
|
|||||||
pub flavour: String,
|
pub flavour: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub release_date: DateTime<Utc>,
|
pub release_date: DateTime<Utc>,
|
||||||
|
pub default_username: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<lnvps_db::VmOsImage> for ApiVmOsImage {
|
impl From<lnvps_db::VmOsImage> for ApiVmOsImage {
|
||||||
@ -465,6 +466,7 @@ impl From<lnvps_db::VmOsImage> for ApiVmOsImage {
|
|||||||
flavour: image.flavour,
|
flavour: image.flavour,
|
||||||
version: image.version,
|
version: image.version,
|
||||||
release_date: image.release_date,
|
release_date: image.release_date,
|
||||||
|
default_username: image.default_username,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use anyhow::{bail, Result};
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use nostr::Url;
|
use nostr::Url;
|
||||||
use reqwest::header::AUTHORIZATION;
|
use reqwest::header::AUTHORIZATION;
|
||||||
use reqwest::{Client, Method, RequestBuilder};
|
use reqwest::{Method, RequestBuilder};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
@ -1,44 +1,99 @@
|
|||||||
use crate::host::{FullVmInfo, TimeSeries, TimeSeriesData, VmHostClient};
|
use crate::host::{
|
||||||
use crate::status::VmState;
|
FullVmInfo, TerminalStream, TimeSeries, TimeSeriesData, VmHostClient, VmHostDiskInfo,
|
||||||
|
VmHostInfo,
|
||||||
|
};
|
||||||
|
use crate::settings::QemuConfig;
|
||||||
|
use crate::status::{VmRunningState, VmState};
|
||||||
|
use crate::KB;
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use chrono::Utc;
|
||||||
use lnvps_db::{async_trait, Vm, VmOsImage};
|
use lnvps_db::{async_trait, Vm, VmOsImage};
|
||||||
|
use virt::connect::Connect;
|
||||||
|
use virt::domain::Domain;
|
||||||
|
use virt::sys::{virDomainCreate, VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE};
|
||||||
|
|
||||||
pub struct LibVirt {}
|
#[derive(Debug)]
|
||||||
|
pub struct LibVirtHost {
|
||||||
|
connection: Connect,
|
||||||
|
qemu: QemuConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LibVirtHost {
|
||||||
|
pub fn new(url: &str, qemu: QemuConfig) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
connection: Connect::open(Some(url))?,
|
||||||
|
qemu,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl VmHostClient for LibVirt {
|
impl VmHostClient for LibVirtHost {
|
||||||
async fn download_os_image(&self, image: &VmOsImage) -> anyhow::Result<()> {
|
async fn get_info(&self) -> Result<VmHostInfo> {
|
||||||
|
let info = self.connection.get_node_info()?;
|
||||||
|
let storage = self
|
||||||
|
.connection
|
||||||
|
.list_all_storage_pools(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)?;
|
||||||
|
Ok(VmHostInfo {
|
||||||
|
cpu: info.cpus as u16,
|
||||||
|
memory: info.memory * KB,
|
||||||
|
disks: storage
|
||||||
|
.iter()
|
||||||
|
.filter_map(|p| {
|
||||||
|
let info = p.get_info().ok()?;
|
||||||
|
Some(VmHostDiskInfo {
|
||||||
|
name: p.get_name().context("storage pool name is missing").ok()?,
|
||||||
|
size: info.capacity,
|
||||||
|
used: info.allocation,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn download_os_image(&self, image: &VmOsImage) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn generate_mac(&self, vm: &Vm) -> Result<String> {
|
||||||
|
Ok("ff:ff:ff:ff:ff:ff".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start_vm(&self, vm: &Vm) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn stop_vm(&self, vm: &Vm) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reset_vm(&self, vm: &Vm) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_vm(&self, cfg: &FullVmInfo) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reinstall_vm(&self, cfg: &FullVmInfo) -> Result<()> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn generate_mac(&self, vm: &Vm) -> anyhow::Result<String> {
|
async fn get_vm_state(&self, vm: &Vm) -> Result<VmState> {
|
||||||
todo!()
|
Ok(VmState {
|
||||||
|
timestamp: Utc::now().timestamp() as u64,
|
||||||
|
state: VmRunningState::Stopped,
|
||||||
|
cpu_usage: 0.0,
|
||||||
|
mem_usage: 0.0,
|
||||||
|
uptime: 0,
|
||||||
|
net_in: 0,
|
||||||
|
net_out: 0,
|
||||||
|
disk_write: 0,
|
||||||
|
disk_read: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_vm(&self, vm: &Vm) -> anyhow::Result<()> {
|
async fn configure_vm(&self, vm: &FullVmInfo) -> Result<()> {
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn stop_vm(&self, vm: &Vm) -> anyhow::Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn reset_vm(&self, vm: &Vm) -> anyhow::Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_vm(&self, cfg: &FullVmInfo) -> anyhow::Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn reinstall_vm(&self, cfg: &FullVmInfo) -> anyhow::Result<()> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_vm_state(&self, vm: &Vm) -> anyhow::Result<VmState> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn configure_vm(&self, vm: &FullVmInfo) -> anyhow::Result<()> {
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +101,11 @@ impl VmHostClient for LibVirt {
|
|||||||
&self,
|
&self,
|
||||||
vm: &Vm,
|
vm: &Vm,
|
||||||
series: TimeSeries,
|
series: TimeSeries,
|
||||||
) -> anyhow::Result<Vec<TimeSeriesData>> {
|
) -> Result<Vec<TimeSeriesData>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect_terminal(&self, vm: &Vm) -> Result<TerminalStream> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ use std::collections::HashSet;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
|
|
||||||
//#[cfg(feature = "libvirt")]
|
#[cfg(feature = "libvirt")]
|
||||||
//mod libvirt;
|
mod libvirt;
|
||||||
#[cfg(feature = "proxmox")]
|
#[cfg(feature = "proxmox")]
|
||||||
mod proxmox;
|
mod proxmox;
|
||||||
|
|
||||||
@ -67,31 +67,28 @@ pub trait VmHostClient: Send + Sync {
|
|||||||
|
|
||||||
pub fn get_host_client(host: &VmHost, cfg: &ProvisionerConfig) -> Result<Arc<dyn VmHostClient>> {
|
pub fn get_host_client(host: &VmHost, cfg: &ProvisionerConfig) -> Result<Arc<dyn VmHostClient>> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
{
|
return Ok(Arc::new(crate::mocks::MockVmHost::new()));
|
||||||
Ok(Arc::new(crate::mocks::MockVmHost::new()))
|
|
||||||
}
|
Ok(match host.kind.clone() {
|
||||||
#[cfg(not(test))]
|
#[cfg(feature = "proxmox")]
|
||||||
{
|
VmHostKind::Proxmox if cfg.proxmox.is_some() => {
|
||||||
Ok(match (host.kind.clone(), &cfg) {
|
let cfg = cfg.proxmox.clone().unwrap();
|
||||||
#[cfg(feature = "proxmox")]
|
Arc::new(proxmox::ProxmoxClient::new(
|
||||||
(
|
|
||||||
VmHostKind::Proxmox,
|
|
||||||
ProvisionerConfig::Proxmox {
|
|
||||||
qemu,
|
|
||||||
ssh,
|
|
||||||
mac_prefix,
|
|
||||||
},
|
|
||||||
) => Arc::new(proxmox::ProxmoxClient::new(
|
|
||||||
host.ip.parse()?,
|
host.ip.parse()?,
|
||||||
&host.name,
|
&host.name,
|
||||||
&host.api_token,
|
&host.api_token,
|
||||||
mac_prefix.clone(),
|
cfg.mac_prefix,
|
||||||
qemu.clone(),
|
cfg.qemu,
|
||||||
ssh.clone(),
|
cfg.ssh,
|
||||||
)),
|
))
|
||||||
_ => bail!("Unknown host config: {}", host.kind),
|
}
|
||||||
})
|
#[cfg(feature = "libvirt")]
|
||||||
}
|
VmHostKind::LibVirt if cfg.libvirt.is_some() => {
|
||||||
|
let cfg = cfg.libvirt.clone().unwrap();
|
||||||
|
Arc::new(libvirt::LibVirtHost::new(&host.ip, cfg.qemu)?)
|
||||||
|
}
|
||||||
|
_ => bail!("Unknown host config: {}", host.kind),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All VM info necessary to provision a VM and its associated resources
|
/// All VM info necessary to provision a VM and its associated resources
|
||||||
|
@ -1198,6 +1198,7 @@ mod tests {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
release_date: Utc::now(),
|
release_date: Utc::now(),
|
||||||
url: "http://localhost.com/ubuntu_server_24.04.img".to_string(),
|
url: "http://localhost.com/ubuntu_server_24.04.img".to_string(),
|
||||||
|
default_username: None
|
||||||
},
|
},
|
||||||
ips: vec![
|
ips: vec![
|
||||||
VmIpAssignment {
|
VmIpAssignment {
|
||||||
|
@ -104,16 +104,27 @@ pub struct SmtpConfig {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum ProvisionerConfig {
|
pub struct ProvisionerConfig {
|
||||||
#[serde(rename_all = "kebab-case")]
|
pub proxmox: Option<ProxmoxConfig>,
|
||||||
Proxmox {
|
pub libvirt: Option<LibVirtConfig>,
|
||||||
/// Generic VM configuration
|
}
|
||||||
qemu: QemuConfig,
|
|
||||||
/// SSH config for issuing commands via CLI
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
ssh: Option<SshConfig>,
|
#[serde(rename_all = "kebab-case")]
|
||||||
/// MAC address prefix for NIC (eg. bc:24:11)
|
pub struct ProxmoxConfig {
|
||||||
mac_prefix: Option<String>,
|
/// Generic VM configuration
|
||||||
},
|
pub qemu: QemuConfig,
|
||||||
|
/// SSH config for issuing commands via CLI
|
||||||
|
pub ssh: Option<SshConfig>,
|
||||||
|
/// MAC address prefix for NIC (eg. bc:24:11)
|
||||||
|
pub mac_prefix: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct LibVirtConfig {
|
||||||
|
/// Generic VM configuration
|
||||||
|
pub qemu: QemuConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
@ -198,16 +209,19 @@ pub fn mock_settings() -> Settings {
|
|||||||
macaroon: Default::default(),
|
macaroon: Default::default(),
|
||||||
},
|
},
|
||||||
read_only: false,
|
read_only: false,
|
||||||
provisioner: ProvisionerConfig::Proxmox {
|
provisioner: ProvisionerConfig {
|
||||||
qemu: QemuConfig {
|
proxmox: Some(ProxmoxConfig {
|
||||||
machine: "q35".to_string(),
|
qemu: QemuConfig {
|
||||||
os_type: "l26".to_string(),
|
machine: "q35".to_string(),
|
||||||
bridge: "vmbr1".to_string(),
|
os_type: "l26".to_string(),
|
||||||
cpu: "kvm64".to_string(),
|
bridge: "vmbr1".to_string(),
|
||||||
kvm: false,
|
cpu: "kvm64".to_string(),
|
||||||
},
|
kvm: false,
|
||||||
ssh: None,
|
},
|
||||||
mac_prefix: Some("ff:ff:ff".to_string()),
|
ssh: None,
|
||||||
|
mac_prefix: Some("ff:ff:ff".to_string()),
|
||||||
|
}),
|
||||||
|
libvirt: None,
|
||||||
},
|
},
|
||||||
delete_after: 0,
|
delete_after: 0,
|
||||||
smtp: None,
|
smtp: None,
|
||||||
|
Reference in New Issue
Block a user