75 Commits

Author SHA1 Message Date
7ae5071cef Merge pull request 'Make the displayed SSH username configurable' (#13) from mirsal/capsul-flask:ssh-username into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #12
2021-08-10 12:00:35 +02:00
d4a9f2f40a Make the displayed SSH username configurable
Some checks failed
continuous-integration/drone/pr Build is failing
This patch allows the SSH username displayed in templates to be
configured through the SSH_USERNAME environment variable.
2021-08-10 00:30:45 +00:00
0ae55712ac Merge pull request 'templates: Display IPv6 addresses on the capsul detail page' (#11) from mirsal/capsul-flask:ipv6 into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #11
2021-08-04 23:53:55 +02:00
8634cda388 templates: Display IPv6 addresses on the capsul detail page
Some checks failed
continuous-integration/drone/pr Build is failing
2021-08-04 19:53:06 +00:00
5f868de9cc Merge pull request 'Support dualstack ipv6 in the controller layer' (#10) from mirsal/capsul-flask:ipv6 into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #10

You Obviously Love Owls
2021-08-04 21:08:11 +02:00
d238bc9551 cosmetics: Remove trailing whitespace
Some checks failed
continuous-integration/drone/pr Build is failing
2021-08-04 15:35:41 +00:00
17c915c1bf Support dualstack ipv6 in the console controller
This commit updates the console controller logic and
a few bits in the model in order to support multiple
address families (ipv4 and ipv6)
2021-08-04 15:35:41 +00:00
8a6d558402 Retrieve IPv6 addresses from VMs
This commit allows the model to fetch IPv6 addresses from running VMs
and populate VirtualMachine objects with the value if it was retrieved
successfully
2021-08-04 15:35:41 +00:00
3wc
ff4e63339f Merge branch 'master' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-08-04 12:19:57 +02:00
7b16606b16 Merge pull request 'shell_scripts: Fix reporting of VM IP addresses' (#9) from mirsal/capsul-flask:ipv6 into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #9
2021-08-04 12:13:44 +02:00
d9f3e68278 shell_scripts: Fix reporting of VM IP addresses
Some checks failed
continuous-integration/drone/pr Build is failing
The shell script which gets informations from libvirt incorrectly
matches virsh output lines which contain VMs' public IPv4 addresses.
It doesn't work when there are multiple address families which breaks
reporting of IPv4 address when a VM also has an IPv6 address and virsh
happen to output that one first.

This commit changes it to explicitely match the first ipv4 address instead.
2021-08-04 00:56:55 +00:00
3wc
bcd1190f50 Fix API 🙈
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-29 11:36:58 +02:00
3wc
6963e22933 Merge branch 'publicapi' into yolocolo 2021-07-29 11:16:54 +02:00
3wc
816be36a52 Revert unthemed template changes, tweak theme 2021-07-29 11:11:51 +02:00
3wc
d6f2f6d0bf Merge branch 'master' into yolocolo 2021-07-29 09:47:31 +02:00
3wc
cfb323bf60 Merge branch 'yc-templates' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-22 01:03:02 +02:00
3wc
8c7197f118 Merge branch 'docker' into yolocolo 2021-07-22 01:02:34 +02:00
3wc
fb83f52162 Merge branch 'master' into yolocolo 2021-07-22 00:57:01 +02:00
3wc
982556a2c5 Tag with current branch, instead of latest
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build is passing
2021-07-22 00:28:33 +02:00
3wc
13646e64da Make docker-compose file less demanding
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2021-07-21 23:50:47 +02:00
3wc
67149f437a Changes from @decentral1se code review 2021-07-21 23:50:47 +02:00
3wc
308ac05fe6 Add openssh-cient to Dockerfile for ssh-keyscan 2021-07-21 23:50:47 +02:00
3wc
c378c2b287 STRIPE_SECRET_KEY not STRIPE_PUBLISHABLE_KEY 2021-07-21 23:50:47 +02:00
3wc
5367822747 Load secrets from files if _FILE vars are set 2021-07-21 23:50:47 +02:00
3wc
e295b4420c Docker updates for libvirtd 2021-07-21 23:50:47 +02:00
3wc
e4180b8306 Use Flask server in development 2021-07-21 23:50:47 +02:00
3wc
5cd5126039 Multi-stage build oh my! 2021-07-21 23:50:47 +02:00
3wc
f8e9ab2482 Initial attempt at Docker 2021-07-21 23:50:47 +02:00
3wc
487a1ac0a3 Revert accidental change to main images 2021-07-21 23:30:14 +02:00
3wc
71344fbf4d Merge branch 'yc-templates' into yolocolo 2021-07-21 22:49:17 +02:00
3wc
6f3ecf991d Custom theme fixes 2021-07-21 22:48:54 +02:00
3wc
40aa3bccd3 Load YOLOCOLO templates from a "theme" folder..
..configurable using the THEME env var.

Defaults to normal Capsul theme.
2021-07-21 22:24:25 +02:00
3wc
bcc3f20b27 Tweak table style 2021-07-21 22:12:13 +02:00
3wc
8bd3971545 Improve form styling 2021-07-21 22:12:13 +02:00
3wc
c639899cd0 Remove cash payment option 2021-07-21 22:12:13 +02:00
3wc
a47c430855 Further colour tweak 2021-07-21 22:12:13 +02:00
3wc
d5362897df Fix typo, tweak colours 2021-07-21 22:12:13 +02:00
3wc
a52d183f2e More cheeky template changes 2021-07-21 22:12:13 +02:00
3wc
73042d1705 Disco updates for YOLOCOLO site 2021-07-21 22:12:13 +02:00
3wc
d3078fc5ce Merge branch 'yc-templates' into yolocolo 2021-07-21 21:07:59 +02:00
3wc
d60238f6f0 Improve form styling 2021-07-21 21:07:51 +02:00
3wc
ab55146d41 Remove cash payment option 2021-07-21 21:07:49 +02:00
3wc
7191213646 Further colour tweak 2021-07-21 21:07:27 +02:00
3wc
8c802a977a Fix typo, tweak colours 2021-07-21 21:07:27 +02:00
3wc
929aa29663 More cheeky template changes 2021-07-21 21:07:26 +02:00
3wc
61beee8d3b Disco updates for YOLOCOLO site 2021-07-21 21:07:03 +02:00
3wc
8586b85095 Merge branch 'master' into yolocolo
Some checks failed
continuous-integration/drone/push Build is failing
2021-07-21 20:38:36 +02:00
3wc
b2b78e0015 Merge branch 'yc-templates' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-21 11:43:17 +02:00
3wc
d6f4be8197 Improve form styling 2021-07-21 11:43:04 +02:00
3wc
5d3d0a1c5e Merge branch 'docker' into yolocolo 2021-07-21 11:38:58 +02:00
3wc
fc7e264178 Make docker-compose file less demanding
Some checks reported errors
continuous-integration/drone/pr Build was killed
continuous-integration/drone/push Build is passing
2021-07-21 11:38:01 +02:00
3wc
157e81e6e5 Merge branch 'yc-templates' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-21 02:02:58 +02:00
3wc
97f9486ea3 Remove cash payment option 2021-07-21 02:02:21 +02:00
3wc
4b90a6c7ae Merge branch 'yc-templates' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-21 01:56:45 +02:00
3wc
adc3342a8c Further colour tweak 2021-07-21 01:56:36 +02:00
3wc
f8bbdcc3c5 Merge branch 'yc-templates' into yolocolo
Some checks reported errors
continuous-integration/drone/push Build was killed
2021-07-21 01:54:35 +02:00
3wc
1133caa8a4 Fix typo, tweak colours 2021-07-21 01:54:21 +02:00
3wc
1f384f34b5 Merge branch 'yc-templates' into yolocolo
Some checks reported errors
continuous-integration/drone/push Build was killed
2021-07-21 01:44:52 +02:00
3wc
c25d85bbdd More cheeky template changes 2021-07-21 01:44:45 +02:00
3wc
0f4ac8e444 Merge branch 'yc-templates' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-21 01:15:51 +02:00
3wc
3cf501a393 Disco updates for YOLOCOLO site 2021-07-21 01:13:52 +02:00
3wc
180efa01af Merge branch 'optional-btcpay' into yolocolo
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-21 00:30:34 +02:00
3wc
f5c079ffc2 Un-hard-code SSH key name
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-20 23:55:03 +02:00
3wc
0e5dfe6bde Last re-hard-coding for "working" joy 2021-07-20 23:55:03 +02:00
3wc
2adbb8d94c Further filthy fix for local libvirt 2021-07-20 23:55:03 +02:00
3wc
8446d11720 Revert hardcoded local path to tank dir 2021-07-20 23:55:03 +02:00
3wc
a580b04659 Bag of hacks to get local libvirt working 2021-07-20 23:55:03 +02:00
3wc
2e6894ad14 Changes from @decentral1se code review
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing
2021-07-20 23:48:23 +02:00
3wc
2e6c6517f3 Add openssh-cient to Dockerfile for ssh-keyscan
Some checks reported errors
continuous-integration/drone/pr Build encountered an error
continuous-integration/drone/push Build is passing
2021-07-20 01:52:17 +02:00
3wc
be6c1b38b7 STRIPE_SECRET_KEY not STRIPE_PUBLISHABLE_KEY
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-07-19 01:17:18 +02:00
3wc
aa8e129913 Load secrets from files if _FILE vars are set 2021-07-19 00:24:22 +02:00
3wc
71e09807a7 Docker updates for libvirtd 2021-07-15 00:13:11 +02:00
3wc
4816170c03 Use Flask server in development 2021-07-15 00:13:11 +02:00
3wc
6af241e8be Multi-stage build oh my! 2021-07-15 00:13:11 +02:00
3wc
c8ec53f207 Initial attempt at Docker 2021-07-15 00:13:11 +02:00
16 changed files with 108 additions and 82 deletions

View File

@ -58,6 +58,7 @@ app.config.from_mapping(
LOG_LEVEL=os.environ.get("LOG_LEVEL", default="INFO"), LOG_LEVEL=os.environ.get("LOG_LEVEL", default="INFO"),
SPOKE_HOST_ID=os.environ.get("SPOKE_HOST_ID", default="baikal"), SPOKE_HOST_ID=os.environ.get("SPOKE_HOST_ID", default="baikal"),
SPOKE_HOST_TOKEN=os.environ.get("SPOKE_HOST_TOKEN", default="changeme"), SPOKE_HOST_TOKEN=os.environ.get("SPOKE_HOST_TOKEN", default="changeme"),
SSH_USERNAME=os.environ.get("SSH_USERNAME", default="cyberian"),
HUB_TOKEN=os.environ.get("HUB_TOKEN", default="changeme"), HUB_TOKEN=os.environ.get("HUB_TOKEN", default="changeme"),
# https://www.postgresql.org/docs/9.1/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS # https://www.postgresql.org/docs/9.1/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS

View File

@ -29,12 +29,14 @@ def make_capsul_id():
letters_n_nummers = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10) letters_n_nummers = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
return f"capsul-{letters_n_nummers}" return f"capsul-{letters_n_nummers}"
def double_check_capsul_address(id, ipv4, get_ssh_host_keys): def double_check_capsul_address(id, ipv4, ipv6, get_ssh_host_keys):
try: try:
result = current_app.config["HUB_MODEL"].get(id, get_ssh_host_keys) result = current_app.config["HUB_MODEL"].get(id, get_ssh_host_keys)
if result != None and result.ipv4 != None and result.ipv4 != ipv4: if result != None and result.ipv4 != None and result.ipv4 != ipv4:
ipv4 = result.ipv4 get_model().update_vm_ipv4(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.ipv6 != None and result.ipv6 != ipv6:
get_model().update_vm_ipv6(email=session["account"], id=id, ipv6=result.ipv6)
if result != None and result.ssh_host_keys != 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)
@ -61,37 +63,38 @@ def index():
# for now we are going to check the IP according to the virt model # 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... # on every request. this could be done by a background job and cached later on...
for vm in vms: for vm in vms:
result = double_check_capsul_address(vm["id"], vm["ipv4"], False) result = double_check_capsul_address(vm["id"], vm["ipv4"], vm["ipv6"], False)
if result is not None: if result is not None:
vm["ipv4"] = result.ipv4 vm["ipv4"] = result.ipv4
vm["ipv6"] = result.ipv6
vm["state"] = result.state vm["state"] = result.state
else: else:
vm["state"] = "unknown" vm["state"] = "unknown"
mappedVms = [] mappedVms = []
for vm in vms: for vm in vms:
ip_display = vm['ipv4'] ip_display = {}
if not ip_display: ip_display_class = {}
if vm["state"] == "running": for af in ['ipv4', 'ipv6']:
ip_display = "..booting.." ip_display[af] = vm[af]
else: ip_display_class[af] = "ok"
ip_display = "unknown" if not ip_display[af]:
if vm["state"] == "running":
ip_display_class = "ok" ip_display[af] = "..booting.."
if not vm['ipv4']: ip_display_class[af] = "waiting-pulse"
if vm["state"] == "running": else:
ip_display_class = "waiting-pulse" ip_display[af] = "unknown"
else: ip_display_class[af] = "yellow"
ip_display_class = "yellow"
mappedVms.append(dict( mappedVms.append(dict(
id=vm['id'], id=vm['id'],
size=vm['size'], size=vm['size'],
state=vm['state'], state=vm['state'],
ipv4=ip_display, ipv4=ip_display['ipv4'],
ipv4_status=ip_display_class, ipv4_status=ip_display_class['ipv4'],
os=vm['os'], ipv6=ip_display['ipv6'],
ipv6_status=ip_display_class['ipv6'],
os=vm['os'],
created=vm['created'].strftime("%b %d %Y") created=vm['created'].strftime("%b %d %Y")
)) ))
@ -109,6 +112,8 @@ def detail(id):
if vm is None: if vm is None:
return abort(404, f"{id} doesn't exist.") return abort(404, f"{id} doesn't exist.")
vm['ssh_username'] = current_app.config['SSH_USERNAME']
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)
@ -168,10 +173,11 @@ def detail(id):
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
vm_from_virt_model = double_check_capsul_address(vm["id"], vm["ipv4"], needs_ssh_host_keys) vm_from_virt_model = double_check_capsul_address(vm["id"], vm["ipv4"], vm['ipv6'], needs_ssh_host_keys)
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["ipv6"] = vm_from_virt_model.ipv6
vm["state"] = vm_from_virt_model.state 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
@ -189,7 +195,7 @@ def detail(id):
duration=duration duration=duration
) )
def _create(vm_sizes, operating_systems, public_keys_for_account, affordable_vm_sizes, server_data): def _create(email, vm_sizes, operating_systems, public_keys_for_account, affordable_vm_sizes, server_data):
errors = list() errors = list()
size = server_data.get("size") size = server_data.get("size")
@ -241,14 +247,14 @@ def _create(vm_sizes, operating_systems, public_keys_for_account, affordable_vm_
if len(errors) == 0: if len(errors) == 0:
id = make_capsul_id() id = make_capsul_id()
current_app.config["HUB_MODEL"].create( current_app.config["HUB_MODEL"].create(
email = session["account"], email = email,
id=id, id=id,
os=os, os=os,
size=size, size=size,
template_image_file_name=operating_systems[os]['template_image_file_name'], template_image_file_name=operating_systems[os]['template_image_file_name'],
vcpus=vm_sizes[size]['vcpus'], vcpus=vm_sizes[size]['vcpus'],
memory_mb=vm_sizes[size]['memory_mb'], memory_mb=vm_sizes[size]['memory_mb'],
ssh_authorized_keys=posted_keys ssh_authorized_keys=list(map(lambda x: dict(name=x['name'], content=x['content']), posted_keys))
) )
return id, errors return id, errors
@ -276,6 +282,7 @@ def create():
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")
id, errors = _create( id, errors = _create(
session['account'],
vm_sizes, vm_sizes,
operating_systems, operating_systems,
public_keys_for_account, public_keys_for_account,

View File

@ -179,10 +179,14 @@ class DBModel:
self.cursor.fetchall() self.cursor.fetchall()
)) ))
def update_vm_ip(self, email, id, ipv4): def update_vm_ipv4(self, email, id, ipv4):
self.cursor.execute("UPDATE vms SET public_ipv4 = %s WHERE email = %s AND id = %s", (ipv4, email, id)) self.cursor.execute("UPDATE vms SET public_ipv4 = %s WHERE email = %s AND id = %s", (ipv4, email, id))
self.connection.commit() self.connection.commit()
def update_vm_ipv6(self, email, id, ipv6):
self.cursor.execute("UPDATE vms SET public_ipv6 = %s WHERE email = %s AND id = %s", (ipv6, email, id))
self.connection.commit()
def update_vm_ssh_host_keys(self, email, id, ssh_host_keys): def update_vm_ssh_host_keys(self, email, id, ssh_host_keys):
for key in ssh_host_keys: for key in ssh_host_keys:
self.cursor.execute(""" self.cursor.execute("""

View File

@ -178,4 +178,4 @@ def on_create_claimed(payload, host_id):
network_name=payload['network_name'], network_name=payload['network_name'],
public_ipv4=payload['public_ipv4'], public_ipv4=payload['public_ipv4'],
ssh_authorized_keys=list(map(lambda x: x["name"], payload['ssh_authorized_keys'])), ssh_authorized_keys=list(map(lambda x: x["name"], payload['ssh_authorized_keys'])),
) )

View File

@ -198,6 +198,10 @@ class CapsulFlaskHub(VirtualizationInterface):
validate_capsul_id(id) validate_capsul_id(id)
online_hosts = get_model().get_online_hosts() online_hosts = get_model().get_online_hosts()
#current_app.logger.debug(f"hub_model.create(): ${len(online_hosts)} hosts") #current_app.logger.debug(f"hub_model.create(): ${len(online_hosts)} hosts")
current_app.logger.error(f'{email}, {id} {os} {size} {template_image_file_name} {vcpus} {memory_mb}')
current_app.logger.error(f'{ssh_authorized_keys}')
payload = json.dumps(dict( payload = json.dumps(dict(
type="create", type="create",
email=email, email=email,
@ -228,12 +232,13 @@ class CapsulFlaskHub(VirtualizationInterface):
except: except:
# no need to do anything here since if it cant be parsed then generic_operation will handle it. # no need to do anything here since if it cant be parsed then generic_operation will handle it.
pass pass
if error_message != "":
raise ValueError(f"create capsul operation {operation_id} on {assigned_hosts} failed with {error_message}")
if number_of_assigned != 1: if number_of_assigned != 1:
assigned_hosts_string = ", ".join(assigned_hosts) assigned_hosts_string = ", ".join(assigned_hosts)
raise ValueError(f"expected create capsul operation {operation_id} to be assigned to one host, it was assigned to {number_of_assigned} ({assigned_hosts_string})") raise ValueError(f"expected create capsul operation {operation_id} to be assigned to one host, it was assigned to {number_of_assigned} ({assigned_hosts_string})")
if error_message != "":
raise ValueError(f"create capsul operation {operation_id} on {assigned_hosts_string} failed with {error_message}")
def destroy(self, email: str, id: str): def destroy(self, email: str, id: str):

View File

@ -22,7 +22,7 @@ def pricing():
@bp.route("/faq") @bp.route("/faq")
def faq(): def faq():
return render_template("faq.html") return render_template("faq.html", ssh_username=current_app.config['SSH_USERNAME'])
@bp.route("/about-ssh") @bp.route("/about-ssh")
def about_ssh(): def about_ssh():

View File

@ -15,7 +15,7 @@ bp = Blueprint("publicapi", __name__, url_prefix="/api")
@bp.route("/capsul/create", methods=["POST"]) @bp.route("/capsul/create", methods=["POST"])
@account_required @account_required
def capsul_create(): def capsul_create():
email = session["account"] email = session["account"][0]
from .console import _create, get_account_balance, get_payments, get_vms from .console import _create, get_account_balance, get_payments, get_vms
@ -36,6 +36,7 @@ def capsul_create():
request.json['ssh_authorized_key_count'] = 1 request.json['ssh_authorized_key_count'] = 1
id, errors = _create( id, errors = _create(
email,
vm_sizes, vm_sizes,
operating_systems, operating_systems,
public_keys_for_account, public_keys_for_account,

View File

@ -3,7 +3,7 @@
# check available RAM and IPv4s # check available RAM and IPv4s
ram_bytes_to_allocate="$1" ram_bytes_to_allocate="$1"
ram_bytes_available=$(grep -E "^(size|memory_available_bytes)" /proc/spl/kstat/zfs/arcstats | awk '{sum+=$3} END {printf "%.0f", sum}') ram_bytes_available="$(($(grep Available /proc/meminfo | grep -o '[0-9]*') * 1024))"
ram_bytes_remainder="$((ram_bytes_available - ram_bytes_to_allocate))" ram_bytes_remainder="$((ram_bytes_available - ram_bytes_to_allocate))"
if echo "$ram_bytes_to_allocate" | grep -vqE "^[0-9]+$"; then if echo "$ram_bytes_to_allocate" | grep -vqE "^[0-9]+$"; then
@ -11,8 +11,8 @@ if echo "$ram_bytes_to_allocate" | grep -vqE "^[0-9]+$"; then
exit 1 exit 1
fi fi
# 20GB # 0.25GB
if [ "$ram_bytes_remainder" -le $((20 * 1024 * 1024 * 1024)) ]; then if [ "$ram_bytes_remainder" -le $((1 * 1024 * 1024 * 1024 / 4)) ]; then
echo "VM is requesting more RAM than $(hostname -f) has available." echo "VM is requesting more RAM than $(hostname -f) has available."
echo "Bytes requested: $ram_bytes_to_allocate" echo "Bytes requested: $ram_bytes_to_allocate"
echo "Bytes available: $ram_bytes_available" echo "Bytes available: $ram_bytes_available"

View File

@ -6,6 +6,7 @@
vmname="$1" vmname="$1"
template_file="/tank/img/$2" template_file="/tank/img/$2"
qemu_tank_dir="/tank"
vcpus="$3" vcpus="$3"
memory="$4" memory="$4"
pubkeys="$5" pubkeys="$5"
@ -50,40 +51,40 @@ if echo "$public_ipv4" | grep -vqE "^[0-9.]+$"; then
exit 1 exit 1
fi fi
disk="/tank/vm/$vmname.qcow2" disk="$vmname.qcow2"
cdrom="/tank/vm/$vmname.iso" cdrom="$vmname.iso"
xml="/tank/vm/$vmname.xml" xml="$vmname.xml"
if [ -f /tank/vm/$vmname.qcow2 ]; then if [ -f /tank/vm/$vmname.qcow2 ]; then
echo "Randomly generated name matched an existing VM! Odds are like one in a billion. Buy a lotto ticket." echo "Randomly generated name matched an existing VM! Odds are like one in a billion. Buy a lotto ticket."
exit 1 exit 1
fi fi
cp "$template_file" "$disk" cp "$template_file" "/tank/vm/$disk"
cp /tank/config/cyberia-cloudinit.yml /tmp/cloudinit.yml cp /tank/config/cyberia-cloudinit.yml /tmp/cloudinit.yml
echo "$pubkeys" | while IFS= read -r line; do echo "$pubkeys" | while IFS= read -r line; do
echo " - $line" >> /tmp/cloudinit.yml echo " - $line" >> /tmp/cloudinit.yml
done done
cloud-localds "$cdrom" /tmp/cloudinit.yml cloud-localds "/tank/vm/$cdrom" /tmp/cloudinit.yml
qemu-img resize "$disk" "$root_volume_size" qemu-img resize "/tank/vm/$disk" "$root_volume_size"
virt-install \ virt-install \
--memory "$memory" \ --memory "$memory" \
--vcpus "$vcpus" \ --vcpus "$vcpus" \
--name "$vmname" \ --name "$vmname" \
--disk "$disk",bus=virtio \ --disk "$qemu_tank_dir/vm/$disk",bus=virtio \
--disk "$cdrom",device=cdrom \ --disk "$qemu_tank_dir/vm/$cdrom",device=cdrom \
--os-type Linux \ --os-type Linux \
--os-variant generic \ --os-variant generic \
--virt-type kvm \ --virt-type kvm \
--graphics vnc,listen=127.0.0.1 \ --graphics vnc,listen=127.0.0.1 \
--network network=$network_name,filterref=clean-traffic,model=virtio \ --network network=$network_name,model=virtio \
--import \ --import \
--print-xml > "$xml" --print-xml > "/tank/vm/$xml"
chmod 0600 "$xml" "$disk" "$cdrom" chmod 0600 "/tank/vm/$xml" "/tank/vm/$disk" "/tank/vm/$cdrom"
virsh define "$xml" virsh define "/tank/vm/$xml"
virsh start "$vmname" virsh start "$vmname"
echo "success" echo "success"

View File

@ -29,7 +29,8 @@ if virsh domuuid "$vmname" | grep -vqE '^[\t\s\n]*$'; then
esac esac
fi fi
# this gets the ipv4 # this gets the vm ip addresses
ipv4="$(virsh domifaddr "$vmname" | awk '/vnet/ {print $4}' | cut -d'/' -f1)" ipv4="$(virsh domifaddr "$vmname" | awk '/ipv4/ {print $4}' | cut -d'/' -f1)"
ipv6="$(virsh domifaddr "$vmname" | awk '/ipv6/ {print $4}' | cut -d'/' -f1)"
echo "$exists $state $ipv4" echo "$exists $state $ipv4 $ipv6"

View File

@ -114,24 +114,30 @@ class ShellScriptSpoke(VirtualizationInterface):
if len(fields) < 3: if len(fields) < 3:
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state) return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state)
ipaddr = fields[2] ip4addr = 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}$", ip4addr):
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state) 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'), ip4addr], 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"], state=state, ipv4=ipaddr, ssh_host_keys=ssh_host_keys) return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ip4addr, 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 {ip4addr}:
{my_exec_info_message(sys.exc_info())}""" {my_exec_info_message(sys.exc_info())}"""
) )
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ipaddr) if len(fields) < 4:
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ip4addr)
ip6addr = fields[3]
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ip4addr, ipv6=ip6addr)
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)

View File

@ -1,8 +1,8 @@
html { html {
color: #bdc7b8; color: #241e1e;
font: calc(0.40rem + 1vmin) monospace; font: calc(0.40rem + 1vmin) monospace;
overflow-y: scroll; overflow-y: scroll;
background-color: #241e1e; background-color: #bdc7b8;
} }
body { body {
@ -19,8 +19,8 @@ body {
} }
a { a {
color:#6CF; color:#00517a;
text-shadow: 1px 1px 0px #000c; text-shadow: 1px 1px 0px #eee;
} }
a.no-shadow { a.no-shadow {
@ -28,7 +28,7 @@ a.no-shadow {
} }
a:hover, a:active, a:visited { a:hover, a:active, a:visited {
color: #b5bd68; color: #323417;
} }
.nav-links a { .nav-links a {
@ -59,11 +59,11 @@ h1, h2, h3, h4, h5 {
margin: initial; margin: initial;
padding: initial; padding: initial;
text-transform: uppercase; text-transform: uppercase;
text-shadow: 2px 2px 0px #0007; text-shadow: 2px 2px 0px #eee;
} }
main { main {
border: 1px dashed #bdc7b8; border: 1px dashed #241e1e;
padding: 1rem; padding: 1rem;
margin-bottom: 2em; margin-bottom: 2em;
@ -143,7 +143,7 @@ input, textarea, select, label {
input, select, textarea { input, select, textarea {
outline: 0; outline: 0;
padding: 0.25em 0.5em; padding: 0.25em 0.5em;
color: #bdc7b8; color: #241e1e;
background-color: #bdc7b805; background-color: #bdc7b805;
} }

View File

@ -79,6 +79,10 @@
<label class="align" for="ipv4">IPv4 Address</label> <label class="align" for="ipv4">IPv4 Address</label>
<span id="ipv4">{{ vm['ipv4'] }}</span> <span id="ipv4">{{ vm['ipv4'] }}</span>
</div> </div>
<div class="row justify-start">
<label class="align" for="ipv6">IPv6 Address</label>
<span id="ipv6">{{ vm['ipv6'] }}</span>
</div>
<div class="row justify-start"> <div class="row justify-start">
<label class="align" for="os_description">Operating System</label> <label class="align" for="os_description">Operating System</label>
<span id="os_description">{{ vm['os_description'] }}</span> <span id="os_description">{{ vm['os_description'] }}</span>
@ -97,7 +101,7 @@
</div> </div>
<div class="row justify-start"> <div class="row justify-start">
<label class="align" for="ssh_username">SSH Username</label> <label class="align" for="ssh_username">SSH Username</label>
<span id="ssh_username">cyberian</span> <span id="ssh_username">{{ vm['ssh_username'] }}</span>
</div> </div>
<div class="row justify-start"> <div class="row justify-start">
<label class="align" for="ssh_authorized_keys">SSH Authorized Keys</label> <label class="align" for="ssh_authorized_keys">SSH Authorized Keys</label>

View File

@ -21,13 +21,13 @@
</li> </li>
<li> <li>
How do I log in? How do I log in?
<p>ssh to the ip provided to you using the cyberian user.</p> <p>ssh to the ip provided to you using the "{{ ssh_username }}" user.</p>
<pre class='code'>$ ssh cyberian@1.2.3.4</pre> <pre class='code'>$ ssh {{ ssh_username }}@1.2.3.4</pre>
<p>For more information, see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>.</p> <p>For more information, see <a href="/about-ssh">Understanding the Secure Shell Protocol (SSH)</a>.</p>
</li> </li>
<li> <li>
How do I change to the root user? How do I change to the root user?
<p>The cyberian user has passwordless sudo access by default. This should work:</p> <p>The "{{ ssh_username }}" user has passwordless sudo access by default. This should work:</p>
<pre class='code'> <pre class='code'>
# Linux # Linux
$ sudo su - $ sudo su -

View File

@ -2,7 +2,7 @@
<head> <head>
<!-- Namecoin Address: N2aVL6pHtBp7EtNGb3jpsL2L2NyjBNbiB1 --> <!-- Namecoin Address: N2aVL6pHtBp7EtNGb3jpsL2L2NyjBNbiB1 -->
<link href="{{ url_for('static', filename='favicon.yolocolo.ico') }}" rel="icon"> <link href="{{ url_for('static', filename='favicon.yolocolo.ico') }}" rel="icon">
<title>{% block title %}{% endblock %}{% if self.title() %} - {% endif %}Capsul</title> <title>{% block title %}{% endblock %}{% if self.title() %} - {% endif %}Serverscoop</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="Description" content="Cyberia Capsul"> <meta name="Description" content="Cyberia Capsul">
@ -14,7 +14,7 @@
<nav> <nav>
<div class="row justify-space-between half-margin"> <div class="row justify-space-between half-margin">
<div> <div>
🦉 <a href="/"><b>YOLOCOLO</b></a> 🍞 <a href="/"><b>serverscoop</b></a>
</div> </div>
<div> <div>
&nbsp; &nbsp;
@ -31,7 +31,7 @@
{% if session["account"] %} {% if session["account"] %}
<a href="/console">Capsuls</a> <a href="/console">Capsuls</a>
<a href="/console/ssh">SSH Public Keys</a> <a href="/console/keys">SSH &amp; API Keys</a>
<a href="/console/account-balance">Account Balance</a> <a href="/console/account-balance">Account Balance</a>
{% endif %} {% endif %}
@ -49,9 +49,7 @@
<footer> <footer>
This server runs <a This server runs <a
href="https://giit.cyberia.club/~forest/capsul-flask">capsul-flask</a> by href="https://giit.cyberia.club/~forest/capsul-flask">capsul-flask</a> by
Cyberia Computer Club, available under the <a Cyberia Computer Club, available under the GNU AFFERO GENERAL PUBLIC LICENSE.<br/><br/>
href="https://creativecommons.org/licenses/by-sa/4.0/">Attribution-ShareAlike
4.0 International</a> licence.<br/><br/>
<a href="https://git.autonomic.zone/3wordchant/capsul-flask/src/branch/yolocolo/capsulflask{% block pagesource %}{% endblock %}">View page source</a> <a href="https://git.autonomic.zone/3wordchant/capsul-flask/src/branch/yolocolo/capsulflask{% block pagesource %}{% endblock %}">View page source</a>
</footer> </footer>
</body> </body>

View File

@ -3,13 +3,11 @@
{% block content %} {% block content %}
<h1> <h1>
<pre> <pre>
_ _ ___ ___ _ ____ _____ _ __ ___ ___ ___ ___ _ __
_ _ ___ | | ___ ___ ___ | | ___ / __|/ _ \ '__\ \ / / _ \ '__/ __|/ __/ _ \ / _ \| '_ \
| | | |/ _ \| |/ _ \ / __/ _ \| |/ _ \ \__ \ __/ | \ V / __/ | \__ \ (_| (_) | (_) | |_) |
| |_| | (_) | | (_) | (_| (_) | | (_) | |___/\___|_| \_/ \___|_| |___/\___\___/ \___/| .__/
\__, |\___/|_|\___/ \___\___/|_|\___/ |_|
|___/
</pre> </pre>
<span>Co-operative hosting using <a href="https://cyberia.club">Cyberia</a>'s Capsul</span> <span>Co-operative hosting using <a href="https://cyberia.club">Cyberia</a>'s Capsul</span>
{% endblock %} {% endblock %}