From d706ae7761c08f274cafe163f9974b8c2e8d7d2e Mon Sep 17 00:00:00 2001 From: forest Date: Sun, 10 May 2020 20:23:00 -0500 Subject: [PATCH] updating virt_model with email and --- capsulflask/__init__.py | 2 +- capsulflask/db_model.py | 7 ++- .../02_up_accounts_vms_etc.sql | 21 ++++--- capsulflask/static/style.css | 29 ++++++++- capsulflask/virt_model.py | 63 ++++++++++++++----- 5 files changed, 93 insertions(+), 29 deletions(-) diff --git a/capsulflask/__init__.py b/capsulflask/__init__.py index cac2cfa..6e966ba 100644 --- a/capsulflask/__init__.py +++ b/capsulflask/__init__.py @@ -28,7 +28,7 @@ def create_app(): @app.route("/") def index(): - return render_template("index.html") + return render_template("index.html") from capsulflask import auth diff --git a/capsulflask/db_model.py b/capsulflask/db_model.py index 20cf67b..921406b 100644 --- a/capsulflask/db_model.py +++ b/capsulflask/db_model.py @@ -30,4 +30,9 @@ class DBModel: self.cursor.execute("DELETE FROM login_tokens WHERE email = %s", (email, )) self.connection.commit() return email - return None \ No newline at end of file + return None + + def allVmIds(self,): + self.cursor.execute("SELECT id FROM vms") + return map(lambda x: x[0], self.cursor.fetchall()) + \ No newline at end of file diff --git a/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql b/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql index 98433f2..ce90c7b 100644 --- a/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql +++ b/capsulflask/schema_migrations/02_up_accounts_vms_etc.sql @@ -14,8 +14,9 @@ CREATE TABLE os_images ( CREATE TABLE vm_sizes ( id TEXT PRIMARY KEY 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 ( @@ -31,6 +32,8 @@ CREATE TABLE vms ( email TEXT REFERENCES accounts(email) ON DELETE RESTRICT, os TEXT REFERENCES os_images(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(), deleted TIMESTAMP, 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'), ('guix110', 'guixsystem-cloud-2020-05.qcow2', 'Guix System 1.1.0'); -INSERT INTO vm_sizes (id, dollars_per_month, memory_megabytes, vcpus) -VALUES ('f1-s', 5.33, 512, 1), - ('f1-m', 7.16, 1024, 1), - ('f1-l', 8.92, 2048, 1), - ('f1-x', 16.16, 4096, 2), - ('f1-xx', 29.66, 8192, 4), - ('f1-xxx', 57.58, 16384, 8); +INSERT INTO vm_sizes (id, dollars_per_month, memory_mb, vcpus, bandwidth_gb_per_month) +VALUES ('f1-s', 5.33, 512, 1, 500), + ('f1-m', 7.16, 1024, 1, 1000), + ('f1-l', 8.92, 2048, 1, 2000), + ('f1-x', 16.16, 4096, 2, 4000), + ('f1-xx', 29.66, 8192, 4, 8000), + ('f1-xxx', 57.58, 16384, 8, 16000); UPDATE schemaversion SET version = 2; \ No newline at end of file diff --git a/capsulflask/static/style.css b/capsulflask/static/style.css index 00cffeb..f8370fa 100644 --- a/capsulflask/static/style.css +++ b/capsulflask/static/style.css @@ -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 { display: inline-block; float: right; @@ -7,4 +34,4 @@ background: red; font-weight: bold; text-align: center; -} \ No newline at end of file +} diff --git a/capsulflask/virt_model.py b/capsulflask/virt_model.py index f7a3356..2e16d21 100644 --- a/capsulflask/virt_model.py +++ b/capsulflask/virt_model.py @@ -5,6 +5,17 @@ from flask import current_app from time import sleep from os.path import join 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: def __init__(self, id, ipv4=None, ipv6=None): @@ -19,21 +30,39 @@ class VirtualizationInterface: def listIds(self) -> list: 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 - def destroy(self, id: str): + def destroy(self, email: str, id: str): 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): - def validateId(self, 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}}$\"") + def validateCompletedProcess(self, completedProcess, email=None): + emailPart = "" + if email != None: + emailPart = f"for {email}" - def validateCompletedProcess(self, completedProcess): 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: {completedProcess.stdout} stderr: @@ -41,7 +70,7 @@ class ShellScriptVirtualization(VirtualizationInterface): """) def get(self, id): - self.validateId(id) + validateCapsulId(id) completedProcess = run([join(current_app.root_path, 'shell_scripts/get.sh'), id], capture_output=True) self.validateCompletedProcess(completedProcess) lines = completedProcess.stdout.splitlines() @@ -56,8 +85,8 @@ class ShellScriptVirtualization(VirtualizationInterface): self.validateCompletedProcess(completedProcess) return completedProcess.stdout.splitlines() - def create(self, id: str, template_file_name: str, vcpus: int, memory_mb: int, ssh_public_keys: list): - self.validateId(id) + def create(self, email: str, template_file_name: str, vcpus: int, memory_mb: int, ssh_public_keys: list): + id = makeCapsulId() 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_.-]$\"") @@ -83,7 +112,7 @@ class ShellScriptVirtualization(VirtualizationInterface): ssh_keys_string ], capture_output=True) - self.validateCompletedProcess(completedProcess) + self.validateCompletedProcess(completedProcess, email) lines = completedProcess.stdout.splitlines() vmSettings = f""" @@ -95,7 +124,7 @@ class ShellScriptVirtualization(VirtualizationInterface): """ 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} stdout: {completedProcess.stdout} @@ -115,18 +144,18 @@ class ShellScriptVirtualization(VirtualizationInterface): if result != None: 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} """) - def destroy(self, id: str): - self.validateId(id) + def destroy(self, email: str, id: str): + validateCapsulId(id) 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() 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: {completedProcess.stdout} stderr: