mirror of
https://github.com/retspen/webvirtcloud
synced 2025-07-31 12:41:08 +00:00
Instances overhaul
This commit is contained in:
parent
f23e6b000f
commit
47009d47ca
69 changed files with 5011 additions and 4127 deletions
|
@ -1,8 +1,10 @@
|
|||
from django.db.models import CharField, IntegerField, Model
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from libvirt import virConnect
|
||||
|
||||
from vrtManager.connection import connection_manager
|
||||
from vrtManager.hostdetails import wvmHostDetails
|
||||
|
||||
|
||||
class Compute(Model):
|
||||
|
@ -15,7 +17,45 @@ class Compute(Model):
|
|||
|
||||
@cached_property
|
||||
def status(self):
|
||||
return connection_manager.host_is_up(self.type, self.hostname)
|
||||
# return connection_manager.host_is_up(self.type, self.hostname)
|
||||
# TODO: looks like socket has problems connecting via VPN
|
||||
if isinstance(self.connection, virConnect):
|
||||
return True
|
||||
else:
|
||||
return self.connection
|
||||
|
||||
@cached_property
|
||||
def connection(self):
|
||||
try:
|
||||
return connection_manager.get_connection(
|
||||
self.hostname,
|
||||
self.login,
|
||||
self.password,
|
||||
self.type,
|
||||
)
|
||||
except Exception as e:
|
||||
return e
|
||||
|
||||
@cached_property
|
||||
def proxy(self):
|
||||
return wvmHostDetails(
|
||||
self.hostname,
|
||||
self.login,
|
||||
self.password,
|
||||
self.type,
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def cpu_count(self):
|
||||
return self.proxy.get_node_info()[3]
|
||||
|
||||
@cached_property
|
||||
def ram_size(self):
|
||||
return self.proxy.get_node_info()[2]
|
||||
|
||||
@cached_property
|
||||
def ram_usage(self):
|
||||
return self.proxy.get_memory_usage()['percent']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
121
computes/templates/computes/instances.html
Normal file
121
computes/templates/computes/instances.html
Normal file
|
@ -0,0 +1,121 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load icons %}
|
||||
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
|
||||
{% endblock %}
|
||||
{% block page_header %}{{ compute.name }}{% endblock page_header %}
|
||||
|
||||
{% block page_header_extra %}
|
||||
<a href="{% url 'instances:create_instance_select_type' compute.id %}"
|
||||
class="btn btn-success btn-header float-right">
|
||||
{% icon 'plus' %}
|
||||
</a>
|
||||
{% if instances %}
|
||||
<div class="float-right search">
|
||||
<input id="filter" class="form-control" type="text" placeholder="{% trans 'Search' %}">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock page_header_extra %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb bg-light shadow-sm">
|
||||
<li class="breadcrumb-item active">
|
||||
<a href="{% url 'overview' compute.id %}">{% icon 'dashboard' %} {% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<span class="font-weight-bold">{% icon 'server' %} {% trans "Instances" %}</span>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'storages' compute.id %}">{% icon 'hdd-o' %} {% trans "Storages" %}</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'networks' compute.id %}">{% icon 'sitemap' %} {% trans "Networks" %}</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'interfaces' compute.id %}">{% icon 'wifi' %} {% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'nwfilters' compute.id %}">{% icon 'filter' %} {% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'secrets' compute.id %}">{% icon 'key' %} {% trans "Secrets" %}</a>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% if not instances %}
|
||||
<div class="alert alert-warning alert-dismissable fade show">
|
||||
{% icon 'exclamation-triangle' %} <strong>{% trans "Warning" %}:</strong>
|
||||
{% trans "Hypervisor doesn't have any Instances" %}
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<table class="table table-hover sortable-theme-bootstrap" data-sortable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% trans 'Name' %}<br>{% trans 'Description' %}</th>
|
||||
<th scope="col">{% trans 'User' %}</th>
|
||||
<th scope="col">{% trans 'Status' %}</th>
|
||||
<th scope="col">{% trans 'VCPU' %}</th>
|
||||
<th scope="col">{% trans 'Memory' %}</th>
|
||||
<th scope="col" data-sortable="false">{% trans 'Actions' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
{% for instance in instances %}
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-secondary" href="{% url 'instances:instance' instance.id %}">
|
||||
{{ instance.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<em>
|
||||
{% if instance.userinstance_set.all.count > 0 %}
|
||||
{{ instance.userinstance_set.all.0.user }}
|
||||
{% if instance.userinstance_set.all.count > 1 %}
|
||||
(+{{ instance.userinstance_set.all.count|add:"-1" }})
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
{% if instance.proxy.instance.info.0 == 1 %}<span
|
||||
class="text-success">{% trans "Active" %}</span>{% endif %}
|
||||
{% if instance.proxy.instance.info.0 == 5 %}<span
|
||||
class="text-danger">{% trans "Off" %}</span>{% endif %}
|
||||
{% if instance.proxy.instance.info.0 == 3 %}<span
|
||||
class="text-warning">{% trans "Suspended" %}</span>{% endif %}
|
||||
</td>
|
||||
<td>{{ instance.proxy.instance.info.3 }}</td>
|
||||
<td>{% widthratio instance.proxy.instance.info.1 1024 1 %} MiB</td>
|
||||
<td class="text-nowrap">
|
||||
{% include 'instance_actions.html' %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||
<script>
|
||||
function open_console(uuid) {
|
||||
window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=685");
|
||||
}
|
||||
</script>
|
||||
<script src="{% static 'js/filter-table.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -83,6 +83,9 @@ class ComputesTestCase(TestCase):
|
|||
response = self.client.get(reverse('storages', args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_storage(self):
|
||||
pass
|
||||
|
||||
def test_default_storage_volumes(self):
|
||||
response = self.client.get(reverse('volumes', kwargs={'compute_id': 1, 'pool': 'default'}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -115,9 +118,9 @@ class ComputesTestCase(TestCase):
|
|||
response = self.client.get(reverse('secrets', args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_instance_select_type(self):
|
||||
response = self.client.get(reverse('create_instance_select_type', args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# def test_create_instance_select_type(self):
|
||||
# response = self.client.get(reverse('create_instance_select_type', args=[1]))
|
||||
# self.assertEqual(response.status_code, 200)
|
||||
|
||||
# TODO: create_instance
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from django.urls import path, include
|
||||
from secrets.views import secrets
|
||||
|
||||
from . import views
|
||||
from . import forms
|
||||
from create.views import create_instance, create_instance_select_type
|
||||
from instances.views import instances
|
||||
from django.urls import include, path
|
||||
|
||||
# from instances.views import create_instance, create_instance_select_type
|
||||
from interfaces.views import interface, interfaces
|
||||
from networks.views import network, networks
|
||||
from nwfilters.views import nwfilter, nwfilters
|
||||
from secrets.views import secrets
|
||||
from storages.views import get_volumes, storage, storages
|
||||
from storages.views import create_volume, get_volumes, storage, storages
|
||||
|
||||
from . import forms, views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.computes, name='computes'),
|
||||
|
@ -23,10 +23,11 @@ urlpatterns = [
|
|||
path('update/', views.compute_update, name='compute_update'),
|
||||
path('delete/', views.compute_delete, name='compute_delete'),
|
||||
path('statistics', views.compute_graph, name='compute_graph'),
|
||||
path('instances/', instances, name='instances'),
|
||||
path('instances/', views.instances, name='instances'),
|
||||
path('storages/', storages, name='storages'),
|
||||
path('storage/<str:pool>/volumes', get_volumes, name='volumes'),
|
||||
path('storage/<str:pool>/volumes/', get_volumes, name='volumes'),
|
||||
path('storage/<str:pool>/', storage, name='storage'),
|
||||
path('storage/<str:pool>/create_volume/', create_volume, name='create_volume'),
|
||||
path('networks/', networks, name='networks'),
|
||||
path('network/<str:pool>/', network, name='network'),
|
||||
path('interfaces/', interfaces, name='interfaces'),
|
||||
|
@ -34,8 +35,8 @@ urlpatterns = [
|
|||
path('nwfilters/', nwfilters, name='nwfilters'),
|
||||
path('nwfilter/<str:nwfltr>/', nwfilter, name='nwfilter'),
|
||||
path('secrets/', secrets, name='secrets'),
|
||||
path('create/', create_instance_select_type, name='create_instance_select_type'),
|
||||
path('create/archs/<str:arch>/machines/<str:machine>/', create_instance, name='create_instance'),
|
||||
# path('create/', create_instance_select_type, name='create_instance_select_type'),
|
||||
# path('create/archs/<str:arch>/machines/<str:machine>/', create_instance, name='create_instance'),
|
||||
path('archs/<str:arch>/machines/', views.get_compute_machine_types, name='machines'),
|
||||
path(
|
||||
'archs/<str:arch>/machines/<str:machine>/disks/<str:disk>/buses/',
|
||||
|
|
13
computes/utils.py
Normal file
13
computes/utils.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from instances.models import Instance
|
||||
|
||||
|
||||
def refresh_instance_database(compute):
|
||||
domains = compute.proxy.wvm.listAllDomains()
|
||||
domain_names = [d.name() for d in domains]
|
||||
# Delete instances that're not on host from DB
|
||||
Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete()
|
||||
# Create instances that're on host but not in DB
|
||||
names = Instance.objects.filter(compute=compute).values_list('name', flat=True)
|
||||
for domain in domains:
|
||||
if domain.name() not in names:
|
||||
Instance(compute=compute, name=domain.name(), uuid=domain.UUIDString()).save()
|
|
@ -15,6 +15,8 @@ from instances.models import Instance
|
|||
from vrtManager.connection import (CONN_SOCKET, CONN_SSH, CONN_TCP, CONN_TLS, connection_manager, wvmConnect)
|
||||
from vrtManager.hostdetails import wvmHostDetails
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
@superuser_only
|
||||
def computes(request):
|
||||
|
@ -30,36 +32,36 @@ def computes(request):
|
|||
|
||||
@superuser_only
|
||||
def overview(request, compute_id):
|
||||
"""
|
||||
:param request:
|
||||
:param compute_id:
|
||||
:return:
|
||||
"""
|
||||
|
||||
error_messages = []
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
status = 'true' if connection_manager.host_is_up(compute.type, compute.hostname) is True else 'false'
|
||||
|
||||
try:
|
||||
conn = wvmHostDetails(
|
||||
compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type,
|
||||
)
|
||||
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
|
||||
hypervisor = conn.get_hypervisors_domain_types()
|
||||
mem_usage = conn.get_memory_usage()
|
||||
emulator = conn.get_emulator(host_arch)
|
||||
version = conn.get_version()
|
||||
lib_version = conn.get_lib_version()
|
||||
conn.close()
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
conn = wvmHostDetails(
|
||||
compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type,
|
||||
)
|
||||
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
|
||||
hypervisor = conn.get_hypervisors_domain_types()
|
||||
mem_usage = conn.get_memory_usage()
|
||||
emulator = conn.get_emulator(host_arch)
|
||||
version = conn.get_version()
|
||||
lib_version = conn.get_lib_version()
|
||||
conn.close()
|
||||
|
||||
return render(request, 'overview.html', locals())
|
||||
|
||||
|
||||
@superuser_only
|
||||
def instances(request, compute_id):
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
|
||||
utils.refresh_instance_database(compute)
|
||||
instances = Instance.objects.filter(compute=compute).prefetch_related('userinstance_set')
|
||||
|
||||
return render(request, 'computes/instances.html', {'compute': compute, 'instances': instances})
|
||||
|
||||
|
||||
@superuser_only
|
||||
def compute_create(request, FormClass):
|
||||
form = FormClass(request.POST or None)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue