basic capsul detail page
This commit is contained in:
parent
afe18b6166
commit
a6e655ef72
@ -1,4 +1,5 @@
|
||||
import re
|
||||
import sys
|
||||
from flask import Blueprint
|
||||
from flask import flash
|
||||
from flask import current_app
|
||||
@ -14,7 +15,7 @@ from nanoid import generate
|
||||
|
||||
from capsulflask.auth import account_required
|
||||
|
||||
from capsulflask.db import get_model
|
||||
from capsulflask.db import get_model, my_exec_info_message
|
||||
|
||||
bp = Blueprint("console", __name__, url_prefix="/console")
|
||||
|
||||
@ -22,9 +23,31 @@ def makeCapsulId():
|
||||
lettersAndNumbers = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
|
||||
return f"capsul-{lettersAndNumbers}"
|
||||
|
||||
def double_check_capsul_address(db_model, email, id, ipv4):
|
||||
try:
|
||||
result = current_app.config["VIRTUALIZATION_MODEL"].get(id)
|
||||
if result.ipv4 != ipv4:
|
||||
ipv4 = result.ipv4
|
||||
db_model.updateVm(email=email, id=id, ipv4=result.ipv4)
|
||||
except:
|
||||
print(f"""
|
||||
error occurred in list capsuls endpoint while trying to grab ip address of {vm['id']}
|
||||
via the virtualization model: {my_exec_info_message(sys.exc_info())}"""
|
||||
)
|
||||
|
||||
return ipv4
|
||||
|
||||
@bp.route("/")
|
||||
@account_required
|
||||
def index():
|
||||
db_model = get_model()
|
||||
vms = db_model.list_vms_for_account(email=session["account"])
|
||||
|
||||
# for now we are going to check the IP according to the virt model
|
||||
# on every request. this could be done by a background job and cached later on...
|
||||
for vm in vms:
|
||||
vm["ipv4"] = double_check_capsul_address(db_model, session["account"], vm["id"], vm["ipv4"])
|
||||
|
||||
vms = list(map(
|
||||
lambda x: dict(
|
||||
id=x['id'],
|
||||
@ -34,54 +57,26 @@ def index():
|
||||
os=x['os'],
|
||||
created=x['created'].strftime("%b %d %Y %H:%M")
|
||||
),
|
||||
get_model().list_vms_for_account(session["account"])
|
||||
vms
|
||||
))
|
||||
|
||||
return render_template("capsuls.html", vms=vms, has_vms=len(vms) > 0)
|
||||
|
||||
@bp.route("/ssh", methods=("GET", "POST"))
|
||||
@bp.route("/<string:id>")
|
||||
@account_required
|
||||
def ssh_public_keys():
|
||||
def detail(id):
|
||||
db_model = get_model()
|
||||
errors = list()
|
||||
vm = db_model.get_vm_detail(email=session["account"], id=id)
|
||||
|
||||
if request.method == "POST":
|
||||
method = request.form["method"]
|
||||
if vm is None:
|
||||
return abort(404, f"{id} doesn't exist.")
|
||||
|
||||
name = request.form["name"]
|
||||
if not name or len(name.strip()) < 1:
|
||||
errors.append("Name is required")
|
||||
elif not re.match(r"^[0-9A-Za-z_ -]+$", name):
|
||||
errors.append("Name must match \"^[0-9A-Za-z_ -]+$\"")
|
||||
vm["ipv4"] = double_check_capsul_address(db_model, session["account"], vm["id"], vm["ipv4"])
|
||||
vm["created"] = vm['created'].strftime("%b %d %Y %H:%M")
|
||||
vm["ssh_public_keys"] = ", ".join(vm["ssh_public_keys"]) if len(vm["ssh_public_keys"]) > 0 else "<deleted>"
|
||||
|
||||
if method == "POST":
|
||||
content = request.form["content"]
|
||||
if not content or len(content.strip()) < 1:
|
||||
errors.append("Content is required")
|
||||
else:
|
||||
content = content.replace("\r", "").replace("\n", "")
|
||||
if not re.match(r"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$", content):
|
||||
errors.append("Content must match \"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$\"")
|
||||
return render_template("capsul-detail.html", vm=vm)
|
||||
|
||||
if db_model.ssh_public_key_name_exists(session["account"], name):
|
||||
errors.append("A key with that name already exists")
|
||||
|
||||
if len(errors) == 0:
|
||||
db_model.create_ssh_public_key(session["account"], name, content)
|
||||
|
||||
elif method == "DELETE":
|
||||
|
||||
if len(errors) == 0:
|
||||
db_model.delete_ssh_public_key(session["account"], name)
|
||||
|
||||
for error in errors:
|
||||
flash(error)
|
||||
|
||||
keys_list=list(map(
|
||||
lambda x: dict(name=x['name'], content=f"{x['content'][:20]}...{x['content'][len(x['content'])-20:]}"),
|
||||
db_model.list_ssh_public_keys_for_account(session["account"])
|
||||
))
|
||||
|
||||
return render_template("ssh-public-keys.html", ssh_public_keys=keys_list, has_ssh_public_keys=len(keys_list) > 0)
|
||||
|
||||
@bp.route("/create", methods=("GET", "POST"))
|
||||
@account_required
|
||||
@ -108,7 +103,7 @@ def create():
|
||||
errors.append(f"Invalid os {os}")
|
||||
|
||||
posted_keys_count = int(request.form["ssh_public_key_count"])
|
||||
posted_keys_contents = list()
|
||||
posted_keys = list()
|
||||
|
||||
if posted_keys_count > 1000:
|
||||
errors.append("something went wrong with ssh keys")
|
||||
@ -116,16 +111,16 @@ def create():
|
||||
for i in range(0, posted_keys_count):
|
||||
if f"ssh_key_{i}" in request.form:
|
||||
posted_name = request.form[f"ssh_key_{i}"]
|
||||
key_content = None
|
||||
for key in ssh_public_keys:
|
||||
if key['name'] == posted_name:
|
||||
key_content = key['content']
|
||||
if key_content:
|
||||
posted_keys_contents.append(key_content)
|
||||
key = None
|
||||
for x in ssh_public_keys:
|
||||
if x['name'] == posted_name:
|
||||
key = x
|
||||
if key:
|
||||
posted_keys.append(key)
|
||||
else:
|
||||
errors.append(f"SSH Key \"{posted_name}\" doesn't exist")
|
||||
|
||||
if len(posted_keys_contents) == 0:
|
||||
if len(posted_keys) == 0:
|
||||
errors.append("At least one SSH Public Key is required")
|
||||
|
||||
if len(errors) == 0:
|
||||
@ -134,7 +129,8 @@ def create():
|
||||
email=session["account"],
|
||||
id=id,
|
||||
size=size,
|
||||
os=os
|
||||
os=os,
|
||||
ssh_public_keys=list(map(lambda x: x["name"], posted_keys))
|
||||
)
|
||||
current_app.config["VIRTUALIZATION_MODEL"].create(
|
||||
email = session["account"],
|
||||
@ -142,7 +138,7 @@ def create():
|
||||
template_image_file_name=operating_systems[os]['template_image_file_name'],
|
||||
vcpus=vm_sizes[size]['vcpus'],
|
||||
memory_mb=vm_sizes[size]['memory_mb'],
|
||||
ssh_public_keys=posted_keys_contents
|
||||
ssh_public_keys=list(map(lambda x: x["content"], posted_keys))
|
||||
)
|
||||
created_os = os
|
||||
|
||||
@ -159,6 +155,52 @@ def create():
|
||||
vm_sizes=vm_sizes
|
||||
)
|
||||
|
||||
@bp.route("/ssh", methods=("GET", "POST"))
|
||||
@account_required
|
||||
def ssh_public_keys():
|
||||
db_model = get_model()
|
||||
errors = list()
|
||||
|
||||
if request.method == "POST":
|
||||
method = request.form["method"]
|
||||
|
||||
name = request.form["name"]
|
||||
if not name or len(name.strip()) < 1:
|
||||
errors.append("Name is required")
|
||||
elif not re.match(r"^[0-9A-Za-z_ -]+$", name):
|
||||
errors.append("Name must match \"^[0-9A-Za-z_ -]+$\"")
|
||||
|
||||
if method == "POST":
|
||||
content = request.form["content"]
|
||||
if not content or len(content.strip()) < 1:
|
||||
errors.append("Content is required")
|
||||
else:
|
||||
content = content.replace("\r", "").replace("\n", "")
|
||||
if not re.match(r"^(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$", content):
|
||||
errors.append("Content must match \"^(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$\"")
|
||||
|
||||
if db_model.ssh_public_key_name_exists(session["account"], name):
|
||||
errors.append("A key with that name already exists")
|
||||
|
||||
if len(errors) == 0:
|
||||
db_model.create_ssh_public_key(session["account"], name, content)
|
||||
|
||||
elif method == "DELETE":
|
||||
|
||||
if len(errors) == 0:
|
||||
db_model.delete_ssh_public_key(session["account"], name)
|
||||
|
||||
for error in errors:
|
||||
flash(error)
|
||||
|
||||
keys_list=list(map(
|
||||
lambda x: dict(name=x['name'], content=f"{x['content'][:20]}...{x['content'][len(x['content'])-20:]}"),
|
||||
db_model.list_ssh_public_keys_for_account(session["account"])
|
||||
))
|
||||
|
||||
return render_template("ssh-public-keys.html", ssh_public_keys=keys_list, has_ssh_public_keys=len(keys_list) > 0)
|
||||
|
||||
|
||||
@bp.route("/billing")
|
||||
@account_required
|
||||
def faq():
|
||||
|
@ -89,11 +89,52 @@ class DBModel:
|
||||
self.cursor.fetchall()
|
||||
))
|
||||
|
||||
def create_vm(self, email, id, size, os):
|
||||
def updateVm(self, email, id, ipv4):
|
||||
self.cursor.execute("UPDATE vms SET last_seen_ipv4 = %s WHERE email = %s AND id = %s", (ipv4, email, id))
|
||||
self.connection.commit()
|
||||
|
||||
def create_vm(self, email, id, size, os, ssh_public_keys):
|
||||
self.cursor.execute("""
|
||||
INSERT INTO vms (email, id, size, os)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
""",
|
||||
(email, id, size, os)
|
||||
)
|
||||
|
||||
for ssh_public_key in ssh_public_keys:
|
||||
self.cursor.execute("""
|
||||
INSERT INTO vm_ssh_public_key (email, vm_id, ssh_public_key_name)
|
||||
VALUES (%s, %s, %s)
|
||||
""",
|
||||
(email, id, ssh_public_key)
|
||||
)
|
||||
self.connection.commit()
|
||||
|
||||
def get_vm_detail(self, email, id):
|
||||
self.cursor.execute("""
|
||||
SELECT vms.id, vms.last_seen_ipv4, vms.last_seen_ipv6, os_images.description, vms.created, vms.deleted,
|
||||
vm_sizes.id, vm_sizes.dollars_per_month, vm_sizes.vcpus, vm_sizes.memory_mb, vm_sizes.bandwidth_gb_per_month
|
||||
FROM vms
|
||||
JOIN os_images on vms.os = os_images.id
|
||||
JOIN vm_sizes on vms.size = vm_sizes.id
|
||||
WHERE vms.email = %s AND vms.id = %s""",
|
||||
(email, id)
|
||||
)
|
||||
rows = self.cursor.fetchall()
|
||||
if len(rows) == 0:
|
||||
return None
|
||||
|
||||
x = rows[0]
|
||||
vm = dict(
|
||||
id=x[0], ipv4=x[1], ipv6=x[2], os_description=x[3], created=x[4], deleted=x[5],
|
||||
size=x[6], dollars_per_month=x[7], vcpus=x[8], memory_mb=x[9], bandwidth_gb_per_month=x[10]
|
||||
)
|
||||
|
||||
self.cursor.execute("""
|
||||
SELECT ssh_public_key_name FROM vm_ssh_public_key
|
||||
WHERE vm_ssh_public_key.email = %s AND vm_ssh_public_key.vm_id = %s""",
|
||||
(email, id)
|
||||
)
|
||||
vm["ssh_public_keys"] = list(map( lambda x: x[0], self.cursor.fetchall() ))
|
||||
|
||||
return vm
|
||||
|
@ -32,8 +32,8 @@ if echo "$memory" | grep -vqE "^[0-9]+$"; then
|
||||
fi
|
||||
|
||||
echo "$pubkeys" | while IFS= read -r line; do
|
||||
if echo "$line" | grep -vqE "^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$"; then
|
||||
echo "pubkey \"$line\" must match "'"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$"'
|
||||
if echo "$line" | grep -vqE "^(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$"; then
|
||||
echo "pubkey \"$line\" must match "'"^(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$"'
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
54
capsulflask/templates/capsul-detail.html
Normal file
54
capsulflask/templates/capsul-detail.html
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Create{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="third-margin">
|
||||
<h1>{{ vm['id'] }}</h1>
|
||||
</div>
|
||||
<div class="half-margin">
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="created">Created</label>
|
||||
<span id=created>{{ vm['created'] }}</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="size">Capsul Size</label>
|
||||
<span id=size>{{ vm['size'] }}</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="dollars_per_month">Monthly Cost</label>
|
||||
<span id=dollars_per_month>${{ vm['dollars_per_month'] }}</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="ipv4">IPv4 Address</label>
|
||||
<span id=ipv4>{{ vm['ipv4'] }}</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="os_description">Operating System</label>
|
||||
<span id=os_description>{{ vm['os_description'] }}</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="vcpus">VCPUs</label>
|
||||
<span id=vcpus>{{ vm['vcpus'] }}</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="memory_mb">Memory</label>
|
||||
<span id=memory_mb>{{ vm['memory_mb'] }}MB</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="bandwidth_gb_per_month">Bandwidth</label>
|
||||
<span id=bandwidth_gb_per_month>{{ vm['bandwidth_gb_per_month'] }}GB/month</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="ssh_username">SSH Username</label>
|
||||
<span id=ssh_username>cyberian</span>
|
||||
</div>
|
||||
<div class="row justify-start">
|
||||
<label class="align" for="ssh_public_keys">SSH Public Keys</label>
|
||||
<a id=ssh_public_keys href="/console/ssh">{{ vm['ssh_public_keys'] }}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagesource %}/templates/create-capsul.html{% endblock %}
|
@ -86,8 +86,8 @@ class ShellScriptVirtualization(VirtualizationInterface):
|
||||
raise ValueError(f"template_image_file_name \"{template_image_file_name}\" must match \"^[a-zA-Z0-9_.-]$\"")
|
||||
|
||||
for ssh_public_key in ssh_public_keys:
|
||||
if not re.match(r"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$", ssh_public_key):
|
||||
raise ValueError(f"ssh_public_key \"{ssh_public_key}\" must match \"^(ssh|ecdsa)-[0-9A-Za-z+/_=@ -]+$\"")
|
||||
if not re.match(r"^(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$", ssh_public_key):
|
||||
raise ValueError(f"ssh_public_key \"{ssh_public_key}\" must match \"^(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$\"")
|
||||
|
||||
if vcpus < 1 or vcpus > 8:
|
||||
raise ValueError(f"vcpus \"{vcpus}\" must match 1 <= vcpus <= 8")
|
||||
|
Loading…
Reference in New Issue
Block a user