2021-01-04 19:32:52 +00:00
|
|
|
import re
|
|
|
|
|
2021-12-09 19:02:21 +00:00
|
|
|
import traceback
|
2022-02-08 23:16:52 +00:00
|
|
|
|
2022-08-05 02:15:23 +00:00
|
|
|
from datetime import datetime, timedelta
|
2021-01-04 19:32:52 +00:00
|
|
|
from flask import current_app
|
2021-01-30 07:39:48 +00:00
|
|
|
from typing import List
|
2021-01-04 19:32:52 +00:00
|
|
|
|
|
|
|
class OnlineHost:
|
|
|
|
def __init__(self, id: str, url: str):
|
|
|
|
self.id = id
|
|
|
|
self.url = url
|
|
|
|
|
2021-01-30 07:39:48 +00:00
|
|
|
# I decided to just use dict everywhere instead because I have to use dict to read it from json
|
|
|
|
# class SSHHostKey:
|
|
|
|
# def __init__(self, key_type=None, content=None, sha256=None):
|
|
|
|
# self.key_type = key_type
|
|
|
|
# self.content = content
|
|
|
|
# self.sha256 = sha256
|
|
|
|
|
2021-01-04 19:32:52 +00:00
|
|
|
class VirtualMachine:
|
2021-02-18 02:50:17 +00:00
|
|
|
def __init__(self, id, host, ipv4=None, ipv6=None, state="unknown", ssh_host_keys: List[dict] = list()):
|
2021-01-04 19:32:52 +00:00
|
|
|
self.id = id
|
|
|
|
self.host = host
|
|
|
|
self.ipv4 = ipv4
|
|
|
|
self.ipv6 = ipv6
|
2021-02-18 02:50:17 +00:00
|
|
|
self.state = state
|
2021-01-30 07:39:48 +00:00
|
|
|
self.ssh_host_keys = ssh_host_keys
|
2021-01-04 19:32:52 +00:00
|
|
|
|
|
|
|
class VirtualizationInterface:
|
|
|
|
def capacity_avaliable(self, additional_ram_bytes: int) -> bool:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get(self, id: str) -> VirtualMachine:
|
|
|
|
pass
|
|
|
|
|
2021-12-11 00:53:14 +00:00
|
|
|
def get_all_by_id(self) -> dict:
|
2021-01-04 19:32:52 +00:00
|
|
|
pass
|
|
|
|
|
2021-02-16 01:44:26 +00:00
|
|
|
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory: int, ssh_authorized_keys: list):
|
2021-01-04 19:32:52 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
def destroy(self, email: str, id: str):
|
|
|
|
pass
|
|
|
|
|
2021-02-17 03:13:51 +00:00
|
|
|
def vm_state_command(self, email: str, id: str, command: str):
|
|
|
|
pass
|
|
|
|
|
2021-12-09 23:25:44 +00:00
|
|
|
def net_set_dhcp(self, email: str, host_id: str, network_name: str, macs: list, remove_ipv4: str, add_ipv4: str):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2021-01-04 19:32:52 +00:00
|
|
|
def validate_capsul_id(id):
|
|
|
|
if not re.match(r"^(cvm|capsul)-[a-z0-9]{10}$", id):
|
|
|
|
raise ValueError(f"vm id \"{id}\" must match \"^capsul-[a-z0-9]{{10}}$\"")
|
|
|
|
|
|
|
|
def authorized_as_hub(headers):
|
|
|
|
if headers.get('Authorization'):
|
|
|
|
auth_header_value = headers.get('Authorization').replace("Bearer ", "")
|
|
|
|
return auth_header_value == current_app.config["HUB_TOKEN"]
|
|
|
|
return False
|
|
|
|
|
2022-02-08 23:16:52 +00:00
|
|
|
def get_account_balance(vms, payments, as_of):
|
|
|
|
|
|
|
|
vm_cost_dollars = 0.0
|
|
|
|
for vm in vms:
|
|
|
|
vm_months = get_vm_months_float(vm, as_of)
|
|
|
|
vm_cost_dollars += vm_months * float(vm["dollars_per_month"])
|
|
|
|
|
|
|
|
payment_dollars_total = float( sum(map(lambda x: 0 if x["invalidated"] else x["dollars"], payments)) )
|
|
|
|
|
|
|
|
return payment_dollars_total - vm_cost_dollars
|
|
|
|
|
|
|
|
|
|
|
|
average_number_of_days_in_a_month = 30.44
|
|
|
|
|
|
|
|
def get_vm_months_float(vm, as_of):
|
|
|
|
end_datetime = vm["deleted"] if vm["deleted"] else as_of
|
|
|
|
days = float((end_datetime - vm["created"]).total_seconds())/float(60*60*24)
|
|
|
|
|
|
|
|
# y / m / d
|
|
|
|
date_when_minimum_run_time_was_changed_to_one_hour = datetime(2022, 2, 7)
|
|
|
|
|
|
|
|
if vm["created"] > date_when_minimum_run_time_was_changed_to_one_hour:
|
|
|
|
one_hour_in_days = float(1)/float(24)
|
|
|
|
if days < one_hour_in_days:
|
|
|
|
days = one_hour_in_days
|
|
|
|
else:
|
|
|
|
if days < 1:
|
|
|
|
days = float(1)
|
|
|
|
|
|
|
|
return days / average_number_of_days_in_a_month
|
|
|
|
|
2024-04-14 16:07:47 +00:00
|
|
|
def btcpay_invoice_is_paid(btcpay_invoice, pending_confirmation_as_paid):
|
|
|
|
is_paid = pending_confirmation_as_paid and btcpay_invoice['status'] == "paid"
|
|
|
|
is_complete = btcpay_invoice['status'] == "complete"
|
|
|
|
is_confirmed = btcpay_invoice['status'] == "confirmed"
|
|
|
|
is_paid_late = btcpay_invoice['status'] == "expired" and btcpay_invoice['exceptionStatus'] == "paidLate"
|
2022-02-08 23:16:52 +00:00
|
|
|
|
2024-04-14 16:07:47 +00:00
|
|
|
return is_paid or is_complete or is_confirmed or is_paid_late
|
2022-02-08 23:16:52 +00:00
|
|
|
|
2022-08-05 00:58:32 +00:00
|
|
|
def get_warnings_list():
|
2022-08-05 01:56:32 +00:00
|
|
|
|
|
|
|
delete_at_account_balance_dollars = -10
|
|
|
|
|
2022-08-05 00:58:32 +00:00
|
|
|
return [
|
|
|
|
dict(
|
|
|
|
id='zero_1w',
|
|
|
|
get_active=lambda balance_1w, balance_1d, balance_now: balance_1w < 0,
|
|
|
|
get_subject=lambda _: "Capsul One Week Payment Reminder",
|
|
|
|
get_body=lambda base_url, pluralize_capsul: (
|
|
|
|
f"{get_warning_headline('zero_1w', pluralize_capsul)}"
|
|
|
|
f"Log in now to re-fill your account! {base_url}/console/account-balance\n\n"
|
|
|
|
"If you believe you have recieved this message in error, please let us know: support@cyberia.club"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
dict(
|
|
|
|
id='zero_1d',
|
|
|
|
get_active=lambda balance_1w, balance_1d, balance_now: balance_1d < 0,
|
|
|
|
get_subject=lambda _: "Capsul One Day Payment Reminder",
|
|
|
|
get_body=lambda base_url, pluralize_capsul: (
|
|
|
|
f"{get_warning_headline('zero_1d', pluralize_capsul)}"
|
|
|
|
f"Log in now to re-fill your account! {base_url}/console/account-balance\n\n"
|
|
|
|
"If you believe you have recieved this message in error, please let us know: support@cyberia.club"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
dict(
|
|
|
|
id='zero_now',
|
|
|
|
get_active=lambda balance_1w, balance_1d, balance_now: balance_now < 0,
|
|
|
|
get_subject=lambda _: "Your Capsul Account is No Longer Funded",
|
|
|
|
get_body=lambda base_url, pluralize_capsul: (
|
|
|
|
f"{get_warning_headline('zero_now', pluralize_capsul)}"
|
|
|
|
f"Log in now to re-fill your account! {base_url}/console/account-balance\n\n"
|
|
|
|
f"If you need help decomissioning your Capsul{pluralize_capsul}, "
|
|
|
|
"would like to request backups, or de-activate your account, please contact: support@cyberia.club"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
dict(
|
|
|
|
id='delete_1w',
|
|
|
|
get_active=lambda balance_1w, balance_1d, balance_now: balance_1w < delete_at_account_balance_dollars,
|
|
|
|
get_subject=lambda pluralize_capsul: f"Your Capsul{pluralize_capsul} Will be Deleted In Less Than a Week",
|
|
|
|
get_body=lambda base_url, pluralize_capsul: (
|
|
|
|
f"{get_warning_headline('delete_1w', pluralize_capsul)}"
|
|
|
|
f"Log in now to re-fill your account! {base_url}/console/account-balance\n\n"
|
|
|
|
f"If you need help decomissioning your Capsul{pluralize_capsul}, "
|
|
|
|
"would like to request backups, or de-activate your account, please contact: support@cyberia.club"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
dict(
|
|
|
|
id='delete_1d',
|
|
|
|
get_active=lambda balance_1w, balance_1d, balance_now: balance_1d < delete_at_account_balance_dollars,
|
|
|
|
get_subject=lambda pluralize_capsul: f"Last Chance to Save your Capsul{pluralize_capsul}: Gone Tomorrow",
|
|
|
|
get_body=lambda base_url, pluralize_capsul: (
|
|
|
|
f"{get_warning_headline('delete_1d', pluralize_capsul)}"
|
|
|
|
f"{base_url}/console/account-balance"
|
|
|
|
)
|
|
|
|
),
|
|
|
|
dict(
|
|
|
|
id='delete_now',
|
|
|
|
get_active=lambda balance_1w, balance_1d, balance_now: balance_now < delete_at_account_balance_dollars,
|
|
|
|
get_subject=lambda pluralize_capsul: f"Capsul{pluralize_capsul} Deleted",
|
|
|
|
get_body=lambda base_url, pluralize_capsul: (
|
|
|
|
f"{get_warning_headline('delete_now', pluralize_capsul)}"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
]
|
|
|
|
|
2022-08-05 02:00:27 +00:00
|
|
|
def get_warning_headline(warning_id, pluralize_capsul):
|
|
|
|
return dict(
|
|
|
|
zero_1w= (
|
|
|
|
"According to our calculations, your Capsul account will run out of funds before this time next week.\n\n"
|
|
|
|
),
|
|
|
|
zero_1d= (
|
|
|
|
"According to our calculations, your Capsul account will run out of funds by this time tomorrow.\n\n"
|
|
|
|
),
|
|
|
|
zero_now= (
|
|
|
|
f"You have run out of funds! You will no longer be able to create Capsuls.\n\n"
|
|
|
|
f"As a courtesy, we'll let your existing Capsul{pluralize_capsul} keep running until your account "
|
|
|
|
"reaches a -$10 balance, at which point they will be deleted.\n\n"
|
|
|
|
),
|
|
|
|
delete_1w= (
|
|
|
|
"You have run out of funds and have not refilled your account.\n\n"
|
|
|
|
f"As a courtesy, we've let your existing Capsul{pluralize_capsul} keep running. "
|
|
|
|
f"However, your account will reach a -$10 balance some time next week and your Capsul{pluralize_capsul} "
|
|
|
|
"will be deleted.\n\n"
|
|
|
|
),
|
|
|
|
delete_1d= (
|
|
|
|
"You have run out of funds and have not refilled your account.\n\n"
|
|
|
|
f"As a courtesy, we have let your existing Capsul{pluralize_capsul} keep running. "
|
|
|
|
f"However, your account will reach a -$10 balance by this time tomorrow and "
|
|
|
|
f"your Capsul{pluralize_capsul} will be deleted.\n\n"
|
|
|
|
f"Last chance to deposit funds now and keep your Capsul{pluralize_capsul} running! "
|
|
|
|
),
|
|
|
|
delete_now= (
|
2024-04-08 23:19:24 +00:00
|
|
|
f"Your account reached a -$10 balance and your Capsul{pluralize_capsul} will be deleted by ops shortly unless we hear from you."
|
2022-08-05 02:00:27 +00:00
|
|
|
)
|
|
|
|
)[warning_id]
|
|
|
|
|
2022-08-05 02:11:05 +00:00
|
|
|
def reset_account_balance_warning(email, model):
|
2022-08-05 00:58:32 +00:00
|
|
|
|
2022-08-05 02:11:05 +00:00
|
|
|
vms = model.list_vms_for_account(email)
|
|
|
|
payments = model.list_payments_for_account(email)
|
2022-08-05 00:58:32 +00:00
|
|
|
balance_1w = get_account_balance(vms, payments, datetime.utcnow() + timedelta(days=7))
|
|
|
|
balance_1d = get_account_balance(vms, payments, datetime.utcnow() + timedelta(days=1))
|
|
|
|
balance_now = get_account_balance(vms, payments, datetime.utcnow())
|
|
|
|
|
|
|
|
warnings = get_warnings_list()
|
|
|
|
next_warning = 0
|
|
|
|
for i in range(0, len(warnings)):
|
|
|
|
if warnings[i]['get_active'](balance_1w, balance_1d, balance_now):
|
|
|
|
next_warning = i
|
|
|
|
|
|
|
|
# inserting a null warning element at the beginning of the list moves the next_warning index back by one element
|
|
|
|
# thats desired because we want to set the warning back to whatever goes before the current warning
|
|
|
|
warnings.insert(0, dict(id=None))
|
|
|
|
|
2022-08-05 02:11:05 +00:00
|
|
|
model.set_account_balance_warning(email, warnings[next_warning]['id'])
|
2022-08-05 00:58:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-04 19:32:52 +00:00
|
|
|
def my_exec_info_message(exec_info):
|
2021-12-09 19:06:44 +00:00
|
|
|
traceback_result = traceback.format_tb(exec_info[2])
|
2021-12-09 19:07:28 +00:00
|
|
|
if isinstance(traceback_result, list):
|
2021-12-09 19:06:44 +00:00
|
|
|
traceback_result = "\n".join(traceback_result)
|
2021-12-09 19:07:28 +00:00
|
|
|
|
2021-12-09 19:06:44 +00:00
|
|
|
return f"{exec_info[0].__module__}.{exec_info[0].__name__}: {exec_info[1]}: {traceback_result}"
|