mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
The VM's keys page (instances:sshkeys) and the cloud-init user data collected the owners' keys with one query an owner. Both now read them in one query joined over the VM's owners, ordered owner by owner as before.
127 lines
3.9 KiB
Python
127 lines
3.9 KiB
Python
import json
|
|
import socket
|
|
|
|
from accounts.models import UserSSHKey
|
|
from computes.models import Compute
|
|
from django.contrib.auth.decorators import login_not_required
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.http import Http404, HttpResponse, JsonResponse
|
|
from django.shortcuts import get_object_or_404, render
|
|
from instances.models import Instance
|
|
from instances.utils import can_open_console
|
|
from libvirt import libvirtError
|
|
from vrtManager.instance import wvmInstance
|
|
|
|
OS_VERSIONS = ["latest", ""]
|
|
OS_UUID = "iid-dswebvirtcloud"
|
|
|
|
|
|
# The cloud-init endpoints are fetched by booting VMs, which do not log in.
|
|
# They expose only a hostname and the public SSH keys of a VM's owners.
|
|
|
|
|
|
@login_not_required
|
|
def os_index(request):
|
|
"""
|
|
:param request:
|
|
:return:
|
|
"""
|
|
response = "\n".join(OS_VERSIONS)
|
|
return HttpResponse(response)
|
|
|
|
|
|
@login_not_required
|
|
def os_metadata_json(request, version):
|
|
"""
|
|
:param request:
|
|
:param version:
|
|
:return:
|
|
"""
|
|
|
|
if version == "latest":
|
|
ip = get_client_ip(request)
|
|
hostname = get_hostname_by_ip(ip) or ip
|
|
response = {"uuid": OS_UUID, "hostname": hostname}
|
|
return JsonResponse(response)
|
|
else:
|
|
err = "Invalid version: %(version)s" % {"version": version}
|
|
raise Http404(err)
|
|
|
|
|
|
@login_not_required
|
|
def os_userdata(request, version):
|
|
"""
|
|
:param request:
|
|
:param version:
|
|
:return:
|
|
"""
|
|
if version == "latest":
|
|
hostname = get_hostname_by_ip(get_client_ip(request))
|
|
|
|
instance_keys = []
|
|
# The VM is named by its address's reverse DNS entry; without one there
|
|
# is no name (the address's first number is no VM's name). VM names
|
|
# are not unique: with two VMs of this name, a user could put their key
|
|
# into the other VM by naming theirs the same. No keys then.
|
|
instances = list(Instance.objects.filter(name=hostname.split(".")[0])[:2]) if hostname else []
|
|
if len(instances) == 1:
|
|
# the keys of the VM's owners, owner by owner
|
|
instance_keys = list(UserSSHKey.objects.filter(user__userinstance__instance=instances[0])
|
|
.order_by("user__userinstance__id", "id").values_list("keypublic", flat=True))
|
|
# quoted, so a comment such as "it's: mine" stays one YAML string
|
|
instance_keys = [json.dumps(key) for key in instance_keys]
|
|
|
|
return render(request, "user_data", locals())
|
|
else:
|
|
err = "Invalid version: %(version)s" % {"version": version}
|
|
raise Http404(err)
|
|
|
|
|
|
def get_client_ip(request):
|
|
"""
|
|
:param request:
|
|
:return:
|
|
"""
|
|
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
|
if x_forwarded_for:
|
|
ip = x_forwarded_for.split(",")[-1].strip()
|
|
else:
|
|
ip = request.META.get("REMOTE_ADDR")
|
|
return ip
|
|
|
|
|
|
def get_hostname_by_ip(ip):
|
|
"""
|
|
:param ip:
|
|
:return: the address's reverse DNS name, or None
|
|
"""
|
|
try:
|
|
return socket.gethostbyaddr(ip)[0]
|
|
except OSError: # no reverse DNS entry or an invalid address
|
|
return None
|
|
|
|
|
|
def get_vdi_url(request, compute_id, vname):
|
|
"""
|
|
:param request:
|
|
:param vname:
|
|
:return:
|
|
"""
|
|
compute = get_object_or_404(Compute, pk=compute_id)
|
|
# The VDI URL points straight at the VM's console: same rule as the console.
|
|
instance = Instance.objects.filter(compute=compute, name=vname).first()
|
|
if instance is None or not can_open_console(request.user, instance):
|
|
raise PermissionDenied
|
|
|
|
try:
|
|
conn = wvmInstance(
|
|
compute.hostname, compute.login, compute.password, compute.type, vname, uuid=instance.uuid
|
|
)
|
|
|
|
fqdn = get_hostname_by_ip(compute.hostname) or compute.hostname
|
|
url = f"{conn.get_console_type()}://{fqdn}:{conn.get_console_port()}"
|
|
response = url
|
|
return HttpResponse(response)
|
|
except libvirtError:
|
|
err = "Error getting VDI URL for %(name)s" % {"name": vname}
|
|
raise Http404(err)
|