diff --git a/.gitignore b/.gitignore index 97ac9f2..d4cce40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,10 @@ -.vagrant -venv -.vscode -.idea -.DS_* *.pyc -db.sqlite3* -console/cert.pem* -tags -dhcpd.* -webvirtcloud/settings.py -*migrations/* +*.pyo +.idea +.vscode +.vagrant +.DS_* +__pycache__ +venv +node_modules +db.sqlite3 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..211c338 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +v0.0.1 (2018-09-01) + +* Init project diff --git a/Dockerfile b/Dockerfile index 3ed8af4..856b15b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,50 +1,23 @@ -FROM phusion/baseimage:0.9.17 -MAINTAINER Jethro Yu +FROM ubuntu:18.04 -RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean +WORKDIR /usr/src/ -RUN apt-get update -qqy -RUN DEBIAN_FRONTEND=noninteractive apt-get -qyy install \ - -o APT::Install-Suggests=false \ - git python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev nginx libsasl2-modules +COPY backend/requirements.txt /usr/src/requirements.txt +COPY backend/requirements-dev.txt /usr/src/requirements-dev.txt -ADD . /srv/webvirtcloud -RUN chown -R www-data:www-data /srv/webvirtcloud +RUN set -ex \ + && apt-get update -q \ + && apt-get -y install \ + python3-pip \ + libvirt-dev \ + libmariadbclient-dev -# Setup webvirtcloud -RUN cd /srv/webvirtcloud && \ - virtualenv venv && \ - . venv/bin/activate && \ - pip install -U pip && \ - pip install -r conf/requirements.txt && \ - chown -R www-data:www-data /srv/webvirtcloud +RUN pip3 install -r requirements-dev.txt +RUN rm -f requirements.txt requirements-dev.txt -RUN cd /srv/webvirtcloud && . venv/bin/activate && \ - python manage.py migrate && \ - chown -R www-data:www-data /srv/webvirtcloud +WORKDIR /app +VOLUME /app -# Setup Nginx -RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf && \ - rm /etc/nginx/sites-enabled/default && \ - chown -R www-data:www-data /var/lib/nginx +EXPOSE 8000 6080 -ADD conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/ - -# Register services to runit -RUN mkdir /etc/service/nginx && \ - mkdir /etc/service/nginx-log-forwarder && \ - mkdir /etc/service/webvirtcloud && \ - mkdir /etc/service/novnc -ADD conf/runit/nginx /etc/service/nginx/run -ADD conf/runit/nginx-log-forwarder /etc/service/nginx-log-forwarder/run -ADD conf/runit/novncd.sh /etc/service/novnc/run -ADD conf/runit/webvirtcloud.sh /etc/service/webvirtcloud/run - -EXPOSE 80 -EXPOSE 6080 - -# Define mountable directories. -#VOLUME [] - -# Use baseimage-docker's init system. -CMD ["/sbin/my_init"] +CMD ["/bin/bash"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f433b1a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md index a4713c4..de219f1 100644 --- a/README.md +++ b/README.md @@ -1,272 +1 @@ -## WebVirtCloud Beta - - -## Features - -* 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!!! - -How to update gstfsd daemon on hypervisor: - -```bash -wget -O - https://clck.ru/9VMRH | sudo tee -a /usr/local/bin/gstfsd -sudo service supervisor restart -``` - -### Description - -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. - -### Generate secret key -You should generate SECRET_KEY after cloning repo. Then put it into webvirtcloud/settings.py. - -```python -import random, string -haystack = string.ascii_letters + string.digits + string.punctuation -print(''.join([random.SystemRandom().choice(haystack) for _ in range(50)])) -``` - -### Install WebVirtCloud panel (Ubuntu) - -```bash -sudo apt-get -y install git virtualenv python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python-guestfs -git clone https://github.com/retspen/webvirtcloud -cd webvirtcloud -cp webvirtcloud/settings.py.template webvirtcloud/settings.py -# now put secret key to webvirtcloud/settings.py -sudo cp conf/supervisor/webvirtcloud.conf /etc/supervisor/conf.d -sudo cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d -cd .. -sudo mv webvirtcloud /srv -sudo chown -R www-data:www-data /srv/webvirtcloud -cd /srv/webvirtcloud -virtualenv venv -source venv/bin/activate -pip install -r conf/requirements.txt -python manage.py migrate -sudo chown -R www-data:www-data /srv/webvirtcloud -sudo rm /etc/nginx/sites-enabled/default -``` - -Restart services for running WebVirtCloud: - -```bash -sudo service nginx restart -sudo service supervisor restart -``` - -Setup libvirt and KVM on server - -```bash -wget -O - https://clck.ru/9V9fH | sudo sh -``` - -### Install WebVirtCloud panel (CentOS) - -```bash -sudo yum -y install python-virtualenv python-devel libvirt-devel glibc gcc nginx supervisor python-lxml git python-libguestfs -``` - -#### Creating directories and cloning repo - -```bash -sudo mkdir /srv && cd /srv -sudo git clone https://github.com/retspen/webvirtcloud && cd webvirtcloud -cp webvirtcloud/settings.py.template webvirtcloud/settings.py -# now put secret key to webvirtcloud/settings.py -``` - -#### Start installation webvirtcloud -``` -sudo virtualenv venv -sudo source venv/bin/activate -sudo venv/bin/pip install -r conf/requirements.txt -sudo cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/ -sudo venv/bin/python manage.py migrate -``` - -#### Configure the supervisor for CentOS -Add the following after the [include] line (after **files = ... ** actually): -```bash -sudo vim /etc/supervisord.conf - -[program:webvirtcloud] -command=/srv/webvirtcloud/venv/bin/gunicorn webvirtcloud.wsgi:application -c /srv/webvirtcloud/gunicorn.conf.py -directory=/srv/webvirtcloud -user=nginx -autostart=true -autorestart=true -redirect_stderr=true - -[program:novncd] -command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd -directory=/srv/webvirtcloud -user=nginx -autostart=true -autorestart=true -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: - -``` -# server { -# listen 80 default_server; -# listen [::]:80 default_server; -# server_name _; -# root /usr/share/nginx/html; -# -# # Load configuration files for the default server block. -# include /etc/nginx/default.d/*.conf; -# -# location / { -# } -# -# error_page 404 /404.html; -# location = /40x.html { -# } -# -# error_page 500 502 503 504 /50x.html; -# location = /50x.html { -# } -# } -} -``` - -Also make sure file in **/etc/nginx/conf.d/webvirtcloud.conf** has the proper paths: -``` -upstream gunicorn_server { - #server unix:/srv/webvirtcloud/venv/wvcloud.socket fail_timeout=0; - server 127.0.0.1:8000 fail_timeout=0; -} -server { - listen 80; - - server_name servername.domain.com; - access_log /var/log/nginx/webvirtcloud-access_log; - - location /static/ { - root /srv/webvirtcloud; - expires max; - } - - location / { - proxy_pass http://gunicorn_server; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for; - proxy_set_header Host $host:$server_port; - proxy_set_header X-Forwarded-Proto $remote_addr; - proxy_connect_timeout 600; - proxy_read_timeout 600; - proxy_send_timeout 600; - client_max_body_size 1024M; - } -} -``` - -Change permissions so nginx can read the webvirtcloud folder: - -```bash -sudo chown -R nginx:nginx /srv/webvirtcloud -``` - -Change permission for selinux: - -```bash -sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webvirtcloud(/.*)" -``` - -Add required user to the kvm group: -```bash -sudo usermod -G kvm -a webvirtmgr -``` - -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 -novncd RUNNING pid 24661, uptime 6:01:40 -webvirtcloud RUNNING pid 24660, uptime 6:01:40 -``` - -#### Apache mod_wsgi configuration -``` -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 -``` - -Done!! - -Go to http://serverip and you should see the login screen. - -### Alternative running novncd via runit -Alternative to running nonvcd via supervisor is runit. - -On Debian systems install runit and configure novncd service -``` -apt install runit runit-systemd -mkdir /etc/service/novncd/ -ln -s /srv/webvirtcloud/conf/runit/novncd.sh /etc/service/novncd/run -systemctl start runit.service -``` - -### Default credentials -
-login: admin
-password: admin
-
- -### 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. -``` -chown www-data -R ~www-data -sudo -u www-data ssh-keygen -cat > ~www-data/.ssh/config << EOF -Host * -StrictHostKeyChecking no -EOF -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. -``` -sudo -u www-data ssh-copy-id root@compute1 -``` - -### Cloud-init -Currently supports only root ssh authorized keys and hostname. Example configuration of the cloud-init client follows. -``` -datasource: - OpenStack: - metadata_urls: [ "http://webvirtcloud.domain.com/datasource" ] -``` - -### How To Update -```bash -sudo virtualenv venv -sudo source venv/bin/activate -git pull -pip install -U -r conf/requirements.txt -python manage.py migrate -sudo service supervisor restart -``` - -### License - -WebVirtCloud is licensed under the [Apache Licence, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). +# webvirtcloud \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile index a92a1ab..935a227 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -2,17 +2,22 @@ # vi: set ft=ruby : Vagrant.configure(2) do |config| - config.vm.box = "ubuntu/trusty64" - config.vm.hostname = "webvirtcloud" - config.vm.network "private_network", ip: "192.168.33.10" - config.vm.provision "shell", inline: <<-SHELL - sudo sh /vagrant/dev/libvirt-bootstrap.sh - sudo sed -i 's/auth_tcp = \"sasl\"/auth_tcp = \"none\"/g' /etc/libvirt/libvirtd.conf - sudo service libvirt-bin restart - sudo adduser vagrant libvirtd - sudo apt-get -y install python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev - virtualenv /vagrant/venv - source /vagrant/venv/bin/activate - pip install -r /vagrant/dev/requirements.txt - SHELL + config.vm.box = "bento/centos-7.5" + config.ssh.insert_key = false + config.vm.provision :shell, path: "devenv/vagrant/bootstrap.sh" + config.vm.network "private_network", ip: "192.168.250.254", auto_config: false + config.vm.network "private_network", ip: "10.16.0.254", auto_config: false + config.vm.network "forwarded_port", guest: 9090, host: 9090 + config.vm.network "forwarded_port", guest: 16509, host: 16509 + + (0..1 - 1).each do |i| + config.vm.define "node#{i}" do |kvm| + kvm.vm.hostname = "node#{i}" + kvm.vm.provider :virtualbox do |vb| + vb.cpus = "2" + vb.memory = "4096" + end + end + + end end diff --git a/_config.yml b/_config.yml deleted file mode 100644 index c419263..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-cayman \ No newline at end of file diff --git a/accounts/backends.py b/accounts/backends.py deleted file mode 100644 index e66b94a..0000000 --- a/accounts/backends.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.contrib.auth.backends import RemoteUserBackend -from accounts.models import UserInstance, UserAttributes -from instances.models import Instance - -class MyRemoteUserBackend(RemoteUserBackend): - - #create_unknown_user = True - - def configure_user(self, user): - #user.is_superuser = True - UserAttributes.configure_user(user) - return user - diff --git a/accounts/forms.py b/accounts/forms.py deleted file mode 100644 index 4127ac4..0000000 --- a/accounts/forms.py +++ /dev/null @@ -1,25 +0,0 @@ -import re -from django import forms -from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import User -from django.conf import settings - - -class UserAddForm(forms.Form): - name = forms.CharField(label="Name", - error_messages={'required': _('No User name has been entered')}, - max_length=20) - password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD, error_messages={'required': _('No password has been entered')},) - - def clean_name(self): - name = self.cleaned_data['name'] - have_symbol = re.match('^[a-z0-9]+$', name) - if not have_symbol: - raise forms.ValidationError(_('The flavor name must not contain any special characters')) - elif len(name) > 20: - raise forms.ValidationError(_('The flavor name must not exceed 20 characters')) - try: - User.objects.get(username=name) - except User.DoesNotExist: - return name - raise forms.ValidationError(_('Flavor name is already use')) diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py deleted file mode 100644 index 3989532..0000000 --- a/accounts/migrations/0001_initial.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('instances', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='UserInstance', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('is_change', models.BooleanField(default=False)), - ('is_delete', models.BooleanField(default=False)), - ('instance', models.ForeignKey(to='instances.Instance')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - options={ - }, - bases=(models.Model,), - ), - ] diff --git a/accounts/migrations/0002_auto_20150325_0846.py b/accounts/migrations/0002_auto_20150325_0846.py deleted file mode 100644 index 8780f97..0000000 --- a/accounts/migrations/0002_auto_20150325_0846.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations - - -def add_useradmin(apps, schema_editor): - from django.utils import timezone - from django.contrib.auth.models import User - - User.objects.create_superuser('admin', None, 'admin', - last_login=timezone.now() - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0001_initial'), - ] - - operations = [ - migrations.RunPython(add_useradmin), - ] diff --git a/accounts/migrations/0003_usersshkey.py b/accounts/migrations/0003_usersshkey.py deleted file mode 100644 index b00bc62..0000000 --- a/accounts/migrations/0003_usersshkey.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('accounts', '0002_auto_20150325_0846'), - ] - - operations = [ - migrations.CreateModel( - name='UserSSHKey', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('keyname', models.CharField(max_length=25)), - ('keypublic', models.CharField(max_length=500)), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/accounts/migrations/0004_userattributes.py b/accounts/migrations/0004_userattributes.py deleted file mode 100644 index fb32539..0000000 --- a/accounts/migrations/0004_userattributes.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.conf import settings - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('accounts', '0003_usersshkey'), - ] - - operations = [ - migrations.CreateModel( - name='UserAttributes', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('max_instances', models.IntegerField(default=0)), - ('max_cpus', models.IntegerField(default=0)), - ('max_memory', models.IntegerField(default=0)), - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/accounts/migrations/0004_userinstance_is_vnc.py b/accounts/migrations/0004_userinstance_is_vnc.py deleted file mode 100644 index 9c1c9b8..0000000 --- a/accounts/migrations/0004_userinstance_is_vnc.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0003_usersshkey'), - ] - - operations = [ - migrations.AddField( - model_name='userinstance', - name='is_vnc', - field=models.BooleanField(default=False), - ), - ] diff --git a/accounts/migrations/0005_userattributes_can_clone_instances.py b/accounts/migrations/0005_userattributes_can_clone_instances.py deleted file mode 100644 index 4539657..0000000 --- a/accounts/migrations/0005_userattributes_can_clone_instances.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0004_userattributes'), - ] - - operations = [ - migrations.AddField( - model_name='userattributes', - name='can_clone_instances', - field=models.BooleanField(default=False), - ), - ] diff --git a/accounts/migrations/0006_userattributes_max_disk_size.py b/accounts/migrations/0006_userattributes_max_disk_size.py deleted file mode 100644 index 3d21f5f..0000000 --- a/accounts/migrations/0006_userattributes_max_disk_size.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0005_userattributes_can_clone_instances'), - ] - - operations = [ - migrations.AddField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=0), - ), - ] diff --git a/accounts/migrations/0007_auto_20160426_0635.py b/accounts/migrations/0007_auto_20160426_0635.py deleted file mode 100644 index 2f92aba..0000000 --- a/accounts/migrations/0007_auto_20160426_0635.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0006_userattributes_max_disk_size'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='max_cpus', - field=models.IntegerField(default=1), - ), - migrations.AlterField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=20), - ), - migrations.AlterField( - model_name='userattributes', - name='max_instances', - field=models.IntegerField(default=1), - ), - migrations.AlterField( - model_name='userattributes', - name='max_memory', - field=models.IntegerField(default=2048), - ), - ] diff --git a/accounts/migrations/0008_merge.py b/accounts/migrations/0008_merge.py deleted file mode 100644 index 8edf672..0000000 --- a/accounts/migrations/0008_merge.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0004_userinstance_is_vnc'), - ('accounts', '0007_auto_20160426_0635'), - ] - - operations = [ - ] diff --git a/accounts/migrations/0009_auto_20171026_0805.py b/accounts/migrations/0009_auto_20171026_0805.py deleted file mode 100644 index 7d035c7..0000000 --- a/accounts/migrations/0009_auto_20171026_0805.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0008_merge'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='can_clone_instances', - field=models.BooleanField(default=True), - ), - ] diff --git a/accounts/migrations/0010_auto_20180625_1236.py b/accounts/migrations/0010_auto_20180625_1236.py deleted file mode 100644 index 23e1548..0000000 --- a/accounts/migrations/0010_auto_20180625_1236.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-06-25 12:36 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0009_auto_20171026_0805'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='max_cpus', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value'), - ), - migrations.AlterField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value'), - ), - migrations.AlterField( - model_name='userattributes', - name='max_instances', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value'), - ), - migrations.AlterField( - model_name='userattributes', - name='max_memory', - field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value'), - ), - ] diff --git a/accounts/migrations/0011_auto_20180625_1313.py b/accounts/migrations/0011_auto_20180625_1313.py deleted file mode 100644 index 1ee194b..0000000 --- a/accounts/migrations/0011_auto_20180625_1313.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-06-25 13:13 -from __future__ import unicode_literals - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0010_auto_20180625_1236'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='max_cpus', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_instances', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_memory', - field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - ] diff --git a/accounts/migrations/0012_auto_20180625_1331.py b/accounts/migrations/0012_auto_20180625_1331.py deleted file mode 100644 index 827599e..0000000 --- a/accounts/migrations/0012_auto_20180625_1331.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-06-25 13:31 -from __future__ import unicode_literals - -import django.core.validators -from django.db import migrations, models -import re - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0011_auto_20180625_1313'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='max_cpus', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_instances', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_memory', - field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]), - ), - ] diff --git a/accounts/migrations/0013_auto_20180625_1358.py b/accounts/migrations/0013_auto_20180625_1358.py deleted file mode 100644 index 1fdec50..0000000 --- a/accounts/migrations/0013_auto_20180625_1358.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-06-25 13:58 -from __future__ import unicode_literals - -import django.core.validators -from django.db import migrations, models -import re - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0012_auto_20180625_1331'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='max_cpus', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_instances', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_memory', - field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]), - ), - ] diff --git a/accounts/migrations/0014_auto_20180808_1436.py b/accounts/migrations/0014_auto_20180808_1436.py deleted file mode 100644 index a4f3c78..0000000 --- a/accounts/migrations/0014_auto_20180808_1436.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-08-08 11:36 -from __future__ import unicode_literals - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0013_auto_20180625_1358'), - ] - - operations = [ - migrations.AlterField( - model_name='userattributes', - name='max_cpus', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_disk_size', - field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_instances', - field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - migrations.AlterField( - model_name='userattributes', - name='max_memory', - field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]), - ), - ] diff --git a/accounts/migrations/0015_auto_20180808_1449.py b/accounts/migrations/0015_auto_20180808_1449.py deleted file mode 100644 index d94905a..0000000 --- a/accounts/migrations/0015_auto_20180808_1449.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.13 on 2018-08-08 11:49 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0014_auto_20180808_1436'), - ] - - operations = [ - migrations.AlterField( - model_name='usersshkey', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/accounts/models.py b/accounts/models.py deleted file mode 100644 index 42c8051..0000000 --- a/accounts/models.py +++ /dev/null @@ -1,58 +0,0 @@ -from django.db import models -from django.contrib.auth.models import User -from django.conf import settings -from instances.models import Instance -from django.core.validators import MinValueValidator - - -class UserInstance(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE) - instance = models.ForeignKey(Instance, on_delete=models.CASCADE) - is_change = models.BooleanField(default=False) - is_delete = models.BooleanField(default=False) - is_vnc = models.BooleanField(default=False) - - def __unicode__(self): - return self.instance.name - - -class UserSSHKey(models.Model): - user = models.ForeignKey(User, on_delete=models.DO_NOTHING) - keyname = models.CharField(max_length=25) - keypublic = models.CharField(max_length=500) - - def __unicode__(self): - return self.keyname - -class UserAttributes(models.Model): - user = models.OneToOneField(User, on_delete=models.CASCADE) - can_clone_instances = models.BooleanField(default=True) - max_instances = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1),]) - max_cpus = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)]) - max_memory = models.IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)]) - max_disk_size = models.IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)]) - - @staticmethod - def create_missing_userattributes(user): - try: - userattributes = user.userattributes - except UserAttributes.DoesNotExist: - userattributes = UserAttributes(user=user) - userattributes.save() - - @staticmethod - def add_default_instances(user): - existing_instances = UserInstance.objects.filter(user=user) - if not existing_instances: - for instance_name in settings.NEW_USER_DEFAULT_INSTANCES: - instance = Instance.objects.get(name=instance_name) - user_instance = UserInstance(user=user, instance=instance) - user_instance.save() - - @staticmethod - def configure_user(user): - UserAttributes.create_missing_userattributes(user) - UserAttributes.add_default_instances(user) - - def __unicode__(self): - return self.user.username diff --git a/accounts/templates/account.html b/accounts/templates/account.html deleted file mode 100644 index 0c5068d..0000000 --- a/accounts/templates/account.html +++ /dev/null @@ -1,140 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% block title %}{% trans "User" %} - {{ user }}{% endblock %} -{% block content %} - -
-
- {% include 'create_user_inst_block.html' %} -

{{ user }}

-
-
- - - {% include 'errors_block.html' %} - - {% if request.user.is_superuser and publickeys %} -
-
-
- - - - - - - - - {% for publickey in publickeys %} - - - - - {% endfor %} - -
{% trans "Key name" %}{% trans "Public key" %}
{{ publickey.keyname }}{{ publickey.keypublic|truncatechars:64 }}
-
-
-
- {% endif %} - -
-
- {% if not user_insts %} -
-
- - {% trans "Warning:" %} {% trans "User doesn't have any Instace" %} -
-
- {% else %} -
- - - - - - - - - - - - - {% for inst in user_insts %} - - - - - - - - - - {% endfor %} - -
#{% trans "Instance" %}{% trans "VNC" %}{% trans "Resize" %}{% trans "Delete" %}{% trans "Action" %}
{{ forloop.counter }}{{ inst.instance.name }}{{ inst.is_vnc }}{{ inst.is_change }}{{ inst.is_delete }} - - - - - - - -
{% csrf_token %} - - -
-
-
- {% endif %} -
-
-{% endblock %} diff --git a/accounts/templates/accounts-list.html b/accounts/templates/accounts-list.html deleted file mode 100644 index f5a6013..0000000 --- a/accounts/templates/accounts-list.html +++ /dev/null @@ -1,175 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% load staticfiles %} -{% block title %}{% trans "Users" %}{% endblock %} -{% block content %} - -
-
- {% include 'create_user_block.html' %} - -

{% trans "Users" %}

-
-
- - - {% include 'errors_block.html' %} - -
- {% if not users %} -
-
- - {% trans "Warning:" %} {% trans "You don't have any User" %} -
-
- {% else %} -
- - - - - - - - - - - - {% for user in users %} - - - - - - - - {% endfor %} - -
UsernameStatusStaffSuperuserClone
- {{ user.username }} - - - - - {% if user.is_active %} - {% trans "Active" %} - {% else %} - {% trans "Blocked" %} - {% endif %} - {% if user.is_staff %}{% endif %}{% if user.is_superuser %}{% endif %}{% if user.userattributes.can_clone_instances %}{% endif %}
- - {% for user in users %} - - - {% endfor %} -
- {% endif %} -
-{% endblock %} -{% block script %} - -{% endblock %} diff --git a/accounts/templates/accounts.html b/accounts/templates/accounts.html deleted file mode 100644 index 59077fc..0000000 --- a/accounts/templates/accounts.html +++ /dev/null @@ -1,144 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% block title %}{% trans "Users" %}{% endblock %} -{% block content %} - -
-
- {% include 'create_user_block.html' %} -

{% trans "Users" %}

-
-
- - - {% include 'errors_block.html' %} - -
- {% if not users %} -
-
- - {% trans "Warning:" %} {% trans "You don't have any User" %} -
-
- {% else %} - {% for user in users %} -
-
- -
-
-

{% trans "Status:" %}

-
-
- {% if user.is_active %} -

{% trans "Active" %}

- {% else %} -

{% trans "Blocked" %}

- {% endif %} -
-
-
-
- - - - {% endfor %} - {% endif %} -
-{% endblock %} diff --git a/accounts/templates/base_auth.html b/accounts/templates/base_auth.html deleted file mode 100644 index a7680bb..0000000 --- a/accounts/templates/base_auth.html +++ /dev/null @@ -1,41 +0,0 @@ -{% load static %} - - - - - - - - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - -
- {% block content %}{% endblock %} -
- - - - - - - - \ No newline at end of file diff --git a/accounts/templates/create_user_block.html b/accounts/templates/create_user_block.html deleted file mode 100644 index 55e5c2f..0000000 --- a/accounts/templates/create_user_block.html +++ /dev/null @@ -1,38 +0,0 @@ -{% load i18n %} -{% if request.user.is_superuser %} - - - - - - -{% endif %} diff --git a/accounts/templates/create_user_inst_block.html b/accounts/templates/create_user_inst_block.html deleted file mode 100644 index df5937e..0000000 --- a/accounts/templates/create_user_inst_block.html +++ /dev/null @@ -1,36 +0,0 @@ -{% load i18n %} -{% if request.user.is_superuser %} - - - - - - -{% endif %} \ No newline at end of file diff --git a/accounts/templates/login.html b/accounts/templates/login.html deleted file mode 100644 index c3ca001..0000000 --- a/accounts/templates/login.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base_auth.html" %} -{% load i18n %} -{% block title %}{% trans "WebVirtCloud - Sign In" %}{% endblock %} -{% block content %} -
- -
- {% if form.errors %} -
- - {% trans "Incorrect username or password." %} -
- {% endif %} - -
-
-{% endblock %} \ No newline at end of file diff --git a/accounts/templates/logout.html b/accounts/templates/logout.html deleted file mode 100644 index 83161d3..0000000 --- a/accounts/templates/logout.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "base_auth.html" %} -{% load i18n %} -{% block title %}{% trans "WebVirtCloud - Sign Out" %}{% endblock %} -{% block content %} -
- -
-
-

{% trans "Successful log out" %}

-
-
-
-{% endblock %} \ No newline at end of file diff --git a/accounts/templates/profile.html b/accounts/templates/profile.html deleted file mode 100644 index d2213b4..0000000 --- a/accounts/templates/profile.html +++ /dev/null @@ -1,117 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% load tags_fingerprint %} -{% block title %}{% trans "Profile" %}{% endblock %} -{% block content %} - -
-
-

{% trans "Profile" %}

-
-
- - - {% include 'errors_block.html' %} - -
-
- -
{% csrf_token %} -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- {% if show_profile_edit_password %} - -
{% csrf_token %} -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- {% endif %} - - {% if publickeys %} -
-
- - - {% for key in publickeys %} - - - - - {% endfor %} - -
{{ key.keyname }} ({% ssh_to_fingerprint key.keypublic %}) -
{% csrf_token %} - - -
-
-
-
- {% endif %} -
{% csrf_token %} -
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
-
-
-{% endblock %} diff --git a/accounts/templatetags/tags_fingerprint.py b/accounts/templatetags/tags_fingerprint.py deleted file mode 100644 index 31f3952..0000000 --- a/accounts/templatetags/tags_fingerprint.py +++ /dev/null @@ -1,15 +0,0 @@ -from django import template -import base64 -import hashlib - -register = template.Library() - - -@register.simple_tag -def ssh_to_fingerprint(line): - try: - key = base64.b64decode(line.strip().split()[1].encode('ascii')) - fp_plain = hashlib.md5(key).hexdigest() - return ':'.join(a + b for a, b in zip(fp_plain[::2], fp_plain[1::2])) - except Exception: - return 'Invalid key' diff --git a/accounts/urls.py b/accounts/urls.py deleted file mode 100644 index c0ea4f1..0000000 --- a/accounts/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls import url -from django.contrib.auth import views as auth_views -from . import views - -urlpatterns = [ - url(r'^login/$', auth_views.LoginView.as_view(template_name='login.html'), name='login'), - url(r'^logout/$', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'), - url(r'^profile/$', views.profile, name='profile'), url(r'^$', views.accounts, name='accounts'), - url(r'^profile/(?P[0-9]+)/$', views.account, name='account'), -] diff --git a/accounts/views.py b/accounts/views.py deleted file mode 100644 index 494bdd6..0000000 --- a/accounts/views.py +++ /dev/null @@ -1,202 +0,0 @@ -from django.shortcuts import render -from django.http import HttpResponseRedirect -from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import User -from django.contrib.auth.decorators import login_required -from accounts.models import * -from instances.models import Instance -from accounts.forms import UserAddForm -from django.conf import settings -from django.core.validators import ValidationError - - - -@login_required -def profile(request): - """ - :param request: - :return: - """ - - error_messages = [] - user = User.objects.get(id=request.user.id) - publickeys = UserSSHKey.objects.filter(user_id=request.user.id) - show_profile_edit_password = settings.SHOW_PROFILE_EDIT_PASSWORD - - if request.method == 'POST': - if 'username' in request.POST: - username = request.POST.get('username', '') - email = request.POST.get('email', '') - user.first_name = username - user.email = email - user.save() - return HttpResponseRedirect(request.get_full_path()) - if 'oldpasswd' in request.POST: - oldpasswd = request.POST.get('oldpasswd', '') - password1 = request.POST.get('passwd1', '') - password2 = request.POST.get('passwd2', '') - if not password1 or not password2: - error_messages.append("Passwords didn't enter") - if password1 and password2 and password1 != password2: - error_messages.append("Passwords don't match") - if not user.check_password(oldpasswd): - error_messages.append("Old password is wrong!") - if not error_messages: - user.set_password(password1) - user.save() - return HttpResponseRedirect(request.get_full_path()) - 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") - error_messages.append(msg) - if keypublic == key.keypublic: - msg = _("Public key already exist") - error_messages.append(msg) - 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.save() - return HttpResponseRedirect(request.get_full_path()) - 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()) - -@login_required -def accounts(request): - """ - :param request: - :return: - """ - if not request.user.is_superuser: - return HttpResponseRedirect(reverse('index')) - - error_messages = [] - users = User.objects.all().order_by('username') - allow_empty_password = settings.ALLOW_EMPTY_PASSWORD - - if request.method == 'POST': - if 'create' in request.POST: - form = UserAddForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - else: - for msg_err in form.errors.values(): - error_messages.append(msg_err.as_text()) - if not error_messages: - new_user = User.objects.create_user(data['name'], None, data['password']) - new_user.save() - UserAttributes.configure_user(new_user) - return HttpResponseRedirect(request.get_full_path()) - if 'edit' in request.POST: - CHECKBOX_MAPPING = {'on': True, 'off': False, } - - user_id = request.POST.get('user_id', '') - user_pass = request.POST.get('user_pass', '') - user_edit = User.objects.get(id=user_id) - - if user_pass != '': user_edit.set_password(user_pass) - user_edit.is_staff = CHECKBOX_MAPPING.get(request.POST.get('user_is_staff', 'off')) - user_edit.is_superuser = CHECKBOX_MAPPING.get(request.POST.get('user_is_superuser', 'off')) - user_edit.save() - - UserAttributes.create_missing_userattributes(user_edit) - user_edit.userattributes.can_clone_instances = CHECKBOX_MAPPING.get(request.POST.get('userattributes_can_clone_instances', 'off')) - user_edit.userattributes.max_instances = request.POST.get('userattributes_max_instances', 0) - user_edit.userattributes.max_cpus = request.POST.get('userattributes_max_cpus', 0) - user_edit.userattributes.max_memory = request.POST.get('userattributes_max_memory', 0) - user_edit.userattributes.max_disk_size = request.POST.get('userattributes_max_disk_size', 0) - - try: - user_edit.userattributes.clean_fields() - except ValidationError as exc: - error_messages.append(exc) - else: - user_edit.userattributes.save() - return HttpResponseRedirect(request.get_full_path()) - if 'block' in request.POST: - user_id = request.POST.get('user_id', '') - user_block = User.objects.get(id=user_id) - user_block.is_active = False - user_block.save() - return HttpResponseRedirect(request.get_full_path()) - if 'unblock' in request.POST: - user_id = request.POST.get('user_id', '') - user_unblock = User.objects.get(id=user_id) - user_unblock.is_active = True - user_unblock.save() - return HttpResponseRedirect(request.get_full_path()) - if 'delete' in request.POST: - user_id = request.POST.get('user_id', '') - try: - del_user_inst = UserInstance.objects.filter(user_id=user_id) - del_user_inst.delete() - finally: - user_delete = User.objects.get(id=user_id) - user_delete.delete() - return HttpResponseRedirect(request.get_full_path()) - - accounts_template_file = 'accounts.html' - if settings.VIEW_ACCOUNTS_STYLE == "list": - accounts_template_file = 'accounts-list.html' - return render(request, accounts_template_file, locals()) - - -@login_required -def account(request, user_id): - """ - :param request: - :return: - """ - - if not request.user.is_superuser: - return HttpResponseRedirect(reverse('index')) - - 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') - publickeys = UserSSHKey.objects.filter(user_id=user_id) - - if request.method == 'POST': - if 'delete' in request.POST: - user_inst = request.POST.get('user_inst', '') - del_user_inst = UserInstance.objects.get(id=user_inst) - del_user_inst.delete() - return HttpResponseRedirect(request.get_full_path()) - if 'permission' in request.POST: - user_inst = request.POST.get('user_inst', '') - inst_vnc = request.POST.get('inst_vnc', '') - inst_change = request.POST.get('inst_change', '') - inst_delete = request.POST.get('inst_delete', '') - edit_user_inst = UserInstance.objects.get(id=user_inst) - edit_user_inst.is_change = bool(inst_change) - edit_user_inst.is_delete = bool(inst_delete) - edit_user_inst.is_vnc = bool(inst_vnc) - edit_user_inst.save() - return HttpResponseRedirect(request.get_full_path()) - if 'add' in request.POST: - inst_id = request.POST.get('inst_id', '') - - if settings.ALLOW_INSTANCE_MULTIPLE_OWNER: - check_inst = UserInstance.objects.filter(instance_id=int(inst_id), user_id=int(user_id)) - else: - check_inst = UserInstance.objects.filter(instance_id=int(inst_id)) - - if check_inst: - msg = _("Instance already added") - error_messages.append(msg) - else: - add_user_inst = UserInstance(instance_id=int(inst_id), user_id=int(user_id)) - add_user_inst.save() - return HttpResponseRedirect(request.get_full_path()) - - return render(request, 'account.html', locals()) diff --git a/accounts/__init__.py b/backend/cloudinit/__init__.py similarity index 100% rename from accounts/__init__.py rename to backend/cloudinit/__init__.py diff --git a/accounts/admin.py b/backend/cloudinit/admin.py similarity index 100% rename from accounts/admin.py rename to backend/cloudinit/admin.py diff --git a/backend/cloudinit/apps.py b/backend/cloudinit/apps.py new file mode 100644 index 0000000..cc854d2 --- /dev/null +++ b/backend/cloudinit/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CloudinitConfig(AppConfig): + name = 'cloudinit' diff --git a/accounts/migrations/__init__.py b/backend/cloudinit/migrations/__init__.py similarity index 100% rename from accounts/migrations/__init__.py rename to backend/cloudinit/migrations/__init__.py diff --git a/console/models.py b/backend/cloudinit/models.py similarity index 100% rename from console/models.py rename to backend/cloudinit/models.py diff --git a/accounts/tests.py b/backend/cloudinit/tests.py similarity index 100% rename from accounts/tests.py rename to backend/cloudinit/tests.py diff --git a/backend/cloudinit/views.py b/backend/cloudinit/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/cloudinit/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/accounts/templatetags/__init__.py b/backend/compute/__init__.py similarity index 100% rename from accounts/templatetags/__init__.py rename to backend/compute/__init__.py diff --git a/computes/admin.py b/backend/compute/admin.py similarity index 100% rename from computes/admin.py rename to backend/compute/admin.py diff --git a/backend/compute/apps.py b/backend/compute/apps.py new file mode 100644 index 0000000..bed51ed --- /dev/null +++ b/backend/compute/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ComputeConfig(AppConfig): + name = 'compute' diff --git a/backend/compute/migrations/0001_initial.py b/backend/compute/migrations/0001_initial.py new file mode 100644 index 0000000..d56c22e --- /dev/null +++ b/backend/compute/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 2.0.8 on 2018-09-22 07:20 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('region', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Compute', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200, validators=[django.core.validators.RegexValidator('^[a-zA-Z0-9.-_]+$')])), + ('description', models.CharField(blank=True, max_length=255)), + ('address', models.CharField(max_length=50, validators=[django.core.validators.RegexValidator('^[a-zA-Z0-9._-]+$')], verbose_name='FQDN or IP Address')), + ('login', models.CharField(blank=True, max_length=20)), + ('password', models.CharField(blank=True, max_length=20)), + ('conn', models.IntegerField(choices=[(1, 'TCP'), (3, 'TLS'), (2, 'SSH'), (4, 'SOCKET')], default=1)), + ('is_active', models.BooleanField(default=False, verbose_name='Active')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('is_deleted', models.BooleanField(default=False, verbose_name='Deleted')), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ('region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='region.Region')), + ], + options={ + 'verbose_name': 'Compute', + 'verbose_name_plural': 'Computes', + 'ordering': ['-id'], + }, + ), + ] diff --git a/computes/__init__.py b/backend/compute/migrations/__init__.py similarity index 100% rename from computes/__init__.py rename to backend/compute/migrations/__init__.py diff --git a/backend/compute/models.py b/backend/compute/models.py new file mode 100644 index 0000000..61ce730 --- /dev/null +++ b/backend/compute/models.py @@ -0,0 +1,81 @@ +from django.db import models +from django.core.validators import RegexValidator +from region.models import Region + + +class Compute(models.Model): + + TCP = 1 + TLS = 3 + SSH = 2 + SOCKET = 4 + + CONN_CHOICES = ( + (TCP, 'TCP'), + (TLS, 'TLS'), + (SSH, 'SSH'), + (SOCKET, 'SOCKET'), + ) + + name = models.CharField( + max_length=200, + validators=[RegexValidator('^[a-zA-Z0-9.-_]+$')], + ) + + region = models.ForeignKey( + Region, + on_delete=models.CASCADE, + ) + + description = models.CharField( + max_length=255, + blank=True, + ) + + address = models.CharField( + 'FQDN or IP Address', + max_length=50, + validators=[RegexValidator('^[a-zA-Z0-9._-]+$')], + ) + + login = models.CharField( + max_length=20, + blank=True, + ) + + password = models.CharField( + max_length=20, + blank=True, + ) + + conn = models.IntegerField( + choices=CONN_CHOICES, + default=TCP, + ) + + is_active = models.BooleanField( + 'Active', + default=False, + ) + + created_at = models.DateTimeField( + auto_now_add=True, + ) + + is_deleted = models.BooleanField( + 'Deleted', + default=False, + ) + + deleted_at = models.DateTimeField( + blank=True, + null=True, + ) + + class Meta: + ordering = ['-id'] + verbose_name = "Compute" + verbose_name_plural = "Computes" + + def __unicode__(self): + return self.name diff --git a/computes/tests.py b/backend/compute/tests.py similarity index 100% rename from computes/tests.py rename to backend/compute/tests.py diff --git a/backend/compute/views.py b/backend/compute/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/compute/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/computes/migrations/__init__.py b/backend/flavor/__init__.py similarity index 100% rename from computes/migrations/__init__.py rename to backend/flavor/__init__.py diff --git a/console/admin.py b/backend/flavor/admin.py similarity index 100% rename from console/admin.py rename to backend/flavor/admin.py diff --git a/backend/flavor/apps.py b/backend/flavor/apps.py new file mode 100644 index 0000000..dd36864 --- /dev/null +++ b/backend/flavor/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FlavorConfig(AppConfig): + name = 'flavor' diff --git a/backend/flavor/migrations/0001_initial.py b/backend/flavor/migrations/0001_initial.py new file mode 100644 index 0000000..b6fe43a --- /dev/null +++ b/backend/flavor/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# Generated by Django 2.0.8 on 2018-09-22 07:20 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import re + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Image', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(max_length=200, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')])), + ('name', models.CharField(max_length=255)), + ('url', models.URLField()), + ('md5sum', models.CharField(max_length=50)), + ('is_active', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('is_deleted', models.BooleanField(default=False)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ], + options={ + 'verbose_name': 'Instance Image', + 'verbose_name_plural': 'Instance Images', + 'ordering': ['-id'], + }, + ), + migrations.CreateModel( + name='Size', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.CharField(max_length=200, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')])), + ('name', models.CharField(blank=True, max_length=255)), + ('cpu', models.IntegerField(default=1)), + ('disk_bytes', models.BigIntegerField(default=21474836480)), + ('memory_bytes', models.BigIntegerField(default=536870912)), + ('is_active', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('is_deleted', models.BooleanField(default=False)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ], + options={ + 'verbose_name': 'Instance Size', + 'verbose_name_plural': 'Instance Sizes', + 'ordering': ['-id'], + }, + ), + migrations.AddField( + model_name='image', + name='required_size', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='flavor.Size'), + ), + ] diff --git a/console/__init__.py b/backend/flavor/migrations/__init__.py similarity index 100% rename from console/__init__.py rename to backend/flavor/migrations/__init__.py diff --git a/backend/flavor/models.py b/backend/flavor/models.py new file mode 100644 index 0000000..3d11f7d --- /dev/null +++ b/backend/flavor/models.py @@ -0,0 +1,111 @@ +from django.db import models +from django.core.validators import validate_slug + + +class Size(models.Model): + + slug = models.CharField( + max_length=200, + validators=[validate_slug], + ) + + name = models.CharField( + max_length=255, + blank=True, + ) + + cpu = models.IntegerField( + default=1, + ) + + disk_bytes = models.BigIntegerField( + default=21474836480, + ) + + memory_bytes = models.BigIntegerField( + default=536870912, + ) + + is_active = models.BooleanField( + default=False, + ) + + created_at = models.DateTimeField( + auto_now_add=True, + ) + + is_deleted = models.BooleanField( + default=False, + ) + + deleted_at = models.DateTimeField( + blank=True, + null=True, + ) + + class Meta: + ordering = ['-id'] + verbose_name = "Instance Size" + verbose_name_plural = "Instance Sizes" + + def __unicode__(self): + return self.name + + def save(self): + if self.ram < 1 * (1024 ** 2): + self.ram = self.ram * (1024 ** 2) + if self.disk < 1 * (1024 ** 3): + self.disk = self.disk * (1024 ** 3) + super(Size, self).save() + + +class Image(models.Model): + + slug = models.SlugField( + max_length=200, + validators=[validate_slug], + ) + + name = models.CharField( + max_length=255, + ) + + url = models.URLField( + max_length=200, + ) + + md5sum = models.CharField( + max_length=50, + ) + + is_active = models.BooleanField( + default=False, + ) + + created_at = models.DateTimeField( + auto_now_add=True, + ) + + is_deleted = models.BooleanField( + default=False, + ) + + deleted_at = models.DateTimeField( + blank=True, + null=True, + ) + + required_size = models.ForeignKey( + Size, + blank=True, + null=True, + on_delete=models.CASCADE, + ) + + class Meta: + ordering = ['-id'] + verbose_name = "Instance Image" + verbose_name_plural = "Instance Images" + + def __unicode__(self): + return self.name diff --git a/console/tests.py b/backend/flavor/tests.py similarity index 100% rename from console/tests.py rename to backend/flavor/tests.py diff --git a/backend/flavor/views.py b/backend/flavor/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/flavor/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/console/migrations/__init__.py b/backend/instance/__init__.py similarity index 100% rename from console/migrations/__init__.py rename to backend/instance/__init__.py diff --git a/create/admin.py b/backend/instance/admin.py similarity index 100% rename from create/admin.py rename to backend/instance/admin.py diff --git a/backend/instance/apps.py b/backend/instance/apps.py new file mode 100644 index 0000000..ba6bd60 --- /dev/null +++ b/backend/instance/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class InstanceConfig(AppConfig): + name = 'instance' diff --git a/create/__init__.py b/backend/instance/migrations/__init__.py similarity index 100% rename from create/__init__.py rename to backend/instance/migrations/__init__.py diff --git a/backend/instance/models.py b/backend/instance/models.py new file mode 100644 index 0000000..a6aa8db --- /dev/null +++ b/backend/instance/models.py @@ -0,0 +1,107 @@ +from django.db import models +from django.conf import settings + +from region.models import Region +from compute.models import Compute +from flavor.models import Size, Image + + +class Instance(models.Model): + + ACTIVE = 1 + INACTIVE = 0 + STATUS_CHOICES = ( + (ACTIVE, 'active'), + (INACTIVE, 'inactive'), + ) + + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + ) + + name = models.CharField( + max_length=255, + ) + + status = models.IntegerField( + choices=STATUS_CHOICES, + default=INACTIVE, + ) + + uuid = models.CharField( + max_length=40, + blank=True, + null=True, + ) + + compute = models.ForeignKey( + Compute, + blank=True, + null=True, + ) + + size = models.ForeignKey( + Size, + ) + + image = models.ForeignKey( + Image, + on_delete=models.CASCADE, + ) + + region = models.ForeignKey( + Region, + on_delete=models.CASCADE, + ) + + disk_bytes = models.BigIntegerField( + default=21474836480, + ) + + created_at = models.DateTimeField( + auto_now_add=True, + ) + + deleted_at = models.DateTimeField( + blank=True, + null=True, + ) + + is_task = models.BooleanField( + default=True, + ) + + task_id = models.IntegerField( + default=1, + ) + + is_private_network = models.BooleanField( + default=False, + ) + + is_locked = models.BooleanField( + default=False, + ) + + is_deleted = models.BooleanField( + default=False, + ) + + public_net_mac = models.CharField( + max_length=18, + default="52:54:00:01:02:03", + ) + + private_net_mac = models.CharField( + max_length=18, + blank=True, + null=True, + ) + + class Meta: + ordering = ['-id'] + verbose_name = "Instance" + verbose_name_plural = "Instances" + + def __unicode__(self): + return str(self.name) diff --git a/create/tests.py b/backend/instance/tests.py similarity index 100% rename from create/tests.py rename to backend/instance/tests.py diff --git a/backend/instance/views.py b/backend/instance/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/instance/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/libvmgr/__init__.py b/backend/libvmgr/__init__.py new file mode 100644 index 0000000..edaaf58 --- /dev/null +++ b/backend/libvmgr/__init__.py @@ -0,0 +1,2 @@ +__author__ = "Anatoliy Guskov" +__license__ = "Apache 2.0" \ No newline at end of file diff --git a/vrtManager/connection.py b/backend/libvmgr/connect.py similarity index 56% rename from vrtManager/connection.py rename to backend/libvmgr/connect.py index 65c2915..47cf2e5 100644 --- a/vrtManager/connection.py +++ b/backend/libvmgr/connect.py @@ -1,9 +1,8 @@ import libvirt import threading import socket -from vrtManager import util -from rwlock import ReadWriteLock -from django.conf import settings +from libvmgr import util +from libvmgr.rwlock import ReadWriteLock from libvirt import libvirtError @@ -14,9 +13,36 @@ CONN_TCP = 1 TLS_PORT = 16514 SSH_PORT = 22 TCP_PORT = 16509 +KEEPALIVE_COUNT = 30 +KEEPALIVE_INTERVAL = 5 -class wvmEventLoop(threading.Thread): +def host_is_up(conn_type, hostname): + """ + returns True if the given host is up and we are able to establish + a connection using the given credentials. + """ + try: + socket_host = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + socket_host.settimeout(2) + if conn_type == CONN_SSH: + if ':' in hostname: + LIBVIRT_HOST, PORT = (hostname).split(":") + PORT = int(PORT) + else: + PORT = SSH_PORT + LIBVIRT_HOST = hostname + socket_host.connect((LIBVIRT_HOST, PORT)) + if conn_type == CONN_TCP: + socket_host.connect((hostname, TCP_PORT)) + if conn_type == CONN_TLS: + socket_host.connect((hostname, TLS_PORT)) + socket_host.close() + except socket.error: + raise libvirtError('Unable to connect to host server: Operation timed out') + + +class wvcEventLoop(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs={}): # register the default event implementation # of libvirt, as we do not have an existing @@ -26,7 +52,7 @@ class wvmEventLoop(threading.Thread): if name is None: name = 'libvirt event loop' - super(wvmEventLoop, self).__init__(group, target, name, args, kwargs) + super(wvcEventLoop, self).__init__(group, target, name, args, kwargs) # we run this thread in deamon mode, so it does # not block shutdown of the server @@ -40,7 +66,7 @@ class wvmEventLoop(threading.Thread): libvirt.virEventRunDefaultImpl() -class wvmConnection(object): +class wvcConnection(object): """ class representing a single connection stored in the Connection Manager # to-do: may also need some locking to ensure to not connect simultaniously in 2 threads @@ -86,10 +112,11 @@ class wvmConnection(object): # * set keep alive interval # * set connection close/fail handler try: - self.connection.setKeepAlive(connection_manager.keepalive_interval, connection_manager.keepalive_count) + self.connection.setKeepAlive(connection_manager.keepalive_interval, + connection_manager.keepalive_count) try: self.connection.registerCloseCallback(self.__connection_close_callback, None) - except: + except Exception: # Temporary fix for libvirt > libvirt-0.10.2-41 pass except libvirtError as e: @@ -124,16 +151,7 @@ class wvmConnection(object): # on server shutdown libvirt module gets freed before the close callbacks are called # so we just check here if it is still present if libvirt is not None: - if (reason == libvirt.VIR_CONNECT_CLOSE_REASON_ERROR): - self.last_error = 'connection closed: Misc I/O error' - elif (reason == libvirt.VIR_CONNECT_CLOSE_REASON_EOF): - self.last_error = 'connection closed: End-of-file from server' - elif (reason == libvirt.VIR_CONNECT_CLOSE_REASON_KEEPALIVE): - self.last_error = 'connection closed: Keepalive timer triggered' - elif (reason == libvirt.VIR_CONNECT_CLOSE_REASON_CLIENT): - self.last_error = 'connection closed: Client requested it' - else: - self.last_error = 'connection closed: Unknown error' + self.last_error = 'Connection closed' # prevent other threads from using the connection (in the future) self.connection = None @@ -211,7 +229,7 @@ class wvmConnection(object): # unregister callback (as it is no longer valid if this instance gets deleted) try: self.connection.unregisterCloseCallback() - except: + except Exception: pass def __unicode__(self): @@ -227,10 +245,10 @@ class wvmConnection(object): return u'qemu+{type}://{user}@{host}/system'.format(type=type_str, user=self.login, host=self.host) def __repr__(self): - return ''.format(connection_str=unicode(self)) + return ''.format(connection_str=str(self)) -class wvmConnectionManager(object): +class wvcConnectionManager(object): def __init__(self, keepalive_interval=5, keepalive_count=5): self.keepalive_interval = keepalive_interval self.keepalive_count = keepalive_count @@ -245,7 +263,7 @@ class wvmConnectionManager(object): self._connections_lock = ReadWriteLock() # start event loop to handle keepalive requests and other events - self._event_loop = wvmEventLoop() + self._event_loop = wvcEventLoop() self._event_loop.start() def _search_connection(self, host, login, passwd, conn): @@ -271,10 +289,10 @@ class wvmConnectionManager(object): returns a connection object (as returned by the libvirt.open* methods) for the given host and credentials raises libvirtError if (re)connecting fails """ - # force all string values to unicode - host = unicode(host) - login = unicode(login) - passwd = unicode(passwd) if passwd is not None else None + # force all string values to unicode changed for Python3 to str + host = str(host) + login = str(login) + passwd = str(passwd) if passwd is not None else None connection = self._search_connection(host, login, passwd, conn) @@ -286,7 +304,7 @@ class wvmConnectionManager(object): connection = self._search_connection(host, login, passwd, conn) if (connection is None): # create a new connection if a matching connection does not already exist - connection = wvmConnection(host, login, passwd, conn) + connection = wvcConnection(host, login, passwd, conn) # add new connection to connection dict if host in self._connections: @@ -307,268 +325,175 @@ class wvmConnectionManager(object): # raise libvirt error raise libvirtError(connection.last_error) - def host_is_up(self, conn_type, hostname): - """ - returns True if the given host is up and we are able to establish - a connection using the given credentials. - """ - try: - socket_host = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - socket_host.settimeout(1) - if conn_type == CONN_SSH: - if ':' in hostname: - LIBVIRT_HOST, PORT = (hostname).split(":") - PORT = int(PORT) - else: - PORT = SSH_PORT - LIBVIRT_HOST = hostname - socket_host.connect((LIBVIRT_HOST, PORT)) - if conn_type == CONN_TCP: - socket_host.connect((hostname, TCP_PORT)) - if conn_type == CONN_TLS: - socket_host.connect((hostname, TLS_PORT)) - socket_host.close() - return True - except Exception as err: - return err -connection_manager = wvmConnectionManager( - settings.LIBVIRT_KEEPALIVE_INTERVAL if hasattr(settings, 'LIBVIRT_KEEPALIVE_INTERVAL') else 5, - settings.LIBVIRT_KEEPALIVE_COUNT if hasattr(settings, 'LIBVIRT_KEEPALIVE_COUNT') else 5 -) +connection_manager = wvcConnectionManager(KEEPALIVE_INTERVAL, KEEPALIVE_COUNT) -class wvmConnect(object): - def __init__(self, host, login, passwd, conn): +class wvcConnect(object): + def __init__(self, host, login=None, passwd=None, conn_type=CONN_SOCKET, keepalive=True): self.login = login self.host = host self.passwd = passwd - self.conn = conn + self.conn_type = conn_type + self.keepalive = keepalive + + # is host up? + host_is_up(self.conn_type, self.host) # get connection from connection manager - self.wvm = connection_manager.get_connection(host, login, passwd, conn) + if self.keepalive: + self.conn = connection_manager.get_connection(host, login, passwd, conn_type) + else: + if self.conn_type == CONN_TCP: + def creds(credentials, user_data): + for credential in credentials: + if credential[0] == libvirt.VIR_CRED_AUTHNAME: + credential[4] = self.login + if len(credential[4]) == 0: + credential[4] = credential[3] + elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: + credential[4] = self.passwd + else: + return -1 + return 0 + + flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE] + auth = [flags, creds, None] + uri = 'qemu+tcp://%s/system' % self.host + try: + self.conn = libvirt.openAuth(uri, auth, 0) + except libvirtError: + raise libvirtError('Connection Failed') + + if self.conn_type == CONN_SSH: + uri = 'qemu+ssh://%s@%s/system' % (self.login, self.host) + try: + self.conn = libvirt.open(uri) + except libvirtError as err: + raise err + + if self.conn_type == CONN_TLS: + def creds(credentials, user_data): + for credential in credentials: + if credential[0] == libvirt.VIR_CRED_AUTHNAME: + credential[4] = self.login + if len(credential[4]) == 0: + credential[4] = credential[3] + elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: + credential[4] = self.passwd + else: + return -1 + return 0 + + flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE] + auth = [flags, creds, None] + uri = 'qemu+tls://%s@%s/system' % (self.login, self.host) + try: + self.conn = libvirt.openAuth(uri, auth, 0) + except libvirtError: + raise libvirtError('Connection Failed') def get_cap_xml(self): """Return xml capabilities""" - return self.wvm.getCapabilities() - - def get_dom_cap_xml(self): - """ Return domcapabilities xml""" - emulatorbin = self.emulator() - machine = self.machine() - arch = self.wvm.getInfo()[0] - virttype = self.hypervisor_type()[arch][0] - return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype) + return self.conn.getCapabilities() def is_kvm_supported(self): """Return KVM capabilities.""" return util.is_kvm_available(self.get_cap_xml()) - def get_storages(self, only_actives=False): + def get_storages(self): storages = [] - for pool in self.wvm.listStoragePools(): + for pool in self.conn.listStoragePools(): + storages.append(pool) + for pool in self.conn.listDefinedStoragePools(): storages.append(pool) - if not only_actives: - for pool in self.wvm.listDefinedStoragePools(): - storages.append(pool) return storages def get_networks(self): virtnet = [] - for net in self.wvm.listNetworks(): + for net in self.conn.listNetworks(): virtnet.append(net) - for net in self.wvm.listDefinedNetworks(): + for net in self.conn.listDefinedNetworks(): virtnet.append(net) return virtnet def get_ifaces(self): interface = [] - for inface in self.wvm.listInterfaces(): + for inface in self.conn.listInterfaces(): interface.append(inface) - for inface in self.wvm.listDefinedInterfaces(): + for inface in self.conn.listDefinedInterfaces(): interface.append(inface) return interface - def get_cache_modes(self): - """Get cache available modes""" - return { - 'default': 'Default', - 'none': 'Disabled', - 'writethrough': 'Write through', - 'writeback': 'Write back', - 'directsync': 'Direct sync', # since libvirt 0.9.5 - 'unsafe': 'Unsafe', # since libvirt 0.9.7 - } - - def hypervisor_type(self): - """Return hypervisor type""" - def hypervisors(ctx): - result = {} - for arch in ctx.xpath('/capabilities/guest/arch'): - domain_types = arch.xpath('domain/@type') - arch_name = arch.xpath('@name')[0] - result[arch_name]= domain_types - return result - return util.get_xml_path(self.get_cap_xml(), func=hypervisors) - - def emulator(self): - """Return emulator """ - return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/emulator") - - def machine(self): - """ Return machine type of emulation""" - return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/machine") - - def get_busses(self): - """Get available busses""" - - def get_bus_list(ctx): - result = [] - for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'): - if disk_enum.xpath("@name")[0] == "bus": - for values in disk_enum: result.append(values.text) - return result - - # return [ 'ide', 'scsi', 'usb', 'virtio' ] - return util.get_xml_path(self.get_dom_cap_xml(), func=get_bus_list) - - - def get_image_formats(self): - """Get available image formats""" - return [ 'raw', 'qcow', 'qcow2' ] - - def get_file_extensions(self): - """Get available image filename extensions""" - return [ 'img', 'qcow', 'qcow2' ] - - def get_video(self): - """ Get available graphics video types """ - - def get_video_list(ctx): - result = [] - for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'): - if video_enum.xpath("@name")[0] == "modelType": - for values in video_enum: result.append(values.text) - return result - return util.get_xml_path(self.get_dom_cap_xml(),func=get_video_list) - def get_iface(self, name): - return self.wvm.interfaceLookupByName(name) + return self.conn.interfaceLookupByName(name) def get_secrets(self): - return self.wvm.listSecrets() + return self.conn.listSecrets() def get_secret(self, uuid): - return self.wvm.secretLookupByUUIDString(uuid) + return self.conn.secretLookupByUUIDString(uuid) def get_storage(self, name): - return self.wvm.storagePoolLookupByName(name) + return self.conn.storagePoolLookupByName(name) def get_volume_by_path(self, path): - return self.wvm.storageVolLookupByPath(path) + return self.conn.storageVolLookupByPath(path) def get_network(self, net): - return self.wvm.networkLookupByName(net) + return self.conn.networkLookupByName(net) def get_instance(self, name): - return self.wvm.lookupByName(name) + return self.conn.lookupByName(name) + + def get_instance_status(self, name): + dom = self.conn.lookupByName(name) + return dom.info()[0] def get_instances(self): instances = [] - for inst_id in self.wvm.listDomainsID(): - dom = self.wvm.lookupByID(int(inst_id)) + for inst_id in self.conn.listDomainsID(): + dom = self.conn.lookupByID(int(inst_id)) instances.append(dom.name()) - for name in self.wvm.listDefinedDomains(): + for name in self.conn.listDefinedDomains(): instances.append(name) return instances def get_snapshots(self): instance = [] - for snap_id in self.wvm.listDomainsID(): - dom = self.wvm.lookupByID(int(snap_id)) + for snap_id in self.conn.listDomainsID(): + dom = self.conn.lookupByID(int(snap_id)) if dom.snapshotNum(0) != 0: instance.append(dom.name()) - for name in self.wvm.listDefinedDomains(): - dom = self.wvm.lookupByName(name) + for name in self.conn.listDefinedDomains(): + dom = self.conn.lookupByName(name) if dom.snapshotNum(0) != 0: instance.append(dom.name()) return instance def get_net_device(self): netdevice = [] - - def get_info(doc): - dev_type = util.get_xpath(doc, '/device/capability/@type') - interface = util.get_xpath(doc, '/device/capability/interface') - return dev_type, interface - - for dev in self.wvm.listAllDevices(0): + for dev in self.conn.listAllDevices(0): xml = dev.XMLDesc(0) - (dev_type, interface) = util.get_xml_path(xml, func=get_info) - if dev_type == 'net': - netdevice.append(interface) + if util.get_xml_data(xml, 'capability', 'type') == 'net': + netdevice.append(util.get_xml_data(xml, 'capability/interface')) return netdevice - def get_host_instances(self, raw_mem_size=False): + def get_host_instances(self): vname = {} - def get_info(doc): - mem = util.get_xpath(doc, "/domain/currentMemory") - mem = int(mem) / 1024 - if raw_mem_size: - mem = int(mem) * (1024*1024) - cur_vcpu = util.get_xpath(doc, "/domain/vcpu/@current") - if cur_vcpu: - vcpu = cur_vcpu - else: - vcpu = util.get_xpath(doc, "/domain/vcpu") - title = util.get_xpath(doc, "/domain/title") - title = title if title else '' - description = util.get_xpath(doc, "/domain/description") - description = description if description else '' - return (mem, vcpu, title, description) for name in self.get_instances(): dom = self.get_instance(name) - xml = dom.XMLDesc(0) - (mem, vcpu, title, description) = util.get_xml_path(xml, func=get_info) - vname[dom.name()] = { - 'status': dom.info()[0], - 'uuid': dom.UUIDString(), - 'vcpu': vcpu, - 'memory': mem, - 'title': title, - 'description': description, - } - return vname - - def get_user_instances(self, name): - dom = self.get_instance(name) - xml = dom.XMLDesc(0) - def get_info(ctx): - mem = util.get_xpath(ctx, "/domain/currentMemory") - mem = int(mem) / 1024 - cur_vcpu = util.get_xpath(ctx, "/domain/vcpu/@current") + mem = util.get_xml_data(dom.XMLDesc(0), 'currentMemory') + mem = round(int(mem) / 1024) + cur_vcpu = util.get_xml_data(dom.XMLDesc(0), 'vcpu', 'current') if cur_vcpu: vcpu = cur_vcpu else: - vcpu = util.get_xpath(ctx, "/domain/vcpu") - title = util.get_xpath(ctx, "/domain/title") - title = title if title else '' - description = util.get_xpath(ctx, "/domain/description") - description = description if description else '' - return (mem, vcpu, title, description) - (mem, vcpu, title, description) = util.get_xml_path(xml, func=get_info) - return { - 'name': dom.name(), - 'status': dom.info()[0], - 'uuid': dom.UUIDString(), - 'vcpu': vcpu, - 'memory': mem, - 'title': title, - 'description': description, - } + vcpu = util.get_xml_data(dom.XMLDesc(0), 'vcpu') + vname[dom.name()] = {'status': dom.info()[0], 'uuid': dom.UUIDString(), 'vcpu': vcpu, 'memory': mem} + return vname def close(self): """Close connection""" - # to-do: do not close connection ;) - # self.wvm.close() - pass + if not self.keepalive: + self.conn.close() diff --git a/backend/libvmgr/host.py b/backend/libvmgr/host.py new file mode 100644 index 0000000..22a6b0a --- /dev/null +++ b/backend/libvmgr/host.py @@ -0,0 +1,81 @@ +import time +from libvmgr import util +from libvmgr.connect import wvcConnect + + +class wvcHost(wvcConnect): + def __init__(self, conn): + self.conn = conn + + def get_node_info(self): + """ + Function return host server information: hostname, cpu, memory, ... + """ + info = list() + info.append(self.conn.getHostname()) + info.append(self.conn.getInfo()[0]) + info.append(self.conn.getInfo()[1] * (1024**2)) + info.append(self.conn.getInfo()[2]) + info.append(util.get_xml_data(self.conn.getSysinfo(0), 'processor/entry[6]')) + info.append(self.conn.getURI()) + return info + + def hypervisor_type(self): + """ + Return hypervisor type + """ + return util.get_xml_data(self.get_cap_xml(), 'guest/arch/domain', 'type') + + def get_memory_usage(self): + """ + Function return memory usage on node. + """ + host_mem = self.conn.getInfo()[1] * (1024**2) + free_mem = self.conn.getMemoryStats(-1, 0) + if isinstance(free_mem, dict): + mem = list(free_mem.values()) + free = (mem[1] + mem[2] + mem[3]) * 1024 + percent = (100 - ((free * 100) / host_mem)) + usage = (host_mem - free) + mem_usage = {'size': host_mem, 'usage': usage, 'percent': round(percent)} + else: + mem_usage = {'size': 0, 'usage': 0, 'percent': 0} + return mem_usage + + def get_storage_usage(self, name): + """ + Function return storage usage on node by name. + """ + pool = self.get_storage(name) + pool.refresh() + if pool.isActive(): + size = pool.info()[1] + free = pool.info()[3] + used = size - free + percent = (used * 100) / size + return {'size': size, 'used': used, 'percent': percent} + else: + return {'size': 0, 'used': 0, 'percent': 0} + + def get_cpu_usage(self): + """ + Function return cpu usage on node. + """ + prev_idle = 0 + prev_total = 0 + diff_usage = 0 + cpu = self.conn.getCPUStats(-1, 0) + if isinstance(cpu, dict): + for num in range(2): + idle = self.conn.getCPUStats(-1, 0)['idle'] + total = sum(self.conn.getCPUStats(-1, 0).values()) + diff_idle = idle - prev_idle + diff_total = total - prev_total + diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 + prev_total = total + prev_idle = idle + if num == 0: + time.sleep(1) + if diff_usage < 0: + diff_usage = 0 + return {'usage': round(diff_usage)} diff --git a/vrtManager/rwlock.py b/backend/libvmgr/rwlock.py similarity index 100% rename from vrtManager/rwlock.py rename to backend/libvmgr/rwlock.py diff --git a/vrtManager/util.py b/backend/libvmgr/util.py similarity index 50% rename from vrtManager/util.py rename to backend/libvmgr/util.py index 795b922..e0c2f03 100644 --- a/vrtManager/util.py +++ b/backend/libvmgr/util.py @@ -1,15 +1,20 @@ +import re +import socket import random -import lxml.etree as etree import libvirt -import string +import paramiko +from time import sleep +from xml.etree import ElementTree +from string import ascii_letters, digits +from passlib.hash import sha512_crypt def is_kvm_available(xml): - kvm_domains = get_xml_path(xml, "//domain/@type='kvm'") - if kvm_domains > 0: - return True - else: - return False + tree = ElementTree.fromstring(xml) + for dom in tree.findall('guest/arch/domain'): + if 'kvm' in dom.get('type'): + return True + return False def randomMAC(): @@ -30,11 +35,6 @@ def randomUUID(): return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u) -def randomPasswd(length=12, alphabet=string.letters + string.digits): - """Generate a random password""" - return ''.join([random.choice(alphabet) for i in xrange(length)]) - - def get_max_vcpus(conn, type=None): """@param conn: libvirt connection to poll for max possible vcpus @type type: optional guest type (kvm, etc.)""" @@ -71,7 +71,7 @@ def compareMAC(p, q): else: return -1 - for i in xrange(len(pa)): + for i in range(len(pa)): n = int(pa[i], 0x10) - int(qa[i], 0x10) if n > 0: return 1 @@ -80,40 +80,28 @@ def compareMAC(p, q): return 0 -def get_xml_path(xml, path=None, func=None): - """ - Return the content from the passed xml xpath, or return the result - of a passed function (receives xpathContext as its only arg) - """ - doc = None - result = None +def get_xml_data(xml, path=None, element=None): + res = '' + if not path and not element: + return '' - doc = etree.fromstring(xml) + tree = ElementTree.fromstring(xml) if path: - result = get_xpath(doc, path) - elif func: - result = func(doc) - + child = tree.find(path) + if child is not None: + if element: + res = child.get(element) + else: + res = child.text else: - raise ValueError("'path' or 'func' is required.") - return result + res = tree.get(element) + return res -def get_xpath(doc, path): - result = None +def get_xml_findall(xml, string): + tree = ElementTree.fromstring(xml) + return tree.findall(string) - ret = doc.xpath(path) - if ret is not None: - if type(ret) == list: - if len(ret) >= 1: - if hasattr(ret[0],'text'): - result = ret[0].text - else: - result = ret[0] - else: - result = ret - - return result def pretty_mem(val): val = int(val) @@ -129,3 +117,43 @@ def pretty_bytes(val): return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0)) else: return "%2.2f MB" % (val / (1024.0 * 1024.0)) + + +def gen_password(length=14): + password = ''.join( + [random.choice(ascii_letters + digits) for dummy in range(length)] + ) + return password + + +def password_to_hash(password): + salt = gen_password(8) + password_hash = sha512_crypt.encrypt(password, salt=salt, rounds=5000) + return password_hash + + +def similar_name(pattern, names): + res = [] + for name in names: + match = re.match(pattern, name) + if match: + res.append(name) + return res + + +def check_ssh_connection(hostname, password, username='root', timeout=90): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + for i in range(timeout): + try: + ssh.connect(hostname, username=username, password=password) + ssh.close() + return True + except (paramiko.BadHostKeyException, + paramiko.AuthenticationException, + paramiko.SSHException, + socket.error, + EOFError): + sleep(1) + return False diff --git a/backend/manage.py b/backend/manage.py new file mode 100755 index 0000000..16a3212 --- /dev/null +++ b/backend/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webvirtcloud.settings.prod') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) diff --git a/create/migrations/__init__.py b/backend/network/__init__.py similarity index 100% rename from create/migrations/__init__.py rename to backend/network/__init__.py diff --git a/datasource/admin.py b/backend/network/admin.py similarity index 100% rename from datasource/admin.py rename to backend/network/admin.py diff --git a/backend/network/apps.py b/backend/network/apps.py new file mode 100644 index 0000000..c6871c0 --- /dev/null +++ b/backend/network/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class NetworkConfig(AppConfig): + name = 'network' diff --git a/datasource/__init__.py b/backend/network/migrations/__init__.py similarity index 100% rename from datasource/__init__.py rename to backend/network/migrations/__init__.py diff --git a/datasource/models.py b/backend/network/models.py similarity index 100% rename from datasource/models.py rename to backend/network/models.py diff --git a/datasource/tests.py b/backend/network/tests.py similarity index 100% rename from datasource/tests.py rename to backend/network/tests.py diff --git a/backend/network/views.py b/backend/network/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/network/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/datasource/migrations/__init__.py b/backend/region/__init__.py similarity index 100% rename from datasource/migrations/__init__.py rename to backend/region/__init__.py diff --git a/instances/admin.py b/backend/region/admin.py similarity index 100% rename from instances/admin.py rename to backend/region/admin.py diff --git a/backend/region/apps.py b/backend/region/apps.py new file mode 100644 index 0000000..b8570c1 --- /dev/null +++ b/backend/region/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class RegionConfig(AppConfig): + name = 'region' diff --git a/backend/region/migrations/0001_initial.py b/backend/region/migrations/0001_initial.py new file mode 100644 index 0000000..524d758 --- /dev/null +++ b/backend/region/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 2.0.8 on 2018-09-22 07:20 + +import django.core.validators +from django.db import migrations, models +import re + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Region', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(max_length=200, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')])), + ('name', models.CharField(blank=True, max_length=255)), + ('is_active', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('is_deleted', models.BooleanField(default=False)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ], + options={ + 'verbose_name': 'Region', + 'verbose_name_plural': 'Regions', + 'ordering': ['-id'], + }, + ), + ] diff --git a/instances/__init__.py b/backend/region/migrations/__init__.py similarity index 100% rename from instances/__init__.py rename to backend/region/migrations/__init__.py diff --git a/backend/region/models.py b/backend/region/models.py new file mode 100644 index 0000000..b3fe53a --- /dev/null +++ b/backend/region/models.py @@ -0,0 +1,40 @@ +from django.db import models +from django.core.validators import validate_slug + + +class Region(models.Model): + + slug = models.SlugField( + max_length=200, + validators=[validate_slug], + ) + + name = models.CharField( + max_length=255, + blank=True, + ) + + is_active = models.BooleanField( + default=False, + ) + + created_at = models.DateTimeField( + auto_now_add=True, + ) + + is_deleted = models.BooleanField( + default=False + ) + + deleted_at = models.DateTimeField( + blank=True, + null=True, + ) + + class Meta: + ordering = ['-id'] + verbose_name = "Region" + verbose_name_plural = "Regions" + + def __unicode__(self): + return self.name diff --git a/instances/tests.py b/backend/region/tests.py similarity index 100% rename from instances/tests.py rename to backend/region/tests.py diff --git a/backend/region/views.py b/backend/region/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/region/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/requirements-dev.txt b/backend/requirements-dev.txt new file mode 100644 index 0000000..5184cb6 --- /dev/null +++ b/backend/requirements-dev.txt @@ -0,0 +1,6 @@ +-r requirements.txt +flake8 +bpython +django-cors-headers +django-rest-swagger +django-debug-toolbar diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..f5b0d54 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,8 @@ +Django==2.0.8 +djangorestframework==3.8.2 +libvirt-python==4.6.0 +passlib==1.7.1 +paramiko==2.4.1 +django-rest-auth==0.9.3 +django-allauth==0.37.1 +mysqlclient==1.3.13 \ No newline at end of file diff --git a/instances/migrations/__init__.py b/backend/support/__init__.py similarity index 100% rename from instances/migrations/__init__.py rename to backend/support/__init__.py diff --git a/interfaces/admin.py b/backend/support/admin.py similarity index 100% rename from interfaces/admin.py rename to backend/support/admin.py diff --git a/backend/support/apps.py b/backend/support/apps.py new file mode 100644 index 0000000..4cfed92 --- /dev/null +++ b/backend/support/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SupportConfig(AppConfig): + name = 'support' diff --git a/instances/templatetags/__init__.py b/backend/support/migrations/__init__.py similarity index 100% rename from instances/templatetags/__init__.py rename to backend/support/migrations/__init__.py diff --git a/interfaces/models.py b/backend/support/models.py similarity index 100% rename from interfaces/models.py rename to backend/support/models.py diff --git a/interfaces/tests.py b/backend/support/tests.py similarity index 100% rename from interfaces/tests.py rename to backend/support/tests.py diff --git a/backend/support/views.py b/backend/support/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/support/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/interfaces/__init__.py b/backend/user/__init__.py similarity index 100% rename from interfaces/__init__.py rename to backend/user/__init__.py diff --git a/logs/admin.py b/backend/user/admin.py similarity index 100% rename from logs/admin.py rename to backend/user/admin.py diff --git a/backend/user/apps.py b/backend/user/apps.py new file mode 100644 index 0000000..35048d4 --- /dev/null +++ b/backend/user/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UserConfig(AppConfig): + name = 'user' diff --git a/interfaces/migrations/__init__.py b/backend/user/migrations/__init__.py similarity index 100% rename from interfaces/migrations/__init__.py rename to backend/user/migrations/__init__.py diff --git a/networks/models.py b/backend/user/models.py similarity index 100% rename from networks/models.py rename to backend/user/models.py diff --git a/backend/user/serializers.py b/backend/user/serializers.py new file mode 100644 index 0000000..cb9552a --- /dev/null +++ b/backend/user/serializers.py @@ -0,0 +1,10 @@ +from rest_framework import serializers + +from django.contrib.auth.models import User + + +class UserSerializer(serializers.ModelSerializer): + + class Meta: + fields = ('id', 'email', 'username', 'first_name', 'last_name', 'date_joined', 'last_login', 'is_active') + model = User diff --git a/logs/tests.py b/backend/user/tests.py similarity index 100% rename from logs/tests.py rename to backend/user/tests.py diff --git a/backend/user/urls.py b/backend/user/urls.py new file mode 100644 index 0000000..0e000fd --- /dev/null +++ b/backend/user/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from .views import * + +urlpatterns = [ + path('', UserView.as_view(), name="user_view"), +] \ No newline at end of file diff --git a/backend/user/views.py b/backend/user/views.py new file mode 100644 index 0000000..979b8f0 --- /dev/null +++ b/backend/user/views.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals + +from django.contrib.auth.models import User + +from rest_framework.generics import RetrieveUpdateAPIView +from .serializers import UserSerializer + +class UserView(RetrieveUpdateAPIView): + serializer_class = UserSerializer + queryset = User.objects.all() + + def get_object(self): + return self.queryset.get(pk=self.request.user.id) diff --git a/logs/__init__.py b/backend/webvirtcloud/__init__.py similarity index 100% rename from logs/__init__.py rename to backend/webvirtcloud/__init__.py diff --git a/logs/migrations/__init__.py b/backend/webvirtcloud/settings/__init__.py similarity index 100% rename from logs/migrations/__init__.py rename to backend/webvirtcloud/settings/__init__.py diff --git a/backend/webvirtcloud/settings/base.py b/backend/webvirtcloud/settings/base.py new file mode 100644 index 0000000..2fb4e66 --- /dev/null +++ b/backend/webvirtcloud/settings/base.py @@ -0,0 +1,144 @@ +""" +Django settings for webvirtcloud project. +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'ky-fn$j*x6$tair==i626b%$7^o5@7^a$p)lol(3dp*4na^u8^' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = False + +ALLOWED_HOSTS = ['*'] + +SITE_ID = 1 + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.sites', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +# Project application + +INSTALLED_APPS += [ + 'user', + 'region', + 'flavor', + 'compute', +] + +# Third party application + +INSTALLED_APPS += [ + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + 'rest_framework', + 'rest_framework.authtoken', + 'rest_auth', + 'rest_auth.registration', +] + +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', +) + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'webvirtcloud.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': ['templates'], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'webvirtcloud.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'webvirtcloud', + 'USER': 'root', + 'PASSWORD': 'root', + 'HOST': 'localhsot', + 'OPTIONS': { + 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", + } + } +} + +# Password validation +# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/2.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.1/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/backend/webvirtcloud/settings/dev.py b/backend/webvirtcloud/settings/dev.py new file mode 100644 index 0000000..375ebdb --- /dev/null +++ b/backend/webvirtcloud/settings/dev.py @@ -0,0 +1,68 @@ +""" +Django development settings for webvirtcloud project. + +""" +try: + from .base import * +except ImportError: + pass + +DEBUG = True +ADMIN_ENABLED = True + +ALLOWED_HOSTS = ['*'] + +INSTALLED_APPS += [ + 'corsheaders', + 'debug_toolbar', + 'rest_framework_swagger', +] + +MIDDLEWARE += [ + 'corsheaders.middleware.CorsMiddleware', + 'debug_toolbar.middleware.DebugToolbarMiddleware', +] + +# DebugToolBar +INTERNAL_IPS = ( + '127.0.0.1', +) +DEBUG_TOOLBAR_CONFIG = { + 'INTERCEPT_REDIRECTS': False, +} + +# CORS settings +CORS_ORIGIN_WHITELIST = ( + 'localhost:8080', + 'localhost:3000', +) + +# Swagger settings +SWAGGER_SETTINGS = { + 'LOGIN_URL': 'login', + 'LOGOUT_URL': 'logout', + 'VALIDATOR_URL': None, + 'JSON_EDITOR': True, + 'USE_SESSION_AUTH': True, + 'SHOW_REQUEST_HEADERS': True, + 'DOC_EXPANSION': 'list', + 'APIS_SORTER': 'alpha', + 'SECURITY_DEFINITIONS': None, +} + +# DB connections +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'webvirtcloud', + 'USER': os.environ.get('DB_USER', 'root'), + 'PASSWORD': os.environ.get('DB_PASSWORD', 'root'), + 'HOST': os.environ.get('DB_HOST', 'localhost'), + 'OPTIONS': { + 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", + }, + } +} + +# Celery settings +CELERY_BROKER_URL = os.environ.get('BROKER_URL', 'amqp://guest:guest@localhost:5672') diff --git a/backend/webvirtcloud/settings/prod.py b/backend/webvirtcloud/settings/prod.py new file mode 100644 index 0000000..19904da --- /dev/null +++ b/backend/webvirtcloud/settings/prod.py @@ -0,0 +1,27 @@ +""" +Django production settings for webvirtcloud project. +""" +try: + from .base import * +except ImportError: + pass + +DEBUG = False +ADMIN_ENABLED = True + +# DB connections +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'webvirtcloud', + 'USER': os.environ.get('DB_USER', 'root'), + 'PASSWORD': os.environ.get('DB_PASSWORD', 'root'), + 'HOST': os.environ.get('DB_HOST', 'localhost'), + 'OPTIONS': { + 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", + }, + } +} + +# Celery settings +CELERY_BROKER_URL = os.environ.get('BROKER_URL', 'amqp://guest:guest@localhost:5672') diff --git a/backend/webvirtcloud/urls.py b/backend/webvirtcloud/urls.py new file mode 100644 index 0000000..998fe11 --- /dev/null +++ b/backend/webvirtcloud/urls.py @@ -0,0 +1,24 @@ +"""webvirtcloud URL Configuration +""" +from django.contrib import admin +from django.urls import path, include +from django.conf import settings + +from rest_framework_swagger.views import get_swagger_view + +admin.site.site_header = "Webvirtcloud" + +api_urlpatterns = [ + path("rest-auth/registration/", include("rest_auth.registration.urls")), + path("rest-auth/", include("rest_auth.urls")), + path("user/", include("user.urls")) +] + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/v1/', include(api_urlpatterns)), +] + +if settings.DEBUG: + schema_view = get_swagger_view(title="Webvirtcloud API") + urlpatterns += [path('docs/', schema_view)] \ No newline at end of file diff --git a/webvirtcloud/wsgi.py b/backend/webvirtcloud/wsgi.py similarity index 64% rename from webvirtcloud/wsgi.py rename to backend/webvirtcloud/wsgi.py index 35ceb9b..1dc0ab4 100644 --- a/webvirtcloud/wsgi.py +++ b/backend/webvirtcloud/wsgi.py @@ -4,11 +4,13 @@ WSGI config for webvirtcloud project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see -https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ +https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ """ import os -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings") from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webvirtcloud.settings.production') + application = get_wsgi_application() diff --git a/computes/forms.py b/computes/forms.py deleted file mode 100644 index ff8e9b9..0000000 --- a/computes/forms.py +++ /dev/null @@ -1,170 +0,0 @@ -import re -from django import forms -from django.utils.translation import ugettext_lazy as _ -from computes.models import Compute - - -class ComputeAddTcpForm(forms.Form): - name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, - max_length=20) - hostname = forms.CharField(error_messages={'required': _('No IP / Domain name has been entered')}, - max_length=100) - login = forms.CharField(error_messages={'required': _('No login has been entered')}, - max_length=100) - password = forms.CharField(error_messages={'required': _('No password has been entered')}, - max_length=100) - details = forms.CharField(max_length=50, required=False) - - def clean_name(self): - name = self.cleaned_data['name'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', name) - if have_symbol: - raise forms.ValidationError(_('The host name must not contain any special characters')) - elif len(name) > 20: - raise forms.ValidationError(_('The host name must not exceed 20 characters')) - try: - Compute.objects.get(name=name) - except Compute.DoesNotExist: - return name - raise forms.ValidationError(_('This host is already connected')) - - def clean_hostname(self): - hostname = self.cleaned_data['hostname'] - have_symbol = re.match('[^a-z0-9.-]+', hostname) - wrong_ip = re.match('^0.|^255.', hostname) - if have_symbol: - raise forms.ValidationError(_('Hostname must contain only numbers, or the domain name separated by "."')) - elif wrong_ip: - raise forms.ValidationError(_('Wrong IP address')) - try: - Compute.objects.get(hostname=hostname) - except Compute.DoesNotExist: - return hostname - raise forms.ValidationError(_('This host is already connected')) - - -class ComputeAddSshForm(forms.Form): - name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, - max_length=20) - hostname = forms.CharField(error_messages={'required': _('No IP / Domain name has been entered')}, - max_length=100) - login = forms.CharField(error_messages={'required': _('No login has been entered')}, - max_length=20) - details = forms.CharField(max_length=50, required=False) - - def clean_name(self): - name = self.cleaned_data['name'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', name) - if have_symbol: - raise forms.ValidationError(_('The name of the host must not contain any special characters')) - elif len(name) > 20: - raise forms.ValidationError(_('The name of the host must not exceed 20 characters')) - try: - Compute.objects.get(name=name) - except Compute.DoesNotExist: - return name - raise forms.ValidationError(_('This host is already connected')) - - def clean_hostname(self): - hostname = self.cleaned_data['hostname'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', hostname) - wrong_ip = re.match('^0.|^255.', hostname) - if have_symbol: - raise forms.ValidationError(_('Hostname must contain only numbers, or the domain name separated by "."')) - elif wrong_ip: - raise forms.ValidationError(_('Wrong IP address')) - try: - Compute.objects.get(hostname=hostname) - except Compute.DoesNotExist: - return hostname - raise forms.ValidationError(_('This host is already connected')) - - -class ComputeAddTlsForm(forms.Form): - name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, - max_length=20) - hostname = forms.CharField(error_messages={'required': _('No IP / Domain name has been entered')}, - max_length=100) - login = forms.CharField(error_messages={'required': _('No login has been entered')}, - max_length=100) - password = forms.CharField(error_messages={'required': _('No password has been entered')}, - max_length=100) - details = forms.CharField(max_length=50, required=False) - - def clean_name(self): - name = self.cleaned_data['name'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', name) - if have_symbol: - raise forms.ValidationError(_('The host name must not contain any special characters')) - elif len(name) > 20: - raise forms.ValidationError(_('The host name must not exceed 20 characters')) - try: - Compute.objects.get(name=name) - except Compute.DoesNotExist: - return name - raise forms.ValidationError(_('This host is already connected')) - - def clean_hostname(self): - hostname = self.cleaned_data['hostname'] - have_symbol = re.match('[^a-z0-9.-]+', hostname) - wrong_ip = re.match('^0.|^255.', hostname) - if have_symbol: - raise forms.ValidationError(_('Hostname must contain only numbers, or the domain name separated by "."')) - elif wrong_ip: - raise forms.ValidationError(_('Wrong IP address')) - try: - Compute.objects.get(hostname=hostname) - except Compute.DoesNotExist: - return hostname - raise forms.ValidationError(_('This host is already connected')) - - -class ComputeEditHostForm(forms.Form): - host_id = forms.CharField() - name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, - max_length=20) - hostname = forms.CharField(error_messages={'required': _('No IP / Domain name has been entered')}, - max_length=100) - login = forms.CharField(error_messages={'required': _('No login has been entered')}, - max_length=100) - password = forms.CharField(max_length=100) - details = forms.CharField(max_length=50, required=False) - - def clean_name(self): - name = self.cleaned_data['name'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', name) - if have_symbol: - raise forms.ValidationError(_('The name of the host must not contain any special characters')) - elif len(name) > 20: - raise forms.ValidationError(_('The name of the host must not exceed 20 characters')) - return name - - def clean_hostname(self): - hostname = self.cleaned_data['hostname'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', hostname) - wrong_ip = re.match('^0.|^255.', hostname) - if have_symbol: - raise forms.ValidationError(_('Hostname must contain only numbers, or the domain name separated by "."')) - elif wrong_ip: - raise forms.ValidationError(_('Wrong IP address')) - return hostname - - -class ComputeAddSocketForm(forms.Form): - name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, - max_length=20) - details = forms.CharField(error_messages={'required': _('No details has been entred')}, - max_length=50) - - def clean_name(self): - name = self.cleaned_data['name'] - have_symbol = re.match('[^a-zA-Z0-9._-]+', name) - if have_symbol: - raise forms.ValidationError(_('The host name must not contain any special characters')) - elif len(name) > 20: - raise forms.ValidationError(_('The host name must not exceed 20 characters')) - try: - Compute.objects.get(name=name) - except Compute.DoesNotExist: - return name - raise forms.ValidationError(_('This host is already connected')) diff --git a/computes/migrations/0001_initial.py b/computes/migrations/0001_initial.py deleted file mode 100644 index e8d6139..0000000 --- a/computes/migrations/0001_initial.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Compute', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=20)), - ('hostname', models.CharField(max_length=20)), - ('login', models.CharField(max_length=20)), - ('password', models.CharField(max_length=14, null=True, blank=True)), - ('type', models.IntegerField()), - ], - options={ - }, - bases=(models.Model,), - ), - ] diff --git a/computes/migrations/0002_compute_details.py b/computes/migrations/0002_compute_details.py deleted file mode 100644 index 1e0fdf5..0000000 --- a/computes/migrations/0002_compute_details.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - -class Migration(migrations.Migration): - - dependencies = [ - ('computes', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='Compute', - name='details', - field=models.CharField(max_length=50, null=True, blank=True), - ), - ] diff --git a/computes/models.py b/computes/models.py deleted file mode 100644 index daedcab..0000000 --- a/computes/models.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.db import models - - -class Compute(models.Model): - name = models.CharField(max_length=20) - hostname = models.CharField(max_length=20) - login = models.CharField(max_length=20) - password = models.CharField(max_length=14, blank=True, null=True) - details = models.CharField(max_length=50, null=True, blank=True) - type = models.IntegerField() - - def __unicode__(self): - return self.hostname diff --git a/computes/templates/computes.html b/computes/templates/computes.html deleted file mode 100644 index 6233122..0000000 --- a/computes/templates/computes.html +++ /dev/null @@ -1,252 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% block title %}{% trans "Computes" %}{% endblock %} -{% block content %} - -
-
- {% include 'create_comp_block.html' %} -

{% trans "Computes" %}

-
-
- - - {% include 'errors_block.html' %} - -
- {% if computes_info %} - {% for compute in computes_info %} -
-
-
- {% ifequal compute.status 1 %} -

- {{ compute.name }} - - - -

- {% else %} -

{{ compute.name }} - - - -

- {% endifequal %} -
-
-
-
-

{% trans "Status:" %}

-
-
- {% if compute.status %} -

{% trans "Connected" %}

- {% else %} -

{% trans "Not Connected" %}

- {% endif %} - {% if compute.details %} -

{% trans compute.details %}

- {% else %} -

{% trans "No details available" %}

- {% endif %} -
-
- - - - -
-
-
- {% endfor %} - {% else %} -
-
- - {% trans "Warning:" %} {% trans "Hypervisor doesn't have any Computes" %} -
-
- {% endif %} -
-{% endblock %} diff --git a/computes/templates/create_comp_block.html b/computes/templates/create_comp_block.html deleted file mode 100644 index 734e4c1..0000000 --- a/computes/templates/create_comp_block.html +++ /dev/null @@ -1,185 +0,0 @@ -{% load i18n %} -{% if request.user.is_superuser %} - - - - - - -{% endif %} diff --git a/computes/templates/overview.html b/computes/templates/overview.html deleted file mode 100644 index 34e68c5..0000000 --- a/computes/templates/overview.html +++ /dev/null @@ -1,160 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% load staticfiles %} -{% block title %}{% trans "Overview" %} - {{ compute.name }}{% endblock %} -{% block content %} - -
-
-

{{ compute.name }}

- -
-
- - - {% include 'errors_block.html' %} - -
- -
-

{% trans "Hostname" %}

-

{% trans "Hypervisors" %}

-

{% trans "Emulator" %}

-

{% trans "Memory" %}

-

{% trans "Architecture" %}

-

{% trans "Logical CPUs" %}

-

{% trans "Processor" %}

-

{% trans "Connection" %}

-

{% trans "Details" %}

-
-
-

{{ hostname }}

-

{% for arch, hpv in hypervisor.items %} - - {{ arch }} - {% for h in hpv %} - {{ h }}{% endfor %} - {% endfor %} -

-

{{ emulator }}

-

{{ host_memory|filesizeformat }}

-

{{ host_arch }}

-

{{ logical_cpu }}

-

{{ model_cpu }}

-

{{ uri_conn }}

-

{{ compute.details }}

-
-
-
-
- -
-
-

{% trans "CPU utilization" %}

-
-
-
-
- -
-
-
-
-
-
-

{% trans "RAM utilization" %}

-
-
-
-
- -
-
-
-
-
-
-{% endblock %} -{% block script %} - - -{% endblock %} diff --git a/computes/urls.py b/computes/urls.py deleted file mode 100644 index 58a0497..0000000 --- a/computes/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.conf.urls import url -from . import views - -urlpatterns = [ - url(r'^$', views.computes, name='computes'), - url(r'^overview/(?P[0-9]+)/$', views.overview, name='overview'), - url(r'^statistics/(?P[0-9]+)/$', - views.compute_graph, name='compute_graph'), -] diff --git a/computes/views.py b/computes/views.py deleted file mode 100644 index 18ad680..0000000 --- a/computes/views.py +++ /dev/null @@ -1,227 +0,0 @@ -import time -import json -from django.http import HttpResponse, HttpResponseRedirect -from django.core.urlresolvers import reverse -from django.shortcuts import render, get_object_or_404 -from django.contrib.auth.decorators import login_required -from computes.models import Compute -from instances.models import Instance -from accounts.models import UserInstance -from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHostForm, ComputeAddTlsForm, ComputeAddSocketForm -from vrtManager.hostdetails import wvmHostDetails -from vrtManager.connection import CONN_SSH, CONN_TCP, CONN_TLS, CONN_SOCKET, connection_manager -from libvirt import libvirtError - - -@login_required -def computes(request): - """ - :param request: - :return: - """ - - if not request.user.is_superuser: - return HttpResponseRedirect(reverse('index')) - - def get_hosts_status(computes): - """ - Function return all hosts all vds on host - """ - compute_data = [] - for compute in computes: - compute_data.append({'id': compute.id, - 'name': compute.name, - 'hostname': compute.hostname, - 'status': connection_manager.host_is_up(compute.type, compute.hostname), - 'type': compute.type, - 'login': compute.login, - 'password': compute.password, - 'details': compute.details - }) - return compute_data - - error_messages = [] - computes = Compute.objects.filter().order_by('name') - computes_info = get_hosts_status(computes) - - if request.method == 'POST': - if 'host_del' in request.POST: - compute_id = request.POST.get('host_id', '') - try: - del_user_inst_on_host = UserInstance.objects.filter(instance__compute_id=compute_id) - del_user_inst_on_host.delete() - finally: - try: - del_inst_on_host = Instance.objects.filter(compute_id=compute_id) - del_inst_on_host.delete() - finally: - del_host = Compute.objects.get(id=compute_id) - del_host.delete() - return HttpResponseRedirect(request.get_full_path()) - if 'host_tcp_add' in request.POST: - form = ComputeAddTcpForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - new_tcp_host = Compute(name=data['name'], - hostname=data['hostname'], - type=CONN_TCP, - login=data['login'], - password=data['password'], - details=data['details']) - new_tcp_host.save() - return HttpResponseRedirect(request.get_full_path()) - else: - for msg_err in form.errors.values(): - error_messages.append(msg_err.as_text()) - if 'host_ssh_add' in request.POST: - form = ComputeAddSshForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - new_ssh_host = Compute(name=data['name'], - hostname=data['hostname'], - type=CONN_SSH, - login=data['login'], - details=data['details']) - new_ssh_host.save() - return HttpResponseRedirect(request.get_full_path()) - else: - for msg_err in form.errors.values(): - error_messages.append(msg_err.as_text()) - if 'host_tls_add' in request.POST: - form = ComputeAddTlsForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - new_tls_host = Compute(name=data['name'], - hostname=data['hostname'], - type=CONN_TLS, - login=data['login'], - password=data['password'], - details=data['details']) - new_tls_host.save() - return HttpResponseRedirect(request.get_full_path()) - else: - for msg_err in form.errors.values(): - error_messages.append(msg_err.as_text()) - if 'host_socket_add' in request.POST: - form = ComputeAddSocketForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - new_socket_host = Compute(name=data['name'], - details=data['details'], - hostname='localhost', - type=CONN_SOCKET, - login='', - password='') - new_socket_host.save() - return HttpResponseRedirect(request.get_full_path()) - else: - for msg_err in form.errors.values(): - error_messages.append(msg_err.as_text()) - if 'host_edit' in request.POST: - form = ComputeEditHostForm(request.POST) - if form.is_valid(): - data = form.cleaned_data - compute_edit = Compute.objects.get(id=data['host_id']) - compute_edit.name = data['name'] - compute_edit.hostname = data['hostname'] - compute_edit.login = data['login'] - compute_edit.password = data['password'] - compute_edit.details = data['details'] - compute_edit.save() - return HttpResponseRedirect(request.get_full_path()) - else: - for msg_err in form.errors.values(): - error_messages.append(msg_err.as_text()) - return render(request, 'computes.html', locals()) - - -@login_required -def overview(request, compute_id): - """ - :param request: - :return: - """ - - if not request.user.is_superuser: - return HttpResponseRedirect(reverse('index')) - - error_messages = [] - compute = get_object_or_404(Compute, pk=compute_id) - - 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.hypervisor_type() - mem_usage = conn.get_memory_usage() - emulator = conn.emulator() - conn.close() - except libvirtError as lib_err: - error_messages.append(lib_err) - - return render(request, 'overview.html', locals()) - - -@login_required -def compute_graph(request, compute_id): - """ - :param request: - :return: - """ - - points = 5 - datasets = {} - cookies = {} - compute = get_object_or_404(Compute, pk=compute_id) - curent_time = time.strftime("%H:%M:%S") - - try: - conn = wvmHostDetails(compute.hostname, - compute.login, - compute.password, - compute.type) - cpu_usage = conn.get_cpu_usage() - mem_usage = conn.get_memory_usage() - conn.close() - except libvirtError: - cpu_usage = 0 - mem_usage = 0 - - try: - cookies['cpu'] = request.COOKIES['cpu'] - cookies['mem'] = request.COOKIES['mem'] - cookies['timer'] = request.COOKIES['timer'] - except KeyError: - cookies['cpu'] = None - cookies['mem'] = None - - if not cookies['cpu'] or not cookies['mem']: - datasets['cpu'] = [0] * points - datasets['mem'] = [0] * points - datasets['timer'] = [0] * points - else: - datasets['cpu'] = eval(cookies['cpu']) - datasets['mem'] = eval(cookies['mem']) - datasets['timer'] = eval(cookies['timer']) - - datasets['timer'].append(curent_time) - datasets['cpu'].append(int(cpu_usage['usage'])) - datasets['mem'].append(int(mem_usage['usage']) / 1048576) - - if len(datasets['timer']) > points: - datasets['timer'].pop(0) - if len(datasets['cpu']) > points: - datasets['cpu'].pop(0) - if len(datasets['mem']) > points: - datasets['mem'].pop(0) - - data = json.dumps({'cpudata': datasets['cpu'], 'memdata': datasets['mem'], 'timeline': datasets['timer']}) - response = HttpResponse() - response['Content-Type'] = "text/javascript" - response.cookies['cpu'] = datasets['cpu'] - response.cookies['timer'] = datasets['timer'] - response.cookies['mem'] = datasets['mem'] - response.write(data) - return response diff --git a/conf/daemon/gstfsd b/conf/daemon/gstfsd deleted file mode 100644 index 2e68312..0000000 --- a/conf/daemon/gstfsd +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -# gstfsd - WebVirtCloud daemon for managing VM's filesystem -# - -import SocketServer -import json -import guestfs -import re - - -PORT = 16510 -ADDRESS = "0.0.0.0" - - -class MyTCPServer(SocketServer.ThreadingTCPServer): - allow_reuse_address = True - - -class MyTCPServerHandler(SocketServer.BaseRequestHandler): - def handle(self): - # recive data - data = json.loads(self.request.recv(1024).strip()) - - # GuestFS - gfs = guestfs.GuestFS(python_return_dict=True) - try: - gfs.add_domain(data['vname']) - gfs.launch() - parts = gfs.list_partitions() - for part in parts: - try: - gfs.mount(part, '/') - if gfs.is_file('/etc/shadow'): - if data['action'] == 'password': - file_shadow = gfs.cat('/etc/shadow') - new_root_hash = "root:" + data['passwd'] + ":" - file_shadow_new = re.sub('^root:.*?:', new_root_hash, file_shadow) - gfs.write('/etc/shadow', file_shadow_new) - gfs.chmod(640, '/etc/shadow') - self.request.sendall(json.dumps({'return': 'success'})) - if data['action'] == 'publickey': - if not gfs.is_dir('/root/.ssh'): - gfs.mkdir('/root/.ssh') - gfs.chmod(0700, "/root/.ssh") - gfs.write('/root/.ssh/authorized_keys', data['key']) - gfs.chmod(0600, '/root/.ssh/authorized_keys') - self.request.sendall(json.dumps({'return': 'success'})) - gfs.umount(part) - except RuntimeError: - pass - gfs.shutdown() - gfs.close() - except RuntimeError, err: - self.request.sendall(json.dumps({'return': 'error', 'message': err.message})) - -server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler) -server.serve_forever() diff --git a/conf/nginx/webvirtcloud.conf b/conf/nginx/webvirtcloud.conf deleted file mode 100644 index 70b8e0e..0000000 --- a/conf/nginx/webvirtcloud.conf +++ /dev/null @@ -1,23 +0,0 @@ -server { - listen 80; - - #server_name webvirtcloud.example.com; - #access_log /var/log/nginx/webvirtcloud-access_log; - - location /static/ { - root /srv/webvirtcloud; - expires max; - } - - location / { - proxy_pass http://127.0.0.1:8000; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for; - proxy_set_header Host $host:$server_port; - proxy_set_header X-Forwarded-Proto $remote_addr; - proxy_connect_timeout 600; - proxy_read_timeout 600; - proxy_send_timeout 600; - client_max_body_size 1024M; - } -} diff --git a/conf/requirements.txt b/conf/requirements.txt deleted file mode 100644 index 49903c0..0000000 --- a/conf/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -Django==1.11.14 -websockify==0.8.0 -gunicorn==19.9.0 -lxml==4.2.3 -libvirt-python==4.4.0 -pytz - diff --git a/conf/runit/nginx b/conf/runit/nginx deleted file mode 100755 index a7cd558..0000000 --- a/conf/runit/nginx +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e -if [[ ! -e /var/log/nginx/error.log ]]; then - # The Nginx log forwarder might be sleeping and waiting - # until the error log becomes available. We restart it in - # 1 second so that it picks up the new log file quickly. - (sleep 1 && sv restart /etc/service/nginx-log-forwarder) -fi -exec /usr/sbin/nginx diff --git a/conf/runit/nginx-log-forwarder b/conf/runit/nginx-log-forwarder deleted file mode 100755 index 07d63c6..0000000 --- a/conf/runit/nginx-log-forwarder +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# Forwards the Nginx error.log to the Docker logs. -set -e -if [[ -e /var/log/nginx/error.log ]]; then - exec tail -F /var/log/nginx/error.log -else - exec sleep 10 -fi diff --git a/conf/runit/novncd.sh b/conf/runit/novncd.sh deleted file mode 100755 index 14b775b..0000000 --- a/conf/runit/novncd.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# `/sbin/setuser www-data` runs the given command as the user `www-data`. -RUNAS=`which setuser` -[ -z $RUNAS ] && RUNAS="`which sudo` -u" -USER=www-data - -DJANGO_PROJECT=/srv/webvirtcloud -PYTHON=$DJANGO_PROJECT/venv/bin/python -NOVNCD=$DJANGO_PROJECT/console/novncd - -# make novncd debug, verbose -#PARAMS="-d -v" - -LOG=/var/log/novncd.log - -cd $DJANGO_PROJECT -exec $RUNAS $USER $PYTHON $NOVNCD $PARAMS >> $LOG 2>&1 diff --git a/conf/runit/webvirtcloud.sh b/conf/runit/webvirtcloud.sh deleted file mode 100755 index 179e34f..0000000 --- a/conf/runit/webvirtcloud.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -# `/sbin/setuser www-data` runs the given command as the user `www-data`. -cd /srv/webvirtcloud -exec /sbin/setuser www-data /srv/webvirtcloud/venv/bin/gunicorn webvirtcloud.wsgi:application -c /srv/webvirtcloud/gunicorn.conf.py >> /var/log/webvirtcloud.log 2>&1 diff --git a/conf/supervisor/gstfsd.conf b/conf/supervisor/gstfsd.conf deleted file mode 100644 index 2834b30..0000000 --- a/conf/supervisor/gstfsd.conf +++ /dev/null @@ -1,7 +0,0 @@ -[program:gstfsd] -command=/usr/bin/python /usr/local/bin/gstfsd -directory=/usr/local/bin -user=root -autostart=true -autorestart=true -redirect_stderr=true \ No newline at end of file diff --git a/conf/supervisor/webvirtcloud.conf b/conf/supervisor/webvirtcloud.conf deleted file mode 100644 index 2994bc9..0000000 --- a/conf/supervisor/webvirtcloud.conf +++ /dev/null @@ -1,15 +0,0 @@ -[program:webvirtcloud] -command=/srv/webvirtcloud/venv/bin/gunicorn webvirtcloud.wsgi:application -c /srv/webvirtcloud/gunicorn.conf.py -directory=/srv/webvirtcloud -user=www-data -autostart=true -autorestart=true -redirect_stderr=true - -[program:novncd] -command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd -directory=/srv/webvirtcloud -user=www-data -autostart=true -autorestart=true -redirect_stderr=true \ No newline at end of file diff --git a/console/novncd b/console/novncd deleted file mode 100755 index e017898..0000000 --- a/console/novncd +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import logging -import django - -DIR_PATH = os.path.dirname(os.path.abspath(__file__)) -ROOT_PATH = os.path.abspath(os.path.join(DIR_PATH, '..', '')) -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings") -CERT = DIR_PATH + '/cert.pem' - -if ROOT_PATH not in sys.path: - sys.path.append(ROOT_PATH) - -django.setup() - -# VENV_PATH = ROOT_PATH + '/venv/lib/python2.7/site-packages' -# if VENV_PATH not in sys.path: -# sys.path.append(VENV_PATH) - -import re -import Cookie -import socket -from webvirtcloud.settings import WS_PORT, WS_HOST, WS_CERT -from vrtManager.connection import CONN_SSH, CONN_SOCKET -from tunnel import Tunnel - -from optparse import OptionParser -parser = OptionParser() - -parser.add_option("-v", - "--verbose", - dest="verbose", - action="store_true", - help="Verbose mode", - default=False) - -parser.add_option("-d", - "--debug", - dest="debug", - action="store_true", - help="Debug mode", - default=False) - -parser.add_option("-H", - "--host", - dest="host", - action="store", - help="Listen host", - default=WS_HOST) - -parser.add_option("-p", - "--port", - dest="port", - action="store", - help="Listen port", - default=WS_PORT or 6080) - -parser.add_option("-c", - "--cert", - dest="cert", - action="store", - help="Certificate file path", - default=WS_CERT or CERT) - -(options, args) = parser.parse_args() - - -FORMAT = "%(asctime)s - %(name)s - %(levelname)s : %(message)s" -if options.debug: - logging.basicConfig(level=logging.DEBUG, format=FORMAT) - options.verbose = True -elif options.verbose: - logging.basicConfig(level=logging.INFO, format=FORMAT) -else: - logging.basicConfig(level=logging.WARNING, format=FORMAT) - -try: - from websockify import WebSocketProxy - try: - from websockify import ProxyRequestHandler - except ImportError: - USE_HANDLER = False - else: - USE_HANDLER = True -except ImportError: - try: - from novnc.wsproxy import WebSocketProxy - except ImportError: - print('Unable to import a websockify implementation, ' + - 'please install one') - sys.exit(1) - else: - USE_HANDLER = False - - -def get_connection_infos(token): - from instances.models import Instance - from vrtManager.instance import wvmInstance - - try: - temptoken = token.split('-', 1) - host = int(temptoken[0]) - uuid = temptoken[1] - instance = Instance.objects.get(compute_id=host, uuid=uuid) - conn = wvmInstance(instance.compute.hostname, - instance.compute.login, - instance.compute.password, - instance.compute.type, - instance.name) - if instance.compute.hostname.count(':'): - connhost = instance.compute.hostname.split(':')[0] - connport = instance.compute.hostname.split(':')[1] - else: - connhost = instance.compute.hostname - connport = 22 - connuser = instance.compute.login - conntype = instance.compute.type - console_host = conn.get_console_listen_addr() - console_port = conn.get_console_port() - console_socket = conn.get_console_socket() - except Exception, e: - logging.error('Fail to retrieve console connection infos for token %s : %s' % (token, e)) - raise - return (connhost, connport, connuser, conntype, console_host, - console_port, console_socket) - - -class CompatibilityMixIn(object): - def _new_client(self, daemon, socket_factory): - cookie = Cookie.SimpleCookie() - cookie.load(self.headers.getheader('cookie')) - if 'token' not in cookie: - self.msg('No token cookie found !') - return False - token = cookie['token'].value - - (connhost, connport, connuser, conntype, console_host, console_port, - console_socket) = get_connection_infos(token) - - cnx_debug_msg = "Connection infos :\n" - cnx_debug_msg += "- connhost : '%s'\n" % connhost - cnx_debug_msg += "- connport : '%s'\n" % connport - cnx_debug_msg += "- connuser : '%s'\n" % connuser - cnx_debug_msg += "- conntype : '%s'\n" % conntype - cnx_debug_msg += "- console_host : '%s'\n" % console_host - cnx_debug_msg += "- console_port : '%s'\n" % console_port - cnx_debug_msg += "- console_socket : '%s'\n" % console_socket - logging.debug(cnx_debug_msg) - - if console_socket and conntype == CONN_SOCKET: - # Local socket on local host - self.msg('Try to open local socket %s' % console_socket) - tsock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - tsock.connect(console_socket) - elif console_socket or re.match('^127\.', console_host): - # Need tunnel to physical host - if conntype != CONN_SSH: - self.msg("Need a tunnel to access console but can't mount " + - "one because it's not a SSH host") - raise Exception(self.msg) - try: - # generate a string with all placeholders to avoid TypeErrors - # in sprintf - # https://github.com/retspen/webvirtmgr/pull/497 - error_msg = "Try to open tunnel on %s@%s:%s on console %s:%s " - error_msg += "(or socket %s)" - self.msg(error_msg % (connuser, connhost, connport, - console_host, console_port, console_socket)) - tunnel = Tunnel() - fd = tunnel.open(connhost, connuser, connport, - console_host, console_port, console_socket) - tsock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) - except Exception as e: - self.msg("Fail to open tunnel : %s" % e) - raise - self.msg("Tunnel opened") - else: - # Direct access - self.msg("connecting to: %s:%s" % (connhost, console_port)) - tsock = socket_factory(connhost, console_port, connect=True) - tunnel = None - - if self.verbose and not daemon: - print(self.traffic_legend) - - # Start proxying - try: - self.msg("Start proxying") - self.do_proxy(tsock) - except: - if tunnel: - self.vmsg( - "%s:%s (via %s@%s:%s) : Target closed" % - (console_host, console_port, connuser, connhost, connport)) - if tsock: - tsock.shutdown(socket.SHUT_RDWR) - tsock.close() - if tunnel: - tunnel.close() - raise - - -if USE_HANDLER: - class NovaProxyRequestHandler(ProxyRequestHandler, CompatibilityMixIn): - def msg(self, *args, **kwargs): - self.log_message(*args, **kwargs) - - def vmsg(self, *args, **kwargs): - if self.verbose: - self.msg(*args, **kwargs) - - def new_websocket_client(self): - """ - Called after a new WebSocket connection has been established. - """ - # Setup variable for compatibility - daemon = self.server.daemon - socket_factory = self.server.socket - - self._new_client(daemon, socket_factory) - -else: - class NovaWebSocketProxy(WebSocketProxy, CompatibilityMixIn): - - def new_client(self): - """ - Called after a new WebSocket connection has been established. - """ - # Setup variable for compatibility - daemon = self.daemon - socket_factory = self.socket - - self._new_client(daemon, socket_factory) - -if __name__ == '__main__': - if USE_HANDLER: - # Create the WebSocketProxy with NovaProxyRequestHandler handler - server = WebSocketProxy(RequestHandlerClass=NovaProxyRequestHandler, - listen_host=options.host, - listen_port=options.port, - source_is_ipv6=False, - verbose=options.verbose, - cert=options.cert, - key=None, - ssl_only=False, - daemon=False, - record=False, - web=False, - traffic=False, - target_host='ignore', - target_port='ignore', - wrap_mode='exit', - wrap_cmd=None) - else: - # Create the NovaWebSockets proxy - server = NovaWebSocketProxy(listen_host=options.host, - listen_port=options.port, - source_is_ipv6=False, - verbose=options.verbose, - cert=options.cert, - key=None, - ssl_only=False, - daemon=False, - record=False, - web=False, - target_host='ignore', - target_port='ignore', - wrap_mode='exit', - wrap_cmd=None) - server.start_server() diff --git a/console/templates/console-base.html b/console/templates/console-base.html deleted file mode 100644 index c7ea6d4..0000000 --- a/console/templates/console-base.html +++ /dev/null @@ -1,131 +0,0 @@ -{% load staticfiles %} -{% load i18n %} - - - - - - - - - - -{% block head %}{% endblock %} - - - - -