forked from 3wordchant/capsul-flask
		
	updating virt_model with email and
This commit is contained in:
		@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,4 +30,9 @@ class DBModel:
 | 
			
		||||
      self.cursor.execute("DELETE FROM login_tokens WHERE email = %s", (email, ))
 | 
			
		||||
      self.connection.commit()
 | 
			
		||||
      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 (
 | 
			
		||||
  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;
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user