mirror of
https://github.com/retspen/webvirtcloud
synced 2025-07-31 12:41:08 +00:00
Compare commits
No commits in common. "6e76eb2cd9437b81b003b65edc8479751981e8de" and "edd265d5a570b350185805c0dcf1f26fd4df38f8" have entirely different histories.
6e76eb2cd9
...
edd265d5a5
25 changed files with 11493 additions and 8604 deletions
|
|
@ -1,37 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load django_bootstrap5 %}
|
||||
|
||||
{% block title %}{% trans "WebVirtCloud" %} - {% trans "Sign In with OTP" %}{% endblock title %}
|
||||
{% block title %}WebVirtCloud{% endblock title %}
|
||||
|
||||
{% block style %}
|
||||
<link href="{% static "css/signin.css" %}" rel="stylesheet">
|
||||
{% endblock style %}
|
||||
{% block page_heading %}WebVirtCloud{% endblock page_heading %}
|
||||
|
||||
{% block content %}
|
||||
<div class="login-box">
|
||||
<div class="page-header">
|
||||
<a href="/"><h1>WebVirtCloud</h1></a>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="col-12" role="main">
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{% bootstrap_form_errors form %}
|
||||
<div class="row">
|
||||
<div class="col-6 offset-3" role="main">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{% if form.errors %}
|
||||
{% bootstrap_form_errors form %}
|
||||
{% endif %}
|
||||
<form class="form-signin" method="post" role="form" aria-label="Login form">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_field form.username layout='horizontal' %}
|
||||
{% bootstrap_field form.password layout='horizontal' %}
|
||||
{% bootstrap_field form.otp_token layout='horizontal' %}
|
||||
<a href="{% url 'accounts:email_otp' %}" class="float-end mb-2">{% trans "I do not have/lost my OTP!" %}</a>
|
||||
<button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
<form class="form-signin" method="post" role="form" aria-label="Login form">{% csrf_token %}
|
||||
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
|
||||
{% bootstrap_field form.username layout='inline' %}
|
||||
{% bootstrap_field form.password layout='inline' %}
|
||||
{% bootstrap_field form.otp_token layout='inline'%}
|
||||
<a href="{% url 'accounts:email_otp' %}" class="float-end">{% trans "I do not have/lost my OTP!" %}</a>
|
||||
<br>
|
||||
<div class="d-grid">
|
||||
<button class="btn btn-lg btn-success" type="submit">{% trans "Sign In" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -2,35 +2,31 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% block title %}{% trans "WebVirtCloud" %} - {% trans "Sign In" %}{% endblock title %}
|
||||
{% block title %}{% trans "WebVirtCloud" %} - {% trans "Sign In" %}{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<link href="{% static "css/signin.css" %}" rel="stylesheet">
|
||||
{% endblock style %}
|
||||
|
||||
{% block content %}
|
||||
<div class="login-box">
|
||||
<div class="page-header">
|
||||
<a href="/"><h1>WebVirtCloud</h1></a>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="col-12" role="main">
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
{% trans "Incorrect username or password." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form class="form-signin" method="post" role="form" aria-label="Login form">{% csrf_token %}
|
||||
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
|
||||
<input type="text" class="form-control" name="username" placeholder="{% trans 'User' %}" autocapitalize="none" autocorrect="off" autofocus required>
|
||||
<input type="password" class="form-control" name="password" placeholder="{% trans 'Password' %}" required>
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<div class="d-grid">
|
||||
<button class="btn btn-lg btn-success" type="submit">{% trans "Sign In" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="page-header">
|
||||
<a class="" href="/"><h1>WebVirtCloud</h1></a>
|
||||
</div>
|
||||
<div class="col-12" role="main">
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger">
|
||||
<button type="button" class="btn-close" data-dismiss="alert" aria-label="Close"></button>
|
||||
{% trans "Incorrect username or password." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<form class="form-signin" method="post" role="form" aria-label="Login form">{% csrf_token %}
|
||||
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
|
||||
<input type="text" class="form-control" name="username" placeholder="{% trans "User" %}" autocapitalize="none" autocorrect="off" autofocus>
|
||||
<input type="password" class="form-control" name="password" placeholder="{% trans "Password" %}">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<div class="d-grid">
|
||||
<button class="btn btn-lg btn-success" type="submit">{% trans "Sign In" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -5,7 +5,7 @@ from django.contrib import messages
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.utils.translation import gettext_noop as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from logs.views import addlogmsg
|
||||
|
||||
from appsettings.models import AppSettings
|
||||
|
|
|
|||
|
|
@ -48,10 +48,6 @@ class Compute(Model):
|
|||
@cached_property
|
||||
def cpu_count(self):
|
||||
return self.proxy.get_node_info()[3]
|
||||
|
||||
@cached_property
|
||||
def cpu_usage(self):
|
||||
return round(self.proxy.get_cpu_usage().get('usage'))
|
||||
|
||||
@cached_property
|
||||
def ram_size(self):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
Django==3.2.19
|
||||
Django==3.2.18
|
||||
django_bootstrap5==23.1
|
||||
django-login-required-middleware==0.9.0
|
||||
django-otp==1.1.6
|
||||
django-icons==23.2
|
||||
django-icons==23.1
|
||||
django-qr-code==3.1.1
|
||||
gunicorn==20.1.0
|
||||
libsass==0.22.0
|
||||
libvirt-python==9.3.0
|
||||
libvirt-python==9.2.0
|
||||
lxml==4.9.2
|
||||
qrcode==7.4.2
|
||||
rwlock==0.0.7
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
-r ../conf/requirements.txt
|
||||
coverage==7.2.5
|
||||
coverage==7.2.3
|
||||
django-debug-toolbar==4.0.0
|
||||
pycodestyle==2.10.0
|
||||
pyflakes==3.0.1
|
||||
pylint==2.17.4
|
||||
pylint==2.17.3
|
||||
yapf==0.33.0
|
||||
black==23.3.0
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@
|
|||
<script src="{% static 'js/filter-table.js' %}"></script>
|
||||
{% if request.user.is_superuser %}
|
||||
<script>
|
||||
function goto_compute(compute) {
|
||||
//let compute = $("#compute_select").val();
|
||||
function goto_compute() {
|
||||
let compute = $("#compute_select").val();
|
||||
window.location.href = "{% url 'instances:create_instance_select_type' 1 %}".replace(1, compute);
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<!-- Modal pool -->
|
||||
<div class="modal fade" id="AddInstance" tabindex="-1" role="dialog" aria-labelledby="AddInstanceModal" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-lg">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{% trans "Choose a compute for new instance" %}</h5>
|
||||
|
|
@ -15,50 +15,33 @@
|
|||
<form method="post" aria-label="Select compute for instance create form">
|
||||
{% csrf_token %}
|
||||
<div class="row">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr style="cursor:default;pointer-events:none">
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "VCPU" %}
|
||||
<th>{% trans "Cpu Usage" %}</th>
|
||||
<th>{% trans "Memory" %}</th>
|
||||
<th>{% trans "Mem Usage" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<label class="col-sm-4 col-form-label">{% trans "Compute" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" id="compute_select">
|
||||
<option>{% trans "Please select" %}</option>
|
||||
{% for compute in computes %}
|
||||
{% if compute.status is True %}
|
||||
<tr style="text-decoration: none">
|
||||
<td>{{ compute.name }}</td>
|
||||
<td>{{ compute.cpu_count }}</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ compute.cpu_usage }}%"
|
||||
aria-valuenow="{{ compute.cpu_usage }}" aria-valuemin="0" aria-valuemax="100">{{ compute.cpu_usage }}%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ compute.ram_size|filesizeformat }}</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ compute.ram_usage }}%"
|
||||
aria-valuenow="{{ compute.ram_usage }}" aria-valuemin="0" aria-valuemax="100">{{ compute.ram_usage }}%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-1">
|
||||
<button class="btn btn-success btn-sm" type="button" onclick="goto_compute('{{ compute.id }}');">
|
||||
{% trans "Choose" %}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<option {% if compute.status is not True %} class="font-italic text-muted" {% else %} value="{{ compute.id }}" {% endif %}>{{ compute.name }}</option>
|
||||
{% empty %}
|
||||
<option value="None">{% trans "None" %}</option>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
{% trans "Close" %}
|
||||
</button>
|
||||
{% if computes %}
|
||||
<button type="submit" class="btn btn-primary" name="choose" onclick='goto_compute()'>
|
||||
{% trans "Choose" %}
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="btn btn-primary disabled">
|
||||
{% trans "Choose" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
|
|
|
|||
|
|
@ -46,10 +46,11 @@
|
|||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="takeextsnapshot">
|
||||
{% if instance.status != 5 %}
|
||||
<p>{% trans "You can get external snapshots within this tab." %}</p>
|
||||
<p class="text-primary">{% trans "External snapshots are experimental in this stage, use it if you know what you are doing. It may require manual intervention." %}</p>
|
||||
{% else %}
|
||||
<p>{% trans "Create an external snapshot" %}</p>
|
||||
{% endif %}
|
||||
<p class="fst-italic">{% trans "External snapshots are experimental in this stage, use it if you know what you are doing. 'Revert Snapshot' may require manual operation with CLI." %}</p>
|
||||
<p class="text-danger">{% trans "Give your External Snapshot a <b>distinctive description</b> so it wouldn't get mixed with other snapshots." %}</p>
|
||||
<form action="{% url 'instances:create_external_snapshot' instance.id %}" method="post" role="form" aria-label="Create snapshot form">
|
||||
{% csrf_token %}
|
||||
<div class="input-group mb-3">
|
||||
|
|
@ -62,7 +63,7 @@
|
|||
<input type="submit" class="btn btn-lg btn-success float-end" name="snapshot" value="{% trans "Take Snapshot" %}" onclick="showPleaseWaitDialog();">
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="text-danger font-monospace small">{% trans "WebVirtCloud supports only one external snapshot at the moment." %}</p>
|
||||
<p class="text-danger">{% trans "WebVirtCloud supports only one external snapshot at the moment." %}</p>
|
||||
</form>
|
||||
<div class="clearfix"></div>
|
||||
</div> <!--tab pane takeextsnapshot-->
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#logs" type="button" role="tab" aria-controls="logs" aria-selected="false" onclick="update_logs_table(vname);">
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#logs" type="button" role="tab" aria-controls="logs" aria-selected="false">
|
||||
{% trans "Logs" %}
|
||||
</button>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ from django.contrib.auth.models import User
|
|||
from django.http import Http404, HttpResponse, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_noop as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from libvirt import (VIR_DOMAIN_UNDEFINE_KEEP_NVRAM,
|
||||
VIR_DOMAIN_UNDEFINE_NVRAM,
|
||||
VIR_DOMAIN_START_PAUSED,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,7 @@ from computes.models import Compute
|
|||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.utils.translation import gettext_noop as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from libvirt import libvirtError
|
||||
from logs.views import addlogmsg
|
||||
from vrtManager import util
|
||||
|
|
|
|||
|
|
@ -1,23 +1,3 @@
|
|||
/* body {
|
||||
background: rgba(203,203,203,1)
|
||||
} */
|
||||
.login-box {
|
||||
margin-top: 25%;
|
||||
display: block;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0px 0px 20px black;
|
||||
border-radius: 5px;
|
||||
padding-top: 5%;
|
||||
padding-bottom: 1%;
|
||||
}
|
||||
h1 {
|
||||
color:#212529;
|
||||
}
|
||||
h2 {
|
||||
color:#212529;
|
||||
}
|
||||
|
||||
.form-signin {
|
||||
max-width: 330px;
|
||||
padding: 15px;
|
||||
|
|
@ -49,22 +29,15 @@ h2 {
|
|||
}
|
||||
|
||||
.form-signin input[type="text"] {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: -1px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.form-signin input[type="password"] {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
}
|
||||
.btn-success:hover {
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.logout {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import contextlib
|
|||
import json
|
||||
import os.path
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
from libvirt import (
|
||||
|
|
@ -1254,7 +1255,6 @@ class wvmInstance(wvmConnect):
|
|||
return iso
|
||||
|
||||
def delete_all_disks(self):
|
||||
self.refresh_instance_pools()
|
||||
disks = self.get_disk_devices()
|
||||
for disk in disks:
|
||||
vol = self.get_volume_by_path(disk.get("path"))
|
||||
|
|
@ -1318,8 +1318,8 @@ class wvmInstance(wvmConnect):
|
|||
</domainsnapshot>"""
|
||||
|
||||
self._snapshotCreateXML(xml, VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY)
|
||||
self.refresh_instance_pools()
|
||||
|
||||
|
||||
def get_external_snapshots(self):
|
||||
return self.get_snapshot(VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL)
|
||||
|
||||
|
|
@ -1350,23 +1350,29 @@ class wvmInstance(wvmConnect):
|
|||
|
||||
|
||||
def revert_external_snapshot(self, name, date, desc):
|
||||
pool = None
|
||||
snap = self.instance.snapshotLookupByName(name, 0)
|
||||
snap_xml = snap.getXMLDesc(0)
|
||||
snapXML = ElementTree.fromstring(snap_xml)
|
||||
disks = snapXML.findall('inactiveDomain/devices/disk')
|
||||
if not disks: disks = snapXML.findall('domain/devices/disk')
|
||||
|
||||
self.start(flags=VIR_DOMAIN_START_PAUSED) if self.get_status() == 5 else None
|
||||
self.delete_all_disks()
|
||||
|
||||
|
||||
disk_info = self.get_disk_devices()
|
||||
for disk in disk_info:
|
||||
vol_snap = self.get_volume_by_path(disk["path"])
|
||||
pool = vol_snap.storagePoolLookupByVolume()
|
||||
pool.refresh(0)
|
||||
vol_snap.delete(0)
|
||||
self.force_shutdown()
|
||||
|
||||
snap.delete(VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY)
|
||||
|
||||
disks = snapXML.findall('inactiveDomain/devices/disk')
|
||||
if not disks: disks = snapXML.findall('domain/devices/disk')
|
||||
for disk in disks:
|
||||
self.instance.updateDeviceFlags(ElementTree.tostring(disk).decode("UTF-8"))
|
||||
name = name.replace("s1", "s2")
|
||||
self.create_external_snapshot(name, date, desc)
|
||||
pool.refresh() if pool else None
|
||||
|
||||
def get_snapshot(self, flag=VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL):
|
||||
snapshots = []
|
||||
|
|
@ -1407,15 +1413,6 @@ class wvmInstance(wvmConnect):
|
|||
def get_wvmStorages(self):
|
||||
return wvmStorages(self.host, self.login, self.passwd, self.conn)
|
||||
|
||||
def refresh_instance_pools(self):
|
||||
disks = self.get_disk_devices()
|
||||
target_paths = set()
|
||||
for disk in disks:
|
||||
disk_path = disk.get("path")
|
||||
target_paths.add(os.path.dirname(disk_path))
|
||||
for target_path in target_paths:
|
||||
self.get_wvmStorages().get_pool_by_target(target_path).refresh(0)
|
||||
|
||||
def fix_mac(self, mac):
|
||||
if ":" in mac:
|
||||
return mac
|
||||
|
|
|
|||
|
|
@ -28,14 +28,6 @@ class wvmStorages(wvmConnect):
|
|||
def define_storage(self, xml, flag):
|
||||
self.wvm.storagePoolDefineXML(xml, flag)
|
||||
|
||||
def get_pool_by_target(self, target):
|
||||
pool_names = self.get_storages()
|
||||
for pool_name in pool_names:
|
||||
stg = wvmStorage(self.host, self.login, self.passwd, self.conn, pool_name)
|
||||
if stg.get_target_path() == target:
|
||||
return self.get_storage(pool_name)
|
||||
return None
|
||||
|
||||
def create_storage(self, stg_type, name, source, target):
|
||||
xml = f"""<pool type='{stg_type}'>
|
||||
<name>{name}</name>"""
|
||||
|
|
|
|||
|
|
@ -120,8 +120,6 @@ LOGIN_URL = "/accounts/login/"
|
|||
|
||||
LOGOUT_REDIRECT_URL = "/accounts/login/"
|
||||
|
||||
LOGIN_REDIRECT_URL="/instances/"
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue