working on support for managing VM state and IP address
This commit is contained in:
parent
9d74c99ce8
commit
cc349d266f
@ -17,6 +17,10 @@ bp = Blueprint("admin", __name__, url_prefix="/admin")
|
|||||||
@bp.route("/")
|
@bp.route("/")
|
||||||
@admin_account_required
|
@admin_account_required
|
||||||
def index():
|
def index():
|
||||||
|
|
||||||
|
# first create the hosts list w/ ip allocation visualization
|
||||||
|
#
|
||||||
|
|
||||||
hosts = get_model().list_hosts_with_networks(None)
|
hosts = get_model().list_hosts_with_networks(None)
|
||||||
vms_by_host_and_network = get_model().non_deleted_vms_by_host_and_network(None)
|
vms_by_host_and_network = get_model().non_deleted_vms_by_host_and_network(None)
|
||||||
network_display_width_px = float(270)
|
network_display_width_px = float(270)
|
||||||
@ -29,6 +33,8 @@ def index():
|
|||||||
{'}'}
|
{'}'}
|
||||||
"""]
|
"""]
|
||||||
|
|
||||||
|
public_ipv4_by_capsul_id = dict()
|
||||||
|
|
||||||
for kv in hosts.items():
|
for kv in hosts.items():
|
||||||
host_id = kv[0]
|
host_id = kv[0]
|
||||||
value = kv[1]
|
value = kv[1]
|
||||||
@ -45,6 +51,7 @@ def index():
|
|||||||
if host_id in vms_by_host_and_network:
|
if host_id in vms_by_host_and_network:
|
||||||
if network['network_name'] in vms_by_host_and_network[host_id]:
|
if network['network_name'] in vms_by_host_and_network[host_id]:
|
||||||
for vm in vms_by_host_and_network[host_id][network['network_name']]:
|
for vm in vms_by_host_and_network[host_id][network['network_name']]:
|
||||||
|
public_ipv4_by_capsul_id[vm['id']] = vm['public_ipv4']
|
||||||
ip_address_int = int(ipaddress.ip_address(vm['public_ipv4']))
|
ip_address_int = int(ipaddress.ip_address(vm['public_ipv4']))
|
||||||
if network_start_int <= ip_address_int and ip_address_int <= network_end_int:
|
if network_start_int <= ip_address_int and ip_address_int <= network_end_int:
|
||||||
allocation = f"{host_id}_{network['network_name']}_{len(network['allocations'])}"
|
allocation = f"{host_id}_{network['network_name']}_{len(network['allocations'])}"
|
||||||
@ -62,10 +69,37 @@ def index():
|
|||||||
|
|
||||||
display_hosts.append(display_host)
|
display_hosts.append(display_host)
|
||||||
|
|
||||||
|
|
||||||
|
# Now creating the capsuls running status ui
|
||||||
|
#
|
||||||
|
|
||||||
|
db_vms = get_model().all_vm_ids_with_desired_state()
|
||||||
|
virt_vms = current_app.config["HUB_MODEL"].list_ids_with_desired_state()
|
||||||
|
|
||||||
|
virt_vms_dict = dict()
|
||||||
|
for vm in virt_vms:
|
||||||
|
virt_vms_dict[vm["id"]] = vm["state"]
|
||||||
|
|
||||||
|
in_db_but_not_in_virt = []
|
||||||
|
needs_to_be_started = []
|
||||||
|
needs_to_be_started_missing_ipv4 = []
|
||||||
|
|
||||||
|
for vm in db_vms:
|
||||||
|
if vm["id"] not in virt_vms_dict:
|
||||||
|
in_db_but_not_in_virt.append(vm["id"])
|
||||||
|
elif vm["desired_state"] == "running" and virt_vms_dict[vm["id"]] != "running":
|
||||||
|
if vm["id"] in public_ipv4_by_capsul_id:
|
||||||
|
needs_to_be_started.append({"id": vm["id"], "ipv4": public_ipv4_by_capsul_id[vm["id"]]})
|
||||||
|
else:
|
||||||
|
needs_to_be_started_missing_ipv4.append(vm["id"])
|
||||||
|
|
||||||
csp_inline_style_nonce = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
|
csp_inline_style_nonce = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
|
||||||
response_text = render_template(
|
response_text = render_template(
|
||||||
"admin.html",
|
"admin.html",
|
||||||
display_hosts=display_hosts,
|
display_hosts=display_hosts,
|
||||||
|
in_db_but_not_in_virt=in_db_but_not_in_virt,
|
||||||
|
needs_to_be_started=needs_to_be_started,
|
||||||
|
needs_to_be_started_missing_ipv4=needs_to_be_started_missing_ipv4,
|
||||||
network_display_width_px=network_display_width_px,
|
network_display_width_px=network_display_width_px,
|
||||||
csp_inline_style_nonce=csp_inline_style_nonce,
|
csp_inline_style_nonce=csp_inline_style_nonce,
|
||||||
inline_style='\n'.join(inline_styles)
|
inline_style='\n'.join(inline_styles)
|
||||||
|
@ -268,23 +268,25 @@ def notify_users_about_account_balance():
|
|||||||
|
|
||||||
|
|
||||||
def ensure_vms_and_db_are_synced():
|
def ensure_vms_and_db_are_synced():
|
||||||
db_ids = get_model().all_non_deleted_vm_ids()
|
db_vms = get_model().all_vm_ids_with_desired_state()
|
||||||
virt_ids = current_app.config["HUB_MODEL"].list_ids()
|
virt_vms = current_app.config["HUB_MODEL"].list_ids_with_desired_state()
|
||||||
|
|
||||||
db_ids_dict = dict()
|
db_ids_dict = dict()
|
||||||
virt_ids_dict = dict()
|
virt_ids_dict = dict()
|
||||||
|
|
||||||
for id in db_ids:
|
for vm in db_vms:
|
||||||
db_ids_dict[id] = True
|
db_ids_dict[vm['id']] = vm['desired_state']
|
||||||
|
|
||||||
for id in virt_ids:
|
for vm in virt_vms:
|
||||||
virt_ids_dict[id] = True
|
virt_ids_dict[vm['id']] = vm['desired_state']
|
||||||
|
|
||||||
errors = list()
|
errors = list()
|
||||||
|
|
||||||
for id in db_ids_dict:
|
for id in db_ids_dict:
|
||||||
if id not in virt_ids_dict:
|
if id not in virt_ids_dict:
|
||||||
errors.append(f"{id} is in the database but not in the virtualization model")
|
errors.append(f"{id} is in the database but not in the virtualization model")
|
||||||
|
elif db_ids_dict[id] != virt_ids_dict[id]:
|
||||||
|
errors.append(f"{id} has the desired state {db_ids_dict[id]} in the database but current state {virt_ids_dict[id]} in the virtualization model")
|
||||||
|
|
||||||
for id in virt_ids_dict:
|
for id in virt_ids_dict:
|
||||||
if id not in db_ids_dict:
|
if id not in db_ids_dict:
|
||||||
|
108
capsulflask/consistency.py
Normal file
108
capsulflask/consistency.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
from capsulflask.db import get_model
|
||||||
|
|
||||||
|
# {
|
||||||
|
# "capsul-123abc45": {
|
||||||
|
# "id": "capsul-123abc45",
|
||||||
|
# "public_ipv4": "123.123.123.123",
|
||||||
|
# "public_ipv6": "::::",
|
||||||
|
# "host": "baikal",
|
||||||
|
# "network_name": "public1",
|
||||||
|
# "virtual_bridge_name": "virbr1",
|
||||||
|
# "state": "running"
|
||||||
|
# },
|
||||||
|
# { ... },
|
||||||
|
# ...
|
||||||
|
# }
|
||||||
|
def get_all_vms_from_db() -> dict:
|
||||||
|
db_hosts = get_model().list_hosts_with_networks(None)
|
||||||
|
db_vms_by_host_and_network = get_model().non_deleted_vms_by_host_and_network(None)
|
||||||
|
|
||||||
|
db_vms_by_id = dict()
|
||||||
|
|
||||||
|
for kv in db_hosts.items():
|
||||||
|
host_id = kv[0]
|
||||||
|
value = kv[1]
|
||||||
|
for network in value['networks']:
|
||||||
|
if host_id in db_vms_by_host_and_network and network['network_name'] in db_vms_by_host_and_network[host_id]:
|
||||||
|
for vm in db_vms_by_host_and_network[host_id][network['network_name']]:
|
||||||
|
vm['network_name'] = network['network_name']
|
||||||
|
vm['virtual_bridge_name'] = network['virtual_bridge_name']
|
||||||
|
vm['host'] = host_id
|
||||||
|
db_vms_by_id[vm['id']] = vm
|
||||||
|
|
||||||
|
# for vm in db_vms:
|
||||||
|
# if vm["id"] not in db_vms_by_id:
|
||||||
|
# # TODO
|
||||||
|
# raise Exception("non_deleted_vms_by_host_and_network did not return a vm that was returned by all_vm_ids_with_desired_state")
|
||||||
|
# else:
|
||||||
|
# db_vms_by_id[vm["id"]]["state"] = vm["desired_state"]
|
||||||
|
|
||||||
|
return db_vms_by_id
|
||||||
|
|
||||||
|
def get_all_vms_from_hosts() -> dict:
|
||||||
|
virt_vms = current_app.config["HUB_MODEL"].virsh_list()
|
||||||
|
virt_networks = current_app.config["HUB_MODEL"].virsh_netlist()
|
||||||
|
db_hosts = get_model().list_hosts_with_networks(None)
|
||||||
|
|
||||||
|
virt_vms_by_id = dict()
|
||||||
|
|
||||||
|
for kv in db_hosts.items():
|
||||||
|
host_id = kv[0]
|
||||||
|
value = kv[1]
|
||||||
|
for network in value['networks']:
|
||||||
|
|
||||||
|
|
||||||
|
for vm in db_vms:
|
||||||
|
if vm["id"] not in db_vms_by_id:
|
||||||
|
# TODO
|
||||||
|
raise Exception("non_deleted_vms_by_host_and_network did not return a vm that was returned by all_vm_ids_with_desired_state")
|
||||||
|
else:
|
||||||
|
db_vms_by_id[vm["id"]]["state"] = vm["desired_state"]
|
||||||
|
|
||||||
|
virt_vms = current_app.config["HUB_MODEL"].get_vm_()
|
||||||
|
|
||||||
|
def ensure_vms_and_db_are_synced():
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Now creating the capsuls running status ui
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for vm in db_vms:
|
||||||
|
db_ids_dict[vm['id']] = vm['desired_state']
|
||||||
|
|
||||||
|
for vm in virt_vms:
|
||||||
|
virt_ids_dict[vm['id']] = vm['desired_state']
|
||||||
|
|
||||||
|
errors = list()
|
||||||
|
|
||||||
|
for id in db_ids_dict:
|
||||||
|
if id not in virt_ids_dict:
|
||||||
|
errors.append(f"{id} is in the database but not in the virtualization model")
|
||||||
|
elif db_ids_dict[id] != virt_ids_dict[id]:
|
||||||
|
errors.append(f"{id} has the desired state {db_ids_dict[id]} in the database but current state {virt_ids_dict[id]} in the virtualization model")
|
||||||
|
|
||||||
|
for id in virt_ids_dict:
|
||||||
|
if id not in db_ids_dict:
|
||||||
|
errors.append(f"{id} is in the virtualization model but not in the database")
|
||||||
|
|
||||||
|
if len(errors) > 0:
|
||||||
|
email_addresses_raw = current_app.config['ADMIN_EMAIL_ADDRESSES'].split(",")
|
||||||
|
email_addresses = list(filter(lambda x: len(x) > 6, map(lambda x: x.strip(), email_addresses_raw ) ))
|
||||||
|
|
||||||
|
current_app.logger.info(f"cron_task: sending inconsistency warning email to {','.join(email_addresses)}:")
|
||||||
|
for error in errors:
|
||||||
|
current_app.logger.info(f"cron_task: {error}.")
|
||||||
|
|
||||||
|
current_app.config["FLASK_MAIL_INSTANCE"].send(
|
||||||
|
Message(
|
||||||
|
"Capsul Consistency Check Failed",
|
||||||
|
sender=current_app.config["MAIL_DEFAULT_SENDER"],
|
||||||
|
body="\n".join(errors),
|
||||||
|
recipients=email_addresses
|
||||||
|
)
|
||||||
|
)
|
@ -27,12 +27,12 @@ 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, 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
|
# 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.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)
|
||||||
@ -59,7 +59,7 @@ 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"], False)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
vm["ipv4"] = result.ipv4
|
vm["ipv4"] = result.ipv4
|
||||||
vm["state"] = result.state
|
vm["state"] = result.state
|
||||||
@ -167,7 +167,7 @@ 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"], 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
|
||||||
|
@ -50,7 +50,7 @@ def init_app(app, is_running_server):
|
|||||||
hasSchemaVersionTable = False
|
hasSchemaVersionTable = False
|
||||||
actionWasTaken = False
|
actionWasTaken = False
|
||||||
schemaVersion = 0
|
schemaVersion = 0
|
||||||
desiredSchemaVersion = 20
|
desiredSchemaVersion = 21
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
@ -86,9 +86,9 @@ class DBModel:
|
|||||||
|
|
||||||
return hosts
|
return hosts
|
||||||
|
|
||||||
def all_non_deleted_vm_ids(self):
|
def all_vm_ids_with_desired_state(self):
|
||||||
self.cursor.execute("SELECT id FROM vms WHERE deleted IS NULL")
|
self.cursor.execute("SELECT id, desired_state FROM vms WHERE deleted IS NULL")
|
||||||
return list(map(lambda x: x[0], self.cursor.fetchall()))
|
return list(map(lambda x: {"id": x[0], "desired_state": x[1]}, self.cursor.fetchall()))
|
||||||
|
|
||||||
def operating_systems_dict(self):
|
def operating_systems_dict(self):
|
||||||
self.cursor.execute("SELECT id, template_image_file_name, description FROM os_images WHERE deprecated = FALSE")
|
self.cursor.execute("SELECT id, template_image_file_name, description FROM os_images WHERE deprecated = FALSE")
|
||||||
@ -332,7 +332,7 @@ class DBModel:
|
|||||||
|
|
||||||
def list_hosts_with_networks(self, host_id: str):
|
def list_hosts_with_networks(self, host_id: str):
|
||||||
query = """
|
query = """
|
||||||
SELECT hosts.id, hosts.last_health_check, host_network.network_name,
|
SELECT hosts.id, hosts.last_health_check, host_network.network_name, host_network.virtual_bridge_name,
|
||||||
host_network.public_ipv4_cidr_block, host_network.public_ipv4_first_usable_ip, host_network.public_ipv4_last_usable_ip
|
host_network.public_ipv4_cidr_block, host_network.public_ipv4_first_usable_ip, host_network.public_ipv4_last_usable_ip
|
||||||
FROM hosts
|
FROM hosts
|
||||||
JOIN host_network ON host_network.host = hosts.id
|
JOIN host_network ON host_network.host = hosts.id
|
||||||
@ -355,9 +355,10 @@ class DBModel:
|
|||||||
|
|
||||||
hosts[row[0]]["networks"].append(dict(
|
hosts[row[0]]["networks"].append(dict(
|
||||||
network_name=row[2],
|
network_name=row[2],
|
||||||
public_ipv4_cidr_block=row[3],
|
virtual_bridge_name=row[3],
|
||||||
public_ipv4_first_usable_ip=row[4],
|
public_ipv4_cidr_block=row[4],
|
||||||
public_ipv4_last_usable_ip=row[5]
|
public_ipv4_first_usable_ip=row[5],
|
||||||
|
public_ipv4_last_usable_ip=row[6]
|
||||||
))
|
))
|
||||||
|
|
||||||
return hosts
|
return hosts
|
||||||
|
@ -37,8 +37,8 @@ class MockHub(VirtualizationInterface):
|
|||||||
|
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=self.default_ipv4)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=self.default_ipv4)
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def get_all_by_host_and_network(self) -> dict:
|
||||||
return get_model().all_non_deleted_vm_ids()
|
return get_model().non_deleted_vms_by_host_and_network()
|
||||||
|
|
||||||
def create(self, email: str, id: str, os: str, size: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list):
|
def create(self, email: str, id: str, os: str, size: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list):
|
||||||
validate_capsul_id(id)
|
validate_capsul_id(id)
|
||||||
@ -164,29 +164,24 @@ class CapsulFlaskHub(VirtualizationInterface):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def get_all_by_host_and_network(self) -> dict:
|
||||||
online_hosts = get_model().get_online_hosts()
|
online_hosts = get_model().get_online_hosts()
|
||||||
payload = json.dumps(dict(type="list_ids"))
|
payload = json.dumps(dict(type="get_all_by_host_and_network"))
|
||||||
results = self.synchronous_operation(online_hosts, None, payload)
|
results = self.synchronous_operation(online_hosts, None, payload)
|
||||||
to_return = []
|
to_return = dict()
|
||||||
for i in range(len(results)):
|
for i in range(len(results)):
|
||||||
host = online_hosts[i]
|
host = online_hosts[i]
|
||||||
result = results[i]
|
result = results[i]
|
||||||
try:
|
try:
|
||||||
result_body = json.loads(result.body)
|
result_body = json.loads(result.body)
|
||||||
if isinstance(result_body, dict) and 'ids' in result_body and isinstance(result_body['ids'], list):
|
|
||||||
all_valid = True
|
has_host = isinstance(result_body, dict) and host.id in result_body and isinstance(result_body[host.id], dict)
|
||||||
for id in result_body['ids']:
|
has_networks = has_host and 'networks' in result_body[host.id] and isinstance(result_body[host.id]['networks'], dict)
|
||||||
try:
|
if has_host and has_networks:
|
||||||
validate_capsul_id(id)
|
to_return[host.id] = result_body[host.id]['networks']
|
||||||
to_return.append(id)
|
|
||||||
except:
|
|
||||||
all_valid = False
|
|
||||||
if not all_valid:
|
|
||||||
current_app.logger.error(f"""error reading ids for list_ids operation, host {host.id}""")
|
|
||||||
else:
|
else:
|
||||||
result_json_string = json.dumps({"error_message": "invalid response, missing 'ids' list"})
|
# result_json_string = json.dumps({"error_message": "invalid response, missing 'networks' list"})
|
||||||
current_app.logger.error(f"""missing 'ids' list for list_ids operation, host {host.id}""")
|
current_app.logger.error(f"""missing 'networks' list for get_all_by_host_and_network operation, host {host.id}""")
|
||||||
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
|
||||||
|
7
capsulflask/schema_migrations/21_down_desired_state.sql
Normal file
7
capsulflask/schema_migrations/21_down_desired_state.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE vms DROP COLUMN desired_state;
|
||||||
|
|
||||||
|
UPDATE schemaversion SET version = 20;
|
||||||
|
|
7
capsulflask/schema_migrations/21_up_desired_state.sql
Normal file
7
capsulflask/schema_migrations/21_up_desired_state.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE vms ADD COLUMN desired_state TEXT DEFAULT 'running';
|
||||||
|
|
||||||
|
UPDATE schemaversion SET version = 21;
|
||||||
|
|
@ -31,7 +31,7 @@ class VirtualizationInterface:
|
|||||||
def get(self, id: str) -> VirtualMachine:
|
def get(self, id: str) -> VirtualMachine:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def get_all_by_host_and_network(self) -> dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory: int, ssh_authorized_keys: list):
|
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory: int, ssh_authorized_keys: list):
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
virsh list --all | grep running | grep -v ' Id' | grep -v -- '----' | awk '{print $2}' | sort
|
|
@ -8,15 +8,15 @@ if echo "$ip_address" | grep -vqE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
printf '['
|
printf '['
|
||||||
DELIMITER=""
|
delimiter=""
|
||||||
ssh-keyscan "$ip_address" 2>/dev/null | while read -r line; do
|
ssh-keyscan "$ip_address" 2>/dev/null | while read -r line; do
|
||||||
if echo "$line" | grep -qE "^$ip_address"' +(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$'; then
|
if echo "$line" | grep -qE "^$ip_address"' +(ssh|ecdsa)-[0-9A-Za-z+/_=@. -]+$'; then
|
||||||
KEY_CONTENT="$(echo "$line" | awk '{ print $2 " " $3 }')"
|
key_content="$(echo "$line" | awk '{ print $2 " " $3 }')"
|
||||||
FINGERPRINT_OUTPUT="$(echo "$KEY_CONTENT" | ssh-keygen -l -E sha256 -f - | sed -E 's/^[0-9]+ SHA256:([0-9A-Za-z+/-]+) .+ \(([A-Z0-9]+)\)$/\1 \2/g')"
|
fingerprint_output="$(echo "$key_content" | ssh-keygen -l -E sha256 -f - | sed -E 's/^[0-9]+ SHA256:([0-9A-Za-z+/-]+) .+ \(([A-Z0-9]+)\)$/\1 \2/g')"
|
||||||
SHA256_HASH="$(echo "$FINGERPRINT_OUTPUT" | awk '{ print $1 }')"
|
sha256_hash="$(echo "$fingerprint_output" | awk '{ print $1 }')"
|
||||||
KEY_TYPE="$(echo "$FINGERPRINT_OUTPUT" | awk '{ print $2 }')"
|
key_type="$(echo "$fingerprint_output" | awk '{ print $2 }')"
|
||||||
printf '%s\n {"key_type":"%s", "content":"%s", "sha256":"%s"}' "$DELIMITER" "$KEY_TYPE" "$KEY_CONTENT" "$SHA256_HASH"
|
printf '%s\n {"key_type":"%s", "content":"%s", "sha256":"%s"}' "$delimiter" "$key_type" "$key_content" "$sha256_hash"
|
||||||
DELIMITER=","
|
delimiter=","
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
printf '\n]\n'
|
printf '\n]\n'
|
||||||
|
13
capsulflask/shell_scripts/virsh-list.sh
Executable file
13
capsulflask/shell_scripts/virsh-list.sh
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
printf '['
|
||||||
|
delimiter=""
|
||||||
|
virsh list --all | while read -r line; do
|
||||||
|
if echo "$line" | grep -qE '(running)|(shut off)'; then
|
||||||
|
capsul_id="$(echo "$line" | awk '{ print $2 }')"
|
||||||
|
capsul_state="$(echo "$line" | sed -E 's/.*((running)|(shut off))\w*/\1/')"
|
||||||
|
printf '%s\n {"id":"%s", "state":"%s"}' "$delimiter" "$capsul_id" "$capsul_state"
|
||||||
|
delimiter=","
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf '\n]\n'
|
3
capsulflask/shell_scripts/virsh-net-list.sh
Normal file
3
capsulflask/shell_scripts/virsh-net-list.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
virsh net-list --all | tail -n +3 | awk '{ print $1 }'
|
@ -49,7 +49,7 @@ def operation_impl(operation_id: int):
|
|||||||
handlers = {
|
handlers = {
|
||||||
"capacity_avaliable": handle_capacity_avaliable,
|
"capacity_avaliable": handle_capacity_avaliable,
|
||||||
"get": handle_get,
|
"get": handle_get,
|
||||||
"list_ids": handle_list_ids,
|
"get_all_by_host_and_network": handle_get_all_by_host_and_network,
|
||||||
"create": handle_create,
|
"create": handle_create,
|
||||||
"destroy": handle_destroy,
|
"destroy": handle_destroy,
|
||||||
"vm_state_command": handle_vm_state_command,
|
"vm_state_command": handle_vm_state_command,
|
||||||
@ -96,8 +96,8 @@ def handle_get(operation_id, request_body):
|
|||||||
|
|
||||||
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))
|
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_get_all_by_host_and_network(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'].get_all_by_host_and_network()))
|
||||||
|
|
||||||
def handle_create(operation_id, request_body):
|
def handle_create(operation_id, request_body):
|
||||||
if not operation_id:
|
if not operation_id:
|
||||||
|
@ -38,8 +38,8 @@ class MockSpoke(VirtualizationInterface):
|
|||||||
|
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=ipv4, state="running")
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], ipv4=ipv4, state="running")
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def get_all_by_host_and_network(self) -> dict:
|
||||||
return get_model().all_non_deleted_vm_ids()
|
return get_model().non_deleted_vms_by_host_and_network(None)
|
||||||
|
|
||||||
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list, network_name: str, public_ipv4: str):
|
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list, network_name: str, public_ipv4: str):
|
||||||
validate_capsul_id(id)
|
validate_capsul_id(id)
|
||||||
@ -133,10 +133,11 @@ class ShellScriptSpoke(VirtualizationInterface):
|
|||||||
|
|
||||||
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ipaddr)
|
return VirtualMachine(id, current_app.config["SPOKE_HOST_ID"], state=state, ipv4=ipaddr)
|
||||||
|
|
||||||
def list_ids(self) -> list:
|
def get_all_by_host_and_network(self) -> list:
|
||||||
completedProcess = run([join(current_app.root_path, 'shell_scripts/list-ids.sh')], capture_output=True)
|
# TODO implement this
|
||||||
|
completedProcess = run([join(current_app.root_path, 'shell_scripts/virsh-list.sh')], capture_output=True)
|
||||||
self.validate_completed_process(completedProcess)
|
self.validate_completed_process(completedProcess)
|
||||||
return list(map(lambda x: x.decode("utf-8"), completedProcess.stdout.splitlines() ))
|
return json.loads(completedProcess.stdout.decode("utf-8"))
|
||||||
|
|
||||||
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list, network_name: str, public_ipv4: str):
|
def create(self, email: str, id: str, template_image_file_name: str, vcpus: int, memory_mb: int, ssh_authorized_keys: list, network_name: str, public_ipv4: str):
|
||||||
validate_capsul_id(id)
|
validate_capsul_id(id)
|
||||||
|
Loading…
Reference in New Issue
Block a user