1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/datasource/views.py
catborise 4dd72766cf perf: a VM's owners' SSH keys in one query
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.
2026-11-04 21:42:18 +03:00

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)