mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 15:15:22 +00:00
fix some linter warnings
This commit is contained in:
parent
119d874e85
commit
922a8e62d1
20 changed files with 266 additions and 189 deletions
77
README.md
77
README.md
|
@ -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>
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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},
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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}'
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
# ensure running as root
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
#Debian doesnt have sudo if root has a password.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)\'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -25,5 +25,5 @@ if settings.DEBUG:
|
|||
urlpatterns += [
|
||||
path('__debug__/', include(debug_toolbar.urls)),
|
||||
]
|
||||
except:
|
||||
except ImportError:
|
||||
pass
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue