From 1dd72fd011f6bb0099eb6c19e5620a3e9fac6b79 Mon Sep 17 00:00:00 2001 From: Kieran Date: Sun, 10 Nov 2024 19:24:51 +0000 Subject: [PATCH] add more tables --- dev_setup.sql | 5 +- migrations/20241103155733_init.sql | 116 ++++++++++++++++++++--------- src/api.rs | 30 +++++++- src/db.rs | 13 ++++ src/provisioner.rs | 24 ++++-- 5 files changed, 141 insertions(+), 47 deletions(-) diff --git a/dev_setup.sql b/dev_setup.sql index 1735a00..4759d4e 100644 --- a/dev_setup.sql +++ b/dev_setup.sql @@ -1,4 +1,5 @@ insert ignore into vm_host(id,kind,name,ip,cpu,memory,enabled,api_token) values(1, 0, "lab", "https://185.18.221.8:8006", 4, 4096*1024, 1, "root@pam!tester=c82f8a57-f876-4ca4-8610-c086d8d9d51c"); -insert ignore into vm_host_disk(id,host_id,name,size,kind,interface,enabled) values(1,1,"local-lvm",1000*1000*1000*100, 0, 0, 1); +insert ignore into vm_host_disk(id,host_id,name,size,kind,interface,enabled) values(1,1,"local-lvm",1000*1000*1000*1000, 0, 0, 1); insert ignore into vm_os_image(id,name,enabled) values(1,"Ubuntu 24.04",1); -insert ignore into ip_range(id,cidr,enabled) values(1,"185.18.221.80/28",1); \ No newline at end of file +insert ignore into ip_range(id,cidr,enabled) values(1,"185.18.221.80/28",1); +insert ignore into vm_template(id,name,enabled,cpu,memory,disk_size,disk_id) values(1,"Basic",1,2,2048,1000*1000*1000*80,1); \ No newline at end of file diff --git a/migrations/20241103155733_init.sql b/migrations/20241103155733_init.sql index 04b6cb9..34a5fe5 100644 --- a/migrations/20241103155733_init.sql +++ b/migrations/20241103155733_init.sql @@ -4,27 +4,43 @@ create table users pubkey binary(32) not null, created timestamp default current_timestamp ); -create unique index ix_user_pubkey on users (pubkey); -create table vm_host +create table user_ssh_key ( id integer unsigned not null auto_increment primary key, - kind smallint unsigned not null, - name varchar(100) not null, - ip varchar(250) not null, + name varchar(100) not null, + user_id integer unsigned not null, + created timestamp default current_timestamp, + key varchar(2048) not null, + + constraint fk_ssh_key_user_id foreign key (user_id) references users (id) +); +create unique index ix_user_pubkey on users (pubkey); +create table vm_host_region +( + id integer unsigned not null auto_increment primary key, + name varchar(100) not null, + enabled bit(1) not null, +); +create table vm_host +( + id integer unsigned not null auto_increment primary key, + kind smallint unsigned not null, + name varchar(100) not null, + ip varchar(250) not null, cpu bigint unsigned not null, memory bigint unsigned not null, - enabled bit(1) not null, - api_token varchar(200) not null + enabled bit(1) not null, + api_token varchar(200) not null ); create table vm_host_disk ( - id integer unsigned not null auto_increment primary key, - host_id integer unsigned not null, + id integer unsigned not null auto_increment primary key, + host_id integer unsigned not null, name varchar(50) not null, size bigint unsigned not null, kind smallint unsigned not null, interface smallint unsigned not null, - enabled bit(1) not null, + enabled bit(1) not null, constraint fk_vm_host_disk foreign key (host_id) references vm_host (id) ); @@ -32,51 +48,81 @@ create table vm_os_image ( id integer unsigned not null auto_increment primary key, name varchar(200) not null, - enabled bit(1) not null + enabled bit(1) not null ); create table ip_range ( id integer unsigned not null auto_increment primary key, cidr varchar(200) not null, - enabled bit(1) not null + enabled bit(1) not null ); -create table vm +create table vm_cost_plan +( + id integer unsigned not null auto_increment primary key, + name varchar(200) not null, + enabled bit(1) not null, + created timestamp default current_timestamp, + expires timestamp, + amount integer unsigned not null, + currency varchar(4) not null, + interval integer unsigned not null, + interval_type smallint unsigned not null, +); +create table vm_template ( id integer unsigned not null auto_increment primary key, - host_id integer unsigned not null, - user_id integer unsigned not null, - image_id integer unsigned not null, + name varchar(200) not null, + enabled bit(1) not null, created timestamp default current_timestamp, - expires timestamp not null, + expires timestamp, cpu smallint unsigned not null, memory bigint unsigned not null, disk_size bigint unsigned not null, - disk_id integer unsigned not null, + disk_id integer unsigned not null, - constraint fk_vm_host foreign key (host_id) references vm_host (id), - constraint fk_vm_user foreign key (user_id) references users (id), - constraint fk_vm_image foreign key (image_id) references vm_os_image (id), - constraint fk_vm_host_disk_id foreign key (disk_id) references vm_host_disk (id) + constraint fk_vm_host_disk_id foreign key (disk_id) references vm_host_disk (id) +); +create table vm +( + id integer unsigned not null auto_increment primary key, + host_id integer unsigned not null, + user_id integer unsigned not null, + image_id integer unsigned not null, + template_id integer unsigned not null, + ssh_key_id integer unsigned not null, + created timestamp default current_timestamp, + expires timestamp not null, + cpu smallint unsigned not null, + memory bigint unsigned not null, + disk_size bigint unsigned not null, + disk_id integer unsigned not null, + + constraint fk_vm_host foreign key (host_id) references vm_host (id), + constraint fk_vm_user foreign key (user_id) references users (id), + constraint fk_vm_image foreign key (image_id) references vm_os_image (id), + constraint fk_vm_host_disk_id foreign key (disk_id) references vm_host_disk (id), + constraint fk_vm_template_id foreign key (template_id) references vm_template (id), + constraint fk_vm_ssh_key_id foreign key (ssh_key_id) references user_ssh_key (id) ); create table vm_ip_assignment ( - id integer unsigned not null auto_increment primary key, - vm_id integer unsigned not null, - ip_range_id integer unsigned not null, + id integer unsigned not null auto_increment primary key, + vm_id integer unsigned not null, + ip_range_id integer unsigned not null, - constraint fk_vm_ip_assignment_vm foreign key (vm_id) references vm (id), - constraint fk_vm_ip_range foreign key (ip_range_id) references ip_range (id) + constraint fk_vm_ip_assignment_vm foreign key (vm_id) references vm (id), + constraint fk_vm_ip_range foreign key (ip_range_id) references ip_range (id) ); create table vm_payment ( - id binary(32) not null, - vm_id integer unsigned not null, - created timestamp default current_timestamp, - expires timestamp not null, - amount bigint unsigned not null, - invoice varchar(2048) not null, - time_value integer unsigned not null, - is_paid bit(1) not null, + id binary(32) not null, + vm_id integer unsigned not null, + created timestamp default current_timestamp, + expires timestamp not null, + amount bigint unsigned not null, + invoice varchar(2048) not null, + time_value integer unsigned not null, + is_paid bit(1) not null, constraint fk_vm_payment_vm foreign key (vm_id) references vm (id) ); \ No newline at end of file diff --git a/src/api.rs b/src/api.rs index 57ef0d7..09f94ec 100644 --- a/src/api.rs +++ b/src/api.rs @@ -3,8 +3,9 @@ use crate::nip98::Nip98Auth; use crate::provisioner::Provisioner; use anyhow::Error; use rocket::serde::json::Json; -use rocket::{get, routes, Responder, Route, State}; -use serde::Serialize; +use rocket::{get, post, routes, Data, Responder, Route, State}; +use serde::{Deserialize, Serialize}; +use crate::vm::VMSpec; pub fn routes() -> Vec { routes![v1_list_vms] @@ -37,6 +38,15 @@ impl From for ApiError { } } +#[derive(Debug, Serialize, Deserialize)] +struct CreateVmRequest {} + +impl From for VMSpec { + fn from(value: CreateVmRequest) -> Self { + todo!() + } +} + #[get("/api/v1/vms")] async fn v1_list_vms(auth: Nip98Auth, provisioner: &State) -> ApiResult> { let pubkey = auth.event.pubkey.to_bytes(); @@ -44,3 +54,19 @@ async fn v1_list_vms(auth: Nip98Auth, provisioner: &State) -> ApiRe let vms = provisioner.list_vms(uid).await?; ApiData::ok(vms) } + +#[get("/api/v1/vm/templates")] +async fn v1_list_vm_templates(provisioner: &State) -> ApiResult> { + let vms = provisioner.list_vm_templates().await?; + ApiData::ok(vms) +} + +#[post("/api/v1/vm", data = "", format = "json")] +async fn v1_provision_vm(auth: Nip98Auth, provisioner: &State, req: Json) -> ApiResult { + let pubkey = auth.event.pubkey.to_bytes(); + let uid = provisioner.upsert_user(&pubkey).await?; + + let req = req.0; + let rsp = provisioner.provision(req.into()).await?; + ApiData::ok(rsp) +} \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index 8886f5c..c29b5d4 100644 --- a/src/db.rs +++ b/src/db.rs @@ -76,6 +76,19 @@ pub struct IpRange { pub enabled: bool, } +#[derive(Serialize, FromRow, Clone, Debug)] +pub struct VmTemplate { + pub id: u64, + pub name: String, + pub enabled: bool, + pub created: DateTime, + pub expires: Option>, + pub cpu: u16, + pub memory: u64, + pub disk_size: u64, + pub disk_id: u64, +} + #[derive(Serialize, FromRow, Clone, Debug)] pub struct Vm { /// Unique VM ID (Same in proxmox) diff --git a/src/provisioner.rs b/src/provisioner.rs index 69a587d..c5c7481 100644 --- a/src/provisioner.rs +++ b/src/provisioner.rs @@ -2,7 +2,7 @@ use crate::db; use crate::db::{VmHost, VmHostDisk}; use crate::host::proxmox::ProxmoxClient; use crate::vm::VMSpec; -use anyhow::Error; +use anyhow::{Error, Result}; use log::{info, warn}; use sqlx::{MySqlPool, Row}; @@ -17,7 +17,7 @@ impl Provisioner { } /// Auto-discover resources - pub async fn auto_discover(&self) -> Result<(), Error> { + pub async fn auto_discover(&self) -> Result<()> { let hosts = self.list_hosts().await?; for host in hosts { let api = ProxmoxClient::new(host.ip.parse()?).with_api_token(&host.api_token); @@ -58,12 +58,12 @@ impl Provisioner { } /// Provision a new VM - pub async fn provision(&self, spec: VMSpec) -> Result { + pub async fn provision(&self, spec: VMSpec) -> Result { todo!() } /// Insert/Fetch user id - pub async fn upsert_user(&self, pubkey: &[u8; 32]) -> Result { + pub async fn upsert_user(&self, pubkey: &[u8; 32]) -> Result { let res = sqlx::query("insert ignore into users(pubkey) values(?) returning id") .bind(pubkey.as_slice()) .fetch_optional(&self.db) @@ -79,8 +79,16 @@ impl Provisioner { } } + /// List VM templates + pub async fn list_vm_templates(&self) -> Result> { + sqlx::query_as("select * from vm_template where enabled = 1 and (expires is null or expires < now())") + .fetch_all(&self.db) + .await + .map_err(Error::new) + } + /// List VM's owned by a specific user - pub async fn list_vms(&self, id: u64) -> Result, Error> { + pub async fn list_vms(&self, id: u64) -> Result> { sqlx::query_as("select * from vm where user_id = ?") .bind(&id) .fetch_all(&self.db) @@ -89,7 +97,7 @@ impl Provisioner { } /// List VM's owned by a specific user - pub async fn list_hosts(&self) -> Result, Error> { + pub async fn list_hosts(&self) -> Result> { sqlx::query_as("select * from vm_host") .fetch_all(&self.db) .await @@ -97,7 +105,7 @@ impl Provisioner { } /// List VM's owned by a specific user - pub async fn list_host_disks(&self, host_id: u64) -> Result, Error> { + pub async fn list_host_disks(&self, host_id: u64) -> Result> { sqlx::query_as("select * from vm_host_disk where host_id = ?") .bind(&host_id) .fetch_all(&self.db) @@ -106,7 +114,7 @@ impl Provisioner { } /// Update host resources (usually from [auto_discover]) - pub async fn update_host(&self, host: VmHost) -> Result<(), Error> { + pub async fn update_host(&self, host: VmHost) -> Result<()> { sqlx::query("update vm_host set name = ?, cpu = ?, memory = ? where id = ?") .bind(&host.name) .bind(&host.cpu)