1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 23:25:24 +00:00

fix some linter warnings

This commit is contained in:
catborise 2020-09-25 15:57:17 +03:00
parent 119d874e85
commit 922a8e62d1
20 changed files with 266 additions and 189 deletions

View file

@ -10,11 +10,10 @@
* Manage Hypervisor Networks
* Instance Console Access with Browsers
* Libvirt API based web management UI
* User Based Authorization and Authentication
* User Based Authorization and Authentication
* User can add SSH public key to root in Instance (Tested only Ubuntu)
* User can change root password in Instance (Tested only Ubuntu)
* Supports cloud-init datasource interface
### Warning!!!
@ -30,8 +29,10 @@ sudo service supervisor restart
WebVirtCloud is a virtualization web interface for admins and users. It can delegate Virtual Machine's to users. A noVNC viewer presents a full graphical console to the guest domain. KVM is currently the only hypervisor supported.
## Quick Install with Installer (Beta)
Install an OS and run specified commands. Installer supported OSes: Ubuntu 18.04, Debian 10, Centos/OEL/RHEL 8.
It can be installed on a virtual machine, physical host or on a KVM host.
```bash
wget https://raw.githubusercontent.com/retspen/webvirtcloud/master/install.sh
chmod 744 install.sh
@ -40,7 +41,9 @@ chmod 744 install.sh
```
## Manual Installation
### Generate secret key
You should generate SECRET_KEY after cloning repo. Then put it into webvirtcloud/settings.py.
```python3
@ -83,6 +86,7 @@ Setup libvirt and KVM on server
```bash
wget -O - https://clck.ru/9V9fH | sudo sh
```
Done!!
Go to http://serverip and you should see the login screen.
@ -106,6 +110,7 @@ sudo sed -r "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/ru
```
#### Start installation webvirtcloud
```bash
virtualenv-3 venv
source venv/bin/activate
@ -115,7 +120,8 @@ python3 manage.py migrate
```
#### Configure the supervisor for CentOS
Add the following after the [include] line (after **files = ... ** actually):
Add the following after the [include] line (after **files = ...** actually):
```bash
sudo vim /etc/supervisord.conf
@ -137,9 +143,10 @@ redirect_stderr=true
```
#### Edit the nginx.conf file
You will need to edit the main nginx.conf file as the one that comes from the rpm's will not work. Comment the following lines:
```
```bash
# server {
# listen 80 default_server;
# listen [::]:80 default_server;
@ -164,7 +171,8 @@ You will need to edit the main nginx.conf file as the one that comes from the rp
```
Also make sure file in **/etc/nginx/conf.d/webvirtcloud.conf** has the proper paths:
```
```bash
upstream gunicorn_server {
#server unix:/srv/webvirtcloud/venv/wvcloud.socket fail_timeout=0;
server 127.0.0.1:8000 fail_timeout=0;
@ -208,11 +216,13 @@ sudo setsebool -P httpd_can_network_connect on -P
```
Add required user to the kvm group(if you not install with root):
```bash
sudo usermod -G kvm -a <username>
```
Allow http ports on firewall:
```bash
sudo firewall-cmd --add-service=http
sudo firewall-cmd --add-service=http --permanent
@ -221,11 +231,13 @@ sudo firewall-cmd --add-port=6080/tcp --permanent
```
Let's restart nginx and the supervisord services:
```bash
sudo systemctl restart nginx && systemctl restart supervisord
```
And finally, check everything is running:
```bash
sudo supervisorctl status
gstfsd RUNNING pid 24662, uptime 6:01:40
@ -234,12 +246,14 @@ webvirtcloud RUNNING pid 24660, uptime 6:01:40
```
#### Apache mod_wsgi configuration
```
```bash
WSGIDaemonProcess webvirtcloud threads=2 maximum-requests=1000 display-name=webvirtcloud
WSGIScriptAlias / /srv/webvirtcloud/webvirtcloud/wsgi_custom.py
```
#### Install final required packages for libvirtd and others on Host Server
```bash
wget -O - https://clck.ru/9V9fH | sudo sh
```
@ -249,10 +263,12 @@ Done!!
Go to http://serverip and you should see the login screen.
### Alternative running novncd via runit(Debian)
Alternative to running nonvcd via supervisor is runit.
On Debian systems install runit and configure novncd service
```
On Debian systems install runit and configure novncd service:
```bash
apt install runit runit-systemd
mkdir /etc/service/novncd/
ln -s /srv/webvirtcloud/conf/runit/novncd.sh /etc/service/novncd/run
@ -260,16 +276,19 @@ systemctl start runit.service
```
### Default credentials
<pre>
```html
login: admin
password: admin
</pre>
```
### Configuring Compute SSH connection
This is a short example of configuring cloud and compute side of the ssh connection.
On the webvirtcloud machine you need to generate ssh keys and optionally disable StrictHostKeyChecking.
```
```bash
chown www-data -R ~www-data
sudo -u www-data ssh-keygen
cat > ~www-data/.ssh/config << EOF
@ -280,44 +299,55 @@ chown www-data -R ~www-data/.ssh/config
```
You need to put cloud public key into authorized keys on the compute node. Simpliest way of doing this is to use ssh tool from the webvirtcloud server.
```
```bash
sudo -u www-data ssh-copy-id root@compute1
```
### Host SMBIOS information is not available
If you see warning
```
```bash
Unsupported configuration: Host SMBIOS information is not available
```
Then you need to install `dmidecode` package on your host using your package manager and restart libvirt daemon.
Debian/Ubuntu like:
```bash
sudo apt-get install dmidecode
sudo service libvirt-bin restart
```
$ sudo apt-get install dmidecode
$ sudo service libvirt-bin restart
```
Arch Linux
```
$ sudo pacman -S dmidecode
$ systemctl restart libvirtd
```bash
sudo pacman -S dmidecode
systemctl restart libvirtd
```
### Cloud-init
Currently supports only root ssh authorized keys and hostname. Example configuration of the cloud-init client follows.
```
```bash
datasource:
OpenStack:
metadata_urls: [ "http://webvirtcloud.domain.com/datasource" ]
```
### Reverse-Proxy
Edit WS_PUBLIC_PORT at settings.py file to expose redirect to 80 or 443. Default: 6080
```
```bash
WS_PUBLIC_PORT = 80
```
## How To Update
```bash
# Go to Installation Directory
cd /srv/webvirtcloud
@ -329,22 +359,27 @@ sudo service supervisor restart
```
### Running tests
Server on which tests will be performed must have libvirt up and running.
It must not contain vms.
It must have `default` storage which not contain any disk images.
It must have `default` network which must be on.
Setup venv
```bash
python -m venv venv
source venv/bin/activate
pip install -r conf/requirements.txt
```
Run tests
```bash
python manage.py test
```
## Screenshots
Instance Detail:
<img src="doc/images/instance.PNG" width="96%" align="center"/>
Instance List:</br>

View file

@ -21,7 +21,7 @@ class UserInstance(models.Model):
objects = UserInstanceManager()
def __str__(self):
return _('Instance "%(inst)s" of user %(user)s') % {'inst': self.instance, 'user': self.user}
return _('Instance "%(inst)s" of user %(user)s') % {"inst": self.instance, "user": self.user}
class Meta:
unique_together = ['user', 'instance']

View file

@ -23,7 +23,7 @@ class AccountsTestCase(TestCase):
client = Client()
response = client.post(reverse('login'), {'username': 'test', 'password': 'test'})
response = client.post(reverse("login"), {"username": "test", "password": "test"})
self.assertRedirects(response, reverse('profile'))
response = client.get(reverse('logout'))

View file

@ -21,17 +21,17 @@ def profile(request):
error_messages = []
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
if request.method == 'POST':
if 'username' in request.POST:
username = request.POST.get('username', '')
email = request.POST.get('email', '')
if request.method == "POST":
if "username" in request.POST:
username = request.POST.get("username", "")
email = request.POST.get("email", "")
request.user.first_name = username
request.user.email = email
request.user.save()
return HttpResponseRedirect(request.get_full_path())
if 'keyname' in request.POST:
keyname = request.POST.get('keyname', '')
keypublic = request.POST.get('keypublic', '')
if "keyname" in request.POST:
keyname = request.POST.get("keyname", "")
keypublic = request.POST.get("keypublic", "")
for key in publickeys:
if keyname == key.keyname:
msg = _("Key name already exist")
@ -39,19 +39,20 @@ def profile(request):
if keypublic == key.keypublic:
msg = _("Public key already exist")
error_messages.append(msg)
if '\n' in keypublic or '\r' in keypublic:
if "\n" in keypublic or "\r" in keypublic:
msg = _("Invalid characters in public key")
error_messages.append(msg)
if not error_messages:
addkeypublic = UserSSHKey(user_id=request.user.id, keyname=keyname, keypublic=keypublic)
addkeypublic = UserSSHKey(
user_id=request.user.id, keyname=keyname, keypublic=keypublic)
addkeypublic.save()
return HttpResponseRedirect(request.get_full_path())
if 'keydelete' in request.POST:
keyid = request.POST.get('keyid', '')
if "keydelete" in request.POST:
keyid = request.POST.get("keyid", "")
delkeypublic = UserSSHKey.objects.get(id=keyid)
delkeypublic.delete()
return HttpResponseRedirect(request.get_full_path())
return render(request, 'profile.html', locals())
return render(request, "profile.html", locals())
@superuser_only
@ -59,43 +60,47 @@ def account(request, user_id):
error_messages = []
user = User.objects.get(id=user_id)
user_insts = UserInstance.objects.filter(user_id=user_id)
instances = Instance.objects.all().order_by('name')
instances = Instance.objects.all().order_by("name")
publickeys = UserSSHKey.objects.filter(user_id=user_id)
return render(request, 'account.html', locals())
return render(request, "account.html", locals())
@permission_required('accounts.change_password', raise_exception=True)
@permission_required("accounts.change_password", raise_exception=True)
def change_password(request):
if request.method == 'POST':
if request.method == "POST":
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Important!
messages.success(request, _('Password Changed'))
return redirect('profile')
messages.success(request, _("Password Changed"))
return redirect("profile")
else:
messages.error(request, _('Wrong Data Provided'))
messages.error(request, _("Wrong Data Provided"))
else:
form = PasswordChangeForm(request.user)
return render(request, 'accounts/change_password_form.html', {'form': form})
return render(
request,
"accounts/change_password_form.html",
{"form": form}
)
@superuser_only
def user_instance_create(request, user_id):
user = get_object_or_404(User, pk=user_id)
form = forms.UserInstanceForm(request.POST or None, initial={'user': user})
form = forms.UserInstanceForm(request.POST or None, initial={"user": user})
if form.is_valid():
form.save()
return redirect(reverse('account', args=[user.id]))
return redirect(reverse("account", args=[user.id]))
return render(
request,
'common/form.html',
"common/form.html",
{
'form': form,
'title': _('Create User Instance'),
"form": form,
"title": _("Create User Instance"),
},
)
@ -106,14 +111,14 @@ def user_instance_update(request, pk):
form = forms.UserInstanceForm(request.POST or None, instance=user_instance)
if form.is_valid():
form.save()
return redirect(reverse('account', args=[user_instance.user.id]))
return redirect(reverse("account", args=[user_instance.user.id]))
return render(
request,
'common/form.html',
{
'form': form,
'title': _('Update User Instance'),
"form": form,
"title": _("Update User Instance"),
},
)
@ -121,17 +126,17 @@ def user_instance_update(request, pk):
@superuser_only
def user_instance_delete(request, pk):
user_instance = get_object_or_404(UserInstance, pk=pk)
if request.method == 'POST':
if request.method == "POST":
user = user_instance.user
user_instance.delete()
next = request.GET.get('next', None)
next = request.GET.get("next", None)
if next:
return redirect(next)
else:
return redirect(reverse('account', args=[user.id]))
return redirect(reverse("account", args=[user.id]))
return render(
request,
'common/confirm_delete.html',
{'object': user_instance},
"common/confirm_delete.html",
{"object": user_instance},
)

View file

@ -74,11 +74,12 @@ class UserForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
if self.instance.id:
password = ReadOnlyPasswordHashField(label=_("Password"),
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=format_lazy(_("""Raw passwords are not stored, so there is no way to see
this user's password, but you can change the password
using <a href='{}'>this form</a>."""),
reverse_lazy('admin:user_update_password', args=[self.instance.id,]))
this user's password, but you can change the password using <a href='{}'>this form</a>."""),
reverse_lazy('admin:user_update_password',
args=[self.instance.id,]))
)
self.fields['Password'] = password

View file

@ -1,9 +1,11 @@
from django.contrib.auth.models import Permission as P
class Permission(P):
"""
Proxy model to Django Permissions model allows us to override __str__
"""
def __str__(self):
return f'{self.content_type.app_label}: {self.name}'

View file

@ -8,7 +8,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import ugettext_lazy as _
from accounts.models import UserAttributes, UserInstance, Instance
from appsettings.models import AppSettings
from appsettings.settings import app_settings
from logs.models import Logs
from . import forms
@ -20,9 +20,9 @@ def group_list(request):
groups = Group.objects.all()
return render(
request,
'admin/group_list.html',
"admin/group_list.html",
{
'groups': groups,
"groups": groups,
},
)
@ -32,14 +32,14 @@ def group_create(request):
form = forms.GroupForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('admin:group_list')
return redirect("admin:group_list")
return render(
request,
'common/form.html',
"common/form.html",
{
'form': form,
'title': _('Create Group'),
"form": form,
"title": _("Create Group"),
},
)
@ -54,10 +54,10 @@ def group_update(request, pk):
return render(
request,
'common/form.html',
"common/form.html",
{
'form': form,
'title': _('Update Group'),
"form": form,
"title": _("Update Group"),
},
)
@ -65,14 +65,14 @@ def group_update(request, pk):
@superuser_only
def group_delete(request, pk):
group = get_object_or_404(Group, pk=pk)
if request.method == 'POST':
if request.method == "POST":
group.delete()
return redirect('admin:group_list')
return redirect("admin:group_list")
return render(
request,
'common/confirm_delete.html',
{'object': group},
"common/confirm_delete.html",
{"object": group},
)
@ -81,10 +81,10 @@ def user_list(request):
users = User.objects.all()
return render(
request,
'admin/user_list.html',
"admin/user_list.html",
{
'users': users,
'title': _('Users'),
"users": users,
"title": _("Users"),
},
)
@ -95,22 +95,22 @@ def user_create(request):
attributes_form = forms.UserAttributesForm(request.POST or None)
if user_form.is_valid() and attributes_form.is_valid():
user = user_form.save()
password = user_form.cleaned_data['password']
password = user_form.cleaned_data["password"]
user.set_password(password)
user.save()
attributes = attributes_form.save(commit=False)
attributes.user = user
attributes.save()
add_default_instances(user)
return redirect('admin:user_list')
return redirect("admin:user_list")
return render(
request,
'admin/user_form.html',
"admin/user_form.html",
{
'user_form': user_form,
'attributes_form': attributes_form,
'title': _('Create User')
"user_form": user_form,
"attributes_form": attributes_form,
"title": _("Create User")
},
)
@ -120,22 +120,24 @@ def user_update(request, pk):
user = get_object_or_404(User, pk=pk)
attributes = UserAttributes.objects.get(user=user)
user_form = forms.UserForm(request.POST or None, instance=user)
attributes_form = forms.UserAttributesForm(request.POST or None, instance=attributes)
attributes_form = forms.UserAttributesForm(
request.POST or None, instance=attributes)
if user_form.is_valid() and attributes_form.is_valid():
user_form.save()
attributes_form.save()
return redirect('admin:user_list')
return redirect("admin:user_list")
return render(
request,
'admin/user_form.html',
"admin/user_form.html",
{
'user_form': user_form,
'attributes_form': attributes_form,
'title': _('Update User')
"user_form": user_form,
"attributes_form": attributes_form,
"title": _("Update User")
},
)
@superuser_only
def user_update_password(request, pk):
user = get_object_or_404(User, pk=pk)
@ -144,33 +146,35 @@ def user_update_password(request, pk):
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Important!
messages.success(request, _('User password changed: {}'.format(user.username)))
return redirect('admin:user_list')
messages.success(request, _(
"User password changed: {}".format(user.username)))
return redirect("admin:user_list")
else:
messages.error(request, _('Wrong Data Provided'))
messages.error(request, _("Wrong Data Provided"))
else:
form = AdminPasswordChangeForm(user)
return render(
request,
'accounts/change_password_form.html',
"accounts/change_password_form.html",
{
'form': form,
'user': user.username
"form": form,
"user": user.username
}
)
@superuser_only
def user_delete(request, pk):
user = get_object_or_404(User, pk=pk)
if request.method == 'POST':
if request.method == "POST":
user.delete()
return redirect('admin:user_list')
return redirect("admin:user_list")
return render(
request,
'common/confirm_delete.html',
{'object': user},
"common/confirm_delete.html",
{"object": user},
)
@ -179,7 +183,7 @@ def user_block(request, pk):
user: User = get_object_or_404(User, pk=pk)
user.is_active = False
user.save()
return redirect('admin:user_list')
return redirect("admin:user_list")
@superuser_only
@ -187,16 +191,16 @@ def user_unblock(request, pk):
user: User = get_object_or_404(User, pk=pk)
user.is_active = True
user.save()
return redirect('admin:user_list')
return redirect("admin:user_list")
@superuser_only
def logs(request):
l = Logs.objects.order_by('-date')
paginator = Paginator(l, int(AppSettings.objects.get(key="LOGS_PER_PAGE").value))
page = request.GET.get('page', 1)
l = Logs.objects.order_by("-date")
paginator = Paginator(l, int(app_settings.LOGS_PER_PAGE))
page = request.GET.get("page", 1)
logs = paginator.page(page)
return render(request, 'admin/logs.html', {'logs': logs})
return render(request, "admin/logs.html", {"logs": logs})
def add_default_instances(user):

View file

@ -21,6 +21,7 @@ class _TunnelScheduler(object):
It's only instantiated once for the whole app, because we serialize
independent of connection, vm, etc.
"""
def __init__(self):
self._thread = None
self._queue = queue.Queue()
@ -44,6 +45,7 @@ class _TunnelScheduler(object):
def lock(self):
self._lock.acquire()
def unlock(self):
self._lock.release()
@ -63,7 +65,7 @@ class _Tunnel(object):
self._closed = True
log.debug("Close tunnel PID=%s ERRFD=%s",
self._pid, self._errfd and self._errfd.fileno() or None)
self._pid, self._errfd and self._errfd.fileno() or None)
# Since this is a socket object, the file descriptor is closed
# when it's garbage collected.
@ -110,13 +112,12 @@ class _Tunnel(object):
self._errfd = errfds[0]
self._errfd.setblocking(0)
log.debug("Opened tunnel PID=%d ERRFD=%d",
pid, self._errfd.fileno())
pid, self._errfd.fileno())
self._pid = pid
def _make_ssh_command(connhost, connuser, connport, gaddr, gport, gsocket):
# Build SSH cmd
argv = ["ssh", "ssh"]
@ -165,7 +166,8 @@ def _make_ssh_command(connhost, connuser, connport, gaddr, gport, gsocket):
class SSHTunnels(object):
def __init__(self, connhost, connuser, connport, gaddr, gport, gsocket):
self._tunnels = []
self._sshcommand = _make_ssh_command(connhost, connuser, connport, gaddr, gport, gsocket)
self._sshcommand = _make_ssh_command(
connhost, connuser, connport, gaddr, gport, gsocket)
self._locked = False
def open_new(self):
@ -206,4 +208,4 @@ class SSHTunnels(object):
def unlock(self, *args, **kwargs):
if self._locked:
_tunnel_scheduler.unlock(*args, **kwargs)
self._locked = False
self._locked = False

View file

@ -6,8 +6,7 @@ from libvirt import libvirtError
from appsettings.settings import app_settings
from instances.models import Instance
from vrtManager.instance import wvmInstance
from webvirtcloud.settings import (WS_PUBLIC_HOST, WS_PUBLIC_PATH,
WS_PUBLIC_PORT)
from webvirtcloud.settings import WS_PUBLIC_HOST, WS_PUBLIC_PATH, WS_PUBLIC_PORT
def console(request):
@ -17,16 +16,19 @@ def console(request):
"""
console_error = None
if request.method == 'GET':
token = request.GET.get('token', '')
view_type = request.GET.get('view', 'lite')
view_only = request.GET.get('view_only', app_settings.CONSOLE_VIEW_ONLY.lower())
scale = request.GET.get('scale', app_settings.CONSOLE_SCALE.lower())
resize_session = request.GET.get('resize_session', app_settings.CONSOLE_RESIZE_SESSION.lower())
clip_viewport = request.GET.get('clip_viewport', app_settings.CONSOLE_CLIP_VIEWPORT.lower())
if request.method == "GET":
token = request.GET.get("token", "")
view_type = request.GET.get("view", "lite")
view_only = request.GET.get(
"view_only", app_settings.CONSOLE_VIEW_ONLY.lower())
scale = request.GET.get("scale", app_settings.CONSOLE_SCALE.lower())
resize_session = request.GET.get(
"resize_session", app_settings.CONSOLE_RESIZE_SESSION.lower())
clip_viewport = request.GET.get(
"clip_viewport", app_settings.CONSOLE_CLIP_VIEWPORT.lower())
try:
temptoken = token.split('-', 1)
temptoken = token.split("-", 1)
host = int(temptoken[0])
uuid = temptoken[1]
instance = Instance.objects.get(compute_id=host, uuid=uuid)
@ -47,20 +49,20 @@ def console(request):
ws_port = console_websocket_port if console_websocket_port else WS_PUBLIC_PORT
ws_host = WS_PUBLIC_HOST if WS_PUBLIC_HOST else request.get_host()
ws_path = WS_PUBLIC_PATH if WS_PUBLIC_PATH else '/'
ws_path = WS_PUBLIC_PATH if WS_PUBLIC_PATH else "/"
if ':' in ws_host:
ws_host = re.sub(':[0-9]+', '', ws_host)
if ":" in ws_host:
ws_host = re.sub(":[0-9]+", "", ws_host)
if console_type == 'vnc' or console_type == 'spice':
if console_type == "vnc" or console_type == "spice":
console_page = "console-" + console_type + "-" + view_type + ".html"
response = render(request, console_page, locals())
else:
if console_type is None:
console_error = f"Fail to get console. Please check the console configuration of your VM."
console_error = "Fail to get console. Please check the console configuration of your VM."
else:
console_error = f"Console type: {console_type} no support"
response = render(request, 'console-vnc-lite.html', locals())
response = render(request, "console-vnc-lite.html", locals())
response.set_cookie('token', token)
response.set_cookie("token", token)
return response

View file

@ -1,3 +1 @@
from django.db import models
# Create your models here.

View file

@ -48,10 +48,10 @@ def os_userdata(request, version):
ip = get_client_ip(request)
hostname = get_hostname_by_ip(ip)
vname = hostname.split('.')[0]
instance_keys = []
userinstances = UserInstance.objects.filter(instance__name=vname)
for ui in userinstances:
keys = UserSSHKey.objects.filter(user=ui.user)
for k in keys:
@ -95,7 +95,7 @@ def get_vdi_url(request, compute_id, vname):
:return:
"""
compute = get_object_or_404(Compute, pk=compute_id)
data = {}
try:
conn = wvmInstance(compute.hostname,
compute.login,
@ -107,6 +107,6 @@ def get_vdi_url(request, compute_id, vname):
url = f"{conn.get_console_type()}://{fqdn}:{conn.get_console_port()}"
response = url
return HttpResponse(response)
except libvirtError as lib_err:
except libvirtError:
err = f"Error getting VDI URL for {vname}"
raise Http404(err)

View file

@ -1,3 +1,5 @@
#!/bin/bash
# ensure running as root
if [ "$(id -u)" != "0" ]; then
#Debian doesnt have sudo if root has a password.

View file

@ -1,16 +1,26 @@
import time
import os.path
try:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_DOMAIN_RUNNING, VIR_DOMAIN_AFFECT_LIVE, \
VIR_DOMAIN_AFFECT_CONFIG, VIR_DOMAIN_UNDEFINE_NVRAM, VIR_DOMAIN_UNDEFINE_KEEP_NVRAM, VIR_DOMAIN_START_PAUSED
from libvirt import VIR_MIGRATE_LIVE, \
VIR_MIGRATE_UNSAFE, \
VIR_MIGRATE_PERSIST_DEST, \
VIR_MIGRATE_UNDEFINE_SOURCE, \
VIR_MIGRATE_OFFLINE,\
VIR_MIGRATE_COMPRESSED, \
VIR_MIGRATE_AUTO_CONVERGE, \
from libvirt import (
libvirtError,
VIR_DOMAIN_XML_SECURE,
VIR_DOMAIN_RUNNING,
VIR_DOMAIN_AFFECT_LIVE,
VIR_DOMAIN_AFFECT_CONFIG,
VIR_DOMAIN_UNDEFINE_NVRAM,
VIR_DOMAIN_UNDEFINE_KEEP_NVRAM,
VIR_DOMAIN_START_PAUSED
)
from libvirt import (
VIR_MIGRATE_LIVE,
VIR_MIGRATE_UNSAFE,
VIR_MIGRATE_PERSIST_DEST,
VIR_MIGRATE_UNDEFINE_SOURCE,
VIR_MIGRATE_OFFLINE,
VIR_MIGRATE_COMPRESSED,
VIR_MIGRATE_AUTO_CONVERGE,
VIR_MIGRATE_POSTCOPY
)
from libvirt import VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT
except:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE

View file

@ -19,6 +19,7 @@ from time import time
# Read write lock
# ---------------
class ReadWriteLock(object):
"""Read-Write lock class. A read-write lock differs from a standard
threading.RLock() by allowing multiple threads to simultaneously hold a

View file

@ -15,9 +15,15 @@ class wvmStorages(wvmConnect):
else:
stg_vol = None
stg_size = stg.info()[1]
storages.append({'name': pool, 'status': stg_status,
'type': stg_type, 'volumes': stg_vol,
'size': stg_size})
storages.append(
{
"name": pool,
"status": stg_status,
"type": stg_type,
"volumes": stg_vol,
"size": stg_size
}
)
return storages
def define_storage(self, xml, flag):
@ -26,14 +32,14 @@ class wvmStorages(wvmConnect):
def create_storage(self, stg_type, name, source, target):
xml = f"""<pool type='{stg_type}'>
<name>{name}</name>"""
if stg_type == 'logical':
if stg_type == "logical":
xml += f"""<source>
<device path='{source}'/>
<name>{name}</name>
<format type='lvm2'/>
</source>"""
if stg_type == 'logical':
target = '/dev/' + name
if stg_type == "logical":
target = "/dev/" + name
xml += f"""
<target>
<path>{target}</path>
@ -41,7 +47,7 @@ class wvmStorages(wvmConnect):
</pool>"""
self.define_storage(xml, 0)
stg = self.get_storage(name)
if stg_type == 'logical':
if stg_type == "logical":
stg.build(0)
stg.create(0)
stg.setAutostart(1)
@ -97,11 +103,16 @@ class wvmStorage(wvmConnect):
return self.pool.name()
def get_status(self):
status = ['Not running', 'Initializing pool, not available', 'Running normally', 'Running degraded']
status = [
"Not running",
"Initializing pool, not available",
"Running normally",
"Running degraded"
]
try:
return status[self.pool.info()[0]]
except ValueError:
return 'Unknown'
return "Unknown"
def get_size(self):
return [self.pool.info()[1], self.pool.info()[3]]
@ -202,10 +213,12 @@ class wvmStorage(wvmConnect):
for volname in vols:
vol_list.append(
{'name': volname,
'size': self.get_volume_size(volname),
'allocation': self.get_volume_allocation(volname),
'type': self.get_volume_type(volname)}
{
"name": volname,
"size": self.get_volume_size(volname),
"allocation": self.get_volume_allocation(volname),
"type": self.get_volume_type(volname)
}
)
return vol_list
@ -213,13 +226,13 @@ class wvmStorage(wvmConnect):
size = int(size) * 1073741824
storage_type = self.get_type()
alloc = size
if vol_fmt == 'unknown':
vol_fmt = 'raw'
if storage_type == 'dir':
if vol_fmt in ('qcow', 'qcow2'):
name += '.' + vol_fmt
if vol_fmt == "unknown":
vol_fmt = "raw"
if storage_type == "dir":
if vol_fmt in ("qcow", "qcow2"):
name += "." + vol_fmt
else:
name += '.img'
name += ".img"
alloc = 0
xml = f"""
<volume>
@ -234,7 +247,7 @@ class wvmStorage(wvmConnect):
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>"""
if vol_fmt == 'qcow2':
if vol_fmt == "qcow2":
xml += """
<compat>1.1</compat>
<features>
@ -251,8 +264,8 @@ class wvmStorage(wvmConnect):
vol_fmt = self.get_volume_type(name)
storage_type = self.get_type()
if storage_type == 'dir':
if vol_fmt in ['qcow', 'qcow2']:
if storage_type == "dir":
if vol_fmt in ["qcow", "qcow2"]:
target_file += '.' + vol_fmt
else:
suffix = '.' + file_suffix
@ -271,7 +284,7 @@ class wvmStorage(wvmConnect):
<mode>{mode}</mode>
<label>virt_image_t</label>
</permissions>"""
if vol_fmt == 'qcow2':
if vol_fmt == "qcow2":
xml += """
<compat>1.1</compat>
<features>

View file

@ -139,13 +139,13 @@ configure_nginx () {
rm /etc/nginx/sites-enabled/default
fi
chown -R $nginx_group:$nginx_group /var/lib/nginx
chown -R "$nginx_group":"$nginx_group" /var/lib/nginx
# Copy new configuration and webvirtcloud.conf
echo " * Copying Nginx configuration"
cp $APP_PATH/conf/nginx/"$distro"_nginx.conf /etc/nginx/nginx.conf
cp $APP_PATH/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
cp "$APP_PATH"/conf/nginx/"$distro"_nginx.conf /etc/nginx/nginx.conf
cp "$APP_PATH"/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
if ! [ -z "$fqdn" ]; then
if [ -n "$fqdn" ]; then
sed -i "s|\\(#server_name\\).*|server_name = $fqdn|" "$nginxfile"
fi
@ -156,7 +156,7 @@ configure_nginx () {
configure_supervisor () {
# Copy template supervisor service for gunicorn and novnc
echo " * Copying supervisor configuration"
cp $APP_PATH/conf/supervisor/webvirtcloud.conf $supervisor_conf_path/$supervisor_file_name
cp "$APP_PATH"/conf/supervisor/webvirtcloud.conf "$supervisor_conf_path"/"$supervisor_file_name"
sed -i "s|^\\(user=\\).*|\\1$nginx_group|" "$supervisor_conf_path/$supervisor_file_name"
}
@ -174,20 +174,20 @@ create_user () {
run_as_app_user () {
if ! hash sudo 2>/dev/null; then
su -c "$@" $APP_USER
su -c "$@" "$APP_USER"
else
sudo -i -u $APP_USER "$@"
sudo -i -u "$APP_USER" "$@"
fi
}
activate_python_environment () {
cd $APP_PATH
virtualenv -p $PYTHON venv
cd "$APP_PATH" || exit
virtualenv -p "$PYTHON" venv
source venv/bin/activate
}
generate_secret_key() {
$PYTHON - <<END
"$PYTHON" - <<END
import random
print(''.join(random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)))
END
@ -418,7 +418,7 @@ case $distro in
fi
;;
ubuntu)
if [ "$version" -ge "18.04" ]; then
if [ "$version" == "18.04" ] || [ "$version" == "20.04" ]; then
# Install for Ubuntu 18 / 20
tzone=\'$(cat /etc/timezone)\'

View file

@ -1,7 +1,4 @@
import json
from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
from libvirt import libvirtError

View file

@ -12,17 +12,17 @@ TEMPLATE_DEBUG = True
INSTALLED_APPS += [
'debug_toolbar',
"debug_toolbar",
]
MIDDLEWARE += [
'debug_toolbar.middleware.DebugToolbarMiddleware',
"debug_toolbar.middleware.DebugToolbarMiddleware",
]
# DebugToolBar
INTERNAL_IPS = (
'127.0.0.1',
"127.0.0.1",
)
DEBUG_TOOLBAR_CONFIG = {
'INTERCEPT_REDIRECTS': False,
}
"INTERCEPT_REDIRECTS": False,
}

View file

@ -25,5 +25,5 @@ if settings.DEBUG:
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
]
except:
except ImportError:
pass

View file

@ -12,11 +12,16 @@ import sys
from django.core.wsgi import get_wsgi_application
# execfile('/srv/webvirtcloud/venv/bin/activate_this.py', dict(__file__='/srv/webvirtcloud/venv/bin/activate_this.py'))
exec(compile(open('/srv/webvirtcloud/venv/bin/activate', "rb").read(),
'/srv/webvirtcloud/venv/bin/activate', 'exec'))
exec(
compile(
open("/srv/webvirtcloud/venv/bin/activate", "rb").read(),
"/srv/webvirtcloud/venv/bin/activate",
"exec"
)
)
sys.path.append('/srv/webvirtcloud')
sys.path.append("/srv/webvirtcloud")
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webvirtcloud.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
application = get_wsgi_application()