4 Commits

Author SHA1 Message Date
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
4 changed files with 48 additions and 33 deletions

View File

@ -29,12 +29,14 @@ def make_capsul_id():
letters_n_nummers = generate(alphabet="1234567890qwertyuiopasdfghjklzxcvbnm", size=10)
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:
result = current_app.config["HUB_MODEL"].get(id, get_ssh_host_keys)
if result != None and result.ipv4 != None and result.ipv4 != ipv4:
ipv4 = result.ipv4
get_model().update_vm_ip(email=session["account"], id=id, ipv4=result.ipv4)
get_model().update_vm_ipv4(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:
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
# on every request. this could be done by a background job and cached later on...
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:
vm["ipv4"] = result.ipv4
vm["ipv6"] = result.ipv6
vm["state"] = result.state
else:
vm["state"] = "unknown"
mappedVms = []
for vm in vms:
ip_display = vm['ipv4']
if not ip_display:
if vm["state"] == "running":
ip_display = "..booting.."
else:
ip_display = "unknown"
ip_display_class = "ok"
if not vm['ipv4']:
if vm["state"] == "running":
ip_display_class = "waiting-pulse"
else:
ip_display_class = "yellow"
ip_display = {}
ip_display_class = {}
for af in ['ipv4', 'ipv6']:
ip_display[af] = vm[af]
ip_display_class[af] = "ok"
if not ip_display[af]:
if vm["state"] == "running":
ip_display[af] = "..booting.."
ip_display_class[af] = "waiting-pulse"
else:
ip_display[af] = "unknown"
ip_display_class[af] = "yellow"
mappedVms.append(dict(
id=vm['id'],
size=vm['size'],
id=vm['id'],
size=vm['size'],
state=vm['state'],
ipv4=ip_display,
ipv4_status=ip_display_class,
os=vm['os'],
ipv4=ip_display['ipv4'],
ipv4_status=ip_display_class['ipv4'],
ipv6=ip_display['ipv6'],
ipv6_status=ip_display_class['ipv6'],
os=vm['os'],
created=vm['created'].strftime("%b %d %Y")
))
@ -168,10 +171,11 @@ def detail(id):
else:
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:
vm["ipv4"] = vm_from_virt_model.ipv4
vm["ipv6"] = vm_from_virt_model.ipv6
vm["state"] = vm_from_virt_model.state
if needs_ssh_host_keys:
vm["ssh_host_keys"] = vm_from_virt_model.ssh_host_keys

View File

@ -179,10 +179,14 @@ class DBModel:
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.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):
for key in ssh_host_keys:
self.cursor.execute("""

View File

@ -29,7 +29,8 @@ if virsh domuuid "$vmname" | grep -vqE '^[\t\s\n]*$'; then
esac
fi
# this gets the ipv4
# this gets the vm ip addresses
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:
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)
if get_ssh_host_keys:
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)
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:
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())}"""
)
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:
completedProcess = run([join(current_app.root_path, 'shell_scripts/list-ids.sh')], capture_output=True)