forked from 3wordchant/capsul-flask
first try at implementing the vm start and stop feature
This commit is contained in:
parent
e8348052a8
commit
ba0b29462c
@ -34,7 +34,7 @@ def double_check_capsul_address(id, ipv4, get_ssh_host_keys):
|
|||||||
ipv4 = result.ipv4
|
ipv4 = result.ipv4
|
||||||
get_model().update_vm_ip(email=session["account"], id=id, ipv4=result.ipv4)
|
get_model().update_vm_ip(email=session["account"], id=id, ipv4=result.ipv4)
|
||||||
|
|
||||||
if result != None and result.ipv4 != None and get_ssh_host_keys:
|
if result != None and result.ssh_host_keys != None and get_ssh_host_keys:
|
||||||
get_model().update_vm_ssh_host_keys(email=session["account"], id=id, ssh_host_keys=result.ssh_host_keys)
|
get_model().update_vm_ssh_host_keys(email=session["account"], id=id, ssh_host_keys=result.ssh_host_keys)
|
||||||
except:
|
except:
|
||||||
current_app.logger.error(f"""
|
current_app.logger.error(f"""
|
||||||
@ -62,11 +62,13 @@ def index():
|
|||||||
result = double_check_capsul_address(vm["id"], vm["ipv4"], False)
|
result = double_check_capsul_address(vm["id"], vm["ipv4"], False)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
vm["ipv4"] = result.ipv4
|
vm["ipv4"] = result.ipv4
|
||||||
|
vm["state"] = result.state
|
||||||
|
|
||||||
vms = list(map(
|
vms = list(map(
|
||||||
lambda x: dict(
|
lambda x: dict(
|
||||||
id=x['id'],
|
id=x['id'],
|
||||||
size=x['size'],
|
size=x['size'],
|
||||||
|
state=x['state'],
|
||||||
ipv4=(x['ipv4'] if x['ipv4'] else "..booting.."),
|
ipv4=(x['ipv4'] if x['ipv4'] else "..booting.."),
|
||||||
ipv4_status=("ok" if x['ipv4'] else "waiting-pulse"),
|
ipv4_status=("ok" if x['ipv4'] else "waiting-pulse"),
|
||||||
os=x['os'],
|
os=x['os'],
|
||||||
@ -92,24 +94,58 @@ def detail(id):
|
|||||||
if vm['deleted']:
|
if vm['deleted']:
|
||||||
return render_template("capsul-detail.html", vm=vm, delete=True, deleted=True)
|
return render_template("capsul-detail.html", vm=vm, delete=True, deleted=True)
|
||||||
|
|
||||||
|
vm["created"] = vm['created'].strftime("%b %d %Y %H:%M")
|
||||||
|
vm["ssh_authorized_keys"] = ", ".join(vm["ssh_authorized_keys"]) if len(vm["ssh_authorized_keys"]) > 0 else "<missing>"
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if "csrf-token" not in request.form or request.form['csrf-token'] != session['csrf-token']:
|
if "csrf-token" not in request.form or request.form['csrf-token'] != session['csrf-token']:
|
||||||
return abort(418, f"u want tea")
|
return abort(418, f"u want tea")
|
||||||
|
|
||||||
if 'are_you_sure' not in request.form or not request.form['are_you_sure']:
|
if 'action' not in request.form:
|
||||||
return render_template(
|
return abort(400, "action is required")
|
||||||
"capsul-detail.html",
|
|
||||||
csrf_token = session["csrf-token"],
|
if request.form['action'] == "start":
|
||||||
vm=vm,
|
current_app.config["HUB_MODEL"].vm_state_command(email=session['account'], id=id, command="start")
|
||||||
delete=True,
|
|
||||||
deleted=False
|
vm["state"] = "starting"
|
||||||
)
|
return render_template("capsul-detail.html", vm=vm)
|
||||||
else:
|
elif request.form['action'] == "delete":
|
||||||
current_app.logger.info(f"deleting {vm['id']} per user request ({session['account']})")
|
if 'are_you_sure' not in request.form or not request.form['are_you_sure']:
|
||||||
current_app.config["HUB_MODEL"].destroy(email=session['account'], id=id)
|
return render_template(
|
||||||
get_model().delete_vm(email=session['account'], id=id)
|
"capsul-detail.html",
|
||||||
|
csrf_token = session["csrf-token"],
|
||||||
|
vm=vm,
|
||||||
|
delete=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
current_app.logger.info(f"deleting {vm['id']} per user request ({session['account']})")
|
||||||
|
current_app.config["HUB_MODEL"].destroy(email=session['account'], id=id)
|
||||||
|
get_model().delete_vm(email=session['account'], id=id)
|
||||||
|
|
||||||
|
return render_template("capsul-detail.html", vm=vm, deleted=True)
|
||||||
|
elif request.form['action'] == "force-stop":
|
||||||
|
if 'are_you_sure' not in request.form or not request.form['are_you_sure']:
|
||||||
|
return render_template(
|
||||||
|
"capsul-detail.html",
|
||||||
|
csrf_token = session["csrf-token"],
|
||||||
|
vm=vm,
|
||||||
|
force_stop=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
current_app.logger.info(f"force stopping {vm['id']} per user request ({session['account']})")
|
||||||
|
current_app.config["HUB_MODEL"].vm_state_command(email=session['account'], id=id, command="force-stop")
|
||||||
|
|
||||||
|
vm["state"] = "stopped"
|
||||||
|
return render_template(
|
||||||
|
"capsul-detail.html",
|
||||||
|
csrf_token = session["csrf-token"],
|
||||||
|
vm=vm,
|
||||||
|
durations=list(map(lambda x: x.strip("_"), metric_durations.keys())),
|
||||||
|
duration=duration
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return abort(400, "action must be either delete, force-stop, or start")
|
||||||
|
|
||||||
return render_template("capsul-detail.html", vm=vm, delete=True, deleted=True)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
needs_ssh_host_keys = "ssh_host_keys" not in vm or len(vm["ssh_host_keys"]) == 0
|
needs_ssh_host_keys = "ssh_host_keys" not in vm or len(vm["ssh_host_keys"]) == 0
|
||||||
@ -118,17 +154,17 @@ def detail(id):
|
|||||||
|
|
||||||
if vm_from_virt_model is not None:
|
if vm_from_virt_model is not None:
|
||||||
vm["ipv4"] = vm_from_virt_model.ipv4
|
vm["ipv4"] = vm_from_virt_model.ipv4
|
||||||
|
vm["state"] = vm_from_virt_model.state
|
||||||
if needs_ssh_host_keys:
|
if needs_ssh_host_keys:
|
||||||
vm["ssh_host_keys"] = vm_from_virt_model.ssh_host_keys
|
vm["ssh_host_keys"] = vm_from_virt_model.ssh_host_keys
|
||||||
|
|
||||||
vm["created"] = vm['created'].strftime("%b %d %Y %H:%M")
|
if vm["state"] == "running" and not vm["ipv4"]:
|
||||||
vm["ssh_authorized_keys"] = ", ".join(vm["ssh_authorized_keys"]) if len(vm["ssh_authorized_keys"]) > 0 else "<missing>"
|
vm["state"] = "starting"
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"capsul-detail.html",
|
"capsul-detail.html",
|
||||||
csrf_token = session["csrf-token"],
|
csrf_token = session["csrf-token"],
|
||||||
vm=vm,
|
vm=vm,
|
||||||
delete=False,
|
|
||||||
durations=list(map(lambda x: x.strip("_"), metric_durations.keys())),
|
durations=list(map(lambda x: x.strip("_"), metric_durations.keys())),
|
||||||
duration=duration
|
duration=duration
|
||||||
)
|
)
|
||||||
|
@ -144,8 +144,8 @@ class CapsulFlaskHub(VirtualizationInterface):
|
|||||||
for result in results:
|
for result in results:
|
||||||
try:
|
try:
|
||||||
result_body = json.loads(result.body)
|
result_body = json.loads(result.body)
|
||||||
if isinstance(result_body, dict) and ('ipv4' in result_body or 'ipv6' in result_body):
|
if isinstance(result_body, dict) and ('state' in result_body):
|
||||||
return VirtualMachine(id, host=host, ipv4=result_body['ipv4'], ipv6=result_body['ipv6'], ssh_host_keys=result_body['ssh_host_keys'])
|
return VirtualMachine(id, host=host, state=result_body['state'], ipv4=result_body['ipv4'], ipv6=result_body['ipv6'], ssh_host_keys=result_body['ssh_host_keys'])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -16,11 +16,12 @@ class OnlineHost:
|
|||||||
# self.sha256 = sha256
|
# self.sha256 = sha256
|
||||||
|
|
||||||
class VirtualMachine:
|
class VirtualMachine:
|
||||||
def __init__(self, id, host, ipv4=None, ipv6=None, ssh_host_keys: List[dict] = list()):
|
def __init__(self, id, host, ipv4=None, ipv6=None, state="unknown", ssh_host_keys: List[dict] = list()):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.host = host
|
self.host = host
|
||||||
self.ipv4 = ipv4
|
self.ipv4 = ipv4
|
||||||
self.ipv6 = ipv6
|
self.ipv6 = ipv6
|
||||||
|
self.state = state
|
||||||
self.ssh_host_keys = ssh_host_keys
|
self.ssh_host_keys = ssh_host_keys
|
||||||
|
|
||||||
class VirtualizationInterface:
|
class VirtualizationInterface:
|
||||||
|
@ -10,11 +10,27 @@ fi
|
|||||||
|
|
||||||
# this will let us know if the vm exists or not
|
# this will let us know if the vm exists or not
|
||||||
exists="false"
|
exists="false"
|
||||||
if virsh domuuid "$vmname" | grep -vqE '^[\t\s\n]*$'; then
|
state = "unknown"
|
||||||
|
if ! virsh domuuid "$vmname" | grep -qE '^[\t\s\n]*$'; then
|
||||||
exists="true"
|
exists="true"
|
||||||
|
|
||||||
|
state_code="$(virsh domstats $vmname | grep state.state | cut -d '=' -f 2)"
|
||||||
|
|
||||||
|
if ! printf "$state_code" | grep -qE '^[0-8]$'; then
|
||||||
|
printf 'state_code was not detected. state_code %s must match ^[0-8]$\n' "$state_code"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$state_code" in
|
||||||
|
1) state = "running" ;;
|
||||||
|
2) state = "blocked" ;;
|
||||||
|
4) state = "stopping" ;;
|
||||||
|
6) state = "crashed" ;;
|
||||||
|
[357]) state = "stopped" ;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# this gets the ipv4
|
# this gets the ipv4
|
||||||
ipv4="$(virsh domifaddr "$vmname" | awk '/vnet/ {print $4}' | cut -d'/' -f1)"
|
ipv4="$(virsh domifaddr "$vmname" | awk '/vnet/ {print $4}' | cut -d'/' -f1)"
|
||||||
|
|
||||||
echo "$exists $ipv4"
|
echo "$exists $state $ipv4"
|
@ -94,7 +94,7 @@ def handle_get(operation_id, request_body):
|
|||||||
if vm is None:
|
if vm is None:
|
||||||
return jsonify(dict(assignment_status="assigned"))
|
return jsonify(dict(assignment_status="assigned"))
|
||||||
|
|
||||||
return jsonify(dict(assignment_status="assigned", id=vm.id, host=vm.host, ipv4=vm.ipv4, ipv6=vm.ipv6, ssh_host_keys=vm.ssh_host_keys))
|
return jsonify(dict(assignment_status="assigned", id=vm.id, host=vm.host, state=vm.state, ipv4=vm.ipv4, ipv6=vm.ipv6, ssh_host_keys=vm.ssh_host_keys))
|
||||||
|
|
||||||
def handle_list_ids(operation_id, request_body):
|
def handle_list_ids(operation_id, request_body):
|
||||||
return jsonify(dict(assignment_status="assigned", ids=current_app.config['SPOKE_MODEL'].list_ids()))
|
return jsonify(dict(assignment_status="assigned", ids=current_app.config['SPOKE_MODEL'].list_ids()))
|
||||||
|
@ -26,9 +26,9 @@ class MockSpoke(VirtualizationInterface):
|
|||||||
{"key_type":"RSA", "content":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvotgzgEP65JUQ8S8OoNKy1uEEPEAcFetSp7QpONe6hj4wPgyFNgVtdoWdNcU19dX3hpdse0G8OlaMUTnNVuRlbIZXuifXQ2jTtCFUA2mmJ5bF+XjGm3TXKMNGh9PN+wEPUeWd14vZL+QPUMev5LmA8cawPiU5+vVMLid93HRBj118aCJFQxLgrdP48VPfKHFRfCR6TIjg1ii3dH4acdJAvlmJ3GFB6ICT42EmBqskz2MPe0rIFxH8YohCBbAbrbWYcptHt4e48h4UdpZdYOhEdv89GrT8BF2C5cbQ5i9qVpI57bXKrj8hPZU5of48UHLSpXG8mbH0YDiOQOfKX/Mt", "sha256":"ghee6KzRnBJhND2kEUZSaouk7CD6o6z2aAc8GPkV+GQ"},
|
{"key_type":"RSA", "content":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvotgzgEP65JUQ8S8OoNKy1uEEPEAcFetSp7QpONe6hj4wPgyFNgVtdoWdNcU19dX3hpdse0G8OlaMUTnNVuRlbIZXuifXQ2jTtCFUA2mmJ5bF+XjGm3TXKMNGh9PN+wEPUeWd14vZL+QPUMev5LmA8cawPiU5+vVMLid93HRBj118aCJFQxLgrdP48VPfKHFRfCR6TIjg1ii3dH4acdJAvlmJ3GFB6ICT42EmBqskz2MPe0rIFxH8YohCBbAbrbWYcptHt4e48h4UdpZdYOhEdv89GrT8BF2C5cbQ5i9qVpI57bXKrj8hPZU5of48UHLSpXG8mbH0YDiOQOfKX/Mt", "sha256":"ghee6KzRnBJhND2kEUZSaouk7CD6o6z2aAc8GPkV+GQ"},
|
||||||
{"key_type":"ECDSA", "content":"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLLgOoATz9R4aS2kk7vWoxX+lshK63t9+5BIHdzZeFE1o+shlcf0Wji8cN/L1+m3bi0uSETZDOAWMP3rHLJj9Hk=", "sha256":"aCYG1aD8cv/TjzJL0bi9jdabMGksdkfa7R8dCGm1yYs"}
|
{"key_type":"ECDSA", "content":"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLLgOoATz9R4aS2kk7vWoxX+lshK63t9+5BIHdzZeFE1o+shlcf0Wji8cN/L1+m3bi0uSETZDOAWMP3rHLJj9Hk=", "sha256":"aCYG1aD8cv/TjzJL0bi9jdabMGksdkfa7R8dCGm1yYs"}
|
||||||
]""")
|
]""")
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4="1.1.1.1", ssh_host_keys=ssh_host_keys)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4="1.1.1.1", state="running", ssh_host_keys=ssh_host_keys)
|
||||||
|
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4="1.1.1.1")
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4="1.1.1.1", state="running")
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def list_ids(self) -> list:
|
||||||
return get_model().all_non_deleted_vm_ids()
|
return get_model().all_non_deleted_vm_ids()
|
||||||
@ -98,24 +98,29 @@ class ShellScriptSpoke(VirtualizationInterface):
|
|||||||
if len(fields) < 2:
|
if len(fields) < 2:
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"])
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"])
|
||||||
|
|
||||||
ipaddr = fields[1]
|
state = fields[1]
|
||||||
|
|
||||||
|
if len(fields) < 3:
|
||||||
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state)
|
||||||
|
|
||||||
|
ipaddr = fields[2]
|
||||||
|
|
||||||
if not re.match(r"^([0-9]{1,3}\.){3}[0-9]{1,3}$", ipaddr):
|
if not re.match(r"^([0-9]{1,3}\.){3}[0-9]{1,3}$", ipaddr):
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"])
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state)
|
||||||
|
|
||||||
if get_ssh_host_keys:
|
if get_ssh_host_keys:
|
||||||
try:
|
try:
|
||||||
completedProcess2 = run([join(current_app.root_path, 'shell_scripts/ssh-keyscan.sh'), ipaddr], capture_output=True)
|
completedProcess2 = run([join(current_app.root_path, 'shell_scripts/ssh-keyscan.sh'), ipaddr], capture_output=True)
|
||||||
self.validate_completed_process(completedProcess2)
|
self.validate_completed_process(completedProcess2)
|
||||||
ssh_host_keys = json.loads(completedProcess2.stdout.decode("utf-8"))
|
ssh_host_keys = json.loads(completedProcess2.stdout.decode("utf-8"))
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=ipaddr, ssh_host_keys=ssh_host_keys)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ipaddr, ssh_host_keys=ssh_host_keys)
|
||||||
except:
|
except:
|
||||||
current_app.logger.warning(f"""
|
current_app.logger.warning(f"""
|
||||||
failed to ssh-keyscan {id} at {ipaddr}:
|
failed to ssh-keyscan {id} at {ipaddr}:
|
||||||
{my_exec_info_message(sys.exc_info())}"""
|
{my_exec_info_message(sys.exc_info())}"""
|
||||||
)
|
)
|
||||||
|
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=ipaddr)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ipaddr)
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def list_ids(self) -> list:
|
||||||
completedProcess = run([join(current_app.root_path, 'shell_scripts/list-ids.sh')], capture_output=True)
|
completedProcess = run([join(current_app.root_path, 'shell_scripts/list-ids.sh')], capture_output=True)
|
||||||
|
@ -131,7 +131,8 @@ pre.wrap {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.align {
|
label.align,
|
||||||
|
.vm-actions form {
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,11 +251,26 @@ table.small td, table.small th {
|
|||||||
table.small td.metrics {
|
table.small td.metrics {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
th.heart-icon {
|
||||||
|
font-size: calc(0.40rem + 2.3vmin);
|
||||||
|
line-height: 1rem;
|
||||||
|
padding-left: 0.3rem;
|
||||||
|
padding-right: 0.2rem;
|
||||||
|
}
|
||||||
|
td.capsul-status {
|
||||||
|
line-height: 1rem;
|
||||||
|
padding: 0;
|
||||||
|
padding-left: 0.07em;
|
||||||
|
font-size: calc(0.4rem + 3.4vmin);
|
||||||
|
padding-top: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
td.metrics img {
|
td.metrics img {
|
||||||
margin-left: -20px;
|
margin-left: -1.2em;
|
||||||
margin-right: -20px;
|
margin-right: -1.2em;
|
||||||
margin-top: -5px;
|
margin-top: -0.25em;
|
||||||
margin-bottom: -5px;
|
margin-bottom: -0.25em;
|
||||||
|
width: 4.3em;
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
border-right: 4px solid #241e1e;
|
border-right: 4px solid #241e1e;
|
||||||
@ -301,6 +317,15 @@ pre.code.wrap {
|
|||||||
color: #777e73bb;
|
color: #777e73bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: #c21d00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
color: #069e5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
footer, p {
|
footer, p {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,34 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if delete %}
|
{% if deleted %}
|
||||||
{% if deleted %}
|
<div class="row third-margin">
|
||||||
|
<h1>DELETED</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row third-margin">
|
||||||
|
<p>{{ vm['id'] }} has been deleted.</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{% if force_stop %}
|
||||||
<div class="row third-margin">
|
<div class="row third-margin">
|
||||||
<h1>DELETED</h1>
|
<h1>Are you sure?</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="row third-margin">
|
<div class="row third-margin">
|
||||||
<p>{{ vm['id'] }} has been deleted.</p>
|
<p>
|
||||||
|
Are you sure you want to force stop {{ vm['id'] }}?
|
||||||
|
This would be like unplugging the power supply.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
<div class="row third-margin">
|
||||||
|
<a href="/console/{{ vm['id'] }}">No, Cancel!</a>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="action" value="force-stop"/>
|
||||||
|
<input type="hidden" name="are_you_sure" value="True"/>
|
||||||
|
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
||||||
|
<input type="submit" class="form-submit-link" value="Yes, Force Stop">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% elif delete %}
|
||||||
<div class="row third-margin">
|
<div class="row third-margin">
|
||||||
<h1>Are you sure?</h1>
|
<h1>Are you sure?</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -21,147 +40,173 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row third-margin">
|
<div class="row third-margin">
|
||||||
<a href="/console/{{ vm['id'] }}">No, Cancel!</a>
|
<a href="/console/{{ vm['id'] }}">No, Cancel!</a>
|
||||||
<form id="delete_action" method="post">
|
<form method="post">
|
||||||
<input type="hidden" name="delete" value="True"/>
|
<input type="hidden" name="action" value="delete"/>
|
||||||
<input type="hidden" name="are_you_sure" value="True"/>
|
<input type="hidden" name="are_you_sure" value="True"/>
|
||||||
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
||||||
<input type="submit" class="form-submit-link" value="Yes, Delete">
|
<input type="submit" class="form-submit-link" value="Yes, Delete">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="row third-margin">
|
||||||
|
<h1>{{ vm['id'] }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row wrap grid-large third-margin">
|
||||||
|
|
||||||
{% endif %}
|
<div class="row justify-start">
|
||||||
{% else %}
|
<label class="align" for="created">Created</label>
|
||||||
<div class="row third-margin">
|
<span id="created">{{ vm['created'] }}</span>
|
||||||
<h1>{{ vm['id'] }}</h1>
|
</div>
|
||||||
</div>
|
<div class="row justify-start">
|
||||||
<div class="row wrap grid-large third-margin">
|
<label class="align" for="size">Capsul Size</label>
|
||||||
|
<span id="size">{{ vm['size'] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-start">
|
||||||
|
<label class="align" for="vm_state">State</label>
|
||||||
|
{% if vm['state'] == 'starting' or vm['state'] == 'stopping' %}
|
||||||
|
<span id="vm_state" class="waiting-pulse">{{ vm['state'] }}</span>
|
||||||
|
{% elif vm['state'] == 'crashed' or vm['state'] == 'blocked' %}
|
||||||
|
<span id="vm_state" class="red">{{ vm['state'] }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span id="vm_state">{{ vm['state'] }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</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_authorized_keys">SSH Authorized Keys</label>
|
||||||
|
<a id="ssh_authorized_keys" href="/console/ssh">{{ vm['ssh_authorized_keys'] }}</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row justify-start">
|
|
||||||
<label class="align" for="created">Created</label>
|
|
||||||
<span id="created">{{ vm['created'] }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-start">
|
<div class="row center justify-start vm-actions">
|
||||||
<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_authorized_keys">SSH Authorized Keys</label>
|
|
||||||
<a id="ssh_authorized_keys" href="/console/ssh">{{ vm['ssh_authorized_keys'] }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="row center justify-start">
|
|
||||||
<label class="align" for="delete_action">Actions</label>
|
<label class="align" for="delete_action">Actions</label>
|
||||||
<form id="delete_action" method="post">
|
<form id="delete_action" method="post">
|
||||||
<input type="hidden" name="delete" value="True"/>
|
<input type="hidden" name="action" value="delete"/>
|
||||||
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
||||||
<input type="submit" class="form-submit-link" value="Delete...">
|
<input type="submit" class="form-submit-link" value="Delete...">
|
||||||
</form>
|
</form>
|
||||||
|
{% if vm['state'] == 'crashed' or vm['state'] == 'stopped' %}
|
||||||
|
<form id="start_action" method="post">
|
||||||
|
<input type="hidden" name="action" value="start"/>
|
||||||
|
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
||||||
|
<input type="submit" class="form-submit-link" value="Start">
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% if vm['state'] != 'stopped' %}
|
||||||
|
<form id="force_stop_action" method="post">
|
||||||
|
<input type="hidden" name="action" value="force-stop"/>
|
||||||
|
<input type="hidden" name="csrf-token" value="{{ csrf_token }}"/>
|
||||||
|
<input type="submit" class="form-submit-link" value="Force Stop...">
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="row third-margin">
|
||||||
<div class="row third-margin">
|
<h1>ssh host key fingerprints</h1>
|
||||||
<h1>ssh host key fingerprints</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<pre class="code">{% for key in vm['ssh_host_keys'] %}
|
|
||||||
SHA256:{{ key.sha256 }} ({{ key.key_type }}){% endfor %}</pre>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<span>(What's this? see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>)</span>
|
|
||||||
</div>
|
|
||||||
<div class="row ">
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
<div class="row third-margin">
|
|
||||||
{% for d in durations %}
|
|
||||||
<a href="/console/{{ vm['id'] }}?duration={{ d }}">
|
|
||||||
{% if d == duration %}
|
|
||||||
<span class="code">{{ d }}</span>
|
|
||||||
{% else %}
|
|
||||||
{{ d }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<div class="row wrap grid-small justify-end">
|
|
||||||
<div class="metric">
|
|
||||||
<h1>cpu</h1>
|
|
||||||
<a href="/metrics/html/cpu/{{ vm['id'] }}/{{ duration }}">
|
|
||||||
<img src="/metrics/cpu/{{ vm['id'] }}/{{ duration }}/m"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="metric">
|
<div class="row">
|
||||||
<h1>memory</h1>
|
<pre class="code">{% for key in vm['ssh_host_keys'] %}
|
||||||
<a href="/metrics/html/memory/{{ vm['id'] }}/{{ duration }}">
|
SHA256:{{ key.sha256 }} ({{ key.key_type }}){% endfor %}</pre>
|
||||||
<img src="/metrics/memory/{{ vm['id'] }}/{{ duration }}/m"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<span>(What's this? see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>)</span>
|
||||||
|
</div>
|
||||||
|
<div class="row ">
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div class="row third-margin">
|
||||||
|
{% for d in durations %}
|
||||||
|
<a href="/console/{{ vm['id'] }}?duration={{ d }}">
|
||||||
|
{% if d == duration %}
|
||||||
|
<span class="code">{{ d }}</span>
|
||||||
|
{% else %}
|
||||||
|
{{ d }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="metric">
|
</a>
|
||||||
<h1>network_in</h1>
|
{% endfor %}
|
||||||
<a href="/metrics/html/network_in/{{ vm['id'] }}/{{ duration }}">
|
|
||||||
<img src="/metrics/network_in/{{ vm['id'] }}/{{ duration }}/m"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row wrap grid-small justify-end">
|
||||||
|
<div class="metric">
|
||||||
|
<h1>cpu</h1>
|
||||||
|
<a href="/metrics/html/cpu/{{ vm['id'] }}/{{ duration }}">
|
||||||
|
<img src="/metrics/cpu/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="metric">
|
<div class="metric">
|
||||||
<h1>network_out</h1>
|
<h1>memory</h1>
|
||||||
<a href="/metrics/html/network_out/{{ vm['id'] }}/{{ duration }}">
|
<a href="/metrics/html/memory/{{ vm['id'] }}/{{ duration }}">
|
||||||
<img src="/metrics/network_out/{{ vm['id'] }}/{{ duration }}/m"/>
|
<img src="/metrics/memory/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="metric">
|
<div class="metric">
|
||||||
<h1>disk</h1>
|
<h1>network_in</h1>
|
||||||
<a href="/metrics/html/disk/{{ vm['id'] }}/{{ duration }}">
|
<a href="/metrics/html/network_in/{{ vm['id'] }}/{{ duration }}">
|
||||||
<img src="/metrics/disk/{{ vm['id'] }}/{{ duration }}/m"/>
|
<img src="/metrics/network_in/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="metric">
|
||||||
|
<h1>network_out</h1>
|
||||||
|
<a href="/metrics/html/network_out/{{ vm['id'] }}/{{ duration }}">
|
||||||
|
<img src="/metrics/network_out/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="metric">
|
||||||
|
<h1>disk</h1>
|
||||||
|
<a href="/metrics/html/disk/{{ vm['id'] }}/{{ duration }}">
|
||||||
|
<img src="/metrics/disk/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="row ">
|
||||||
<div class="row ">
|
<hr/>
|
||||||
<hr/>
|
</div>
|
||||||
</div>
|
<div class="row half-margin">
|
||||||
<div class="row half-margin">
|
add the following to your ~/.ssh/known_hosts file (optional)
|
||||||
add the following to your ~/.ssh/known_hosts file (optional)
|
</div>
|
||||||
</div>
|
<div class="row">
|
||||||
<div class="row">
|
<pre class="code wrap break-all smalltext">{% for key in vm['ssh_host_keys'] %}
|
||||||
<pre class="code wrap break-all smalltext">{% for key in vm['ssh_host_keys'] %}
|
{{ vm['ipv4'] }} {{ key.content }}{% endfor %}
|
||||||
{{ vm['ipv4'] }} {{ key.content }}{% endfor %}
|
</pre>
|
||||||
</pre>
|
</div>
|
||||||
</div>
|
<div class="row">
|
||||||
<div class="row">
|
<span>(What's this? see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>)</span>
|
||||||
<span>(What's this? see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>)</span>
|
</div>
|
||||||
</div>
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pagesource %}/templates/create-capsul.html{% endblock %}
|
{% block pagesource %}/templates/create-capsul.html{% endblock %}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th class="heart-icon">❦</th>
|
||||||
<th>id</th>
|
<th>id</th>
|
||||||
<th>size</th>
|
<th>size</th>
|
||||||
<th>cpu</th>
|
<th>cpu</th>
|
||||||
@ -33,6 +34,14 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for vm in vms %}
|
{% for vm in vms %}
|
||||||
<tr>
|
<tr>
|
||||||
|
{% if vm['state'] == 'starting' or vm['state'] == 'stopping' %}
|
||||||
|
<td class="capsul-status waiting-pulse">•</td>
|
||||||
|
{% elif vm['state'] == 'crashed' or vm['state'] == 'blocked' or vm['state'] == 'stopped' %}
|
||||||
|
<td class="capsul-status red">•</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="capsul-status green">•</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<td><a class="no-shadow" href="/console/{{ vm['id'] }}">{{ vm["id"] }}</a></td>
|
<td><a class="no-shadow" href="/console/{{ vm['id'] }}">{{ vm["id"] }}</a></td>
|
||||||
<td>{{ vm["size"] }}</td>
|
<td>{{ vm["size"] }}</td>
|
||||||
<td class="metrics"><img src="/metrics/cpu/{{ vm['id'] }}/5m/s"/></td>
|
<td class="metrics"><img src="/metrics/cpu/{{ vm['id'] }}/5m/s"/></td>
|
||||||
|
Loading…
Reference in New Issue
Block a user