updating virt_model with email and

This commit is contained in:
forest 2020-05-10 20:23:00 -05:00
parent 838348a0fb
commit d706ae7761
5 changed files with 93 additions and 29 deletions

View File

@ -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

View File

@ -30,4 +30,9 @@ class DBModel:
self.cursor.execute("DELETE FROM login_tokens WHERE email = %s", (email, )) self.cursor.execute("DELETE FROM login_tokens WHERE email = %s", (email, ))
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())

View File

@ -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;

View File

@ -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;
@ -7,4 +34,4 @@
background: red; background: red;
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
} }

View File

@ -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: