metrics are working!!!
This commit is contained in:
parent
7337375ae8
commit
30464ac8e5
2
Pipfile
2
Pipfile
@ -25,6 +25,8 @@ typed-ast = "==1.4.1"
|
|||||||
Werkzeug = "==1.0.1"
|
Werkzeug = "==1.0.1"
|
||||||
wrapt = "==1.12.1"
|
wrapt = "==1.12.1"
|
||||||
stripe = "*"
|
stripe = "*"
|
||||||
|
matplotlib = "*"
|
||||||
|
requests = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
91
Pipfile.lock
generated
91
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "8355b0bc9024432220ab4f05b2997f827af534691520249c6ff8bb2db9014dc8"
|
"sha256": "30ae24715ab8f45ada9d367e0a7ef6e1b1a6a5f88cb0dd6bb6c3cd3b385f817f"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -53,6 +53,13 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==7.1.2"
|
"version": "==7.1.2"
|
||||||
},
|
},
|
||||||
|
"cycler": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d",
|
||||||
|
"sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"
|
||||||
|
],
|
||||||
|
"version": "==0.10.0"
|
||||||
|
},
|
||||||
"flask": {
|
"flask": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
|
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
|
||||||
@ -107,6 +114,27 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.11.2"
|
"version": "==2.11.2"
|
||||||
},
|
},
|
||||||
|
"kiwisolver": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:03662cbd3e6729f341a97dd2690b271e51a67a68322affab12a5b011344b973c",
|
||||||
|
"sha256:18d749f3e56c0480dccd1714230da0f328e6e4accf188dd4e6884bdd06bf02dd",
|
||||||
|
"sha256:247800260cd38160c362d211dcaf4ed0f7816afb5efe56544748b21d6ad6d17f",
|
||||||
|
"sha256:443c2320520eda0a5b930b2725b26f6175ca4453c61f739fef7a5847bd262f74",
|
||||||
|
"sha256:4eadb361baf3069f278b055e3bb53fa189cea2fd02cb2c353b7a99ebb4477ef1",
|
||||||
|
"sha256:556da0a5f60f6486ec4969abbc1dd83cf9b5c2deadc8288508e55c0f5f87d29c",
|
||||||
|
"sha256:603162139684ee56bcd57acc74035fceed7dd8d732f38c0959c8bd157f913fec",
|
||||||
|
"sha256:60a78858580761fe611d22127868f3dc9f98871e6fdf0a15cc4203ed9ba6179b",
|
||||||
|
"sha256:7cc095a4661bdd8a5742aaf7c10ea9fac142d76ff1770a0f84394038126d8fc7",
|
||||||
|
"sha256:c31bc3c8e903d60a1ea31a754c72559398d91b5929fcb329b1c3a3d3f6e72113",
|
||||||
|
"sha256:c955791d80e464da3b471ab41eb65cf5a40c15ce9b001fdc5bbc241170de58ec",
|
||||||
|
"sha256:d069ef4b20b1e6b19f790d00097a5d5d2c50871b66d10075dab78938dc2ee2cf",
|
||||||
|
"sha256:d52b989dc23cdaa92582ceb4af8d5bcc94d74b2c3e64cd6785558ec6a879793e",
|
||||||
|
"sha256:e586b28354d7b6584d8973656a7954b1c69c93f708c0c07b77884f91640b7657",
|
||||||
|
"sha256:efcf3397ae1e3c3a4a0a0636542bcad5adad3b1dd3e8e629d0b6e201347176c8",
|
||||||
|
"sha256:fccefc0d36a38c57b7bd233a9b485e2f1eb71903ca7ad7adacad6c28a56d62d2"
|
||||||
|
],
|
||||||
|
"version": "==1.2.0"
|
||||||
|
},
|
||||||
"lazy-object-proxy": {
|
"lazy-object-proxy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
|
"sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
|
||||||
@ -173,6 +201,26 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.1.1"
|
"version": "==1.1.1"
|
||||||
},
|
},
|
||||||
|
"matplotlib": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2466d4dddeb0f5666fd1e6736cc5287a4f9f7ae6c1a9e0779deff798b28e1d35",
|
||||||
|
"sha256:282b3fc8023c4365bad924d1bb442ddc565c2d1635f210b700722776da466ca3",
|
||||||
|
"sha256:4bb50ee4755271a2017b070984bcb788d483a8ce3132fab68393d1555b62d4ba",
|
||||||
|
"sha256:56d3147714da5c7ac4bc452d041e70e0e0b07c763f604110bd4e2527f320b86d",
|
||||||
|
"sha256:7a9baefad265907c6f0b037c8c35a10cf437f7708c27415a5513cf09ac6d6ddd",
|
||||||
|
"sha256:aae7d107dc37b4bb72dcc45f70394e6df2e5e92ac4079761aacd0e2ad1d3b1f7",
|
||||||
|
"sha256:af14e77829c5b5d5be11858d042d6f2459878f8e296228c7ea13ec1fd308eb68",
|
||||||
|
"sha256:c1cf735970b7cd424502719b44288b21089863aaaab099f55e0283a721aaf781",
|
||||||
|
"sha256:ce378047902b7a05546b6485b14df77b2ff207a0054e60c10b5680132090c8ee",
|
||||||
|
"sha256:d35891a86a4388b6965c2d527b9a9f9e657d9e110b0575ca8a24ba0d4e34b8fc",
|
||||||
|
"sha256:e06304686209331f99640642dee08781a9d55c6e32abb45ed54f021f46ccae47",
|
||||||
|
"sha256:e20ba7fb37d4647ac38f3c6d8672dd8b62451ee16173a0711b37ba0ce42bf37d",
|
||||||
|
"sha256:f4412241e32d0f8d3713b68d3ca6430190a5e8a7c070f1c07d7833d8c5264398",
|
||||||
|
"sha256:ffe2f9cdcea1086fc414e82f42271ecf1976700b8edd16ca9d376189c6d93aee"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.2.1"
|
||||||
|
},
|
||||||
"mccabe": {
|
"mccabe": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||||
@ -189,6 +237,32 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.0.0"
|
"version": "==2.0.0"
|
||||||
},
|
},
|
||||||
|
"numpy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:00d7b54c025601e28f468953d065b9b121ddca7fff30bed7be082d3656dd798d",
|
||||||
|
"sha256:02ec9582808c4e48be4e93cd629c855e644882faf704bc2bd6bbf58c08a2a897",
|
||||||
|
"sha256:0e6f72f7bb08f2f350ed4408bb7acdc0daba637e73bce9f5ea2b207039f3af88",
|
||||||
|
"sha256:1be2e96314a66f5f1ce7764274327fd4fb9da58584eaff00b5a5221edefee7d6",
|
||||||
|
"sha256:2466fbcf23711ebc5daa61d28ced319a6159b260a18839993d871096d66b93f7",
|
||||||
|
"sha256:2b573fcf6f9863ce746e4ad00ac18a948978bb3781cffa4305134d31801f3e26",
|
||||||
|
"sha256:3f0dae97e1126f529ebb66f3c63514a0f72a177b90d56e4bce8a0b5def34627a",
|
||||||
|
"sha256:50fb72bcbc2cf11e066579cb53c4ca8ac0227abb512b6cbc1faa02d1595a2a5d",
|
||||||
|
"sha256:57aea170fb23b1fd54fa537359d90d383d9bf5937ee54ae8045a723caa5e0961",
|
||||||
|
"sha256:709c2999b6bd36cdaf85cf888d8512da7433529f14a3689d6e37ab5242e7add5",
|
||||||
|
"sha256:7d59f21e43bbfd9a10953a7e26b35b6849d888fc5a331fa84a2d9c37bd9fe2a2",
|
||||||
|
"sha256:904b513ab8fbcbdb062bed1ce2f794ab20208a1b01ce9bd90776c6c7e7257032",
|
||||||
|
"sha256:96dd36f5cdde152fd6977d1bbc0f0561bccffecfde63cd397c8e6033eb66baba",
|
||||||
|
"sha256:9933b81fecbe935e6a7dc89cbd2b99fea1bf362f2790daf9422a7bb1dc3c3085",
|
||||||
|
"sha256:bbcc85aaf4cd84ba057decaead058f43191cc0e30d6bc5d44fe336dc3d3f4509",
|
||||||
|
"sha256:dccd380d8e025c867ddcb2f84b439722cf1f23f3a319381eac45fd077dee7170",
|
||||||
|
"sha256:e22cd0f72fc931d6abc69dc7764484ee20c6a60b0d0fee9ce0426029b1c1bdae",
|
||||||
|
"sha256:ed722aefb0ebffd10b32e67f48e8ac4c5c4cf5d3a785024fdf0e9eb17529cd9d",
|
||||||
|
"sha256:efb7ac5572c9a57159cf92c508aad9f856f1cb8e8302d7fdb99061dbe52d712c",
|
||||||
|
"sha256:efdba339fffb0e80fcc19524e4fdbda2e2b5772ea46720c44eaac28096d60720",
|
||||||
|
"sha256:f22273dd6a403ed870207b853a856ff6327d5cbce7a835dfa0645b3fc00273ec"
|
||||||
|
],
|
||||||
|
"version": "==1.18.4"
|
||||||
|
},
|
||||||
"psycopg2": {
|
"psycopg2": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535",
|
"sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535",
|
||||||
@ -216,11 +290,26 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.5.2"
|
"version": "==2.5.2"
|
||||||
},
|
},
|
||||||
|
"pyparsing": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||||
|
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||||
|
],
|
||||||
|
"version": "==2.4.7"
|
||||||
|
},
|
||||||
|
"python-dateutil": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||||
|
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||||
|
],
|
||||||
|
"version": "==2.8.1"
|
||||||
|
},
|
||||||
"requests": {
|
"requests": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||||
],
|
],
|
||||||
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.0'",
|
"markers": "python_version >= '3.0'",
|
||||||
"version": "==2.23.0"
|
"version": "==2.23.0"
|
||||||
},
|
},
|
||||||
|
@ -24,6 +24,8 @@ app.config.from_mapping(
|
|||||||
MAIL_PASSWORD=os.environ.get("MAIL_PASSWORD", default=""),
|
MAIL_PASSWORD=os.environ.get("MAIL_PASSWORD", default=""),
|
||||||
MAIL_DEFAULT_SENDER=os.environ.get("MAIL_DEFAULT_SENDER", default="forest@nullhex.com"),
|
MAIL_DEFAULT_SENDER=os.environ.get("MAIL_DEFAULT_SENDER", default="forest@nullhex.com"),
|
||||||
|
|
||||||
|
PROMETHEUS_URL=os.environ.get("PROMETHEUS_URL", default="https://prometheus.cyberia.club"),
|
||||||
|
|
||||||
STRIPE_API_VERSION=os.environ.get("STRIPE_API_VERSION", default="2020-03-02"),
|
STRIPE_API_VERSION=os.environ.get("STRIPE_API_VERSION", default="2020-03-02"),
|
||||||
STRIPE_SECRET_KEY=os.environ.get("STRIPE_SECRET_KEY", default=""),
|
STRIPE_SECRET_KEY=os.environ.get("STRIPE_SECRET_KEY", default=""),
|
||||||
STRIPE_PUBLISHABLE_KEY=os.environ.get("STRIPE_PUBLISHABLE_KEY", default="")
|
STRIPE_PUBLISHABLE_KEY=os.environ.get("STRIPE_PUBLISHABLE_KEY", default="")
|
||||||
@ -40,12 +42,14 @@ from capsulflask import db
|
|||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
from capsulflask import auth, landing, console, payment_stripe
|
from capsulflask import auth, landing, console, payment_stripe, payment_btcpay, metrics
|
||||||
|
|
||||||
app.register_blueprint(landing.bp)
|
app.register_blueprint(landing.bp)
|
||||||
app.register_blueprint(auth.bp)
|
app.register_blueprint(auth.bp)
|
||||||
app.register_blueprint(console.bp)
|
app.register_blueprint(console.bp)
|
||||||
app.register_blueprint(payment_stripe.bp)
|
app.register_blueprint(payment_stripe.bp)
|
||||||
|
app.register_blueprint(payment_btcpay.bp)
|
||||||
|
app.register_blueprint(metrics.bp)
|
||||||
|
|
||||||
app.add_url_rule("/", endpoint="index")
|
app.add_url_rule("/", endpoint="index")
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ from flask_mail import Message
|
|||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
from nanoid import generate
|
from nanoid import generate
|
||||||
|
|
||||||
|
from capsulflask.metrics import durations as metric_durations
|
||||||
from capsulflask.auth import account_required
|
from capsulflask.auth import account_required
|
||||||
|
|
||||||
from capsulflask.db import get_model, my_exec_info_message
|
from capsulflask.db import get_model, my_exec_info_message
|
||||||
|
|
||||||
bp = Blueprint("console", __name__, url_prefix="/console")
|
bp = Blueprint("console", __name__, url_prefix="/console")
|
||||||
@ -53,7 +53,7 @@ def index():
|
|||||||
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'],
|
||||||
created=x['created'].strftime("%b %d %Y %H:%M")
|
created=x['created'].strftime("%b %d %Y")
|
||||||
),
|
),
|
||||||
vms
|
vms
|
||||||
))
|
))
|
||||||
@ -63,6 +63,11 @@ def index():
|
|||||||
@bp.route("/<string:id>")
|
@bp.route("/<string:id>")
|
||||||
@account_required
|
@account_required
|
||||||
def detail(id):
|
def detail(id):
|
||||||
|
|
||||||
|
duration=request.args.get('duration')
|
||||||
|
if not duration:
|
||||||
|
duration = "5m"
|
||||||
|
|
||||||
vm = get_model().get_vm_detail(email=session["account"], id=id)
|
vm = get_model().get_vm_detail(email=session["account"], id=id)
|
||||||
|
|
||||||
if vm is None:
|
if vm is None:
|
||||||
@ -72,7 +77,12 @@ def detail(id):
|
|||||||
vm["created"] = vm['created'].strftime("%b %d %Y %H:%M")
|
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>"
|
vm["ssh_public_keys"] = ", ".join(vm["ssh_public_keys"]) if len(vm["ssh_public_keys"]) > 0 else "<deleted>"
|
||||||
|
|
||||||
return render_template("capsul-detail.html", vm=vm)
|
return render_template(
|
||||||
|
"capsul-detail.html",
|
||||||
|
vm=vm,
|
||||||
|
durations=list(map(lambda x: x.strip("_"), metric_durations.keys())),
|
||||||
|
duration=duration
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/create", methods=("GET", "POST"))
|
@bp.route("/create", methods=("GET", "POST"))
|
||||||
|
@ -27,7 +27,11 @@ def init_app(app):
|
|||||||
schemaMigrationsPath = join(app.root_path, 'schema_migrations')
|
schemaMigrationsPath = join(app.root_path, 'schema_migrations')
|
||||||
print("loading schema migration scripts from {}".format(schemaMigrationsPath))
|
print("loading schema migration scripts from {}".format(schemaMigrationsPath))
|
||||||
for filename in listdir(schemaMigrationsPath):
|
for filename in listdir(schemaMigrationsPath):
|
||||||
key = re.search(r"^\d+_(up|down)", filename).group()
|
result = re.search(r"^\d+_(up|down)", filename)
|
||||||
|
if not result:
|
||||||
|
print(f"schemaVersion {filename} must match ^\d+_(up|down). exiting.")
|
||||||
|
exit(1)
|
||||||
|
key = result.group()
|
||||||
with open(join(schemaMigrationsPath, filename), 'rb') as file:
|
with open(join(schemaMigrationsPath, filename), 'rb') as file:
|
||||||
schemaMigrations[key] = file.read().decode("utf8")
|
schemaMigrations[key] = file.read().decode("utf8")
|
||||||
|
|
||||||
|
@ -111,6 +111,10 @@ class DBModel:
|
|||||||
)
|
)
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
|
|
||||||
|
# def vm_exists(self, email, id):
|
||||||
|
# self.cursor.execute("SELECT id FROM vms WHERE email = %s AND id = %s ", (email, id))
|
||||||
|
# return len(self.cursor.fetchall()) > 0
|
||||||
|
|
||||||
def get_vm_detail(self, email, id):
|
def get_vm_detail(self, email, id):
|
||||||
self.cursor.execute("""
|
self.cursor.execute("""
|
||||||
SELECT vms.id, vms.last_seen_ipv4, vms.last_seen_ipv6, os_images.description, vms.created, vms.deleted,
|
SELECT vms.id, vms.last_seen_ipv4, vms.last_seen_ipv6, os_images.description, vms.created, vms.deleted,
|
||||||
|
@ -1,16 +1,5 @@
|
|||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import flash
|
|
||||||
from flask import current_app
|
|
||||||
from flask import g
|
|
||||||
from flask import redirect
|
|
||||||
from flask import url_for
|
|
||||||
from flask import request
|
|
||||||
from flask import session
|
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from flask_mail import Message
|
|
||||||
from werkzeug.exceptions import abort
|
|
||||||
|
|
||||||
from capsulflask.db import get_model
|
|
||||||
|
|
||||||
bp = Blueprint("landing", __name__, url_prefix="/")
|
bp = Blueprint("landing", __name__, url_prefix="/")
|
||||||
|
|
||||||
|
234
capsulflask/metrics.py
Normal file
234
capsulflask/metrics.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import matplotlib.ticker as ticker
|
||||||
|
import matplotlib.pyplot as pyplot
|
||||||
|
import matplotlib.dates as mdates
|
||||||
|
import requests
|
||||||
|
#import json
|
||||||
|
from datetime import datetime
|
||||||
|
from io import BytesIO
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask import current_app
|
||||||
|
from flask import session
|
||||||
|
from flask import render_template, make_response
|
||||||
|
from werkzeug.exceptions import abort
|
||||||
|
|
||||||
|
from capsulflask.db import get_model
|
||||||
|
from capsulflask.auth import account_required
|
||||||
|
|
||||||
|
bp = Blueprint("metrics", __name__, url_prefix="/metrics")
|
||||||
|
|
||||||
|
durations = dict(
|
||||||
|
_5m=[60*5, 15],
|
||||||
|
_1h=[60*60, 60],
|
||||||
|
_1d=[60*60*24, 60*20],
|
||||||
|
_30d=[60*60*24*30, 60*300]
|
||||||
|
)
|
||||||
|
sizes = dict(
|
||||||
|
s=[0.77, 0.23, 4],
|
||||||
|
m=[1, 1, 2],
|
||||||
|
l=[6, 4, 1],
|
||||||
|
)
|
||||||
|
|
||||||
|
green = (121/255, 240/255, 50/255)
|
||||||
|
blue = (70/255, 150/255, 255/255)
|
||||||
|
red = (255/255, 50/255, 8/255)
|
||||||
|
|
||||||
|
@bp.route("/html/<string:metric>/<string:capsulid>/<string:duration>")
|
||||||
|
@account_required
|
||||||
|
def display_metric(metric, capsulid, duration):
|
||||||
|
vm = get_model().get_vm_detail(session["account"], capsulid)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"display-metric.html",
|
||||||
|
vm=vm,
|
||||||
|
duration=duration,
|
||||||
|
durations=list(map(lambda x: x.strip("_"), durations.keys())),
|
||||||
|
metric=metric
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/<string:metric>/<string:capsulid>/<string:duration>/<string:size>")
|
||||||
|
@account_required
|
||||||
|
def metric_png(metric, capsulid, duration, size):
|
||||||
|
result = get_plot_bytes(metric, capsulid, duration, size)
|
||||||
|
|
||||||
|
if result[0] != 200:
|
||||||
|
abort(result[0])
|
||||||
|
|
||||||
|
response = make_response(result[1])
|
||||||
|
response.headers.set('Content-Type', 'image/png')
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def get_plot_bytes(metric, capsulid, duration, size):
|
||||||
|
|
||||||
|
duration = f"_{duration}"
|
||||||
|
|
||||||
|
if duration not in durations:
|
||||||
|
return (404, None)
|
||||||
|
|
||||||
|
if size not in sizes:
|
||||||
|
return (404, None)
|
||||||
|
|
||||||
|
vm = get_model().get_vm_detail(session["account"], capsulid)
|
||||||
|
|
||||||
|
if not vm:
|
||||||
|
return (404, None)
|
||||||
|
|
||||||
|
now_unix = int(datetime.strftime(datetime.now(), "%s"))
|
||||||
|
duration_seconds = durations[duration][0]
|
||||||
|
interval_seconds = durations[duration][1] * sizes[size][2]
|
||||||
|
if interval_seconds < 30:
|
||||||
|
interval_seconds = 30
|
||||||
|
|
||||||
|
metric_queries = dict(
|
||||||
|
cpu=f"irate(libvirtd_domain_info_cpu_time_seconds_total{{domain='{capsulid}'}}[30s])",
|
||||||
|
memory=f"libvirtd_domain_info_memory_usage_bytes{{domain='{capsulid}'}}",
|
||||||
|
network_in=f"rate(libvirtd_domain_interface_stats_receive_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])",
|
||||||
|
network_out=f"rate(libvirtd_domain_interface_stats_transmit_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])",
|
||||||
|
disk=f"rate(libvirtd_domain_block_stats_read_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])%2Brate(libvirtd_domain_block_stats_write_bytes_total{{domain='{capsulid}'}}[{interval_seconds}s])",
|
||||||
|
)
|
||||||
|
|
||||||
|
scales = dict(
|
||||||
|
cpu=vm["vcpus"],
|
||||||
|
memory=vm["memory_mb"]*1024*1024,
|
||||||
|
network_in=1024*1024*2,
|
||||||
|
network_out=1024*200,
|
||||||
|
disk=1024*1024*2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if metric not in metric_queries:
|
||||||
|
return (404, None)
|
||||||
|
|
||||||
|
range_and_interval = f"start={now_unix-duration_seconds}&end={now_unix}&step={interval_seconds}"
|
||||||
|
|
||||||
|
prometheus_range_url = f"{current_app.config['PROMETHEUS_URL']}/api/v1/query_range"
|
||||||
|
|
||||||
|
#print(f"{prometheus_range_url}?query={metric_queries[metric]}&{range_and_interval}")
|
||||||
|
|
||||||
|
prometheus_response = requests.get(f"{prometheus_range_url}?query={metric_queries[metric]}&{range_and_interval}")
|
||||||
|
if prometheus_response.status_code >= 300:
|
||||||
|
return (502, None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
time_series_data = list(map(
|
||||||
|
lambda x: (datetime.fromtimestamp(x[0]), float(x[1])),
|
||||||
|
prometheus_response.json()["data"]["result"][0]["values"]
|
||||||
|
))
|
||||||
|
|
||||||
|
plot_bytes = draw_plot_bytes(time_series_data, scale=scales[metric], size_x=sizes[size][0], size_y=sizes[size][1])
|
||||||
|
|
||||||
|
return (200, plot_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
def draw_plot_bytes(data, scale, size_x=3, size_y=1):
|
||||||
|
|
||||||
|
pyplot.style.use("seaborn-dark")
|
||||||
|
fig, my_plot = pyplot.subplots(figsize=(size_x, size_y))
|
||||||
|
|
||||||
|
# x=range(1, 15)
|
||||||
|
# y=[1,4,6,8,4,5,3,2,4,1,5,6,8,7]
|
||||||
|
|
||||||
|
divide_by = 1
|
||||||
|
unit = ""
|
||||||
|
|
||||||
|
if scale > 1024 and scale < 1024*1024*1024:
|
||||||
|
divide_by = 1024*1024
|
||||||
|
unit = "MB"
|
||||||
|
if scale > 1024*1024*1024:
|
||||||
|
divide_by = 1024*1024*1024
|
||||||
|
unit = "GB"
|
||||||
|
|
||||||
|
scale /= divide_by
|
||||||
|
|
||||||
|
if scale > 10:
|
||||||
|
my_plot.get_yaxis().set_major_formatter( ticker.FuncFormatter(lambda x, p: "{}{}".format(int(x), unit)) )
|
||||||
|
elif scale > 1:
|
||||||
|
my_plot.get_yaxis().set_major_formatter( ticker.FuncFormatter(lambda x, p: "{:.1f}{}".format(x, unit)) )
|
||||||
|
else:
|
||||||
|
my_plot.get_yaxis().set_major_formatter( ticker.FuncFormatter(lambda x, p: "{:.2f}{}".format(x, unit)) )
|
||||||
|
|
||||||
|
x=list(map(lambda x: x[0], data))
|
||||||
|
y=list(map(lambda x: x[1]/divide_by, data))
|
||||||
|
|
||||||
|
minutes = float((x[len(x)-1] - x[0]).total_seconds())/float(60)
|
||||||
|
hours = minutes/float(60)
|
||||||
|
days = hours/float(24)
|
||||||
|
|
||||||
|
day_locator = mdates.WeekdayLocator()
|
||||||
|
minute_locator = mdates.MinuteLocator()
|
||||||
|
ten_minute_locator = mdates.MinuteLocator(interval=10)
|
||||||
|
hour_locator = mdates.HourLocator(interval=6)
|
||||||
|
hour_minute_formatter = mdates.DateFormatter('%H:%M')
|
||||||
|
day_formatter = mdates.DateFormatter('%b %d')
|
||||||
|
|
||||||
|
if minutes < 10:
|
||||||
|
my_plot.xaxis.set_major_locator(minute_locator)
|
||||||
|
my_plot.xaxis.set_major_formatter(hour_minute_formatter)
|
||||||
|
elif hours < 2:
|
||||||
|
my_plot.xaxis.set_major_locator(ten_minute_locator)
|
||||||
|
my_plot.xaxis.set_major_formatter(hour_minute_formatter)
|
||||||
|
elif days < 2:
|
||||||
|
my_plot.xaxis.set_major_locator(hour_locator)
|
||||||
|
my_plot.xaxis.set_major_formatter(hour_minute_formatter)
|
||||||
|
else:
|
||||||
|
my_plot.xaxis.set_major_locator(day_locator)
|
||||||
|
my_plot.xaxis.set_major_formatter(day_formatter)
|
||||||
|
|
||||||
|
|
||||||
|
average=(sum(y)/len(y))/scale
|
||||||
|
average=average*1.25+0.1
|
||||||
|
|
||||||
|
bg_color=color_gradient(average)
|
||||||
|
|
||||||
|
average -= 0.1
|
||||||
|
|
||||||
|
fill_color=color_gradient(average)
|
||||||
|
highlight_color=lerp_rgb_tuples(fill_color, (1,1,1), 0.5)
|
||||||
|
|
||||||
|
my_plot.fill_between( x, scale, color=bg_color, alpha=0.13)
|
||||||
|
my_plot.fill_between( x, y, color=highlight_color, alpha=0.3)
|
||||||
|
my_plot.plot(x, y, 'r-', color=highlight_color)
|
||||||
|
|
||||||
|
my_plot.patch.set_facecolor('red')
|
||||||
|
my_plot.patch.set_alpha(0.5)
|
||||||
|
|
||||||
|
if size_y < 4:
|
||||||
|
my_plot.set_yticks([0, scale*0.5, scale])
|
||||||
|
my_plot.set_ylim(0, scale)
|
||||||
|
|
||||||
|
my_plot.xaxis.label.set_color(highlight_color)
|
||||||
|
my_plot.tick_params(axis='x', colors=highlight_color)
|
||||||
|
my_plot.yaxis.label.set_color(highlight_color)
|
||||||
|
my_plot.tick_params(axis='y', colors=highlight_color)
|
||||||
|
|
||||||
|
if size_x < 4:
|
||||||
|
my_plot.set_xticklabels([])
|
||||||
|
if size_y < 1:
|
||||||
|
my_plot.set_yticklabels([])
|
||||||
|
|
||||||
|
image_binary = BytesIO()
|
||||||
|
fig.savefig(image_binary, transparent=True, bbox_inches="tight", pad_inches=0.05)
|
||||||
|
return image_binary.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def lerp_rgb_tuples(a, b, lerp):
|
||||||
|
if lerp < 0:
|
||||||
|
lerp = 0
|
||||||
|
if lerp > 1:
|
||||||
|
lerp = 1
|
||||||
|
return (
|
||||||
|
a[0]*(1.0-lerp)+b[0]*lerp,
|
||||||
|
a[1]*(1.0-lerp)+b[1]*lerp,
|
||||||
|
a[2]*(1.0-lerp)+b[2]*lerp
|
||||||
|
)
|
||||||
|
|
||||||
|
def color_gradient(value):
|
||||||
|
if value < 0:
|
||||||
|
value = 0
|
||||||
|
if value > 1:
|
||||||
|
value = 1
|
||||||
|
if value < 0.5:
|
||||||
|
return lerp_rgb_tuples(green, blue, value*2)
|
||||||
|
else:
|
||||||
|
return lerp_rgb_tuples(blue, red, (value-0.5)*2)
|
12
capsulflask/payment_btcpay.py
Normal file
12
capsulflask/payment_btcpay.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
from flask import render_template
|
||||||
|
|
||||||
|
from capsulflask.db import get_model
|
||||||
|
from capsulflask.auth import account_required
|
||||||
|
|
||||||
|
bp = Blueprint("btcpay", __name__, url_prefix="/btcpay")
|
||||||
|
|
||||||
|
@bp.route("/")
|
||||||
|
@account_required
|
||||||
|
def index():
|
||||||
|
return render_template("btcpay.html")
|
@ -92,4 +92,7 @@ VALUES ('forest.n.johnson@gmail.com');
|
|||||||
INSERT INTO payments (email, dollars, created)
|
INSERT INTO payments (email, dollars, created)
|
||||||
VALUES ('forest.n.johnson@gmail.com', 20.00, TO_TIMESTAMP('2020-05-05','YYYY-MM-DD'));
|
VALUES ('forest.n.johnson@gmail.com', 20.00, TO_TIMESTAMP('2020-05-05','YYYY-MM-DD'));
|
||||||
|
|
||||||
|
INSERT INTO vms (id, email, os, size)
|
||||||
|
VALUES ('capsul-yi9ffqbjly', 'forest.n.johnson@gmail.com', 'alpine311', 'f1-xx');
|
||||||
|
|
||||||
UPDATE schemaversion SET version = 2;
|
UPDATE schemaversion SET version = 2;
|
@ -61,12 +61,15 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.full-margin {
|
.full-margin {
|
||||||
|
width: 100%;
|
||||||
margin: 3rem 0;
|
margin: 3rem 0;
|
||||||
}
|
}
|
||||||
.half-margin {
|
.half-margin {
|
||||||
|
width: 100%;
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
.third-margin {
|
.third-margin {
|
||||||
|
width: 100%;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +82,12 @@ main {
|
|||||||
.wrap {
|
.wrap {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
.row.grid-large > div {
|
||||||
|
flex: 1 1 20em;
|
||||||
|
}
|
||||||
|
.row.grid-small > div {
|
||||||
|
flex: 0 0 8em;
|
||||||
|
}
|
||||||
.justify-start {
|
.justify-start {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
@ -163,6 +172,12 @@ input[type=number] {
|
|||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type=image].submit {
|
||||||
|
background-color: #0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5 {
|
h1, h2, h3, h4, h5 {
|
||||||
font-size:calc(0.40rem + 1vmin);
|
font-size:calc(0.40rem + 1vmin);
|
||||||
margin: initial;
|
margin: initial;
|
||||||
@ -174,6 +189,10 @@ ul li {
|
|||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
table{
|
table{
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
@ -188,6 +207,15 @@ table.small td, table.small th {
|
|||||||
font: calc(0.35rem + 0.83vmin) monospace;
|
font: calc(0.35rem + 0.83vmin) monospace;
|
||||||
padding: 0.1em 1em;
|
padding: 0.1em 1em;
|
||||||
}
|
}
|
||||||
|
table.small td.metrics {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
td.metrics img {
|
||||||
|
margin-left: -20px;
|
||||||
|
margin-right: -20px;
|
||||||
|
margin-top: -5px;
|
||||||
|
margin-bottom: -5px;
|
||||||
|
}
|
||||||
th {
|
th {
|
||||||
border-right: 4px solid #241e1e;
|
border-right: 4px solid #241e1e;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@ -204,6 +232,13 @@ td {
|
|||||||
to { color: rgba(221, 169, 56, 0.2); }
|
to { color: rgba(221, 169, 56, 0.2); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.metric {
|
||||||
|
display: flex;
|
||||||
|
align-items: last baseline;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.5em 1.2em;
|
padding: 0.5em 1.2em;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% block title %}Account Balance{% endblock %}
|
{% block title %}Account Balance{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>Account Balance: ${{ account_balance }}</h1>
|
<h1>Account Balance: ${{ account_balance }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="half-margin">
|
<div class="half-margin">
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% if has_payments %}
|
{% if has_payments %}
|
||||||
<div>
|
<div>
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>Payments</h1>
|
<h1>Payments</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -52,33 +52,36 @@
|
|||||||
|
|
||||||
|
|
||||||
{% if has_vms %}
|
{% if has_vms %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>Capsuls Billed</h1>
|
<h1>Capsuls Billed</h1>
|
||||||
</div>
|
</div>
|
||||||
<table class="small">
|
<div class="row">
|
||||||
<thead>
|
<table class="small">
|
||||||
<tr>
|
<thead>
|
||||||
<th>id</th>
|
|
||||||
<th>created</th>
|
|
||||||
<th>deleted</th>
|
|
||||||
<th>$/month</th>
|
|
||||||
<th>months</th>
|
|
||||||
<th>$ billed</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for vm in vms_billed %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ vm["id"] }}</td>
|
<th>id</th>
|
||||||
<td>{{ vm["created"] }}</td>
|
<th>created</th>
|
||||||
<td>{{ vm["deleted"] }}</td>
|
<th>deleted</th>
|
||||||
<td>${{ vm["dollars_per_month"] }}</td>
|
<th>$/month</th>
|
||||||
<td>{{ vm["months"] }}</td>
|
<th>months</th>
|
||||||
<td>${{ vm["dollars"] }}</td>
|
<th>$ billed</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{% for vm in vms_billed %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ vm["id"] }}</td>
|
||||||
|
<td>{{ vm["created"] }}</td>
|
||||||
|
<td>{{ vm["deleted"] }}</td>
|
||||||
|
<td>${{ vm["dollars_per_month"] }}</td>
|
||||||
|
<td>{{ vm["months"] }}</td>
|
||||||
|
<td>${{ vm["dollars"] }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
38
capsulflask/templates/btcpay.html
Normal file
38
capsulflask/templates/btcpay.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Pay with Cryptocurrency{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row third-margin">
|
||||||
|
<h1>Pay with Cryptocurrency</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row half-margin">
|
||||||
|
|
||||||
|
<form method="POST" action="https://btcpay.cyberia.club/api/v1/invoices">
|
||||||
|
<input type="hidden" name="storeId" value="FgYNGKEHKm2tBhwejo1zdSQ15DknPWvip2pXLKBv96wc">
|
||||||
|
<input type="hidden" name="currency" value="USD">
|
||||||
|
<div class="row">
|
||||||
|
<label for="btcpay-input-price">$</label>
|
||||||
|
<input
|
||||||
|
id="btcpay-input-price"
|
||||||
|
name="price"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="2000"
|
||||||
|
oninput="event.preventDefault();isNaN(event.target.value) || event.target.value <= 0 ? document.querySelector('#btcpay-input-price').value = 0 : event.target.value"
|
||||||
|
/>
|
||||||
|
<input type="image" class="submit" name="submit"
|
||||||
|
src="https://btcpay.cyberia.club/img/paybutton/pay.svg"
|
||||||
|
style="width:168px"
|
||||||
|
alt="Pay with BtcPay"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block pagesource %}/templates/btcpay.html{% endblock %}
|
@ -3,10 +3,11 @@
|
|||||||
{% block title %}{{ vm['id'] }}{% endblock %}
|
{% block title %}{{ vm['id'] }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>{{ vm['id'] }}</h1>
|
<h1>{{ vm['id'] }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="half-margin">
|
<div class="row wrap grid-large third-margin">
|
||||||
|
|
||||||
<div class="row justify-start">
|
<div class="row justify-start">
|
||||||
<label class="align" for="created">Created</label>
|
<label class="align" for="created">Created</label>
|
||||||
<span id=created>{{ vm['created'] }}</span>
|
<span id=created>{{ vm['created'] }}</span>
|
||||||
@ -47,7 +48,57 @@
|
|||||||
<label class="align" for="ssh_public_keys">SSH Public Keys</label>
|
<label class="align" for="ssh_public_keys">SSH Public Keys</label>
|
||||||
<a id=ssh_public_keys href="/console/ssh">{{ vm['ssh_public_keys'] }}</a>
|
<a id=ssh_public_keys href="/console/ssh">{{ vm['ssh_public_keys'] }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
</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 class="metric">
|
||||||
|
<h1>memory</h1>
|
||||||
|
<a href="/metrics/html/memory/{{ vm['id'] }}/{{ duration }}">
|
||||||
|
<img src="/metrics/memory/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="metric">
|
||||||
|
<h1>network_in</h1>
|
||||||
|
<a href="/metrics/html/network_in/{{ vm['id'] }}/{{ duration }}">
|
||||||
|
<img src="/metrics/network_in/{{ vm['id'] }}/{{ duration }}/m"/>
|
||||||
|
</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>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% block title %}Capsuls{% endblock %}
|
{% block title %}Capsuls{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>Capsuls</h1>
|
<h1>Capsuls</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="third-margin">
|
<div class="third-margin">
|
||||||
@ -11,30 +11,37 @@
|
|||||||
<div class="row third-margin justify-end">
|
<div class="row third-margin justify-end">
|
||||||
<a href="/console/create">Create Capsul</a>
|
<a href="/console/create">Create Capsul</a>
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<div class="row">
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>id</th>
|
|
||||||
<th>size</th>
|
|
||||||
<th>ipv4</th>
|
|
||||||
<th>os</th>
|
|
||||||
<th>created</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for vm in vms %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/console/{{ vm['id'] }}">{{ vm["id"] }}</a></td>
|
<th>id</th>
|
||||||
<td>{{ vm["size"] }}</td>
|
<th>size</th>
|
||||||
<td class="{{ vm['ipv4_status'] }}">{{ vm["ipv4"] }}</td>
|
<th>cpu</th>
|
||||||
<td>{{ vm["os"] }}</td>
|
<th>mem</th>
|
||||||
<td>{{ vm["created"] }}</td>
|
<th>ipv4</th>
|
||||||
|
<th>os</th>
|
||||||
|
<th>created</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{% for vm in vms %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="/console/{{ vm['id'] }}">{{ vm["id"] }}</a></td>
|
||||||
|
<td>{{ vm["size"] }}</td>
|
||||||
|
<td class="metrics"><img src="/metrics/cpu/{{ vm['id'] }}/5m/s"/></td>
|
||||||
|
<td class="metrics"><img src="/metrics/memory/{{ vm['id'] }}/5m/s"/></td>
|
||||||
|
<td class="{{ vm['ipv4_status'] }}">{{ vm["ipv4"] }}</td>
|
||||||
|
<td>{{ vm["os"] }}</td>
|
||||||
|
<td>{{ vm["created"] }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
You don't have any Capsuls running. <a href="/console/create">Create one</a> today!
|
<div class="row">You don't have any Capsuls running. <a href="/console/create">Create one</a> today!</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% block title %}Changelog{% endblock %}
|
{% block title %}Changelog{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="full-margin"><h1>CHANGELOG</h1></div>
|
<div class="row full-margin"><h1>CHANGELOG</h1></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block subcontent %}
|
{% block subcontent %}
|
||||||
<p>
|
<p>
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
{% block title %}Create{% endblock %}
|
{% block title %}Create{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>Create Capsul</h1>
|
<h1>Create Capsul</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="third-margin">
|
<div class="row third-margin"><div>
|
||||||
|
|
||||||
{% if created_os %}
|
{% if created_os %}
|
||||||
<p>
|
<p>
|
||||||
Your Capsul was successfully created! You should already see it listed on the
|
Your Capsul was successfully created! You should already see it listed on the
|
||||||
@ -46,7 +47,8 @@
|
|||||||
* net is calculated as a per-month average
|
* net is calculated as a per-month average
|
||||||
* all VMs come standard with one public IPv4 addr</pre>
|
* all VMs come standard with one public IPv4 addr</pre>
|
||||||
<pre>
|
<pre>
|
||||||
Your <a href="/console/account-balance">account balance</a>: ${{ account_balance }}</div>
|
Your <a href="/console/account-balance">account balance</a>: ${{ account_balance }}
|
||||||
|
</pre>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<div class="row justify-start">
|
<div class="row justify-start">
|
||||||
@ -79,12 +81,13 @@
|
|||||||
<input type="submit" value="Create">
|
<input type="submit" value="Create">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block subcontent %}
|
{% block subcontent %}
|
||||||
|
31
capsulflask/templates/display-metric.html
Normal file
31
capsulflask/templates/display-metric.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}{{ vm['id'] }} - {{ metric }} - {{ duration }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="row half-margin">
|
||||||
|
<h1>{{ vm['id'] }} - {{ metric }} - {{ duration }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row third-margin">
|
||||||
|
{% for d in durations %}
|
||||||
|
<a href="/metrics/html/{{ metric }}/{{ vm['id'] }}/{{ d }}">
|
||||||
|
{% if d == duration %}
|
||||||
|
<span class="code">{{ d }}</span>
|
||||||
|
{% else %}
|
||||||
|
{{ d }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
<h1>cpu</h1>
|
||||||
|
<img src="/metrics/{{ metric }}/{{ vm['id'] }}/{{ duration }}/l"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block pagesource %}/templates/display-metric.html{% endblock %}
|
@ -3,7 +3,7 @@
|
|||||||
{% block title %}FAQ{% endblock %}
|
{% block title %}FAQ{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="full-margin"><h1>Frequently Asked Questions</h1></div>
|
<div class="row full-margin"><h1>Frequently Asked Questions</h1></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block subcontent %}
|
{% block subcontent %}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
\ /
|
\ /
|
||||||
`"`
|
`"`
|
||||||
</pre>
|
</pre>
|
||||||
|
<img src="/metrics/cpu/capsul-yi9ffqbjly/5m/l"/>
|
||||||
<span>Simple, fast, private compute by <a href="https://cyberia.club">cyberia.club</a></span>
|
<span>Simple, fast, private compute by <a href="https://cyberia.club">cyberia.club</a></span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% block title %}check your email{% endblock %}
|
{% block title %}check your email{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="full-margin">Check your email. A login link has been sent to {{ email }}</div>
|
<div class="row full-margin">Check your email. A login link has been sent to {{ email }}</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pagesource %}/templates/login-landing.html{% endblock %}
|
{% block pagesource %}/templates/login-landing.html{% endblock %}
|
@ -3,7 +3,7 @@
|
|||||||
{% block title %}login{% endblock %}
|
{% block title %}login{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="half-margin">
|
<div class="row half-margin">
|
||||||
<h1>LOGIN</h1>
|
<h1>LOGIN</h1>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" class="half-margin">
|
<form method="post" class="half-margin">
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
{% block title %}SSH Public Keys{% endblock %}
|
{% block title %}SSH Public Keys{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>SSH PUBLIC KEYS</h1>
|
<h1>SSH PUBLIC KEYS</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="third-margin">
|
<div class="row third-margin"><div>
|
||||||
{% if has_ssh_public_keys %} <hr/> {% endif %}
|
{% if has_ssh_public_keys %} <hr/> {% endif %}
|
||||||
|
|
||||||
{% for ssh_public_key in ssh_public_keys %}
|
{% for ssh_public_key in ssh_public_keys %}
|
||||||
@ -51,7 +51,7 @@
|
|||||||
<input type="submit" value="Upload">
|
<input type="submit" value="Upload">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pagesource %}/templates/ssh-public-keys.html{% endblock %}
|
{% block pagesource %}/templates/ssh-public-keys.html{% endblock %}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block title %}Capsuls{% endblock %}
|
{% block title %}Pay with Stripe{% endblock %}
|
||||||
|
|
||||||
{% block head %}<script src="https://js.stripe.com/v3/"></script>{% endblock %}
|
{% block head %}<script src="https://js.stripe.com/v3/"></script>{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="third-margin">
|
<div class="row third-margin">
|
||||||
<h1>PAY WITH STRIPE</h1>
|
<h1>PAY WITH STRIPE</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="row half-margin">
|
<div class="row half-margin">
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
{% block title %}Support{% endblock %}
|
{% block title %}Support{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="half-margin">
|
<div class="row half-margin">
|
||||||
<h1>SUPPORT</h1>
|
<h1>SUPPORT</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="half-margin">
|
<div class="row half-margin">
|
||||||
<a href="mailto:support@cyberia.club?subject=Please%20halp!">support@cyberia.club</a>
|
<a href="mailto:support@cyberia.club?subject=Please%20halp!">support@cyberia.club</a>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user