forked from 3wordchant/capsul-flask
updating virt_model with email and
This commit is contained in:
parent
838348a0fb
commit
d706ae7761
@ -28,7 +28,7 @@ def create_app():
|
|||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
return render_template("index.html")
|
return render_template("index.html")
|
||||||
|
|
||||||
from capsulflask import auth
|
from capsulflask import auth
|
||||||
|
|
||||||
|
@ -31,3 +31,8 @@ class DBModel:
|
|||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
return email
|
return email
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def allVmIds(self,):
|
||||||
|
self.cursor.execute("SELECT id FROM vms")
|
||||||
|
return map(lambda x: x[0], self.cursor.fetchall())
|
||||||
|
|
@ -14,8 +14,9 @@ CREATE TABLE os_images (
|
|||||||
CREATE TABLE vm_sizes (
|
CREATE TABLE vm_sizes (
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
dollars_per_month NUMERIC(8, 2) NOT NULL,
|
dollars_per_month NUMERIC(8, 2) NOT NULL,
|
||||||
memory_megabytes INTEGER NOT NULL,
|
vcpus INTEGER NOT NULL,
|
||||||
vcpus INTEGER NOT NULL
|
memory_mb INTEGER NOT NULL,
|
||||||
|
bandwidth_gb_per_month INTEGER NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE ssh_public_keys (
|
CREATE TABLE ssh_public_keys (
|
||||||
@ -31,6 +32,8 @@ CREATE TABLE vms (
|
|||||||
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
|
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
|
||||||
os TEXT REFERENCES os_images(id) ON DELETE RESTRICT,
|
os TEXT REFERENCES os_images(id) ON DELETE RESTRICT,
|
||||||
size TEXT REFERENCES vm_sizes(id) ON DELETE RESTRICT,
|
size TEXT REFERENCES vm_sizes(id) ON DELETE RESTRICT,
|
||||||
|
last_seen_ipv4 TEXT,
|
||||||
|
last_seen_ipv6 TEXT,
|
||||||
created TIMESTAMP NOT NULL DEFAULT NOW(),
|
created TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||||
deleted TIMESTAMP,
|
deleted TIMESTAMP,
|
||||||
UNIQUE (id, email)
|
UNIQUE (id, email)
|
||||||
@ -68,12 +71,12 @@ VALUES ('debian10', 'debian-10-genericcloud-amd64-20191117-80.qcow2', 'Debian 1
|
|||||||
('openbsd66', 'openbsd-cloud-2020-05.qcow2', 'OpenBSD 6.6'),
|
('openbsd66', 'openbsd-cloud-2020-05.qcow2', 'OpenBSD 6.6'),
|
||||||
('guix110', 'guixsystem-cloud-2020-05.qcow2', 'Guix System 1.1.0');
|
('guix110', 'guixsystem-cloud-2020-05.qcow2', 'Guix System 1.1.0');
|
||||||
|
|
||||||
INSERT INTO vm_sizes (id, dollars_per_month, memory_megabytes, vcpus)
|
INSERT INTO vm_sizes (id, dollars_per_month, memory_mb, vcpus, bandwidth_gb_per_month)
|
||||||
VALUES ('f1-s', 5.33, 512, 1),
|
VALUES ('f1-s', 5.33, 512, 1, 500),
|
||||||
('f1-m', 7.16, 1024, 1),
|
('f1-m', 7.16, 1024, 1, 1000),
|
||||||
('f1-l', 8.92, 2048, 1),
|
('f1-l', 8.92, 2048, 1, 2000),
|
||||||
('f1-x', 16.16, 4096, 2),
|
('f1-x', 16.16, 4096, 2, 4000),
|
||||||
('f1-xx', 29.66, 8192, 4),
|
('f1-xx', 29.66, 8192, 4, 8000),
|
||||||
('f1-xxx', 57.58, 16384, 8);
|
('f1-xxx', 57.58, 16384, 8, 16000);
|
||||||
|
|
||||||
UPDATE schemaversion SET version = 2;
|
UPDATE schemaversion SET version = 2;
|
@ -1,3 +1,30 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
color: #c5c8c6;
|
||||||
|
font: calc(0.40rem + 1vmin) monospace;
|
||||||
|
text-align: center;
|
||||||
|
overflow-y: scroll;
|
||||||
|
background-color: #241e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color:#6CF;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:active, a:visited {
|
||||||
|
color: #b5bd68;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
span.home {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
span.account {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.float-right {
|
.float-right {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -5,6 +5,17 @@ from flask import current_app
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
from os.path import join
|
from os.path import join
|
||||||
from subprocess import run
|
from subprocess import run
|
||||||
|
from nanoid import generate
|
||||||
|
|
||||||
|
from capsulflask.db import get_model
|
||||||
|
|
||||||
|
def makeCapsulId():
|
||||||
|
lettersAndNumbers = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
|
||||||
|
return f"capsul-{lettersAndNumbers}"
|
||||||
|
|
||||||
|
def validateCapsulId(id):
|
||||||
|
if not re.match(r"^capsul-[a-z0-9]{10}$", id):
|
||||||
|
raise ValueError(f"vm id \"{id}\" must match \"^capsul-[a-z0-9]{{10}}$\"")
|
||||||
|
|
||||||
class VirtualMachine:
|
class VirtualMachine:
|
||||||
def __init__(self, id, ipv4=None, ipv6=None):
|
def __init__(self, id, ipv4=None, ipv6=None):
|
||||||
@ -19,21 +30,39 @@ class VirtualizationInterface:
|
|||||||
def listIds(self) -> list:
|
def listIds(self) -> list:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create(self, id: str, template_file_name: str, memory: int, vcpus: int, ssh_public_keys: list) -> VirtualMachine:
|
def create(self, email: str, template_file_name: str, vcpus: int, memory: int, ssh_public_keys: list) -> VirtualMachine:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def destroy(self, id: str):
|
def destroy(self, email: str, id: str):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class MockVirtualization(VirtualizationInterface):
|
||||||
|
def get(self, id):
|
||||||
|
validateCapsulId(id)
|
||||||
|
return VirtualMachine(id, ipv4="1.1.1.1")
|
||||||
|
|
||||||
|
def listIds(self) -> list:
|
||||||
|
return get_model().allVmIds()
|
||||||
|
|
||||||
|
def create(self, email: str, template_file_name: str, vcpus: int, memory_mb: int, ssh_public_keys: list):
|
||||||
|
id = makeCapsulId()
|
||||||
|
print(f"mock create: {id} for {email}")
|
||||||
|
sleep(5)
|
||||||
|
return VirtualMachine(id, ipv4="1.1.1.1")
|
||||||
|
|
||||||
|
def destroy(self, email: str, id: str):
|
||||||
|
print(f"mock destroy: {id} for {email}")
|
||||||
|
|
||||||
|
|
||||||
class ShellScriptVirtualization(VirtualizationInterface):
|
class ShellScriptVirtualization(VirtualizationInterface):
|
||||||
|
|
||||||
def validateId(self, id):
|
def validateCompletedProcess(self, completedProcess, email=None):
|
||||||
if not re.match(r"^capsul-[a-z0-9]{10}$", id):
|
emailPart = ""
|
||||||
raise ValueError(f"vm id \"{id}\" must match \"^capsul-[a-z0-9]{{10}}$\"")
|
if email != None:
|
||||||
|
emailPart = f"for {email}"
|
||||||
|
|
||||||
def validateCompletedProcess(self, completedProcess):
|
|
||||||
if completedProcess.returncode != 0:
|
if completedProcess.returncode != 0:
|
||||||
raise RuntimeError(f"""{" ".join(completedProcess.args)} failed with exit code {completedProcess.returncode}
|
raise RuntimeError(f"""{" ".join(completedProcess.args)} failed {emailPart} with exit code {completedProcess.returncode}
|
||||||
stdout:
|
stdout:
|
||||||
{completedProcess.stdout}
|
{completedProcess.stdout}
|
||||||
stderr:
|
stderr:
|
||||||
@ -41,7 +70,7 @@ class ShellScriptVirtualization(VirtualizationInterface):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
self.validateId(id)
|
validateCapsulId(id)
|
||||||
completedProcess = run([join(current_app.root_path, 'shell_scripts/get.sh'), id], capture_output=True)
|
completedProcess = run([join(current_app.root_path, 'shell_scripts/get.sh'), id], capture_output=True)
|
||||||
self.validateCompletedProcess(completedProcess)
|
self.validateCompletedProcess(completedProcess)
|
||||||
lines = completedProcess.stdout.splitlines()
|
lines = completedProcess.stdout.splitlines()
|
||||||
@ -56,8 +85,8 @@ class ShellScriptVirtualization(VirtualizationInterface):
|
|||||||
self.validateCompletedProcess(completedProcess)
|
self.validateCompletedProcess(completedProcess)
|
||||||
return completedProcess.stdout.splitlines()
|
return completedProcess.stdout.splitlines()
|
||||||
|
|
||||||
def create(self, id: str, template_file_name: str, vcpus: int, memory_mb: int, ssh_public_keys: list):
|
def create(self, email: str, template_file_name: str, vcpus: int, memory_mb: int, ssh_public_keys: list):
|
||||||
self.validateId(id)
|
id = makeCapsulId()
|
||||||
|
|
||||||
if not re.match(r"^[a-zA-Z0-9_.-]$", template_file_name):
|
if not re.match(r"^[a-zA-Z0-9_.-]$", template_file_name):
|
||||||
raise ValueError(f"template_file_name \"{template_file_name}\" must match \"^[a-zA-Z0-9_.-]$\"")
|
raise ValueError(f"template_file_name \"{template_file_name}\" must match \"^[a-zA-Z0-9_.-]$\"")
|
||||||
@ -83,7 +112,7 @@ class ShellScriptVirtualization(VirtualizationInterface):
|
|||||||
ssh_keys_string
|
ssh_keys_string
|
||||||
], capture_output=True)
|
], capture_output=True)
|
||||||
|
|
||||||
self.validateCompletedProcess(completedProcess)
|
self.validateCompletedProcess(completedProcess, email)
|
||||||
lines = completedProcess.stdout.splitlines()
|
lines = completedProcess.stdout.splitlines()
|
||||||
|
|
||||||
vmSettings = f"""
|
vmSettings = f"""
|
||||||
@ -95,7 +124,7 @@ class ShellScriptVirtualization(VirtualizationInterface):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not lines[len(lines)-1] == "success":
|
if not lines[len(lines)-1] == "success":
|
||||||
raise ValueError(f"""failed to create vm with:
|
raise ValueError(f"""failed to create vm for {email} with:
|
||||||
{vmSettings}
|
{vmSettings}
|
||||||
stdout:
|
stdout:
|
||||||
{completedProcess.stdout}
|
{completedProcess.stdout}
|
||||||
@ -115,18 +144,18 @@ class ShellScriptVirtualization(VirtualizationInterface):
|
|||||||
if result != None:
|
if result != None:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
raise TimeoutError(f"""timed out waiting for vm to obtain an IP address:
|
raise TimeoutError(f"""timed out waiting for vm {id} ({email}) to obtain an IP address:
|
||||||
{vmSettings}
|
{vmSettings}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def destroy(self, id: str):
|
def destroy(self, email: str, id: str):
|
||||||
self.validateId(id)
|
validateCapsulId(id)
|
||||||
completedProcess = run([join(current_app.root_path, 'shell_scripts/destroy.sh'), id], capture_output=True)
|
completedProcess = run([join(current_app.root_path, 'shell_scripts/destroy.sh'), id], capture_output=True)
|
||||||
self.validateCompletedProcess(completedProcess)
|
self.validateCompletedProcess(completedProcess, email)
|
||||||
lines = completedProcess.stdout.splitlines()
|
lines = completedProcess.stdout.splitlines()
|
||||||
|
|
||||||
if not lines[len(lines)-1] == "success":
|
if not lines[len(lines)-1] == "success":
|
||||||
raise ValueError(f"""failed to destroy vm "{id}":
|
raise ValueError(f"""failed to destroy vm "{id}" for {email}:
|
||||||
stdout:
|
stdout:
|
||||||
{completedProcess.stdout}
|
{completedProcess.stdout}
|
||||||
stderr:
|
stderr:
|
||||||
|
Loading…
Reference in New Issue
Block a user