1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2025-07-31 12:41:08 +00:00

Compare commits

...

1177 commits
v1.0 ... master

Author SHA1 Message Date
catborise
af774ebcc8
Merge pull request #676 from hudeng-go/upstream/master
feat: Add ISO file upload progress support.
2025-07-22 12:15:32 +03:00
hudeng
326579a6ea feat: Add ISO file upload progress support. 2025-07-17 17:11:30 +08:00
catborise
c0f31909bd
Merge pull request #675 from catborise/master
update packages && installation script
2025-07-01 15:08:25 +03:00
catborise
9324b9c36d update installation scripts 2025-07-01 15:07:02 +03:00
catborise
b909a81f51 update requirements 2025-07-01 15:06:29 +03:00
catborise
79450eac6a
Merge pull request #671 from lando814/patch-1
ubdate base image
2025-05-20 16:51:35 +03:00
lando814
a56ce2a58f
ubdate base image
updated base image from jammy to noble due to error message during docker build

ERROR: No matching distribution found for crypt-r==3.13.1
2025-05-19 17:21:29 +02:00
catborise
4646597b39
Merge pull request #668 from catborise/master
update minimum python3 requirements. python3 >= 3.11
2025-05-02 10:25:20 +03:00
catborise
659a6edb73 update minimum python3 requirements. python3 >= 3.11 2025-05-02 10:23:51 +03:00
catborise
099769d305
Merge pull request #666 from liangsuilong/master
crypt-r replaces crypt stdlib, support python-3.13
2025-05-02 10:10:40 +03:00
catborise
120f406b48
Merge pull request #667 from catborise/master
update requirements
2025-05-02 10:10:18 +03:00
catborise
fd54c68e6b
Merge pull request #664 from fangebee/master
Some fixes regarding Ceph/RBD volumes handling
2025-05-02 10:07:48 +03:00
catborise
34f08fc555 update requirements 2025-05-02 10:06:09 +03:00
Liang Suilong
8bca7db917 crypt-r replaces crypt stdlib, support python-3.13 2025-03-30 10:33:51 +08:00
fangebee
f26fa3d050
Detect RDB pools too 2025-03-14 10:31:40 +01:00
fangebee
8c4f1d9fe8
Workaround issue #663
do not confuse a network volume with a network interface
2025-03-13 18:17:21 +01:00
catborise
fb108f9ac9
Merge pull request #662 from catborise/master
update requirements
2025-03-11 10:43:14 +03:00
catborise
1a800b4535 update requirements 2025-03-11 10:38:38 +03:00
catborise
b892af989b
Merge pull request #657 from catborise/master
update requirements
2024-10-15 16:38:48 +03:00
catborise
b55552f05a update requirements 2024-10-15 16:37:21 +03:00
catborise
3d53f45f27
Merge pull request #656 from catborise/master
update requirements
2024-08-27 10:46:55 +03:00
catborise
24f751774b update requirements 2024-08-27 10:45:51 +03:00
catborise
f725d8ec18
Merge pull request #654 from wahello/master
pip install supervisor
2024-08-02 10:37:05 +03:00
nono
9e4dc3256e pip install supervisor 2024-08-02 09:41:06 +08:00
catborise
218ec5dd78
Merge pull request #653 from catborise/master
update requirements
2024-07-23 14:54:43 +03:00
catborise
cb7b690a10 update requirements 2024-07-23 14:53:41 +03:00
catborise
f4651d816d
Merge pull request #650 from catborise/master
fix urls for bus and capabilities
2024-06-24 10:25:48 +03:00
catborise
1edce931ed fix urls for bus and capabilities 2024-06-24 10:22:49 +03:00
catborise
b76b6e382e
Merge pull request #649 from catborise/master
update req
2024-06-24 09:34:25 +03:00
catborise
072a922c15 update requirements 2024-06-24 09:32:41 +03:00
catborise
ee2c55dac7 update chipset url 2024-06-24 08:53:01 +03:00
catborise
6d97557cd9
Merge pull request #646 from ckbaker10/feat-ubuntu-systemd
Ubuntu Service files
2024-06-08 15:25:18 +03:00
Your Name
950e668631 Silly Mistake fixed 2024-05-28 11:33:40 +02:00
Your Name
46029d533c Ubuntu Service files 2024-05-28 11:22:20 +02:00
catborise
45be006694
Merge pull request #643 from catborise/master
update requirements
2024-04-29 12:18:40 +03:00
catborise
fc60819ff1 update requirements 2024-04-29 12:13:04 +03:00
catborise
3f23acbb9d
Merge pull request #638 from catborise/master
fix default nic type problem
2024-02-28 08:18:08 +03:00
catborise
765b759953 if nic type is default then make it virtio 2024-02-28 08:16:44 +03:00
catborise
d8fe9c2f64 add user to libvirt group also 2024-02-28 08:15:02 +03:00
catborise
a048adef8e do not show warning message when instance is shutdown 2024-02-28 08:13:08 +03:00
catborise
d54d50dc99
Merge pull request #635 from catborise/master
update locale
2024-02-14 09:21:01 +03:00
catborise
241f7462f7 update locales and tr translations 2024-02-14 09:19:29 +03:00
catborise
7da5e4cbbe add none svg to icon cache 2024-02-13 16:53:53 +03:00
catborise
9ee1571f5b
Merge pull request #634 from catborise/master
fix user machine for listing
2024-02-13 14:35:03 +03:00
catborise
f86eb78cbf add smbios package to debian install 2024-02-13 14:33:20 +03:00
catborise
6144760456 update requirements 2024-02-13 14:28:13 +03:00
catborise
81b96b6512 fix listing vms for normal users 2024-02-13 14:24:38 +03:00
catborise
1f90a6c8c9
Merge pull request #631 from catborise/master
update requirements
2024-01-17 10:24:13 +03:00
catborise
bfe7da1ccc add tzdata package and reorder package names 2024-01-17 10:20:06 +03:00
catborise
9a05c182cc fix chevron up down icons 2024-01-17 10:19:03 +03:00
catborise
f0cd0bc04f update requirements 2024-01-17 09:28:01 +03:00
catborise
bbe9e54395
Merge pull request #628 from kuletco/work
fix bug and update translations for zh
2023-12-13 09:30:24 +03:00
Wang Hong
462e5d8403 update translation for zh 2023-12-13 14:03:00 +08:00
Wang Hong
c58d6f484d Corrected untranslated items 2023-12-13 14:00:02 +08:00
Wang Hong
e0d83a397b fix icon missing bug 2023-12-13 13:57:38 +08:00
catborise
d261f4d2c9
Merge pull request #624 from catborise/master
add white noise for static_root/staticfiles_dir problem for debug/non…
2023-11-10 16:36:09 +03:00
catborise
296081d1e3
Merge branch 'master' into master 2023-11-10 16:35:51 +03:00
catborise
e324a19401 add white noise for static_root/staticfiles_dir problem for debug/non-debug running mode 2023-11-10 16:29:48 +03:00
catborise
6c0cc3c274
Merge pull request #620 from MisterBlueBear/global_setting_nic_type
Global setting for NIC type
2023-11-02 11:06:30 +03:00
catborise
bcf91ad7b7
Merge pull request #621 from MisterBlueBear/install_script_default_hostname
Install script accepts default hostname
2023-11-02 11:01:48 +03:00
MisterBlueBear
2f24f77368 Install script accepts default hostname 2023-11-01 20:35:15 -04:00
MisterBlueBear
7e8c05fff9 Global setting for NIC type 2023-11-01 18:08:38 -04:00
catborise
2ff0abf8c5
Merge pull request #619 from catborise/master
update requirements
2023-10-30 15:59:51 +03:00
catborise
7171a70f75 update requirements 2023-10-30 15:56:54 +03:00
catborise
8995f47c13
Merge pull request #617 from 0x6d61726b/patch-01_X-Forwarded-Proto
Fixed nginx X-Forwarded-Proto to match the protocol ("http", "https")
2023-10-30 15:53:40 +03:00
catborise
f912dfccdb
Merge pull request #618 from 0x6d61726b/patch-02_install-nginx-upstream-wsnovncd
Fixes Quick Install overrides nginx upstream wssocketiod
2023-10-30 15:52:01 +03:00
catborise
911bd54916
Merge pull request #613 from MisterBlueBear/nginx_conf
nginx conf - Fix for issue #577
2023-10-30 15:51:24 +03:00
catborise
6974eae62e static_root and static_dirs fix 2023-10-30 15:36:40 +03:00
Mark
cc86730441
Corrected X-Forwarded-Proto meaning for Gunicorn 2023-10-26 22:43:27 +02:00
Mark
5c6bcbe610
Fixes issue #614: Quick Install overrides nginx upstream wssocketiod port 2023-10-26 22:18:17 +02:00
Mark
2072890fc5
Fixed nginx X-Forwarded-Proto to match the protocol ("http", "https") 2023-10-26 21:53:04 +02:00
MisterBlueBear
4aafad668b Location /websockify proxy passes to wsnovncd 2023-10-26 13:12:20 -04:00
catborise
9a675918c6
Merge pull request #611 from MisterBlueBear/MAC_OUI
Get MAC OUI from settings.py
2023-10-20 23:37:53 +03:00
catborise
91aaa93c2b
Merge pull request #612 from MisterBlueBear/base_templates
Fix base templates
2023-10-20 23:31:50 +03:00
MisterBlueBear
31cace9994 nginx conf - Fix for issue #577 2023-10-18 21:42:57 -04:00
MisterBlueBear
149044a90c Fix templates - extends base.html 2023-10-18 18:04:00 -04:00
MisterBlueBear
5a211c0c56 Get MAC OUI from settings.py 2023-10-18 17:34:40 -04:00
catborise
58e215f755
Merge pull request #609 from catborise/master
show last login info for users
2023-10-06 16:31:10 +03:00
catborise
9755f307a6 add libsasl2-dev and libldap2-dev packages for Debian 12 2023-10-06 16:28:30 +03:00
catborise
f5e896813c add used icons to local cache 2023-10-06 16:16:09 +03:00
catborise
e47d909e41 show last login time of users 2023-10-06 16:15:35 +03:00
catborise
ce8432ce9e
Merge pull request #608 from fathonix/master
Fix errors that prevent socketiod from running
2023-09-27 20:34:15 +03:00
Aldo Adirajasa Fathoni
64fdb9315d
Fix socketio default host
The previous value caused webvirtcloud to
try to connect to socketiod with 0.0.0.0.
2023-09-26 21:56:27 +07:00
Aldo Adirajasa Fathoni
84e22e4a8c
Ignore if pty process has exited 2023-09-26 13:23:20 +07:00
Aldo Adirajasa Fathoni
3ef0fe19f8
Fix calling write() from int instead of os 2023-09-26 13:21:02 +07:00
catborise
cea2309c64
Merge pull request #607 from catborise/master
Fixes
2023-09-25 20:25:08 +03:00
catborise
35afff26f4
Merge pull request #606 from krejcar25/fix-604
Replace ifnotequal with if
2023-09-25 20:23:50 +03:00
Amélie Krejčí
53d1407483
Replace ifnotequal with if
ifequal and ifnotequal are deprecated in Django 4.0
2023-09-25 14:40:22 +02:00
catborise
43fc419490
Merge pull request #603 from krejcar25/master
Get UserAttributes object using get_or_create
2023-09-23 11:17:49 +03:00
Amélie Krejčí
1b2b3a3bce
Get UserAttributes object using get_or_create
This is done to automatically create the UserAttributes object in case LDAP User Backend didn't create it.
2023-09-23 09:52:48 +02:00
catborise
ca54ae0c65
Merge pull request #600 from krejcar25/master
Switch to django-auth-ldap for LDAP authentication
2023-09-13 12:11:25 +03:00
Amélie Krejčí
561fedfccd
Add new template config and README blocks 2023-09-11 21:39:05 +02:00
Amélie Krejčí
74ee2c073a
Remove old LDAP backend files 2023-09-11 21:19:36 +02:00
Amélie Krejčí
01a4225a84
Add django-auth-ldap dependency 2023-09-11 21:16:51 +02:00
catborise
4a4a998d4c add auth_unix_rw settings for debian and ubuntu 2023-09-06 14:50:59 +03:00
catborise
635101a1b6 remove deprecated ifnotequal dtl tag 2023-09-06 13:30:58 +03:00
catborise
5e368d1ee0
Merge pull request #599 from catborise/master
update requirements & fix install scripts
2023-09-06 09:50:56 +03:00
catborise
cbd1f6f8b0 update requirement and fix install script 2023-09-06 09:41:08 +03:00
catborise
cedb7beedd set CSRF TRUSTED ORIGIN modification on settings.py 2023-09-06 09:28:50 +03:00
catborise
3facb9e445
Merge pull request #598 from catborise/master
CSRF adding and debian 12 install fix
2023-09-06 09:14:03 +03:00
catborise
b58c4ef781 add CSRF_TRUSTED_ORIGINS with django 4 2023-09-05 09:08:06 +03:00
catborise
77c4f8981b fix debian qemu package renaming 2023-09-05 09:01:59 +03:00
catborise
5d3a11e7c9
Merge pull request #596 from catborise/master
update to django 4.2 LTS
2023-08-31 09:17:33 +03:00
catborise
cf6f74f6c3 fix sr-only toggle dropdown to hide 2023-08-23 09:26:39 +03:00
catborise
56c92d90b5 fix show snapshot exist indicator 2023-08-23 09:26:39 +03:00
catborise
2935c9d382 update django 3.2.20 -> 4.2.4 2023-08-23 09:26:39 +03:00
catborise
a02df250bf
Merge pull request #595 from catborise/master
various updates
2023-08-17 13:52:04 +03:00
catborise
fc2063eccb update requirements 2023-08-17 13:35:55 +03:00
cserma
f2222a6ea8 change name for network model to nic type 2023-08-17 13:22:18 +03:00
cserma
7d1cfb69b0 network interface model selection added for new instance and new interface 2023-08-16 09:13:11 +03:00
catborise
b47ec2e8f9 bootstrap icon conversion fixes 2023-08-11 09:40:38 +03:00
catborise
7e88db9d2b add template profile for django toolbar 2023-08-11 09:40:02 +03:00
catborise
0926a3c3aa delete old fontawesome font files 2023-08-10 11:33:58 +03:00
catborise
1e8d94731b override googleapis-font for zephyr template and make inter font local 2023-08-10 11:31:24 +03:00
Emre Serdengeçti
1cbdf76df6
logger updates (#31)
* Log for failed login attempts

* Logger configuration for logging to file

* interface fixes

* login log fix, added logged in too

* bootstrap icons setup

* font-awesome icons replaced with bootstrap icons

* replaced i-tags with django_bootstrap_icons

* removed icons library from project

* bug fix

---------

Co-authored-by: catborise <catborise@gmail.com>
2023-08-09 09:20:18 +03:00
catborise
82de7f4262
Merge pull request #590 from catborise/master
update req & fixes
2023-07-11 11:16:34 +03:00
catborise
07d7a6d752 update requirements 2023-07-11 11:14:56 +03:00
catborise
91ec098aa0 fix cpu_usage waiting for instance creation. 2023-05-18 14:35:19 +03:00
catborise
4d5b346e44
Merge pull request #587 from catborise/master
enchancements
2023-05-12 12:10:49 +03:00
catborise
54954137c2
Merge branch 'master' into master 2023-05-12 12:10:28 +03:00
catborise
8bf28a4f33 add loading spinner after submit to login pages 2023-05-12 12:01:54 +03:00
catborise
c0513f7c6b change progress bar color & fix typo 2023-05-12 11:18:17 +03:00
catborise
6e76eb2cd9 reorganize otp_login and login pages 2023-05-12 10:51:18 +03:00
catborise
c099cd06ef update requirements 2023-05-12 10:51:18 +03:00
catborise
beb13cdea1
Merge pull request #585 from catborise/master
various addings and fixes
2023-05-12 10:44:47 +03:00
catborise
86b03abd59 reorganize otp_login and login pages 2023-05-12 10:30:50 +03:00
catborise
9671f7b3e0 update requirements 2023-05-12 10:30:50 +03:00
cserma
2941840f0d 'create new instance' screen improvements 2023-05-12 10:15:01 +03:00
catborise
5880b91c7a
Merge pull request #29 from cserma/master
various fixes - new create instance view
2023-05-11 11:42:43 +03:00
cserma
3cd4212cdd modified 'create new instance' screen with compute details 2023-05-10 13:59:13 +03:00
cserma
96ea999926 redirect after login fix 2023-05-10 13:57:01 +03:00
cserma
3403d21fea log i18n fixes 2023-05-10 13:57:01 +03:00
cserma
907d47ab78 snapshots tab improvements 2023-05-10 13:57:01 +03:00
catborise
edd265d5a5
Merge pull request #582 from catborise/master
update requirements
2023-04-27 16:58:09 +03:00
catborise
0754d07540 update requirements 2023-04-27 16:56:55 +03:00
cserma
da9fbeaff4 refresh instance pools implementation 2023-04-27 16:04:43 +03:00
cserma
407605761f new login screen 2023-04-27 16:02:29 +03:00
catborise
58af536dbf
Merge pull request #581 from catborise/master
external snapshot & add filter for logs & fixes
2023-04-24 14:46:42 +03:00
catborise
012bc97a59 add showPleasewait dialog while starting instance. 2023-04-24 14:39:22 +03:00
catborise
71f05089f6 experimental: external snapshot fixes 3 2023-04-24 14:38:51 +03:00
catborise
f4aa925e2a experimental: external snapshot fixes 2 2023-04-14 16:11:13 +03:00
catborise
06994b0423
Merge branch 'retspen:master' into master 2023-04-13 12:27:12 +03:00
catborise
33d49a68a7 experimental: external snapshot fixes 2023-04-13 12:25:42 +03:00
cserma
68b0494350 external snapshot logs and bug fixes 2023-04-13 12:25:42 +03:00
cserma
fd6b2ec4bf external snapshot implementation 2023-04-13 12:25:42 +03:00
cserma
ad9f1db643 added filter for logs 2023-04-13 12:25:42 +03:00
catborise
5a78713d1a
Merge pull request #576 from cserma/master
added filter for logs
2023-03-16 16:20:32 +03:00
catborise
bea6b1454b fix grouped non grouped instance view 2023-03-13 11:22:40 +03:00
cserma
8160819b39 added filter for logs 2023-03-01 14:11:30 +03:00
catborise
75ee073ed4
Merge pull request #572 from catborise/master
update requirements
2023-02-16 09:13:23 +03:00
catborise
9f8aaee15a update requirements 2023-02-16 09:11:49 +03:00
catborise
4d1e9cedc2
Merge pull request #569 from catborise/master
update requirements
2023-02-06 09:28:05 +03:00
catborise
33002d5a49 update requirements 2023-02-06 09:03:29 +03:00
catborise
c26e7f5e6f
Merge pull request #567 from catborise/master
replace old bootstrap statements: fas->fa, btn-block->d-grid, data-to…
2023-01-30 09:43:33 +03:00
catborise
6201a2fe9f replace old bootstrap statements: fas->fa, btn-block->d-grid, data-toggle->data-bs-toogle 2023-01-30 09:19:10 +03:00
catborise
711870b5a1
Merge pull request #565 from catborise/master
update requirements
2023-01-24 15:12:38 +03:00
catborise
2fdcc86b4e update requirements 2023-01-24 15:10:33 +03:00
catborise
561b5ead43
Merge pull request #563 from catborise/master
bootstrap update
2023-01-23 09:29:42 +03:00
catborise
8e1c3fa03b change iso path hint 2023-01-23 08:25:44 +03:00
catborise
6d9025ea20 update base image to jammy 1.0.1 2023-01-23 08:25:44 +03:00
catborise
63216d6283 update jquery to 3.6.1 2023-01-23 08:25:44 +03:00
catborise
12d3530641 update bootstrap-multiselect to 1.1.2 2023-01-23 08:25:44 +03:00
catborise
e3fb820b50 update bootstrap&bootswatch 5.0.2 -> 5.2.2. Seperate bootswatch overrides for future update easiness 2023-01-23 08:25:44 +03:00
catborise
1348679997 resolve dependency problem with markdown 2023-01-23 08:25:44 +03:00
catborise
31b029847f
Merge pull request #562 from D-Jy-lab/master
fix zombie processes
2023-01-23 08:20:20 +03:00
D-Jy
bb9fe83836
fix zombie processes
Temporary fix for zombie processes.
2023-01-20 12:06:03 +08:00
D-Jy
ce9498d06d
fix zombie processes
Temporary fix for zombie processes.
2023-01-20 12:04:04 +08:00
D-Jy
2aa0d17db5
fix zombie processes
Temporary fix for zombie processes.
2023-01-20 12:02:37 +08:00
catborise
fe550317d2
Merge pull request #561 from weidongkl/master
enhancement: optimize regular expressions
2022-12-26 09:09:32 +03:00
weidong
1be57f987e enhancement: optimize regular expressions 2022-12-26 11:42:14 +08:00
catborise
ad7781b545
Merge pull request #556 from Regan-He/master
The storage pool path should be allowed to contain '-' and '_'
2022-11-18 21:09:26 +03:00
herengui
fea1571e1a The storage pool path should be allowed to contain '-' and '_'
Signed-off-by: herengui <herengui@uniontech.com>
2022-11-15 17:19:03 +08:00
catborise
7db75784c6
Merge pull request #555 from catborise/master
code black format
2022-11-14 09:17:45 +03:00
catborise
012b3041e8
Merge pull request #554 from tolbkni/fix-issue-553
fix syntax error while import settings.py
2022-11-03 21:18:31 +03:00
Jamie Gao
fe7452fcc6 fix syntax error while import settings.py 2022-11-04 01:45:04 +08:00
catborise
e2a15d926c eliminate code repetition with class structure 2022-11-03 10:20:16 +03:00
catborise
a1eab70e2d reorg imports and black format 2022-11-02 16:05:41 +03:00
catborise
b43d7b0a8c
Merge pull request #552 from catborise/master
format black code style
2022-11-02 15:59:31 +03:00
catborise
fcd4b79431
Merge branch 'master' into master 2022-11-02 15:59:19 +03:00
catborise
af38e90f41 fix dhcp type interface error 2022-11-02 11:32:44 +03:00
catborise
217e106c8b format python code with black 2022-11-02 08:54:35 +03:00
catborise
ea409ca863 generate PNG for qr code for email compatibility. 2022-10-06 11:47:38 +03:00
catborise
51b12a8dec add email config template 2022-10-06 11:46:16 +03:00
catborise
773709ce57 update requirements 2022-10-06 11:45:52 +03:00
catborise
7b33f511cf
Merge pull request #542 from catborise/master
add description to snapshots. Stress out live snapshot risks
2022-09-26 11:03:05 +03:00
catborise
c9f1a7d7c5 add description to snapshots. Stress out live snapshot risks 2022-09-19 15:44:57 +03:00
catborise
2dc4048053
Merge pull request #538 from catborise/master
make cd-rom and input devices optional while instance creating
2022-09-08 09:07:50 +03:00
catborise
2ff188da45 fix add-cdrom error while creating 2022-09-08 08:15:54 +03:00
catborise
48a2ab4997
Merge pull request #537 from Info-IIG/develop
Fix DRBD command
2022-09-08 08:04:50 +03:00
catborise
481c110f01 make adding input devices optional. give bus control for input devices whie instance creating 2022-09-08 08:02:49 +03:00
catborise
5f5bd379af make adding cd-rom optional while vm creating. Add default value for cd-rom bus 2022-09-07 13:46:50 +03:00
Info-IIG
4569dd03f9 Fix DRBD command 2022-09-07 11:39:27 +02:00
catborise
ea6524f79e add more default machine type and instance type 2022-09-05 15:39:41 +03:00
catborise
c804b2cde8 fix identation 2022-09-05 15:38:00 +03:00
catborise
4abf62702a take over precedense sata to scsi 2022-09-05 15:37:23 +03:00
catborise
97acbd9843 fix typo error 2022-09-05 15:36:37 +03:00
catborise
31c575e852 call function with parameter names 2022-09-05 15:35:51 +03:00
catborise
5425519408 change firmware, graphic, video fields max lenght 2022-09-02 16:54:05 +03:00
catborise
6eb06280c5 add makemigrations command for missing model scripts 2022-08-29 14:55:00 +03:00
catborise
3b43081b9d
Merge pull request #530 from catborise/master
other fixes after rest-framework addition
2022-08-24 15:31:50 +03:00
catborise
66a9edfae1 fix Docker installation bugs 2022-08-24 15:16:08 +03:00
catborise
afffaa8589 fix linter warnings/error 2022-08-24 15:15:27 +03:00
catborise
aeee56fe45 add swagger-ui css/js files 2022-08-24 15:14:14 +03:00
catborise
46514d0897 add collect static command for rest/swagger ui 2022-08-24 10:29:29 +03:00
catborise
234638a85c add missing migrations for new models 2022-08-24 09:34:16 +03:00
catborise
f500d44492 Optimize snapshot capabilities to snap UEFI type machines. by @qrwitu 2022-08-23 15:24:00 +03:00
catborise
2910a3229a apply recommendation of code-review-doctor 2022-08-23 08:42:19 +03:00
catborise
6d3550bb24 console_listen_address not in the context, it's instance.console_listen_address. fixed by @wadefelix 2022-08-23 08:40:36 +03:00
catborise
2db7a4fa27
Merge pull request #528 from catborise/master
fix setting.py.template & decodestring convert
2022-08-22 16:22:32 +03:00
catborise
33a7e30fc5 convert decodestring -> decodebytes for python 3.10 compatibility 2022-08-22 16:14:22 +03:00
catborise
aebcbb63fd remove duplicate restframework module entry 2022-08-22 16:10:31 +03:00
catborise
a67a51eaed
Merge pull request #527 from catborise/master
Rest framework (#24)
2022-08-22 15:35:19 +03:00
catborise
1c84f241ad
Merge branch 'master' into master 2022-08-22 15:34:51 +03:00
catborise
cfce71ec2b
Rest framework (#24)
* Add rest framework for API: First Commit

* modify some shell scripts to make variable references safer; modify some python scripts to reduce the code complexity and cyclomatic complexity of functions.

* Add REST API for some webvirtcloud functions. Instance list/delete/create, compute list/delete/create, storages-network list/retrieve. Add swagger and redoc for API interface

* update requirements

Co-authored-by: herengui <herengui@uniontech.com>
2022-08-22 15:12:33 +03:00
catborise
76e3a8b823
Merge pull request #526 from Regan-He/master
Reduce code complexity
2022-08-22 08:21:38 +03:00
herengui
4d78917c35 modify some shell scripts to make variable references safer; modify some python scripts to reduce the code complexity and cyclomatic complexity of functions. 2022-08-22 09:57:07 +08:00
catborise
aaea1d9451
Merge pull request #522 from catborise/master
add socketio parameters
2022-07-21 15:41:53 +03:00
catborise
92254401dc add socketio parameters 2022-07-21 15:39:50 +03:00
catborise
d23896ddf6
Merge pull request #521 from catborise/master
remove css urls, add missing socketio requirements, fix cookie import…
2022-07-21 13:36:52 +03:00
catborise
0680f02240 remove css urls, add missing socketio requirements, fix cookie import, and rearrange socketiod connetion 2022-07-21 13:31:37 +03:00
catborise
d24d6b037d
Merge pull request #520 from catborise/master
update reqs and ace.js
2022-07-19 16:19:27 +03:00
catborise
6f7d62a860 update libvirt + Django req 2022-07-19 16:16:02 +03:00
catborise
92a20c0aaa
Merge pull request #519 from GaetanF/master
Implement Libvirt Serial Console as Console on WebVirtCloud
2022-07-18 21:01:09 +03:00
Gaëtan Ferez
9bf3b7e88f Fix process call for consolecallback 2022-07-17 13:37:21 +02:00
Gaëtan Ferez
65dce10ce1 Implement Libvirt Serial Console as Console on WebVirtCloud 2022-07-17 13:25:47 +02:00
catborise
f6915ac51f
Merge pull request #514 from sianciou/security_issue
fix insecure randomness
2022-07-08 23:11:44 +03:00
sianciou
8699575e6f fix insecure randomness 2022-07-05 16:13:04 +08:00
catborise
89ca8010f4
Merge pull request #512 from Info-IIG/develop
Fix dependencies reference nonexistent in appsettings migrations
2022-06-30 22:29:23 +03:00
Info-IIG
22ac6f1c60 Fix dependencies reference nonexistent in appsettings migrations 2022-06-30 09:48:47 +02:00
catborise
a272740be6
Merge pull request #511 from Info-IIG/develop
Fix LDAP password and added VM DRBD Status to appsettings
2022-06-30 08:15:11 +03:00
catborise
e2a1bc5f7e update ace.js files for XML editors 2022-06-30 08:04:31 +03:00
Info-IIG
421f17d5ca Added VM DRBD Status to appsettings 2022-06-29 15:08:07 +02:00
Info-IIG
af5acc8c24 Fix ldap password error 2022-06-29 11:55:36 +02:00
catborise
d0e3c11ccf update spice-html5 packages to release 0.30 2022-06-28 12:20:16 +03:00
catborise
bb31c09d25
Merge pull request #510 from catborise/master
update secret generator with new python secrets module - urlsafe
2022-06-28 11:22:12 +03:00
catborise
7bb5d6f313 update secret generator with new python secrets module - urlsafe 2022-06-28 11:13:23 +03:00
catborise
f620b88f73
Merge pull request #508 from catborise/master
update reqs
2022-06-27 11:35:10 +03:00
catborise
f3d1c17a23 update requirements 2022-06-27 11:17:58 +03:00
catborise
a4baeae934 update docker image 2022-06-27 11:17:21 +03:00
catborise
f60bb055a8 add almalinux support for installation 2022-06-27 10:49:42 +03:00
catborise
2c07df4c8f
Merge pull request #506 from Info-IIG/develop
New additions
2022-06-20 08:54:10 +03:00
Info-IIG
052610dd21 Added permission can snapshot instances 2022-06-16 14:55:08 +02:00
Info-IIG
e26a114c44 Added ldap password encryption 2022-06-15 17:08:16 +02:00
Info-IIG
02b02d3321 Added DRBD status 2022-06-15 13:51:56 +02:00
Info-IIG
32eb91f53f Automatic input first name last name and email with ldap 2022-06-14 15:41:38 +02:00
Info-IIG
de2dce7573 Added Technicians group 2022-06-14 15:10:33 +02:00
Info-IIG
c817d3e61a Add permission can view instances 2022-06-13 09:58:46 +02:00
catborise
12f0c70069
Merge pull request #503 from catborise/master
add alma linux for know distributions
2022-05-18 14:08:26 +03:00
catborise
f1a9444f24
Merge pull request #502 from Regan-He/master
Enhanced system support
2022-05-18 14:07:03 +03:00
catborise
995b333038 add alma linux for know distributions 2022-05-18 14:01:35 +03:00
herengui
9ddba262a4 Enhanced system support
UOS is not a specific os, but a series of different OS, they use either dnf or apt package manager

Signed-off-by: herengui <herengui@uniontech.com>
2022-05-18 17:23:29 +08:00
catborise
448c66110b
Merge pull request #497 from catborise/master
update requirements
2022-04-25 08:14:28 +03:00
catborise
2877f7b120 update requirements 2022-04-25 08:08:55 +03:00
catborise
5016458ff7
Merge pull request #492 from ggvl/master
add support openEuler
2022-03-14 10:11:39 +03:00
Lv Genggeng
73201ebc8f add support openEuler
Signed-off-by: Lv Genggeng <lvgenggeng@uniontech.com>
2022-03-14 10:19:24 +08:00
catborise
2154f55e8b fix yes/no question confilict 2022-03-09 13:53:50 +03:00
catborise
df13d762a9
Merge pull request #490 from ReganHe93/master
add support uos
2022-03-07 11:26:34 +03:00
herengui
5b685a100e add support uos
Signed-off-by: herengui <herengui@uniontech.com>
2022-03-03 10:59:20 +08:00
catborise
3ddfa28189
Merge pull request #489 from manas-rust/master
webvirtcloud.sh support rockylinux
2022-02-23 16:56:33 +03:00
王卿
7858825258
webvirtcloud.sh support rockylinux 2022-02-23 16:51:05 +08:00
catborise
9554ffad49
Merge pull request #488 from catborise/master
update requirements
2022-02-10 15:35:23 +03:00
catborise
acca92ecb4 update requirements 2022-02-10 15:33:54 +03:00
catborise
ba06af9775
Merge pull request #485 from openfans-org/master
Chinese translation correction
2022-01-14 22:17:24 +03:00
aiminick
ac8db03102
Chinese translation correction. 2022-01-15 01:43:06 +08:00
catborise
9977c650db
Merge pull request #484 from catborise/master
updates & fixes 1
2022-01-12 13:41:10 +03:00
catborise
3d4c0aa5ef remove nl locales 2022-01-12 13:38:58 +03:00
catborise
79aeb3e23f update requirements 2022-01-12 12:32:17 +03:00
catborise
e0767f4145 update IPy.py to handle python3.10 and update readme 2022-01-12 12:29:49 +03:00
catborise
9e832c2612 add ldap3 explanations for errors 2021-12-23 12:32:36 +03:00
catborise
13f332c9a5 fix migrate logs to show source and destination hosts correctly 2021-12-22 11:27:32 +03:00
catborise
e6f7588593 update locale tr, fix commit errors 2021-12-20 11:00:02 +03:00
catborise
6c3d716b5a update docker ubuntu base image 2021-12-20 09:52:19 +03:00
catborise
53240548a2
Merge pull request #477 from catborise/master
fix iso upload path error
2021-12-17 09:24:24 +03:00
catborise
9ddcd0e0f3 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2021-12-17 09:22:12 +03:00
catborise
62d4cf88ba fix upload iso method 2021-12-17 09:21:42 +03:00
catborise
973cf9ab97
Merge pull request #475 from catborise/master
rbd support enhanced
2021-12-16 11:40:34 +03:00
catborise
2547e74400
Merge branch 'master' into master 2021-12-16 11:40:12 +03:00
catborise
a33283e172 update dev/conf requirements 2021-12-16 11:11:54 +03:00
catborise
c77e049e82 add rbd support for add new/existing vol. create from rbd based flavor 2021-12-16 11:03:03 +03:00
Anatoliy Guskov
e0e6e2b8aa
Merge pull request #474 from retspen/dependabot/pip/conf/django-3.2.10
Bump django from 3.2.9 to 3.2.10 in /conf
2021-12-13 21:57:16 +02:00
dependabot[bot]
0b16f85658
Bump django from 3.2.9 to 3.2.10 in /conf
Bumps [django](https://github.com/django/django) from 3.2.9 to 3.2.10.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.2.9...3.2.10)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-13 19:56:31 +00:00
Anatoliy Guskov
0234cf0e1a
Merge pull request #473 from retspen/dependabot/pip/conf/lxml-4.6.5
Bump lxml from 4.6.4 to 4.6.5 in /conf
2021-12-13 21:56:07 +02:00
dependabot[bot]
8b858f8964
Bump lxml from 4.6.4 to 4.6.5 in /conf
Bumps [lxml](https://github.com/lxml/lxml) from 4.6.4 to 4.6.5.
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-4.6.4...lxml-4.6.5)

---
updated-dependencies:
- dependency-name: lxml
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-13 19:48:33 +00:00
catborise
86723a8271 add missing pipe mark after snapshot icon 2021-12-10 14:39:56 +03:00
catborise
5c9a5daedc remove eval for security concerns 2021-12-06 09:22:02 +03:00
catborise
f690d1fa60 handle security concerns with file uploading 2021-12-06 08:42:50 +03:00
catborise
b8971a1fca
Merge pull request #470 from catborise/master
add MAC address inputs for instance create methods
2021-12-03 16:45:06 +03:00
catborise
c354393685 add MAC address inputs for instance create methods 2021-12-03 16:41:50 +03:00
catborise
83709eddbf
Merge pull request #467 from catborise/master
update requirements at conf/dev
2021-11-22 11:38:37 +03:00
catborise
61d822efaa update requirements at conf/dev 2021-11-22 11:37:04 +03:00
catborise
877995d6d6
Merge pull request #464 from catborise/master
fixes somethings
2021-10-05 16:15:08 +03:00
catborise
4d21a3184b fix: please wait dialog. 2021-10-04 16:18:36 +03:00
catborise
c1488eee52 navbars position is changed to stick-top 2021-10-01 09:48:09 +03:00
catborise
185444009b
Merge pull request #462 from catborise/master
some fixes ...
2021-09-13 10:53:55 +03:00
catborise
607e396352 update requirements 2021-09-13 10:51:58 +03:00
catborise
890f592148 also show add flavor button if there is not any 2021-09-13 10:51:43 +03:00
catborise
536caacc80 add title info to vm which list under computes 2021-09-13 10:51:28 +03:00
catborise
8a81a83f5e update required packages 2021-08-06 08:50:00 +03:00
catborise
2f2bd9622d fix vm clone log message error 2021-08-06 08:50:00 +03:00
catborise
2e3480677c fix create instance log error 2021-07-12 13:36:16 +03:00
catborise
5c9b9ce15f fix font-awesome ref for search fas ->fa (4.7) 2021-07-12 13:36:16 +03:00
catborise
950347c5d3 fix quartz secondary color palette 2021-07-12 13:36:16 +03:00
catborise
b08df75c53 fix form-select conversion from from-control 2021-07-12 13:36:16 +03:00
catborise
8db77f6136 fix some problems with quartz theme 2021-07-12 13:36:16 +03:00
catborise
45b727ae84
Merge pull request #452 from catborise/master
Bootstrap5
2021-07-07 14:14:57 +03:00
catborise
073b7b6717
Bootstrap5 (#17)
* Bootstrap5 migration
2021-07-07 14:12:38 +03:00
catborise
375479cf2f
Merge pull request #450 from catborise/master
Django Update & other
2021-06-15 14:30:45 +03:00
catborise
1663a49cee fix references to django 3 migration 2021-06-15 14:29:20 +03:00
catborise
ede5710d7c update secret references; move secret to virtsecret because of django compatibility 2021-06-15 14:17:55 +03:00
catborise
f161d6a1f3 update django and package names for travis ci 2021-06-15 14:16:37 +03:00
catborise
f3ac40277a Update ubuntu image to 20.04 2021-06-15 14:00:42 +03:00
catborise
18971f01a6 fix indentation error for ldapbackend.py 2021-06-15 13:44:05 +03:00
catborise
990b69ac51 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2021-06-15 13:34:29 +03:00
catborise
5aa22ac042 Django 3.2.4 LTS upgrade 2021-06-15 13:28:47 +03:00
catborise
d7c0f9445d rearrange search functionality 2021-06-15 13:28:47 +03:00
catborise
a47d006771 fix edit instance volume for running instances to prevent bus error 2021-06-15 13:28:47 +03:00
catborise
d9c23291de rearrange search functionality 2021-06-10 15:17:53 +03:00
catborise
646eae59a2 fix edit instance volume for running instances to prevent bus error 2021-06-10 13:46:08 +03:00
catborise
7c1eb5c854
Merge pull request #448 from catborise/master
update packages
2021-06-10 13:19:29 +03:00
catborise
768b49ea88 update django-icons to 4.0.0 2021-06-10 13:17:49 +03:00
catborise
cb3ed61872 exclude login view for login required necessity to prevent ERR_TOO_MANY_REDIRECTS error 2021-06-10 11:17:44 +03:00
catborise
5328c4c2ba update required packages 2021-06-10 11:16:15 +03:00
catborise
2c41a0ce76
Update codeql-analysis.yml 2021-06-02 10:00:34 +03:00
catborise
d113e34796
Merge pull request #444 from catborise/master
Host info for logs
2021-06-02 09:48:37 +03:00
kendarorg
e9b57bfcf7
LDAP Integration ( https://github.com/retspen/webvirtcloud/issues/243 ) (#443)
* Added ldap support

* Update

* Added logging

* Update

* Working

* Working

* Working

* Working

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Check for ldap3 existence

* Add eol

Co-authored-by: Kendar <unknown@kendar.org>
2021-06-02 09:46:12 +03:00
catborise
24cd3f70dd make dynamic websockify path for console-vnc-full 2021-06-02 09:11:54 +03:00
catborise
610efd5520 add host name for logs beside others 2021-05-31 11:39:09 +03:00
catborise
a20fa8e8d7
Merge pull request #433 from catborise/master
downgrade django otp requirements. it prevents login
2021-04-25 11:28:55 +03:00
catborise
70bc5a4408 downgrade django otp requirements. it prevents login 2021-04-25 11:27:41 +03:00
catborise
6980c92970
Merge pull request #430 from catborise/master
update ubuntu and debian package requirements
2021-04-21 12:08:13 +03:00
catborise
c940e6fa5a update ubuntu and debian package requirements 2021-04-21 12:07:14 +03:00
catborise
8fd4df554d
Merge pull request #428 from catborise/master
update various requirements
2021-04-19 10:33:02 +03:00
catborise
83159f3365 update various requirements 2021-04-19 10:27:59 +03:00
catborise
1bb25be234
Merge pull request #427 from hyperzlib/master
Fix a translate problem for zh-hans
2021-04-18 12:33:06 +03:00
Lex Lim
b01d86b038
Fix a translate problem for zh-hans
Instance (实例) and Interface (接口) are confused.
2021-04-18 14:39:29 +08:00
catborise
e3398bd6ff
Merge pull request #423 from decryptus/master
[TYPO] Removed useless = in nginx server_name variable
2021-04-04 09:34:00 +03:00
Adrien Delle Cave
9065922eb8 [TYPO] Removed useless = in nginx server_name variable 2021-04-04 01:31:15 +02:00
catborise
bc6631a330
Merge pull request #421 from catborise/master
package updates
2021-03-31 08:16:18 +03:00
catborise
483e5c9a68 fix host network interface name error 2021-03-30 15:43:58 +03:00
catborise
a5c1d8c308 update bootstrap to 4.6.0 & bootswatch 4.6.0 - fix some themes 2021-03-30 15:34:31 +03:00
catborise
05e4c72df0 update js-cookie to 2.2.1 2021-03-30 09:51:58 +03:00
catborise
90c3cc08fe update chart.js 2.9.3 to 2.9.4 2021-03-30 09:51:06 +03:00
catborise
5d14252957 update bootstrap 4.5.3 to 4.6.0 2021-03-30 09:48:23 +03:00
catborise
8bf2a50047 update jquery 3.5.2 -> 3.6.0 2021-03-30 09:47:37 +03:00
catborise
1e6ea063ab
Merge pull request #420 from catborise/master
update dependencies; libvirt, lxml & pylint pyflakes etc
2021-03-29 09:00:27 +03:00
catborise
71fc29daf3 update dependencies; libvirt, lxml & pylint pyflakes etc 2021-03-29 08:59:36 +03:00
catborise
1d8a63a329
Merge pull request #417 from catborise/master
update django req & coverage
2021-03-19 09:12:15 +03:00
catborise
a5b97ed994 update django req & coverage 2021-03-16 14:44:16 +03:00
catborise
49dd51d40c
Merge pull request #414 from decryptus/master
[BUG] Fixed sed variables and nginx fqdn config
2021-03-15 16:03:18 +03:00
Adrien Delle Cave
b2068592f5 [BUG] Fixed sed variables and nginx fqdn config 2021-03-12 12:38:52 +01:00
catborise
3caf1d538c
Merge pull request #411 from catborise/master
instance network change
2021-02-25 09:49:28 +03:00
catborise
fa4a3149c9 fix network add and change methods to handle bridge/mavctap differentation correctly. 2021-02-12 16:06:12 +03:00
catborise
2f1b11b3ca reorganize host interfaces. remove it from instance methods 2021-02-12 16:05:16 +03:00
catborise
6571ab372f
Merge pull request #409 from catborise/master
script fixes etc
2021-01-26 09:20:23 +03:00
catborise
59af676f68 fix centos 8 bootstrap script to handle new libvirt start mechanism 2021-01-25 16:53:09 +03:00
catborise
47331ba08f fix python string variables for snapshot and video model 2021-01-25 16:36:24 +03:00
catborise
2c915e04e0
Merge pull request #406 from catborise/master
update python packages
2021-01-11 12:24:41 +03:00
catborise
77d8ad95d8 update python packages 2021-01-11 12:21:12 +03:00
catborise
596d55d481
fix uuid and name changes and update them on db correctly. (#404) 2020-12-31 11:30:33 +03:00
catborise
e68a9bd47a fix uuid and name changes and update them on db correctly. 2020-12-31 11:26:35 +03:00
catborise
0a8beacf29
Merge pull request #401 from catborise/master
Add guest info(hostname,os,version,kernel) tab. indendation fixes
2020-12-18 10:05:22 +03:00
catborise
2b33847e8a
Merge pull request #400 from aiminickwong/master
add locale/zh/LC_MESSAGES files
2020-12-18 10:00:41 +03:00
aiminick
40af106ecc
Add files via upload 2020-12-17 23:50:00 +08:00
aiminick
8b840153e5
Delete django.mo 2020-12-17 23:48:58 +08:00
aiminick
fe15742519
add zh (Simple Chines) Translate po/mo support 2020-12-17 23:06:13 +08:00
aiminick
66b1d7b8a6
Merge pull request #2 from retspen/master
update
2020-12-17 23:03:24 +08:00
catborise
d30e6470e2 Add guest info like:hostname,os,version,kernel. indendation fixes 2020-12-17 14:29:07 +03:00
catborise
0e858a7eb6
Merge pull request #398 from catborise/master
fixes
2020-12-15 11:43:09 +03:00
catborise
993a247228 update uuid number if it is changed to fix console view error 2020-12-15 11:39:58 +03:00
catborise
bd44b03bb0 add missing instance title for grouped instances list 2020-12-01 09:46:00 +03:00
catborise
82ded115b9
Merge pull request #392 from catborise/master
security update
2020-11-20 15:36:38 +03:00
catborise
3b74571580 disable all codebase linting but only changed ones 2020-11-20 14:20:29 +03:00
catborise
88c261e278 prevent users to access others vnc console if he is not owner 2020-11-20 14:19:53 +03:00
catborise
dbf3fc0db3
Merge pull request #391 from catborise/master
update ace & multiselect
2020-11-19 10:41:10 +03:00
catborise
cf01625945 bootstrap-multiselect update to 0.9.16: bootstrap4 support 2020-11-16 12:06:22 +03:00
catborise
60b5de924f Ace.js upgrade to 1.4.12 2020-11-06 16:40:44 +03:00
catborise
1b3e7594c0
Merge pull request #386 from catborise/master
upgrades
2020-11-06 16:18:42 +03:00
catborise
e2e62a3ad4 Bootstrap & Bootswatch Upgrade: 4.5 -> 4.5.3 2020-11-06 16:16:40 +03:00
catborise
12fa9e7cf1 upgrade required python packages 2020-11-06 14:53:39 +03:00
catborise
4b7554ea14 increase nginx timeout. Enable live snapshot. 2020-11-06 14:30:41 +03:00
catborise
9aed7c7716
Merge pull request #385 from catborise/master
black & isort linter fixes- bug fixes
2020-11-05 14:28:53 +03:00
catborise
c83e26c797 fix merge conflicts for locale files 2020-11-05 14:27:57 +03:00
catborise
917d14c74d localization updates 2020-11-05 12:40:56 +03:00
catborise
508e3609be lint with black python. convert f style strings to old one. some small fixes 2020-11-05 12:34:31 +03:00
catborise
159e06221a localization updates 2020-10-23 11:38:52 +03:00
catborise
c20c353a40 localization updates 2020-10-23 11:35:32 +03:00
catborise
488b71a68f page header rearragements 2020-10-21 12:10:42 +03:00
catborise
85d4c5f132 fix to prevent not opened connection close error 2020-10-21 12:10:42 +03:00
catborise
5a3e0ad6f2 page header rearragements 2020-10-21 12:09:06 +03:00
catborise
f759dc6564 fix to prevent not opened connection close error 2020-10-21 12:08:58 +03:00
catborise
83c8eccde3
Merge pull request #378 from Real-Gecko/master
OTP improvements #341
2020-10-20 13:46:08 +03:00
Real-Gecko
1f26bd0c31 Do not send email if user email is not set 2020-10-19 17:11:28 +06:00
Real-Gecko
5a19f0c949 OTP improvements 2020-10-19 14:26:08 +06:00
catborise
1f642a4381 fix linter problems-2: bash 2020-10-17 23:30:00 +03:00
catborise
7d22fec124 fix linter problems: dockerfile 2020-10-17 23:30:00 +03:00
catborise
38c6ef12df add excluded folders & create venv 2020-10-17 23:30:00 +03:00
catborise
b4430eddcc fix linter errors: bash scripts 2020-10-17 23:30:00 +03:00
catborise
25d5df47da
Merge pull request #373 from Real-Gecko/master
Templates cleanup
2020-10-16 11:39:29 +03:00
catborise
9402ae2abb fix superlinter dependencies & others 2020-10-16 11:39:06 +03:00
catborise
737e0faef7 add code analysis for security 2020-10-16 11:39:06 +03:00
Real-Gecko
01043ba8a8 Templates cleanup 2020-10-15 20:18:45 +06:00
catborise
4f959eaee8
Merge pull request #370 from Real-Gecko/master
More instances tests
2020-10-15 16:31:47 +03:00
Real-Gecko
d04267eaa4 Minor improvements for post_migrate signals 2020-10-15 17:49:36 +06:00
Real-Gecko
0f66187e80 More instances tests 2020-10-15 11:58:42 +06:00
catborise
9e56378682
Merge pull request #369 from Real-Gecko/master
replaced ugettext_lazy with gettext_lazy
2020-10-14 18:50:13 +03:00
Real-Gecko
1b3fd9d05e replaced ugettext_lazy with gettext_lazy 2020-10-14 18:27:57 +06:00
Real-Gecko
5172a9f619 Accounts app improvements and tests 2020-10-14 14:25:05 +03:00
Real-Gecko
8afef36656 Downgrade libvirt-python to fix taking snapshots 2020-10-12 16:19:29 +03:00
catborise
106985bc7e
Merge pull request #363 from saimintech/saimintech/gitpod-setup
Fully automate dev setup with Gitpod
2020-10-09 22:52:28 +03:00
catborise
116b39018a
Merge pull request #365 from Real-Gecko/2fa
Implemented OTP #341
2020-10-09 22:50:00 +03:00
catborise
a20a2fe955
Merge pull request #362 from Real-Gecko/master
Replaced f strings in instances according to #339
2020-10-09 17:49:31 +03:00
saimintech
28a58011b8 Fully automate dev setup with Gitpod
This commit implements a fully-automated development setup using Gitpod.io, an
online IDE for GitHub and GitLab that enables Dev-Environments-As-Code.
This makes it easy for anyone to get a ready-to-code workspace for any branch,
issue or pull request almost instantly with a single click.
2020-10-08 17:44:07 +00:00
Real-Gecko
0052323190 Implemented OTP #341 2020-10-08 17:57:51 +06:00
Real-Gecko
a942f55854 Fixed error handling from gstfsd in instances 2020-10-07 18:04:43 +06:00
Real-Gecko
d19ec05638 Replaced f strings in instances according to #339 2020-10-07 12:08:48 +06:00
Anatoliy Guskov
cbac82ba07
Merge pull request #360 from catborise/master
some linter fixes
2020-09-27 17:12:01 +03:00
Anatoliy Guskov
f39e174db4
Merge pull request #359 from pytker/master
edit choices field length
2020-09-27 15:31:15 +03:00
catborise
922a8e62d1 fix some linter warnings 2020-09-25 15:57:17 +03:00
catborise
119d874e85 linter update & django update 2020-09-25 14:11:03 +03:00
catborise
ba8fa20737 fix linter warnings for bash 2020-09-25 13:26:17 +03:00
catborise
32343c1ddc update libvirt-python, django, libsass requirements 2020-09-25 09:53:34 +03:00
catborise
aee7fa34b8 fix user not found while editing user profile 2020-09-25 09:21:37 +03:00
sergey kilin
3c72bedd7a edit choices field length 2020-09-21 16:22:44 +04:00
Anatoliy Guskov
2fe1f1c73a
Merge pull request #357 from catborise/master
fixes and getvvfile download option
2020-09-16 16:30:07 +03:00
catborise
45e3b00180 add getvvfile for desktop viewers.(virt-viewer) 2020-09-11 15:56:11 +03:00
catborise
82f5fbf159 change choices size from 50 to 70 2020-09-11 15:35:32 +03:00
Anatoliy Guskov
a1fec1ebb5
Merge pull request #354 from catborise/master
fixes and others
2020-08-22 11:20:58 +03:00
catborise
372ba5a0f2 fix create user error 2020-08-21 20:53:16 +03:00
catborise
7dd9127572 Automatic install option for Webvirtcloud is added (beta) 2020-08-21 18:38:10 +03:00
catborise
c2bd8d8547 add nginx configuration files for os type 2020-08-19 16:17:12 +03:00
Anatoliy Guskov
a13247080e
Merge pull request #352 from shokinn/fix_novncd_port
Fix Port configuration for novncd
2020-08-18 19:00:47 +03:00
catborise
83ba6da572 fix instance create error handling 2020-08-17 09:58:42 +03:00
catborise
6d52587e60 fix messages.error error 2020-08-14 16:17:10 +03:00
Philip Henning
5850d14722 Fix Port configuration for novncd
Reverte accidentally change variable for novncd port configuration.

Change variable `WS_PUBLIC_PORT` back to `WS_PORT`
2020-08-13 17:55:40 +02:00
catborise
43f1461e29 update novnc 1.1.0 ->1.2.0 2020-08-11 15:05:09 +03:00
Anatoliy Guskov
8e4073f4b7
Merge pull request #346 from catborise/master
fix some errors
2020-07-25 10:47:31 +03:00
catborise
bb935b3713 fix computes list table layout 2020-07-23 13:57:10 +03:00
catborise
6a8d713ae6 hide compute login password for tcp/tls connections 2020-07-23 13:49:29 +03:00
catborise
015719b952 add macvtap support : create macvtap virtual net, add/edit instance network for macvtap 2020-07-23 13:36:49 +03:00
catborise
46884304b0 fix macvtap network list 2020-07-23 13:36:36 +03:00
catborise
de63d9746d add change other user password ability to superusers 2020-07-23 13:16:39 +03:00
catborise
fb3ef6be98 fix sendctrlaltdel for spice console 2020-07-23 11:07:22 +03:00
catborise
6ab32e1697 fix missing uuid record on db while creating instance 2020-07-22 16:09:49 +03:00
catborise
6bbebdd37c not push only try to import debug_toolbar if settings.debug is true 2020-07-22 11:24:01 +03:00
catborise
02654f6eda update README.md: app screenshots 2020-07-21 16:06:48 +03:00
catborise
2ef6563d09 update locale 2020-07-20 12:37:04 +03:00
catborise
400dd6c86e add action definition for destroy operation 2020-07-20 12:27:36 +03:00
catborise
2d6aaaf707 fix redundant notification message 2020-07-20 12:21:16 +03:00
catborise
84a545dc61 fix divide remainder error for memory resize etc 2020-07-20 12:10:30 +03:00
catborise
74028f35b8 fix console keymap auto related error 2020-07-20 12:00:01 +03:00
Anatoliy Guskov
845c78c564
Merge pull request #345 from skyline75489/fix/none-console-type
Handle None console type
2020-07-20 11:41:12 +03:00
Anatoliy Guskov
618d5ec7c1
Merge pull request #344 from catborise/console_enrichments
vnc console options added
2020-07-20 11:40:51 +03:00
catborise
abfbc54594 fix:move from accounts, after permission migrate applyings to instances 2020-07-20 10:44:33 +03:00
catborise
30948ca445 superlinter update to v3 2020-07-20 10:17:35 +03:00
skyline75489
0e0521c61f Handle None console type 2020-07-17 20:32:06 +08:00
catborise
b5b43b71bd add dev options to settings + url 2020-07-17 15:27:57 +03:00
catborise
523f58b55e update package version & add apps to travis 2020-07-17 14:42:57 +03:00
catborise
519234b521 fix typo, etc 2020-07-17 14:00:11 +03:00
catborise
44aa746f4b make console password optional for users with permissions 2020-07-17 11:05:47 +03:00
catborise
74a4a1a3ef vnc console options added 2020-07-17 11:04:21 +03:00
Anatoliy Guskov
522b95fe39
Merge pull request #343 from catborise/consol_tunnel_fix
Consol tunnel fix
2020-07-14 19:05:49 +03:00
Anatoliy Guskov
3d3069d94d
Merge pull request #342 from Real-Gecko/instances
Instances overhaul
2020-07-14 19:04:40 +03:00
catborise
0b86e34203 replace tunnel.py with sshtunnel.py some small fixes 2020-07-14 15:44:20 +03:00
catborise
b6cb81c3bc tunnel.py fix socket creation problem 2020-07-14 15:10:03 +03:00
Real-Gecko
47009d47ca Instances overhaul 2020-07-13 15:33:09 +06:00
Anatoliy Guskov
f23e6b000f
Merge pull request #334 from catborise/wsproxy
wsproxy patch
2020-06-28 20:59:34 +03:00
catborise
61060c0b06 update super-linter ci 2020-06-26 16:53:37 +03:00
catborise
c0f707854a wsproxy patch 2020-06-26 16:31:41 +03:00
Anatoliy Guskov
6c79c2bace
Merge pull request #333 from catborise/master
spice console password fix & translate additions
2020-06-26 15:27:03 +03:00
Anatoliy Guskov
ed7cffd073
Merge pull request #332 from Real-Gecko/master
https://github.com/retspen/webvirtcloud/issues/323#issuecomment-64998
2020-06-26 15:26:37 +03:00
catborise
1320753d6c spice console password fix & translate additions 2020-06-26 10:06:51 +03:00
Real-Gecko
1430c73c13 https://github.com/retspen/webvirtcloud/issues/323#issuecomment-649986187 2020-06-26 12:48:53 +06:00
Anatoliy Guskov
ce123d1f22
Merge pull request #330 from catborise/console
Console
2020-06-25 21:02:36 +03:00
catborise
4f8a1fd50d fix console password & sendkey & css fixes & add scale vb 2020-06-24 12:39:32 +03:00
Anatoliy Guskov
9a0b456e34
Merge pull request #328 from glzjin/patch-1
Fix #325
2020-06-22 10:16:41 +03:00
Anatoliy Guskov
5a73f14889
Merge pull request #327 from Real-Gecko/master
Fixed #326
2020-06-22 10:16:12 +03:00
catborise
48f9ba6d73 add super linter in to ci 2020-06-22 08:17:13 +03:00
glzjin
720b5cab84
Fix #325 2020-06-21 20:16:38 +08:00
Real-Gecko
ce7ebeb55c Fixed #326 2020-06-19 10:38:06 +06:00
Anatoliy Guskov
ecaf11e02a
Merge pull request #324 from Real-Gecko/master
Some improvements for appsettings app
2020-06-18 12:22:45 +03:00
Real-Gecko
eda233ada7 Fix appsettings 2020-06-17 15:23:44 +06:00
Real-Gecko
bb8f95be14 Some improvements for appsettings app 2020-06-17 15:15:58 +06:00
Anatoliy Guskov
c791f582af
Merge pull request #322 from Real-Gecko/master
Minor fixes and more tests
2020-06-17 12:01:58 +03:00
Real-Gecko
7eee811e65 Namespaced instances urls 2020-06-16 18:57:50 +06:00
Real-Gecko
636b5bb1bc Reworked some account views 2020-06-16 18:35:08 +06:00
Real-Gecko
1a3cc36ada Moved admin/common/form.html to project templates 2020-06-16 18:04:37 +06:00
Real-Gecko
d9785e397b Test password change view 2020-06-16 15:17:21 +06:00
Real-Gecko
b85e246aed Moved password changing to a separate view 2020-06-16 14:36:53 +06:00
Real-Gecko
2951d0b035 Cleaned up UserAttributes model 2020-06-16 11:26:58 +06:00
Real-Gecko
20d4aaac30 Removed UserAddForm as it is not used 2020-06-16 11:09:55 +06:00
Real-Gecko
d3c59acb3f More tests for computes 2020-06-16 11:04:09 +06:00
Real-Gecko
a4d28f2953 Reworked some computes views and added more tests 2020-06-15 17:52:05 +06:00
Real-Gecko
68f7376d15 Moved confirm_delete.html to project templates 2020-06-15 16:30:27 +06:00
Real-Gecko
354163fa1d Added pagination to logs list template 2020-06-15 15:48:32 +06:00
Real-Gecko
e4e86ed576 replaced django-fa with django-icons 2020-06-15 14:46:13 +06:00
Real-Gecko
9a75ceb0e5 Replaced __unicode__ with __str__ for models 2020-06-15 12:57:17 +06:00
Real-Gecko
cfd739778c Added missing migrations 2020-06-15 12:38:44 +06:00
Real-Gecko
ca3b88097e Updated requiremets and added dev reqs 2020-06-15 12:31:16 +06:00
Anatoliy Guskov
ad4b417695
Merge pull request #321 from catborise/master
Translation and locale file updates
2020-06-13 16:55:19 +03:00
catborise
e97d592e51 fix some messages, add translation for some keywords, update tr locale file 2020-06-12 14:46:17 +03:00
catborise
fd3212de90 fix layout for instance qos configuration 2020-06-10 16:25:29 +03:00
catborise
b7fc8a5fea Language change activation. Fix typos and add new translations 2020-06-10 15:16:58 +03:00
catborise
7bb6bd310b update locale files and TR translations. 2020-06-10 11:15:14 +03:00
catborise
31c86a6a7b add bottom space for computes cards 2020-06-09 14:13:40 +03:00
catborise
21fcb8cd64 workaround for django 2.2 bug related with '0011_update_proxy_permissions' migration 2020-06-09 11:44:29 +03:00
Anatoliy Guskov
f263d97377
Merge pull request #320 from catborise/bigupdates
Bigupdates
2020-06-08 12:37:40 +03:00
catborise
a0665e9682
Merge branch 'master' into bigupdates 2020-06-08 09:20:17 +03:00
catborise
898de1ffbc add new file at the end of wvc-main.scss & computes url add slash 2020-06-08 09:15:06 +03:00
dependabot[bot]
c43c10dece Bump django from 2.2.12 to 2.2.13 in /conf
Bumps [django](https://github.com/django/django) from 2.2.12 to 2.2.13.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/2.2.12...2.2.13)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-08 09:12:43 +03:00
Anatoliy Guskov
3d95f6abae
Merge pull request #318 from retspen/dependabot/pip/conf/django-2.2.13
Bump django from 2.2.12 to 2.2.13 in /conf
2020-06-07 20:06:38 +03:00
Anatoliy Guskov
dff60e4be4
Merge pull request #317 from catborise/rets
fixes and updates
2020-06-07 20:03:34 +03:00
dependabot[bot]
d834cbbb7b
Bump django from 2.2.12 to 2.2.13 in /conf
Bumps [django](https://github.com/django/django) from 2.2.12 to 2.2.13.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/2.2.12...2.2.13)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-06 06:16:13 +00:00
catborise
07be61a557 fix detach cdrom problem and cd-rom list's layout problem 2020-06-05 09:29:26 +03:00
catborise
509c57d8ab fix secret list details 2020-06-04 15:45:20 +03:00
catborise
6a0d04d300 replace deprecated ifequal/ifnotequal with if 2020-06-04 15:40:57 +03:00
catborise
e46bb99b3b fix typos and rearrange imports & add new lines EOF 2020-06-04 15:22:55 +03:00
catborise
e21f423bcf Convert LOGS_PER_PAGE from static settings to appsettings 2020-06-04 14:35:34 +03:00
catborise
5e00f896d4 add SHOW_PROFILE_EDIT_PASSWORD constant 2020-06-04 14:33:53 +03:00
catborise
c6f0a05f48 django2 style conversion of setting environment 2020-06-04 14:32:51 +03:00
catborise
028ce0c208 urls.py: convert re_path to path 2020-06-04 14:31:37 +03:00
catborise
fc2662bd91 fix secret types sync with libvirt library 2020-06-04 12:44:47 +03:00
catborise
fe13e4dd66 remove Account View Style settings 2020-06-04 12:42:04 +03:00
catborise
91a6dcc9f6 Move Allow Empty Password Appsettings to settings.py file 2020-06-04 12:40:15 +03:00
catborise
24a578f61b Remove some constants from settings.py 2020-06-04 12:36:56 +03:00
catborise
7538ddab4f Add all settings to appsettings page & Convert settings choices 2020-06-04 12:32:00 +03:00
catborise
62f8ece0ef update django files to Django 2 2020-06-04 12:06:23 +03:00
catborise
bf15de507a add aria-label definition to forms 2020-06-04 12:05:55 +03:00
catborise
8bafaa8720 add scope to th tags for screen readers 2020-06-04 12:04:46 +03:00
catborise
c9d9fdb9e8 add log & alert messages for appsettings changes 2020-06-04 12:04:19 +03:00
catborise
2d5c701789 Add Settings page: Move theme & lang changer to settings page 2020-06-04 12:03:26 +03:00
catborise
7409c197ed truncate long file names longer than 55 characters 2020-06-04 11:39:02 +03:00
catborise
7c93ee1e2f bootstrap 4.5 fixations & Update strings style to f-string 2020-06-04 11:38:41 +03:00
catborise
ccd947a04c update language files 2020-06-04 11:37:13 +03:00
catborise
a93f8b3321 convert strings to python3 f-Strings 2020-06-04 11:37:01 +03:00
catborise
0461df7685 bootstrap4.5: fix modal headers, some glitches 2020-06-04 11:36:47 +03:00
catborise
9ab776197c hide details with collapse while creating instance with flavor 2020-06-04 11:36:18 +03:00
catborise
871083967f bootstrap 4.5 fixations & fix various localizations 2020-06-04 11:35:57 +03:00
catborise
bc25e22ae4 add localization files 2020-06-04 11:34:26 +03:00
catborise
34006ad223 hide some details with a collapse while custom/template instance creation 2020-06-04 11:34:08 +03:00
catborise
0e5840498e bootstrap 4.5 & Bootswatch Themes 2020-06-04 11:33:25 +03:00
catborise
e2b7b77da0 jquery update 3.4.1 -> 3.5.1 2020-06-04 08:52:32 +03:00
catborise
11fcb6c0f3 show socket error & check with is True 2020-06-04 08:52:21 +03:00
catborise
566a9d446a add host status checking for statistic request 2020-06-04 08:52:07 +03:00
catborise
0c72ccd066 add localhost is up checking 2020-06-04 08:51:54 +03:00
catborise
b48b112a40 Update Ace XML Editor 1.4.10 - Move seperate dir 2020-06-04 08:51:27 +03:00
catborise
d384c914a4 stop getting status info before instance delete to prevent domain not found error 2020-06-04 08:51:14 +03:00
catborise
966da065b4 get host info from compute not instance 2020-06-04 08:50:33 +03:00
catborise
e72073fa79 fix setting title/description function (xml -> lxml) 2020-06-04 08:50:17 +03:00
catborise
e911db8124 fix isinstance method call 2020-06-04 08:49:09 +03:00
catborise
22cb8f702a fix code conventions for pylint 2020-06-04 08:48:43 +03:00
catborise
d283e0e1b3 remove unused resize request from instance 2020-06-04 08:25:22 +03:00
Anatoliy Guskov
37022df459
Merge pull request #315 from Real-Gecko/master
Fixed migrations for new deployments, added unit tests
2020-06-03 10:25:53 +03:00
Real-Gecko
10b6f88dbb Added coverage.py support 2020-05-29 19:37:49 +06:00
Real-Gecko
7103c52380 Reworked some computes forms and views 2020-05-29 19:25:36 +06:00
Real-Gecko
5ab22ba947 Added accounts test(moved from admin)
Actuall added admin tests
2020-05-29 15:47:13 +06:00
Real-Gecko
7b0016c0b2 Some test for computes 2020-05-29 14:00:51 +06:00
Real-Gecko
83b1dde673 Added admin app unit tests 2020-05-29 12:46:25 +06:00
Real-Gecko
85929b5327 Fixe migrations for new deployments 2020-05-28 18:19:25 +06:00
Anatoliy Guskov
9718b4c215
Merge pull request #314 from Real-Gecko/master
Some fixes and changes
2020-05-28 11:37:26 +03:00
Real-Gecko
e4e79d3d4b Added info from #18 to README 2020-05-28 11:36:49 +06:00
Real-Gecko
3d0493537f Added 'instances:clone_instances' permission
Replaces 'can_clone_instances' user attribute
2020-05-28 11:20:23 +06:00
Real-Gecko
a62daad87b Added change password permission
Replaces SHOW_PROFILE_EDIT_PASSWORD option
Can be set on per user or per group basis
2020-05-27 18:46:37 +06:00
Real-Gecko
2a0d240038 Fix admin urls not included into main urls.py 2020-05-27 18:41:34 +06:00
Real-Gecko
27f62dff6c Added admin application
- Manage users
- Manage groups
- Manage logs
2020-05-27 18:24:06 +06:00
Real-Gecko
38befa4362 Added django-login-required-middleware
Thus eliminating need for login_requred decorator on every view
2020-05-27 12:43:29 +06:00
Real-Gecko
6d82c2820b Fix instance bottom bar overlapping main content 2020-05-25 17:32:36 +06:00
Real-Gecko
618d88f1c4 Fix default admin user
- Fix error: RelatedObjectDoesNotExist: User has no userattributes.
  "instances/views.py", line 162, in check_user_quota
2020-05-25 16:39:34 +06:00
Anatoliy Guskov
d9fa43463b
Merge pull request #308 from catborise/master
Django2 & Python3 Migrate
2020-04-20 10:17:09 +03:00
catborise
2a490cc820
Merge branch 'master' into master 2020-04-17 18:07:22 +03:00
catborise
52c856c504 add show/hide bottom bar option in instance details 2020-04-17 15:37:34 +03:00
catborise
f37d6601e1 create admin user while migrating 2020-04-17 14:03:42 +03:00
catborise
6aa6ea0817 change libguestfs version python3 2020-04-17 14:03:03 +03:00
catborise
d3de4bbc30 change statements python -> python3 2020-04-17 14:02:18 +03:00
catborise
f9b837841c update django 2.2.10 to 2.2.12 2020-04-06 14:14:17 +03:00
catborise
9926edf208 fix hostname for user quota connection 2020-04-06 14:13:10 +03:00
catborise
d5156303ee fix hostname for user instance list 2020-04-06 13:05:20 +03:00
catborise
4d40de1b55 Python3 & Django 2.2 Migration - Fix & Updates 2020-03-16 16:59:45 +03:00
Anatoliy Guskov
fd9465c769
Merge pull request #300 from catborise/master
some small fixes
2020-02-25 16:24:02 +02:00
catborise
fc8612c604 change os.environ set style which is new one. 2020-02-24 10:08:48 +03:00
catborise
450f633e79 fix computes url: remove redundant slash 2020-02-24 10:07:43 +03:00
Anatoliy Guskov
4ff77e31c1
Merge pull request #294 from retspen/dependabot/pip/conf/django-1.11.28
Bump django from 1.11.26 to 1.11.28 in /conf
2020-02-14 13:31:11 +02:00
dependabot[bot]
cf9530dabd
Bump django from 1.11.26 to 1.11.28 in /conf
Bumps [django](https://github.com/django/django) from 1.11.26 to 1.11.28.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/1.11.26...1.11.28)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-11 22:03:22 +00:00
Anatoliy Guskov
ff311a609c
Merge pull request #288 from catborise/master
various fixes and updates
2020-01-24 21:14:43 +02:00
catborise
1c9c717f67 fix indentation. fix imports and description. 2020-01-24 14:30:37 +03:00
catborise
a6f7618854 convert django.core.urlresolver to django.urls. fix i18n. fix identation 2020-01-24 10:09:46 +03:00
catborise
39b53dbfc7 correct except error 2020-01-24 10:07:45 +03:00
catborise
b768dbf59d update chart.bunjle.js to 2.9.3 2020-01-23 16:39:04 +03:00
catborise
562fe5c3dc Update spice-html5 2020-01-23 15:49:43 +03:00
catborise
34394c2b5e console layout rearranged. fix indentation. add translate tags 2020-01-23 15:45:41 +03:00
catborise
d95b5a4017 allinstances.html: fix identation. Remove redundant div blocks 2020-01-22 14:50:12 +03:00
catborise
57665c2ab9 add cdrom to instance errors fixed. make cdrom readonly, disable cache. 2020-01-22 14:48:48 +03:00
catborise
9198615076 guest agent status indicator changed 2020-01-21 15:39:16 +03:00
catborise
c79a635923 change compute name, hostaname, details char size. url.py typo fix 2020-01-21 15:26:35 +03:00
catborise
70032fc2e1 re organize instance overview. add snapshot indicator if there is... add bottom bar for navigation. 2020-01-20 15:47:14 +03:00
catborise
0c8a0523a8 show snapshot list while instance is running. User wanto to see list of running snapshot and delete it. 2020-01-17 16:14:28 +03:00
Anatoliy Guskov
d5fe941b2f
Merge pull request #284 from catborise/master
New additions, changes
2020-01-15 10:43:13 +02:00
catborise
59dcdacffc
Merge branch 'master' into master 2020-01-14 09:13:22 +03:00
catborise
6cebd2f70e add seperation between nics, show nic model 2020-01-13 16:18:19 +03:00
catborise
2a86384826 Show notification after resize operation 2020-01-13 16:14:21 +03:00
catborise
c37ae1dca7 fix layout to show correctly 'not connected' panels 2020-01-13 09:00:51 +03:00
Tomas Hlavacek
2058395081 Mark the unreachable computes properly
When a compute can not be connected (for whatever reason) mark it as
not connected in the template.

The generic check

  if compute.status

is not enough because the status variable can contain a string with a
connection error.
2020-01-13 08:37:48 +03:00
Anatoliy Guskov
6f23ce4644
Merge pull request #279 from tmshlvck/master
Mark the unreachable computes properly
2020-01-12 22:26:31 +02:00
catborise
c62e6ba3bc Add changing option of network model. Show model on instance network list 2020-01-10 17:55:42 +03:00
catborise
8b9fe4e887 rearrange instance network to show ipv4 and ipv6 address. Add capability to show all ip address not one of them. Add ipv4 address to instance summary 2020-01-10 15:28:09 +03:00
catborise
f2ba2b58b0 Add editing disk capability to instance. Add instance disk options like io, discard, detect zeroes. rewrite detach disk 2020-01-08 11:28:46 +03:00
catborise
c05729158a add close button for dialog 2020-01-08 11:25:55 +03:00
catborise
b3538ee87b add id for aria-label 2020-01-08 11:25:13 +03:00
catborise
30ed71e8ae add disk options choices 2020-01-08 11:24:22 +03:00
catborise
2b96089542 add disk options for instance. add keyboard input type. 2020-01-08 11:23:10 +03:00
catborise
0ef876c7a7 remove blank spaces 2020-01-08 08:53:50 +03:00
Tomas Hlavacek
9aee37ab60 Mark the unreachable computes properly
When a compute can not be connected (for whatever reason) mark it as
not connected in the template.

The generic check

  if compute.status

is not enough because the status variable can contain a string with a
connection error.
2020-01-01 23:28:07 +01:00
Anatoliy Guskov
3da734e804
Merge pull request #271 from catborise/master
Some improvements
2019-12-26 10:56:53 +02:00
catborise
38054d9882 for instances add guest agent indicator: installed, connected. 2019-12-25 14:36:43 +03:00
catborise
e87d7463fe check if host supports kvm, if it is, use kvm else qemu 2019-12-25 14:34:55 +03:00
catborise
9d58e56d16 add/remove guest agent channel function added 2019-12-24 17:19:11 +03:00
catborise
ecf31b0b5b adds link state option for instance networks. Enable/disable link while instance running 2019-12-24 14:54:04 +03:00
catborise
01f2290dd9 host ifaces configuration error prevents management of instance. It is isolated. 2019-12-24 09:23:49 +03:00
catborise
592112590c add ability to show interface's ip address with qemu agent. Add checking agent if installed or not 2019-12-23 15:48:11 +03:00
catborise
adfdad013c change cd-rom bus for q35 type machines; it is sata not ide 2019-12-20 16:43:14 +03:00
Anatoliy Guskov
fa852566aa
Merge pull request #270 from catborise/master
New options for instance create & handle nvram & migrate options
2019-12-19 17:35:22 +02:00
catborise
2fa5a98844 Add new options for migrate: Auto converge, postcopy and compress 2019-12-19 16:06:51 +03:00
catborise
c6cdb4929c Check firmware secure keyword with safe way to prevent key not exist 2019-12-19 16:06:06 +03:00
catborise
d401d2f3ff Add virtio support check, add more control while creating instance 2019-12-19 13:49:41 +03:00
catborise
6634207ef5 Add nvram clone, rewrite instance migration, add some helper functions 2019-12-19 13:47:07 +03:00
catborise
1b0324e3e3 Add is_support_virtio function and is_qemu functions 2019-12-19 12:06:42 +03:00
catborise
ab024acc78 setting.py.template add blank line at the end of file 2019-12-19 12:05:35 +03:00
catborise
ff96ce6648 Storage.py upper constant variable names. make mode and suffix variables a parameter 2019-12-19 12:03:25 +03:00
catborise
28ea64cd16 Fix column offset for small screens 2019-12-19 12:01:34 +03:00
catborise
a5e77ef01e How to update section change: remove creating virtual env command 2019-12-19 12:00:24 +03:00
catborise
8df71efdca Update django requirement 1.11.23 -> 1.11.26, update libvirt-api: 5.3 -> 5.10 and lxml 2019-12-18 09:04:50 +03:00
catborise
dd16a5b2d5 Enrich Instance Create operation: -add capability to create arm, ppc, i686, aarch64 instances, -add option to choose firmware, -add options to choose chipset. -add capability to choose volume driver options. -add new default settings 2019-12-13 16:47:51 +03:00
catborise
28b001e7cb Add capability to remove UEFI instances: Add option for NVRAM delete 2019-12-13 16:40:59 +03:00
catborise
3a925af3c2 fix connection undefined error while getting volumes 2019-12-13 16:38:21 +03:00
catborise
b91f1cc36b Add UEFI Architecture Patterns 2019-12-13 16:36:34 +03:00
catborise
b27d27d532 fix variable to show bus types 2019-12-09 13:41:31 +03:00
catborise
3752ad5160 Hypervisor list rearrange view on Host overview, if it is more than 4 2019-12-05 11:33:07 +03:00
Anatoliy Guskov
8ed1630686
Merge pull request #266 from catborise/master
add hot cpu enabling disabling functionality
2019-12-02 13:11:05 +02:00
catborise
49e179590e adding enable/disable cpu hotplug, live add/remove vcpu to instance. 2019-11-29 14:48:24 +03:00
catborise
e4223dde5d qemu.conf for vnc and spice listen address configured for all OS. Add sasl2 config for debian10 2019-11-25 15:21:14 +03:00
catborise
44df080b72 Rearrange supported hypervisors list: if there are hypervisor support more than 5, it spans to multiple line which causes distortion. 2019-11-25 11:02:50 +03:00
catborise
941a97201e settings.py: add ws_public_port comment and set default value 6080 2019-11-25 11:00:25 +03:00
Anatoliy Guskov
c2d69d0f9b
Merge pull request #265 from relzhong/patch-1
fix: missing fi bugs
2019-11-24 12:02:00 +02:00
Rel Zhong
ba91c70d83
fix: missing fi bugs 2019-11-24 15:19:38 +08:00
Anatoliy Guskov
b816bb28e3
Merge pull request #264 from catborise/master
Debian10 installation & And Qos fix
2019-11-22 19:05:52 +02:00
catborise
34df35c5d7 libvirt-bootstrap.sh: add debian 10 package install support 2019-11-22 11:52:08 +03:00
catborise
b297e10115 instance.html: Add new column for Qos, and send qos data with different form to prevent validation problem 2019-11-22 09:36:51 +03:00
Anatoliy Guskov
3bae40c8aa
Merge pull request #263 from catborise/master
Some addition & Fixes
2019-11-21 12:28:37 +02:00
Anatoliy Guskov
68107aee58
Merge pull request #260 from busyrack/master
Max length increased and public port
2019-11-20 16:36:52 +02:00
catborise
b83528658e fix datasource/templates/user_data, add newline at the end of ssh key and dont add other empty lines by @honza801 2019-11-20 16:53:17 +03:00
Ing. Jan Krcmar
f42a910d9b instance.is_template checkbox enabled for user.is_staff by @honz801 2019-11-20 16:48:31 +03:00
catborise
74eb8004d7 changing video card model functionality added to options. 2019-11-20 16:24:01 +03:00
catborise
60684cc7b1 Live resize VM Memory activate. Redirection fix for resizing operations. Some typo & indent fix 2019-11-20 11:41:50 +03:00
catborise
228a0949d1 Full VNC view distortion fixed. Add translate for some texts 2019-11-20 09:19:57 +03:00
catborise
d7f283f089 network qos adding, listing, backend revamped. Add network bug for nat networks solved 2019-11-20 08:42:09 +03:00
catborise
5282d3e556 add translate for some keywords 2019-11-20 08:40:20 +03:00
catborise
ddd3dd5f65 instance network revamped. Qos config for instance net is added 2019-11-20 08:37:59 +03:00
catborise
718388ffef add translate for some keywords 2019-11-19 14:20:05 +03:00
catborise
33407719b6 connection.py getting networking format information function 2019-11-19 14:18:36 +03:00
T. Tran
579ba922e2 Merge branch 'master' of github.com:retspen/webvirtcloud 2019-11-18 00:11:10 +07:00
Anatoliy Guskov
3a4da5eb11
Merge pull request #259 from catborise/master
Add Qos for Network Functions
2019-11-16 17:17:27 +02:00
catborise
69bc58d94f
Update README.md
Add features, add iproute-tc package to centos req
2019-11-15 16:35:02 +03:00
catborise
f93fed9437 network.html Add Qos Details 2019-11-15 11:44:48 +03:00
catborise
d7b350a591 networks/view.py Add Qos Functions. Edit with XML corrections 2019-11-15 11:35:22 +03:00
catborise
79abcd460b Add Qos functions and Edit network with xml function 2019-11-15 11:29:16 +03:00
T. Tran
488aa23d14 Add WS_PUBLIC_PORT to settings template 2019-11-09 00:24:40 +07:00
T. Tran
d05a948078 Merge branch 'master' of github.com:retspen/webvirtcloud 2019-11-09 00:08:57 +07:00
T. Tran
08e694db4d Increase some max_length 2019-11-09 00:07:02 +07:00
Anatoliy Guskov
0a55804251
Merge pull request #258 from catborise/master
ipv6 support enhanced & updates
2019-11-08 19:03:42 +02:00
catborise
930cef24be IPv6 Support: Create ipv6 network support added. Some small fixes 2019-11-08 18:26:47 +03:00
catborise
0974193e68 update chart.js 2.8.0 -> 2.9.2 2019-11-07 17:05:55 +03:00
catborise
8bc316355d Reconfig to create nat routed network without subnet 2019-11-07 16:49:40 +03:00
catborise
78ec7ac746 network: Add network pool mac info. dhcp->dhcp4 conversion. 2019-11-07 14:07:57 +03:00
Anatoliy Guskov
23e496cf0c
Merge pull request #257 from catborise/master
ipv6 support and bridge slave list & pep8 corrections
2019-11-07 10:53:41 +02:00
catborise
c5a96b7662 Add IPv6 support. 2019-11-07 10:33:36 +03:00
catborise
0738ec7ec4 adds bridge slave list to details of interface 2019-11-04 12:03:13 +03:00
catborise
f3f4f0afe8 Fix typos. Code Inspection for pep8 conventions 2019-10-30 11:05:50 +03:00
catborise
568ff92449 libvirt does not have the connection close reason contants anymore. pep8 conventions apply 2019-10-30 11:05:00 +03:00
catborise
38ae62d093 define network with XML is related with network object. It is converted 2019-10-30 11:02:38 +03:00
Anatoliy Guskov
a66ebba12b
Merge pull request #256 from catborise/master
Some fixes and updates 2
2019-10-28 22:39:55 +02:00
catborise
c212a60bba Delete instance disk: detach and delete. Notification added if detach completed but not deleted 2019-10-28 11:20:39 +03:00
catborise
537cefe90a Fix host bus and storages variables to get list of them 2019-10-28 10:45:27 +03:00
catborise
f1dbc6a199 fix key checking in dict which could be causes KeyError 2019-10-28 10:40:33 +03:00
catborise
360f212583 update requirements, websockify: 0.8.0->0.9.0, django: 1.11.23->1.11.25 2019-10-28 09:40:01 +03:00
catborise
54a6408107 add i18n translate and correct typo 2019-10-28 09:00:47 +03:00
catborise
a59dcde688 errors_block: add i18n translate 2019-10-28 08:58:24 +03:00
catborise
f72d969c25 messages_block: add i18n translate 2019-10-28 08:56:55 +03:00
catborise
d46306bc8a travis.yml: update dependency and add missing modules 2019-10-28 08:55:25 +03:00
catborise
97e39fa6b8 storage.html: show usage and size seperately. 2019-10-25 16:03:27 +03:00
Anatoliy Guskov
fb26a002c2
Merge pull request #255 from catborise/master
Some Updates & Rearrange/fix
2019-10-24 14:08:28 +03:00
catborise
a0324ac60e Capitalized iso suffixs not seen by cdrom. Make them case insensitive. 2019-10-24 13:26:58 +03:00
dependabot[bot]
1a8eb7c2c3 Bump django from 1.11.21 to 1.11.23 in /conf
Bumps [django](https://github.com/django/django) from 1.11.21 to 1.11.23.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/1.11.21...1.11.23)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-23 09:25:13 +03:00
Anatoliy Guskov
7ffbedb44a
Merge pull request #254 from retspen/dependabot/pip/conf/django-1.11.23
Bump django from 1.11.21 to 1.11.23 in /conf
2019-10-22 16:38:33 +03:00
dependabot[bot]
509ef79edf
Bump django from 1.11.21 to 1.11.23 in /conf
Bumps [django](https://github.com/django/django) from 1.11.21 to 1.11.23.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/1.11.21...1.11.23)

Signed-off-by: dependabot[bot] <support@github.com>
2019-10-17 19:09:48 +00:00
catborise
1e2fbc8453 add redirection for instance_actions error 2019-09-11 11:51:20 +03:00
catborise
0fdc2dae33 fix redirection for network connection error 2019-09-11 11:12:33 +03:00
catborise
caf00bd4f9 create/views.py: rearrange variable order 2019-09-10 16:06:30 +03:00
catborise
d22aed5ef7 Rearrange host resources variables and change variable names 2019-09-10 16:05:23 +03:00
catborise
828a271789 pep8 style code corrections 2019-09-10 09:48:31 +03:00
catborise
fdbb6739c1 instances/view.py: Fix resize_disk and pep8 style corrections 2019-09-10 09:45:49 +03:00
catborise
1e22547b1a IPy.py update to 1.0 2019-09-09 16:53:52 +03:00
catborise
e47f97facf instance: seperate resize operations as cpu, mem and disk 2019-08-27 17:18:33 +03:00
catborise
b18290794e Translate, indentation and typo fixes 2019-07-31 11:03:48 +03:00
catborise
f3b8b251b5 Instance actions rearranged to reflect disable vm actions on instances list 2019-07-31 09:59:44 +03:00
Anatoliy Guskov
094f054063
Merge pull request #248 from catborise/master
Updates
2019-07-24 21:56:21 +03:00
catborise
15c42ee0f6 ace.js update to 1.4.5 2019-07-23 13:49:59 +03:00
catborise
7404a1212a Remove unnecessary comments 2019-07-22 17:09:37 +03:00
catborise
1ded650463 Update Ubuntu Image, Add prod/dev config for vagrant up/vagrant up prod. 2019-07-22 17:08:08 +03:00
catborise
baf1e92774 Update django version to 1.11.21 2019-07-22 17:07:08 +03:00
Anatoliy Guskov
6a47c48f68
Merge pull request #247 from S-N-A/fix_long_instance_waiting
Fix bug with err instead of True in connection_manager.host_is_up, which lead to freeze in panel if compute is down
2019-07-22 12:11:22 +03:00
catborise
a3e7a5472f Live network interface add remove capability is added. No need to shutdown to add/remove network interfaces 2019-07-17 13:52:14 +03:00
Ilya Trefilov
edd4887a2d Fix bug with err instead of True in connection_manager.host_is_up, which
lead to freeze in panel if compute is down
2019-07-04 15:04:08 +03:00
Ilya Trefilov
ac3ec24332 Fix bug with err instead of True in connection_manager.host_is_up, which
lead to freeze in panel if compute is down
2019-07-03 17:49:22 +03:00
Anatoliy Guskov
0ac126ee0d
Merge pull request #244 from catborise/master
Dockerfile and Requirement Updates
2019-06-24 19:59:12 +03:00
catborise
e601c23b4a noVNC Update 1.0.0 -> 1.1.0 2019-06-24 14:26:25 +03:00
catborise
61703b3faf Add live new/existing disk operation for instance 2019-06-21 11:29:41 +03:00
catborise
1db6bb4cce Update requirements: pyflakes 2.0.0 -> 2.1.1, pylint -> 1.9.4 2019-06-21 11:26:05 +03:00
catborise
0549323aca Update requirements: django 1.11.20 -> 1.11.21 2019-06-21 11:25:21 +03:00
catborise
0056541c07 Update base ubuntu image 0.9.17 -> 0.11 2019-06-20 16:36:31 +03:00
Anatoliy Guskov
e05fa2354b
Merge pull request #239 from catborise/master
Updating chartjs and some others
2019-05-24 22:20:24 +03:00
catborise
181bd9d392 Update chartjs for instance statistics and add memory usage info for instances 2019-05-21 09:12:28 +03:00
catborise
9ab198bd8c Update chartjs for node statistics 2019-05-21 09:10:32 +03:00
catborise
455f239093 Add total memory info for memory usage and change var names 2019-05-21 09:08:42 +03:00
catborise
a0147818b9 Add mem usage function 2019-05-21 09:07:01 +03:00
catborise
98edc599f2 Update Django requirement 1.11.17 to 1.11.20 2019-05-21 09:05:24 +03:00
catborise
3879a1bded Update Chartjs 1.1.1 to 2.8.0 2019-05-21 09:04:42 +03:00
catborise
7986784375 Update jQuery 3.3.1 -> 3.4.1 2019-05-21 09:03:47 +03:00
Anatoliy Guskov
44fcb4b748
Merge pull request #237 from catborise/master
Fix and Addition
2019-05-04 11:13:04 +03:00
catborise
cb81505b05 Convert settings of VNC to Console 2019-04-29 16:10:49 +03:00
catborise
30e1f0eb2d Remove .img extension label 2019-04-29 14:11:20 +03:00
catborise
3a15b49796 Fix image clone/add logs 2019-04-29 14:10:54 +03:00
catborise
6bc4e895b6 Add qcow2/qcow extension for vol clone. Make image features additions for only qcow2 images. 2019-04-29 14:09:17 +03:00
Ing. Jan Krcmar
52fbe95e30 staff users are allowed to change vnc settings of instances 2019-04-10 10:09:05 +03:00
catborise
311f1b793e add libguestfs-tools and epel-release for supervisor to centos 2019-04-09 13:46:00 +03:00
Anatoliy Guskov
b3928d5026
Merge pull request #234 from catborise/master
fix
2019-04-09 09:48:44 +03:00
catborise
3cffa6e505 Fix disk clone xml for proper disk properties 2019-04-04 16:04:34 +03:00
catborise
3ad520209f Rearrange tabs for non superusers 2019-04-04 16:03:22 +03:00
Ing. Jan Krcmar
a457c262e1 wvmStorage.create_volume, wvmCreate.create_volume image naming respects image/volume format (qcow,qcow2) 2019-04-04 14:23:54 +03:00
Anatoliy Guskov
ad373cff7b
Merge pull request #231 from catborise/master
fix & Updates
2019-03-28 09:45:28 +02:00
catborise
1bd2c558f2 Update: js.cookie.js 2.1.1 -> 2.2.0 2019-03-21 10:55:32 +03:00
catborise
6b571ffb22 Update: jQuery 1.12.4 -> 3.3.1 2019-03-20 16:19:10 +03:00
catborise
c5776fc13b Update: Bootstrap 3.4.0 -> 3.4.1 2019-03-20 09:53:25 +03:00
catborise
fffbc92151 Fix: for clone instance multiple disk name generating fixed 2019-03-19 14:39:34 +03:00
catborise
5d1df70d41 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2019-03-18 17:19:35 +03:00
catborise
e63aaa926e Fix: True/False value for Add new disk with metadata option 2019-03-18 16:50:11 +03:00
catborise
dbe38f31a6
Update README.md
remove img closing tags
2019-03-14 11:54:30 +03:00
Anatoliy Guskov
a9a2e1167b
Merge pull request #224 from catborise/master
Add Screenshot and one fix
2019-02-22 22:01:11 +02:00
catborise
378066767e
Remove Beta keywors
update readme
2019-02-15 15:32:11 +03:00
catborise
4edbf24703
Add Screenshot of Webvirtcloud
Some ss add
2019-02-15 15:31:05 +03:00
catborise
ca6b3bbca1 Move images to images directory 2019-02-15 15:04:54 +03:00
catborise
7ece722fb1
Add Images Folder
Add images folder
2019-02-15 15:03:41 +03:00
catborise
03265d52dc
Add screenshots
Screen shot adding
2019-02-15 14:59:41 +03:00
catborise
3f32ae1adc
Delete images 2019-02-15 14:48:55 +03:00
catborise
f7d732302a
Create images 2019-02-15 14:48:36 +03:00
catborise
1ade3a4042
Create readme.md 2019-02-15 14:48:07 +03:00
catborise
2dc83f39a2 Check media before adding new/existing volume 2019-02-14 16:49:12 +03:00
Anatoliy Guskov
c483925ea4
Merge pull request #223 from catborise/master
cosmetic fixes & others
2019-02-12 20:09:32 +02:00
catborise
ebf251ff5e Update bootstrap 3.3.7->3.4.0, update jquery 1.11.1->1.12.4 2019-02-11 11:08:36 +03:00
catborise
03370ac705 Fix css class name for prevention of multiselect(->multipleselect) 2019-02-08 17:17:01 +03:00
catborise
f76ce4d485 Multiple UUID check on instance database. Fix error for multiple uuid console error 2019-02-07 11:09:56 +03:00
catborise
4b6ab68e7f Remove cache tag for if device type is cdrom from add function. 2019-01-29 09:29:52 +03:00
catborise
e28cc71710 Do not show Network devices header if there is not any. 2019-01-29 09:28:42 +03:00
catborise
a7e0e49007 Make visible add cdrom/media space/button even if instance is running. Check media and disk for cdrom target dev 2019-01-28 16:38:15 +03:00
Anatoliy Guskov
aea7545030
Merge pull request #220 from catborise/master
fix for dhcp fixed address
2019-01-28 11:14:24 +02:00
catborise
3dc44662a0 Add trans tag some text. Add a text for empty cdrom and fix aligment. 2019-01-28 11:58:55 +03:00
catborise
8d90bc1372 Change collabsible panels id to differentiate. 2019-01-25 15:06:51 +03:00
catborise
d3cc19b4f8 Change network devices visual style to prevent erroneous delete 2019-01-25 13:57:32 +03:00
catborise
5c3a9e2a81 Fix network change operation: add interface type network config 2019-01-25 13:42:48 +03:00
catborise
1f005e90e1 Arrange search component placement 2019-01-25 08:51:52 +03:00
catborise
cba5147cd1 Arrange CSS for tables. Remove unnecessary add nwfilter button. 2019-01-25 08:49:00 +03:00
catborise
ead0414a4d Add XML config editing option for a network 2019-01-24 15:02:19 +03:00
catborise
573df2acaa Add XML Config to collapsible accordion 2019-01-24 15:00:45 +03:00
catborise
8d2b3e3024 Add explanation for boot menu changes. Minor cosmetic fixes 2019-01-24 08:41:45 +03:00
catborise
b757c62d4f Add forcibly power off option to suspended instance 2019-01-23 14:30:33 +03:00
catborise
a3be93874c Handle empty name for fixed networks 2019-01-23 09:14:54 +03:00
Anatoliy Guskov
6d178a67d8
Merge pull request #218 from catborise/master
Boot Menu/Order Functionality And some fixes
2019-01-22 22:37:19 +02:00
catborise
bd63e3e4e6 Add Fixed Network Address operations. Add Modify dhcp range for stopped networks. 2019-01-22 16:53:10 +03:00
catborise
b812a05cdc Add modify dhcp range function. Change Current config to live+config 2019-01-22 16:50:49 +03:00
catborise
be1acf8d77 Add fixed network modify delete update functions 2019-01-22 09:08:32 +03:00
catborise
d568680747 Add mac addr validation 2019-01-22 09:01:18 +03:00
catborise
8da5527a2b Update IPy.py file from version 0.70 -> 0.83 2019-01-22 09:00:49 +03:00
catborise
276097a44e Fix instance current status update after action. Fix log related error.Some pep8 compatibility fix. 2019-01-17 10:38:53 +03:00
catborise
198c100524 Add title some buttons. Add trans. Remove redundant dom elements 2019-01-16 09:43:58 +03:00
catborise
96efde814a Add boot menu/order functionality. Minor improvements. Some bug fixes 2019-01-15 15:55:05 +03:00
catborise
6b06ed25ff Add disk cache info for instance 2019-01-09 15:31:42 +03:00
catborise
fc6761aabc Add destination storage option for instance creation from a template. 2019-01-09 14:18:19 +03:00
catborise
1aea7baada update requirements 2019-01-03 13:56:07 +03:00
Anatoliy Guskov
4b9ae89324
Merge pull request #211 from catborise/master
fix some glitches & update
2018-12-28 13:50:33 +02:00
catborise
867a10d0c5 Ubuntu 18.04 installation support added 2018-12-27 16:17:51 +03:00
catborise
4efc311dec remove artifacts old implementation 2018-12-18 10:51:34 +03:00
catborise
fc3f04c244 Add pool refresh before getting list of volumes 2018-12-18 10:50:42 +03:00
catborise
a519980610 Narrow down with pool searching existing volume while creating instance from template/flavour 2018-12-18 10:46:23 +03:00
catborise
a63e9036fe Add trans label to headers 2018-12-18 10:18:01 +03:00
Anatoliy Guskov
b7639d2ba6
Merge pull request #208 from catborise/master
New Features: Add/Remove CD-Rom Device, Enchance VM Creation
2018-12-18 07:29:59 +02:00
catborise
c98f0b1232 Add lib and qemu version info to compute overview 2018-12-05 11:08:21 +03:00
catborise
04d3568d1a add instance count to host collapse 2018-12-04 16:05:24 +03:00
catborise
62908f477c Qemu Guest Agent channel add option while creating instance 2018-11-26 15:40:02 +03:00
catborise
a67acc5d24 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-11-26 08:48:04 +03:00
Anatoliy Guskov
01019ddf3b
Merge pull request #207 from krp-kp/master
Fix selector for hostname with "."
2018-11-25 15:58:18 +02:00
Roman Kravchuk
64987b4ef4
Fix selector for hostname with "." 2018-11-25 12:41:49 +02:00
Anatoliy Guskov
c04596f078
Merge pull request #206 from krp-kp/master
Fix collapse on hostname with "."
2018-11-25 12:12:15 +02:00
Roman Kravchuk
91598357dc
Fix collapse on hostname with "."
Fix "Error: Syntax error, unrecognized expression: [host=pc.local]"
2018-11-25 11:27:30 +02:00
catborise
7672cc06f1 Merge branch 'online_resize' 2018-11-23 15:23:01 +03:00
catborise
be75381bea Change column sizes. Add icon for deselecting of disks/networks. add bus selection for disk. add device selection for disk. Some ui changes. 2018-11-23 15:20:51 +03:00
catborise
eb8aae957e Bundle: Add/Delete Cdrom function added. Add disk device type/bus choice for custom instance creation. Change some cosmetics on create instance. Change some functions signature for compatibility. Add getting disk device types function. Fixed some bugs 2018-11-23 15:18:32 +03:00
catborise
469cc0560c rename type variable, add bus info while getting media devices. remove unnecessary control while detaching the disk 2018-11-23 15:05:30 +03:00
catborise
08451a06a4 remove font-weight on labels. Increase max volume size limit to 3-5. 2018-11-22 15:48:02 +03:00
catborise
84bf04a137 add getting list of disk device types. Rename getting disk bus types function 2018-11-22 14:45:12 +03:00
catborise
44d73cc780 Add ability to resize only vm memory while it is running. 2018-11-20 17:07:19 +03:00
catborise
45720d3bcf convert HDD selection of new instance creation to user request. Before that, instance creation gets all images of all pools. If there is a problem with some pools other than default. It causes problem and sometimes it takes too long to opeen create instance page. With that extension getting volumes is up to request. Firs you must select pool then select volume. For now the create page opens instantly. 2018-11-16 16:08:35 +03:00
catborise
91f01d884b add get_volumes function to view. And Remove unused 'get' artifact. 2018-11-16 16:04:26 +03:00
catborise
5b60dd7f84 fix validation error message of network. 2018-11-16 16:03:32 +03:00
catborise
65733197fa add volumes url to get volume list of specified storage pool 2018-11-16 16:02:15 +03:00
catborise
4eda0fb866 add chevron-toggle to collapse_all on grouped_index 2018-11-14 14:39:18 +03:00
catborise
45c3f9b176 index_grouped: remove unused data-sortable. index_nongrouped: add trans statement for actions 2018-11-14 14:04:37 +03:00
catborise
40ada89c4a add log message when an instance is created 2018-11-14 11:46:13 +03:00
catborise
605d24d699 define allow_admin_or_not_template variable, template cannot use parenthesis @honza801 2018-11-08 14:56:31 +03:00
catborise
5872700455 remove caret: bootstrap-multiselect, inheritClass 2018-11-07 15:36:03 +03:00
catborise
d3b43b2e6a add hide/show all instances funtionality 2018-11-07 14:29:36 +03:00
catborise
8ecd453efe fix: initialize used_size variable. it causes error if instance disks are deleted. 2018-11-05 15:19:25 +03:00
catborise
fab3b2891f add filter bar to 'add disk' dropdown. Limit the size of dropdown list. 2018-10-31 17:07:39 +03:00
catborise
69be623eeb upgrade bootstrap-multiselect to v0.9.15 2018-10-31 16:51:11 +03:00
catborise
f68d53f3e4 upgrade chartjs 1.0.2 -> 1.1.1 2018-10-31 16:08:13 +03:00
Anatoliy Guskov
cd940c99e5
Merge pull request #197 from catborise/master
NWFilters Addition & Instances Tab & Other Fixes
2018-10-30 09:08:46 +02:00
catborise
2ca2add444 fix typo. remove unused variable. add form-control to volume dropdown. 2018-10-26 17:30:52 +03:00
catborise
07984a2a9a attach volume dropdown menu rearranged to show selected pool 2018-10-25 09:57:41 +03:00
catborise
1196fb38c9 add log message to attaching disk 2018-10-25 09:55:57 +03:00
catborise
82c87f82bc add/modify alert messages while detaching and deleting volumes. 2018-10-24 17:15:10 +03:00
catborise
1971029e3c Merge commit 'cdb5c4f412' 2018-10-24 17:00:48 +03:00
catborise
1b913fd4d6 instance poweron checks is_template attribute. templates should not be e started. by @honza801 2018-10-24 16:56:05 +03:00
catborise
e80bbfd85b console/templates/console-base.html fix html tag console/templates/console-spice-lite.html add console log message by @honza801 2018-10-24 16:45:23 +03:00
catborise
b3b9596a12 secure instance snapshot, media, options. check userinstance.is_change and instance.is_template correctly. secure mount_iso, snapshots for templates, not userinstance.is_change by @honza801 2018-10-24 16:42:00 +03:00
catborise
b5f38afbca clone instance: create db record first, then run clone process. delete db record if exception while cloning 2018-10-24 16:19:30 +03:00
catborise
9d07c2d7b6 new instance moved to tabs instead of collapsed divs by @honza801 2018-10-24 16:14:14 +03:00
catborise
2b04a89100 make some details showing style popover 2018-10-24 14:09:29 +03:00
catborise
03ffa3a295 move add new disk to instance disk tab. add existing disk option to attach disk. Small typo fixes 2018-10-24 12:04:05 +03:00
catborise
ea5e9cfead Add 'Adding, deleting and detaching disk volumes' to instances 2018-10-19 16:14:33 +03:00
catborise
8f5cc5755a make table headers translatable 2018-10-19 16:11:22 +03:00
catborise
d63c0a8163 activate default cache which is in settings for vm creation. 2018-10-10 15:50:59 +03:00
catborise
ca545878c4 add link to favicon.ico 2018-10-08 16:44:08 +03:00
catborise
f2d88b9c65 add performance parameters and owner properties to clone_template and create volume 2018-10-05 17:07:25 +03:00
catborise
016e93ebca fix app artifact 2018-10-05 16:32:49 +03:00
catborise
df4cf01f05 add image owner option to clone_volume, create_volume functions 2018-10-05 15:49:02 +03:00
catborise
e1910c75ff add warning for dublicate instance name on other host 2018-10-04 10:14:08 +03:00
catborise
d75df231a8 generate randum uuid function update. Add validation of uuid function 2018-10-04 09:31:30 +03:00
catborise
7d21c138b7 calling hypervisor, machine, emulator operation is linked to arch variable. 2018-10-02 15:15:41 +03:00
catborise
ea4f2cba8b Fontawesome update 4.30 to 4.70 2018-10-02 10:47:02 +03:00
catborise
93a8625aca url fix for clone 2018-10-01 15:09:04 +03:00
catborise
358f9ae28a url fix for clone 2018-10-01 11:44:29 +03:00
catborise
b81b01e468 url fix for clone 2018-10-01 11:42:01 +03:00
catborise
bc321f1475 convert default_image_owner to seperate guid and uid 2018-09-28 17:07:35 +03:00
catborise
37b5093e13 convert default_image_owner to seperate guid and uid 2018-09-28 13:46:42 +03:00
catborise
25e6381fc9 add Compute instances tab. Minimal invasion. minimal change. keep structure 2018-09-28 13:33:21 +03:00
catborise
e44e01cad4 volume owner/group converted to choosable from settings.py, cloning and creating volume owner parameter added. 2018-09-27 09:45:10 +03:00
catborise
b916c9dcf9 instance network tab modified. Changing function modified. deleting function added. network info of nwfilters added. and some small fixes applied 2018-09-26 17:20:46 +03:00
catborise
ba212971fa success message converted to translate ready 2018-09-26 17:18:30 +03:00
catborise
5d4a600908 success message converted to translate ready 2018-09-26 17:16:48 +03:00
catborise
f7d2d24d0b message.success fix - (request was missing) 2018-09-26 16:19:32 +03:00
Ing. Jan KRCMAR
cdb5c4f412 fix wvmInterface get_ipv4/6: xml_path should specify protocol family 2018-09-26 09:53:23 +02:00
Ing. Jan KRCMAR
a4bea0e765 Merge branch 'master' of github.com:honza801/webvirtcloud 2018-09-25 23:30:17 +02:00
Ing. Jan KRCMAR
812bbb2ccc console-spice-full fix scheme wss when accessing from https 2018-09-25 23:25:06 +02:00
catborise
4d14fdceea selection added to domain creation for nwfilters. Add nwfilter option while adding network card. NWfilters minor fixes 2018-09-24 14:41:13 +03:00
catborise
f45666d88b Merge branch 'nwfilters' 2018-09-24 09:38:59 +03:00
catborise
658309f022 Merge branch 'nwfilters' of https://github.com/catborise/webvirtcloud into nwfilters 2018-09-24 09:15:54 +03:00
Anatoliy Guskov
5c2232f4e8
Merge pull request #185 from honza801/clone_instance_auto_migrate
allow automatic migrations of newly cloned instances to random host
2018-09-23 09:33:54 +03:00
Anatoliy Guskov
f474905719
Merge pull request #183 from catborise/master
create instance from template enhanced and others
2018-09-23 09:33:28 +03:00
catborise
a3a8814cc9 add nwfilters app 2018-09-21 19:11:17 +03:00
catborise
7f2104c19d Check usage of filter refs from domains. 2018-09-21 19:11:17 +03:00
catborise
35bc8c67e4 Add NWFilters 2018-09-21 19:11:17 +03:00
catborise
0d90e4bb46 Make domain creation work with XML. Fix typo 2018-09-21 19:07:47 +03:00
catborise
1802ad0413 Check usage of filter refs from domains. 2018-09-21 18:46:33 +03:00
catborise
3f1acf09ef Add NWFilters 2018-09-21 16:50:44 +03:00
Ing. Jan KRCMAR
0a7c9f3826 add settings.CLONE_INSTANCE_AUTO_MIGRATE
please add this setting to your current config, or it breaks your installation

allow automatic migrations of newly cloned instances to random host
2018-09-20 13:48:53 +02:00
catborise
be6c1e91f8 patch for old libvirts to populate video graphics type. - 2 2018-09-18 09:58:10 +03:00
catborise
33f334289c patch for old libvirts to populate video graphics type. 2018-09-18 09:53:34 +03:00
catborise
1bf99b796f Merge remote-tracking branch 'remotes/retspen/master' 2018-09-17 11:54:48 +03:00
catborise
1f9041b42a add new properties to creation instance from custom/flavour 2018-09-17 10:55:05 +03:00
Anatoliy Guskov
53d7d1365d
Merge pull request #180 from honza801/clone_revisited
Clone revisited
2018-09-16 22:08:07 +03:00
catborise
13dc4ff510 console password random and empty options modified. 2018-09-14 23:02:07 +03:00
catborise
b58277c621 Instance create with template is revisited. Add password, console, listener address options. add wait dialog. change to console password optional. nwfilter become option. 2018-09-14 16:23:25 +03:00
catborise
f477dd6a11 add domain capabilities info. add getting list of hypervisor in detail. add getting bus types from dom cap. add getting graphics video type info. 2018-09-13 16:55:51 +03:00
catborise
8e3dde808d move hypervisor info to connection.py 2018-09-13 16:54:17 +03:00
catborise
717241a421 Add Emulator info. Revisit Hypervisors info on overview page 2018-09-13 16:52:23 +03:00
Ing. Jan KRCMAR
aba4ee8623 instance/clone disk names revisited 2018-09-11 15:11:13 +02:00
catborise
52ade2ee9d update version 0.5 to 0.8 2018-09-10 16:18:01 +03:00
Ing. Jan KRCMAR
fb03f3c554 Merge remote-tracking branch 'stratus/master' 2018-09-10 11:30:21 +02:00
Ing. Jan KRCMAR
01c7991d80 update novncd runit script, make it debian compatible, update docs 2018-09-10 11:27:05 +02:00
Ing. Jan KRCMAR
76f857d218 toggle host instances button in grouped view 2018-09-10 12:10:39 +03:00
catborise
7da5095220 Merge remote-tracking branch 'remotes/retspen/master' 2018-09-10 11:57:49 +03:00
Anatoliy Guskov
7153a6196c
Merge pull request #179 from honza801/master
patch for cloning rbd images
2018-09-09 20:09:31 +03:00
Ing. Jan KRCMAR
105b8c180a view instance: check_user_quota is not performed for superuser 2018-09-06 14:26:29 +02:00
Ing. Jan KRCMAR
6486c20168 patch for cloning rbd images 2018-09-06 13:36:02 +02:00
Ing. Jan KRCMAR
3b4a1089c3 README.md add ### Configuring Compute SSH connection 2018-09-06 13:36:02 +02:00
Ing. Jan KRCMAR
1e3c06b98b README.md install debian requires package virtualenv 2018-09-06 13:36:02 +02:00
catborise
6a992b03c0 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-09-05 09:31:27 +03:00
catborise
0f7a110535 show vdi url on access tab. some typo and convention fix. gethostbyaddr exception mitigated. 2018-09-05 09:30:08 +03:00
catborise
cb5b0c1ecb add title to snapshot operations and fix redirect path 2018-09-04 15:28:51 +03:00
catborise
8055616b1f small cosmetic changes of snapshot to describe of operation 2018-09-04 15:14:08 +03:00
catborise
33513ca648 small cosmetic changes of snapshot to describe of operation 2018-09-04 15:01:11 +03:00
catborise
1120b89c13 Merge remote-tracking branch 'remotes/retspen/master' 2018-08-31 09:25:43 +03:00
Anatoliy Guskov
7efec407c2
Merge pull request #177 from honza801/master
add network for instance
2018-08-30 21:55:31 +03:00
catborise
6f8462d822 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-08-29 15:10:05 +03:00
catborise
d0a964c516 Set theme jekyll-theme-cayman 2018-08-29 14:59:10 +03:00
Anatoliy Guskov
bcd13c29fe
Merge pull request #176 from catborise/master
console fn-keys functionality correction
2018-08-28 17:14:55 +03:00
catborise
14e145e0d8 twice calling function generates two vnc console. ES6 requirement no longer necessary. it is disabled and problem solved. 2018-08-28 15:55:11 +03:00
Ing. Jan KRCMAR
e168d90bb0 Merge remote-tracking branch 'retspen/master' 2018-08-28 12:37:15 +02:00
Ing. Jan KRCMAR
d5d56a2b50 instance/network: select actually configured net-source 2018-08-28 12:35:40 +02:00
Ing. Jan KRCMAR
b4da655644 instance/network: allow to select compute interface, previously only compute network 2018-08-28 12:18:35 +02:00
Ing. Jan KRCMAR
9c37dcc2dc #addInstanceNetwork modal box 2018-08-28 10:55:27 +02:00
catborise
e9e2185fb4
Update console-spice-full.html
typo-fix
2018-08-27 15:43:33 +03:00
catborise
76e6388ec5 novnc ES6 compatibility is not working. I reverse it. Spice/VNC sending ctrl+alt+fn functionality restored. 2018-08-27 15:30:50 +03:00
Anatoliy Guskov
3f98aa2370
Merge pull request #175 from honza801/master
some fixes
2018-08-24 18:08:19 +03:00
Ing. Jan KRCMAR
21ef6871cf instance: template displays "compute.name - compute.hostname" only if hostname and name differ 2018-08-21 10:29:04 +02:00
Ing. Jan KRCMAR
01ace81c32 Merge remote-tracking branch 'retspen/master' into merge
Conflicts:
	instances/templates/instance.html
	instances/urls.py
	instances/views.py
2018-08-21 09:50:29 +02:00
catborise
f73271e677 missing js files and keyfunctions added 2018-08-16 11:07:35 +03:00
catborise
2a5a761f05 spice config corrections 2018-08-14 16:49:25 +03:00
Anatoliy Guskov
3748a46d8f
Merge pull request #167 from catborise/master
some fixing operations and minor additions
2018-08-14 16:15:43 +03:00
catborise
0dd2eafc97 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-08-14 15:37:42 +03:00
catborise
e7d649636c spice problem correction 2018-08-14 15:37:26 +03:00
catborise
720937edcf
Delete console-vnc.html
console-*-lite, console-*-full .html
2018-08-14 15:15:06 +03:00
catborise
edb59947af novnc is updated to 1.0.0 and add console views: lite,full option 2018-08-14 15:11:49 +03:00
catborise
019d1523cd fix typo 2018-08-08 14:51:25 +03:00
catborise
aa32d826d9 django on_delete statement is must after than 2018-08-08 14:50:58 +03:00
catborise
b05a252d7c spice-html5 updated. 2018-08-08 14:00:35 +03:00
catborise
12c80c5021 python does not approve raise without type. raise exception added 2018-08-08 13:59:19 +03:00
catborise
5bad045835 correction of supervisorctl output 2018-08-08 11:27:39 +03:00
catborise
c80e142522 correction of variable. both are working but latest change is right 2018-08-08 11:26:36 +03:00
catborise
fcb2924e95 fix typo 2018-08-08 11:24:59 +03:00
Ing. Jan KRCMAR
c231feb575 instance/options/is_template form input value must be True 2018-08-02 12:42:06 +02:00
Ing. Jan KRCMAR
3b99c983d5 vrtManager/instance.py: get_media_device fix: media.xpath returns list 2018-08-02 10:47:13 +02:00
catborise
f300c1156f some text converted to trans, progres bar enhanced and band views of tables changed 2018-08-02 10:22:36 +03:00
catborise
aba92e30b5 x character converted to &times; 2018-08-01 17:18:51 +03:00
catborise
fc7ddacdca some text tagged as trans 2018-08-01 17:01:49 +03:00
catborise
f9f216224f fix typo 2018-08-01 16:52:29 +03:00
catborise
88f187c94f volume creation parameters changed to support latest version of qemu compatibility 2018-07-31 16:26:28 +03:00
catborise
07ae335d79 add progress bar to image cloning 2018-07-31 16:25:12 +03:00
catborise
eb621ef2c6 broken compute details info is fixed. 2018-07-31 10:15:59 +03:00
catborise
17c619606d
fix typo 2018-07-30 14:05:27 +03:00
catborise
aa236df40f add link to overview of compute from instances 2018-07-30 13:54:20 +03:00
catborise
6a57903fd6 make instances view for administrators choosable. grouped-nongrouped nongrouped first version of instances. with settings.conf it can be changed. 2018-07-30 13:33:09 +03:00
catborise
dc9a5eb327 Fixed pip error by @retspen 2018-07-30 09:31:30 +03:00
catborise
858d1f87d5 integer_validator causes import problem. it removed 2018-07-30 09:24:29 +03:00
catborise
c355a4014f missing csrf_token added to form. 2018-07-27 14:50:39 +03:00
catborise
340d93463e Random mac address for cloned vm issue fixed by @honza801 2018-07-26 15:29:56 +03:00
Ing. Jan KRCMAR
951498e841 README fix ubuntu installation package +libxslt1-dev
conf/requirements.txt pytz is required by migrate
2018-07-26 13:36:40 +02:00
Ing. Jan KRCMAR
b909b9d0f1 Merge remote-tracking branch 'retspen/master' into upgrade_django 2018-07-26 12:44:22 +02:00
Ing. Jan KRCMAR
13ffaae522 instance/clone: move random_mac logic into instances.views.random_mac_address url ^random_mac_address/
fix instance/clone: get random mac if dhcp not found
2018-07-26 11:35:37 +02:00
Ing. Jan KRCMAR
9f7ec62226 accounts: add public key check 2018-07-26 11:33:12 +02:00
catborise
1700ddf8f1 undo extension adding changes 2018-07-25 11:28:05 +03:00
catborise
c7b8d1ece0 revert of image extension choosing changes. and modify create xmls 2018-07-25 11:16:03 +03:00
catborise
171f98b232 db migrations and storage list allocation info changes 2018-07-25 09:37:38 +03:00
catborise
4126ad2591 Volumes list allocation info added. While creating image files it is recommended username and group name info. Storage.html does not show messages. messages section added 2018-07-25 09:35:35 +03:00
catborise
2585e64cfd While cloning volume it breaks if volume name is longer than 20 char. It i more realistic longer than 20 char. 2018-07-25 09:33:06 +03:00
catborise
948d657376 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-07-24 13:56:14 +03:00
catborise
9176cb6204 lxml conversion of get_info 2018-07-24 13:55:33 +03:00
catborise
b178bad93e instance network page reconfigured. libxml2 artifacts cleaned. Some minor makeups 2018-07-24 13:52:47 +03:00
catborise
8b2451284f Cleaning libxml2 artifact.convert to lxml and variable name correcition 2018-07-20 15:43:29 +03:00
catborise
8b347c1024 Cleaning libxml2 artifact.convert to lxml 2018-07-20 15:19:51 +03:00
catborise
41b80bc719 libxml2 artifact correction. 2018-07-20 14:58:22 +03:00
catborise
332de3709b
Update settings.py.template 2018-07-20 14:51:16 +03:00
catborise
5adeead68d remove data-sort 2018-07-20 14:25:56 +03:00
catborise
e387c3a21d Host gets list of all storages active/inactive. If there is some inactive storages it gives error. But it coulde be inactive. It is normal. Changing the behaviour of getting list of storages. 2018-07-20 14:04:13 +03:00
catborise
e7ecf29359 volume filename extensions reorganized. 2018-07-20 13:42:13 +03:00
catborise
b7150a1fae volume filename extensions reorganized. 2018-07-20 13:41:39 +03:00
catborise
4769c5cf1b volume filename extension converted to choosable with settings. And file extensions reorganized. 2018-07-20 13:40:49 +03:00
Anatoliy Guskov
8e43523331
Fixed pip error 2018-07-20 12:36:22 +03:00
Anatoliy Guskov
056a300b38
Merge pull request #163 from catborise/master
some minor corrections
2018-07-20 12:35:46 +03:00
catborise
73ce98b63e if there is some inactive storages, listing volumes causes exception then breaks.. checking activeness of storage pool fixes that 2018-07-20 11:05:24 +03:00
catborise
7200a34699 fix if there is an exception int get_storage_images mac_auto and cache_mode does not work. So sequence change fixes that. 2018-07-20 11:03:01 +03:00
catborise
7862fa8fdf variable typo fix 2018-07-20 09:54:26 +03:00
catborise
65769eb894 intances reformat code. Fix typo error 2018-07-19 09:39:41 +03:00
catborise
5e2144b113 libxml2 artifact cleaned 2018-07-19 09:23:19 +03:00
catborise
db67157907 redundant default bus statement 2018-07-19 09:22:41 +03:00
catborise
654dcddf03 minor corrections 2018-07-18 15:46:11 +03:00
catborise
19b3d867f3 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-07-18 15:27:12 +03:00
catborise
eb9160cadb minor correction 2018-07-18 15:26:15 +03:00
catborise
cd072e488b
Merge branch 'master' into master 2018-07-18 15:09:50 +03:00
catborise
d44c65a3f4 unused/old bootstrap file deleted. instances.html make-up.. 2018-07-18 15:01:09 +03:00
catborise
bee2e4222b unused/old bootstrap file deleted. instances.html make-up.. 2018-07-18 15:01:01 +03:00
catborise
bdfdd9238f Correction of requirements 2018-07-18 11:41:34 +03:00
catborise
30e2a66be7 Correction of requirements and add guestfs req. Minor cosmetic change instances 2018-07-18 11:22:23 +03:00
catborise
c38b679c88 Merge branch 'master' of https://github.com/catborise/webvirtcloud 2018-07-18 10:09:26 +03:00
catborise
0778116a40 instances view changed. memory usage converted to progress-bar. some bandi007 changes applied 2018-07-18 10:08:02 +03:00
Anatoliy Guskov
f4de792eff
Merge pull request #160 from Bandic007/master
Fixing django version requirement and instances page not loading
2018-07-06 19:18:14 +03:00
Bandic007
32b03b7184 adding new django version to update procedure, fixing missing deps 2018-07-06 17:26:57 +03:00
Bandic007
904df30386 adding new django version to update procedure 2018-07-06 15:56:11 +03:00
Bandic007
3628e5f355 fixing django version to 1.11.14 + other 2018-07-06 15:48:38 +03:00
Bandic007
1204c6d1b4 return original README file 2018-07-06 14:18:12 +03:00
Bandic007
cfa9fd6066 initial comit 2018-07-06 14:16:45 +03:00
Anatoliy Guskov
85aac6fa0a
Merge pull request #156 from catborise/master
Django version requirement, Libxml2->lxml change, account detail validation add, some bugs
2018-07-06 12:07:47 +03:00
catborise
360dab0892
Update README.md
lxml package requirement change
2018-06-29 15:12:12 +03:00
catborise
6a7f30d4a6 host details cpu bug fixed 2018-06-29 15:09:01 +03:00
catborise
20e8b876e4 instance template change to instances as group of hosts. bootstrap.min.css and js updated. some info added. 2018-06-28 16:55:36 +03:00
catborise
a933ffb00f lxml change 2018-06-27 10:43:09 +03:00
catborise
ace30ca952 Libxml2 converted to lxml, django framework upgrade to 1.11.x and some bug fixes 2018-06-27 10:20:51 +03:00
Ing. Jan KRCMAR
19489cb08e fix 4404d59: clone instance cannot set new vname 2018-06-22 10:55:24 +02:00
Ing. Jan KRCMAR
5e8adec424 fix 920739c: instances/views/instance must initialize console_listen_address 2018-06-21 15:13:12 +02:00
Ing. Jan KRCMAR
464e7bee39 add vdiconsole datasource endpoint. provides vdi url for remote-viewer.
add instance/access/vdi panel
2018-06-21 14:53:35 +02:00
Ing. Jan KRCMAR
27a7a7a365 Merge branch 'master' of github.com:honza801/webvirtcloud 2018-06-19 13:07:07 +02:00
Ing. Jan KRCMAR
4404d5941b add settings.CLONE_INSTANCE_AUTO_NAME. add mechanism that automatically selects new vname, mac, disk image names. 2018-06-19 13:06:57 +02:00
Anatoliy Guskov
beea57189c
Merge pull request #154 from honza801/master
please merge new features userlist in grid, cloud-init interface
2018-06-15 19:14:21 +03:00
Anatoliy Guskov
920739c4c5
Merge branch 'master' into master 2018-06-15 19:13:41 +03:00
Anatoliy Guskov
16ff1fb52e
Merge pull request #153 from hongweipeng/patch-1
fix:sorted btween 'dict' and 'dict'
2018-06-15 19:07:30 +03:00
Ing. Jan KRCMAR
22d03da60f add views/instance/settings/vnc listen addresses
configures console listen addresses for instance

update webvirtcloud/settings.py QEMU_CONSOLE_LISTEN_ADDRESSES according to template, before use

instances/views.py remove include webvirtcloud.settings (duplicate)
2018-06-15 14:13:50 +02:00
Jan Krcmar
6b444075b6
Update README.md
apache configuration should use wsgi_custom.py
2018-06-13 11:20:39 +02:00
Ing. Jan KRCMAR
956b321928 settings.py.template: add INSTALLED_APPS datasource, comment RemoteUserBackend, move SHOW_PROFILE_EDIT_PASSWORD 2018-06-13 11:09:44 +02:00
Ing. Jan KRCMAR
5f24d5d222 Merge remote-tracking branch 'retspen/master' 2018-06-13 11:03:04 +02:00
Ing. Jan KRCMAR
82eb5abe52 add new application datasource. provides basic interface for cloud-init tool (hostname, root ssh authorized keys) 2018-06-13 10:50:36 +02:00
HongWeipeng
078905df8a
fix:sorted btween 'dict' and 'dict' 2018-06-11 12:41:46 +08:00
retspen
dd2830eddb Quick fix if console type not support 2018-05-31 12:38:03 +03:00
retspen
b533d59e2f Added vscode 2018-05-31 11:04:19 +03:00
retspen
6587308dda Fixed default auth model 2018-05-31 11:02:05 +03:00
retspen
aba3e5d714 Merge remote-tracking branch 'origin/master' 2018-05-31 10:36:23 +03:00
retspen
0622e577f4 Updated requirements 2018-05-31 10:36:11 +03:00
Anatoliy Guskov
2450e9caff
Update libvirt-bootstrap.sh 2018-05-30 12:17:26 +03:00
Anatoliy Guskov
564bb45c87
Fixed default settings for auth 2018-05-16 21:08:27 +03:00
Ing. Jan KRCMAR
43a8fb6dc1 add accounts view style list. configurable via settings.VIEW_ACCOUNTS_STYLE 2018-05-15 14:22:10 +02:00
Ing. Jan KRCMAR
8d46bd40fd create_user_block.html fix broken dom 2018-05-15 14:11:31 +02:00
aiminick
4013b95d14
Merge pull request #1 from retspen/master
add
2018-05-14 02:02:53 +08:00
retspen
0ccc366fba Merge branch 'honza801-master' 2018-05-07 12:41:01 +03:00
retspen
5bba739e6b Fixed conflict 2018-05-07 12:40:19 +03:00
Ing. Jan KRCMAR
6e5e10594c SHOW_PROFILE_EDIT_PASSWORD, SHOW_ACCESS_ROOT_PASSWORD, SHOW_ACCESS_SSH_KEYS in settings.py are not inherited in templates. they are now forwarded directly into views. 2018-05-07 10:51:23 +02:00
Ing. Jan KRCMAR
c58374ea2e instance view order Computes by name 2018-04-27 14:12:56 +02:00
Ing. Jan KRCMAR
40049540ee instances.html displays memory MB only in header. table sorts now int value, instead of string 2018-04-11 13:35:01 +02:00
Ing. Jan KRCMAR
41095ffb6d sshkeys view can send keys in plain format 2018-04-05 16:26:17 +02:00
Ing. Jan KRCMAR
1ad2f03b52 add delete_instance function in instances.view 2018-03-16 08:18:22 +01:00
Ing. Jan KRCMAR
748a79d9db accounts/templates/account.html fix missig tr in table 2018-03-08 10:34:52 +01:00
Ing. Jan KRCMAR
6cbad06f2c instances/view/instance do not refresh media_iso when no media present 2018-02-15 10:54:39 +01:00
Ing. Jan KRCMAR
15d7216368 page speed serving enhancements
handle xml doc enhancements
2018-02-14 15:22:57 +01:00
Jan Krcmar
9dc9fea2a1 Update README.md
Generate secret key for django
2018-01-24 10:01:57 +01:00
Ing. Jan KRCMAR
6574532c16 webvirtcloud/settings.py -> webvirtcloud/settings.py.template
ignore webvirtcloud/settings.py
2018-01-24 09:25:58 +01:00
Ing. Jan KRCMAR
b0e489abe7 NEW_USER_DEFAULT_INSTANCES remove debian8-template 2018-01-24 09:15:25 +01:00
Ing. Jan KRCMAR
62467854db gitignore console/cert.pem* 2018-01-03 15:51:56 +01:00
Ing. Jan KRCMAR
839b8d420d instance view: added logs section to graphs (renamed graphs -> stats) 2017-12-11 15:07:01 +01:00
Ing. Jan KRCMAR
fdca3cef9c account view now displays user public keys 2017-11-15 12:55:05 +01:00
Ing. Jan KRCMAR
6d153a6acf users with can_clone_instances ability are allowed to change_options of vm 2017-10-26 15:51:10 +02:00
Ing. Jan KRCMAR
96674221b4 add migration userattributes.can_clone_instances dafaults to True 2017-10-26 10:10:52 +02:00
Ing. Jan KRCMAR
ede9bb6b10 Instance.created added. means the creation (clone) of machine. dafaults auto_now_add=True 2017-10-26 10:09:58 +02:00
Ing. Jan KRCMAR
80c023f2f3 ignore db.sqlite3* (backups, dumps) 2017-10-05 08:30:15 +02:00
Ing. Jan KRCMAR
b7cd731fda UserAttributes.default_instances moved to django.conf.settings.NEW_USER_DEFAULT_INSTANCES 2017-10-05 08:08:04 +02:00
Ing. Jan KRCMAR
2ef672ffb5 user.is_staff can do resize of all machines 2017-10-05 07:58:01 +02:00
Ing. Jan KRCMAR
d4158a33ee instance owner view: add owner button, delete owner button 2017-09-15 12:40:37 +02:00
Ing. Jan KRCMAR
ad3b4d817b migration implicit option: live (if wm is running), offline (if vm is off) 2017-09-15 10:30:45 +02:00
Ing. Jan KRCMAR
53f5518706 added new feature: create and attach new volume to existing instance
move wvmCreate.get_cache_modes() to wvmConnect

add wvmConnect.get_busses(), wvmConnect.get_image_formats(), used in forms

add corresponding default values to settings (INSTANCE_VOLUME_DEFAULT_FORMAT INSTANCE_VOLUME_DEFAULT_BUS INSTANCE_VOLUME_DEFAULT_CACHE)
2017-07-19 15:34:03 +02:00
Ing. Jan KRCMAR
b095a77da5 configure_user() moved from class MyRemoteUserBackend to class UserAttributes, as static factory.
call configure_user() while adding user from form
2017-07-18 15:05:30 +02:00
Ing. Jan KRCMAR
f2e1273f85 fix profile/sshkeys form. empty public keys broke the page 2017-06-27 10:29:41 +02:00
Ing. Jan KRCMAR
fdf383a647 added SHOW_ACCESS_ROOT_PASSWORD, SHOW_ACCESS_SSH_KEYS, SHOW_PROFILE_EDIT_PASSWORD -- hides specific forms 2017-06-27 09:22:09 +02:00
Ing. Jan KRCMAR
fd87cfd86e fix disk_size NoneType while determining vm disks size 2017-06-26 14:45:12 +02:00
Ing. Jan KRCMAR
437be0df99 instance clone validate mac address 2017-06-15 11:57:31 +02:00
Ing. Jan KRCMAR
3e709cd253 accounts/views: do not redirect to profile, if user wants to see self 2017-06-06 15:07:02 +02:00
Ing. Jan KRCMAR
c5f86358be fix STATIC_URL in templates. static urls should be generated by "static" template tag 2017-06-06 14:49:11 +02:00
Ing. Jan KRCMAR
d715e996b0 templates/instance.html view redesign. smaller instance details, instance title added 2017-06-06 12:51:32 +02:00
Ing. Jan KRCMAR
f959dc7016 settings: TEMPLATE_DIRS is deprecated, using TEMPLATES instead 2017-06-06 12:49:33 +02:00
Ing. Jan KRCMAR
4d08f956fb add /instance/sshkeys/ endpoint for retrieving instance owners ssh keys 2017-05-11 10:46:39 +02:00
Ing. Jan KRCMAR
b7457a4362 automatic remove instances with the same name
sometimes (fe. after manual migrations) some instances appear
multiple times in the database. this patch removes them from
the database while refreshing instance uuid. only under superuser.
records log message.
2017-04-20 14:47:05 +02:00
Ing. Jan KRCMAR
6858448b60 fix multiple media selection in instance settings form 2017-04-20 14:45:01 +02:00
Ing. Jan KRCMAR
14b0751930 UserAttributes can_clone_instances defaults to True 2017-03-31 14:33:03 +02:00
Ing. Jan KRCMAR
fe89af9d8f using custom accounts.backends.MyRemoteUserBackend, which adds default instances and attributes for user
create_missing_userattributes removed from accounts view
2017-03-31 14:29:27 +02:00
anatoliy.guskov@gmail.com
d94ca38e5c Little fix #133 2017-03-10 21:02:52 +02:00
Ing. Jan KRCMAR
609651d707 disable page reloading while migrating vm and pleaseWaitDialog is displayed
fixes: http redirect after migrate does not jump to new compute node
2017-03-09 15:14:38 +01:00
Anatoliy Guskov
531ea1e652 Merge pull request #88 from honza801/master
multiple enhancements and fixes
2017-03-09 15:34:21 +02:00
Ing. Jan KRCMAR
b5f9f638f1 autostart stays persistent after instance migration 2017-03-09 14:26:55 +01:00
Ing. Jan KRCMAR
20d0e5a09d add offline migration option 2017-01-04 13:14:30 +01:00
Ing. Jan KRCMAR
48371ff92d deleting instance with disk causes also delete of all corresponding snapshots. previously deleting instance with snapshots ended with error and probably also database inconsistency. 2016-11-04 09:33:49 +01:00
Anatoliy Guskov
aa2a996e3f Merge pull request #129 from ctechguy/ctechguy/add_storage_regex_issue
Fixing add storage regex issue on 'iso path' and 'lvm name'
2016-10-15 10:52:02 +03:00
chris
89f49b120b Fixin add storage regex issue on 'iso path' and 'lvm name' 2016-10-15 00:25:21 -04:00
Ing. Jan KRCMAR
a3a572a8a8 Fixed: Conflicting migrations detected (0004_userinstance_is_vnc, 0007_auto_20160426_0635 in accounts) 2016-09-12 11:02:01 +02:00
Ing. Jan KRCMAR
ed4cb864ad Merge remote-tracking branch 'qdaniel/master' 2016-09-12 10:57:14 +02:00
Ing. Jan KRCMAR
525d42a74d instances/views.py check instance name, alloweed r^[a-zA-Z0-9-]+$ 2016-09-06 13:01:45 +02:00
Daniel Rieper
29b722ff41 Merge branch 'master' of https://github.com/honza801/webvirtcloud
Conflicts:
	instances/templates/instance.html
	instances/views.py
2016-07-29 13:05:54 +02:00
QDaniel
01fc85e1fc listen 127.0.0.1 2016-07-21 23:14:48 +02:00
QDaniel
9e294ade3c Security Bug: No external Connections 2016-07-13 12:49:14 +02:00
Anatoliy Guskov
346b96b319 Merge pull request #112 from nitmir/user-vnc
Add an option to allow an user to change VNC settings
2016-07-11 10:13:33 +03:00
Ing. Jan KRCMAR
08cc19900c guess_clone_name fix. should check hostname, not fqdn.
guess_clone_name calls guess_mac_address in instance view
2016-06-08 13:37:26 +02:00
Jacco Koning
14e8418472 Fix typos 2016-06-02 13:47:48 +02:00
Ing. Jan KRCMAR
3666ff0738 settings.ALLOW_EMPTY_PASSWORD added. allows to create user withnout password. useful with SSO authentication. 2016-06-02 13:39:18 +02:00
Ing. Jan KRCMAR
f484598414 add guess button for cloned instance name. this reads dhcp conf and uses settings.CLONE_INSTANCE_DEFAULT_PREFIX. 2016-05-27 14:13:24 +02:00
Jan Krcmar
0b80b030fe add refresh button into instance view 2016-05-12 10:06:16 +02:00
Valentin Samir
7efbfec17f Add an option to allow an user to change VNC settings 2016-05-09 12:09:21 +02:00
Valentin Samir
7b3fcd17ea Check user permission before delete or resize
Else a user without these permission could delete or resize and instance
by forging a good post request.
2016-05-09 12:07:30 +02:00
Anatoliy Guskov
4ce76f57c6 Merge pull request #110 from nitmir/passwd-vnc
Create instances with a random generated vnc password by default
2016-05-08 20:22:18 +03:00
Valentin Samir
e75fc99449 Create instances with a random generated vnc password by default 2016-05-08 12:24:43 +02:00
Jan Krcmar
572f7b12cd instances_filter cookie expires after 1 day 2016-05-06 14:40:24 +02:00
Jan Krcmar
9327cf36c4 accounts view sorts users by username 2016-05-06 14:39:52 +02:00
Jan Krcmar
c4ae3d2982 instances filter stores filter value into cookie and auto-fills/applies filter on document ready 2016-05-06 10:56:50 +02:00
Jan Krcmar
99bd6930b9 add js.cookie.js to base.html 2016-05-06 10:55:24 +02:00
Anatoliy Guskov
1425a7ae1f Merge pull request #101 from savichev/patch-12
Update libvirt-bootstrap.sh
2016-05-05 21:34:10 +03:00
Jan Krcmar
e966e6c030 add user information per instance on the instances list
wider .container (900px)
2016-05-02 12:23:18 +02:00
Jan Krcmar
e45c712d67 add instance/options/users tab. lists all owners of the instance 2016-04-28 12:50:11 +02:00
Jan Krcmar
380cc2d09b wvmInstance._set_options options.get(o) returns unicode, so cannot be str()ed 2016-04-27 15:32:40 +02:00
Jan Krcmar
fc71884cd4 please wait dialog added on migrate/clone event 2016-04-26 10:30:48 +02:00
Jan Krcmar
8de4c6b131 /accounts view creates userattributes automatically 2016-04-26 08:50:58 +02:00
Jan Krcmar
25529835b5 set default UserAttributes to instances=1, vcpus=1, memory=2048, disk=20 2016-04-26 08:36:14 +02:00
Jan Krcmar
db1ab88f51 Merge remote-tracking branch 'retspen/master'
Conflicts:
	conf/requirements.txt
2016-04-21 13:34:27 +02:00
Jan Krcmar
0e7c5c25b7 fix instance compute node after migration 2016-04-21 13:08:20 +02:00
retspen
17cb7ace88 Fixed #105 2016-04-21 13:14:20 +03:00
Anatoliy Guskov
05a7944c0e Merge pull request #104 from brenard/html5_console
Unified VNC/SPICE console interfaces with bootstrap
2016-04-20 11:29:23 +03:00
Anatoliy Guskov
958e769258 Merge pull request #103 from brenard/rbd_support
Improve RBD support
2016-04-20 11:28:15 +03:00
Benjamin Renard
69b955261f Unified VNC/SPICE console interfaces with bootstrap 2016-04-18 00:25:46 +02:00
Benjamin Renard
9520282a4b Add possibility to configure HDD cache mode during instance creation 2016-04-17 23:41:40 +02:00
Benjamin Renard
e1d3be17f1 Fix pep8 errors 2016-04-16 15:06:39 +02:00
Benjamin Renard
ee2b97d62f Retreive all RBD hosts (and their port if defined) from pool definition during instance creation 2016-04-16 15:04:28 +02:00
savichev
9268cd3a61 Update libvirt-bootstrap.sh 2016-04-04 14:52:14 +05:00
Jan Krcmar
f8e681dbf4 added user disk quota limit
added quota warning in instance page
2016-03-31 13:12:52 +02:00
retspen
2f63d35804 Updated Python dependencies 2016-03-30 16:24:22 +03:00
savichev
9b5d745fb9 Update requirements.txt 2016-03-30 18:15:35 +05:00
savichev
90fc5db411 Update requirements.txt 2016-03-30 14:51:16 +05:00
savichev
c5c8d29dff Update requirements.txt
Warning! this string installing libxml2-python version 2.9.2
2016-03-30 12:48:15 +05:00
Jan Krcmar
6546fa2570 show instance title in instances view for non superuser 2016-03-24 14:51:29 +01:00
Jan Krcmar
e9e62e3c41 instance templates show clone button instead of poweron on instances list page 2016-03-23 15:18:20 +01:00
Jan Krcmar
16ef164ed9 correct instance summary display after unsuccessfull resize 2016-03-23 14:29:40 +01:00
Jan Krcmar
1e84dcbbc9 instance resize check_user_quota correct cpu/memory resize amount. only differrence from current state should be provided for the function 2016-03-23 14:11:23 +01:00
Jan Krcmar
bc0552e12e not superuser delete instance causes delete for all corresponding user_instances (not only current user) 2016-03-23 13:47:04 +01:00
Jan Krcmar
8148620025 instances.views.check_user_quota correct cpu/memory sum
instance resize respects quota
2016-03-23 13:25:28 +01:00
Jan Krcmar
27030113fb account max mamory label Max memory (MB), added MB 2016-03-23 13:24:14 +01:00
Jan Krcmar
738993d97a bugfix: instances list block poweron when is_template for normal users 2016-03-23 12:44:24 +01:00
Jan Krcmar
7cdac17b53 instances list block poweron when is_template for normal users 2016-03-23 12:07:52 +01:00
Jan Krcmar
c51e986b8c clone check_user_quota correct application logic 2016-03-23 12:04:15 +01:00
Jan Krcmar
027bbbc776 Merge remote-tracking branch 'andrem/master' 2016-03-23 10:54:25 +01:00
Jan Krcmar
6afcd00e2e logs view adds paging ability
settings.LOGS_PER_PAGE controls logs count per page
2016-03-23 10:38:33 +01:00
Jan Krcmar
317c2a85ae user can now clone instances, admin can specify user quotas (instances,cpus,memory)
user can only select predefined instance names, mac and disk names are selected automatically
2016-03-23 09:00:42 +01:00
Jan Krcmar
de5cb19913 class UserAddForm: remove is_staff, is_superuser 2016-03-23 08:53:29 +01:00
Jan Krcmar
78dcb10a57 .gitignore dhcpd.* 2016-03-22 10:32:16 +01:00
Jan Krcmar
c2b3938f2a add instances/migrations/0002_instance_is_template.py 2016-03-22 10:31:49 +01:00
Jan Krcmar
9832e662a8 account create_user_instance form sorts instances by name 2016-03-21 10:24:09 +01:00
andrem
b6350e134e add details for local socket 2016-03-18 15:21:44 -03:00
andrem
fc56e66555 add migration for new column details for hypervisor. 2016-03-18 12:07:42 -03:00
Anatoliy Guskov
ff4c05f90b Merge pull request #94 from andrem/master
adjust path to storage for url.
2016-03-17 23:10:44 +02:00
andrem
84fe28e9d7 adjust path to storage for url. 2016-03-17 17:19:55 -03:00
Jan Krcmar
2aa81ccecb instance/clone adds title and description input fields 2016-02-24 13:08:43 +01:00
Jan Krcmar
6c4a3a93e3 instance/clone view: generation of clone_data algorithm enhanced
if disk or meta in post: results always true, so removed. this pushes all POST data to conn.clone_instance()
2016-02-24 12:50:43 +01:00
Jan Krcmar
679f84f117 template/instance: correct placement of options tab panel div, should be after xmledit div 2016-02-24 12:44:14 +01:00
Jan Krcmar
e1b4fdf5de instances display virtual title under name 2016-02-23 15:03:33 +01:00
Jan Krcmar
2ceb456a4f section #template renamed to #options, added title and description fields 2016-02-23 14:43:32 +01:00
Jan Krcmar
510e0e6ee5 guess_mac checks for existing dhcp_file 2016-02-11 14:48:41 +01:00
Jan Krcmar
16510dee59 instance/clone block new instance creating if instance with same name found 2016-02-11 14:46:42 +01:00
Jan Krcmar
2958a21ad1 instance/check_instance service endpoint added. checks for existing instance (returns json) 2016-02-11 14:37:26 +01:00
Jan Krcmar
a1d5edebe2 guess_mac_address reads /srv/webvirtcloud/dhcpd.conf 2016-02-11 12:56:36 +01:00
Jan Krcmar
96982ce58b instance/settings: inputs renamed according to action type (net -> clone-net-mac, net -> net-mac)
instance/network: correct multiple interfaces handling, add mac handling
2016-02-10 17:52:50 +01:00
Jan Krcmar
71c6161291 accounts/edit added is_staff and is_superuser checkboxes
list all users (previously staff and admins were not displayed)
2016-02-09 11:16:36 +01:00
Jan Krcmar
39f3c9e12b instance/clone added Guess mac address button. search for mac address of Clone Name via ajax request. Currently scans only local dhcpd configuration file. 2016-02-08 12:28:52 +01:00
Jan Krcmar
33916c6a82 instance/clone: changing clone name input changes disk image name according to clone name (similar approach as instances/views:show_clone_disk)
clone input boxes enlarged (class=col-sm-4)
2016-02-08 10:17:01 +01:00
Jan Krcmar
323e0a10d5 show_clone_disk generates cloned images name with "-*" suffix. fe: vname=vm1 vm1-root becomes vm1-clone-root (was vm1-root-clone) 2016-02-08 10:13:19 +01:00
Jan Krcmar
a34c55d3bc clone instance fixes mac address not separated by :, to correct format xx:xx:... 2016-01-29 14:44:19 +01:00
Jan Krcmar
646bdbbe0e Added is_template attribute to instances. If true, instance cannot be started. 2016-01-20 15:40:09 +01:00
Jan Krcmar
4ab8561360 correct value=true of checked checkboxes 2016-01-20 14:49:49 +01:00
Jan Krcmar
d4fa71ff25 default true: live migration and delete original 2016-01-20 14:35:23 +01:00
Jan Krcmar
8deb844c35 adding new instance to user checks existing instances assigned to user 2016-01-19 15:43:56 +01:00
Jan Krcmar
d036d582fd .gitignore: tags 2016-01-19 15:39:56 +01:00
Jan Krcmar
ca328a7527 instances/templates/instance.html checkbox delete_disk default checked 2016-01-15 15:09:23 +01:00
Jan Krcmar
6151792d9b allow multiple owners of single instance. this is controlled by settings.ALLOW_INSTANCE_MULTIPLE_OWNER 2016-01-15 10:12:03 +01:00
Jan Krcmar
50ddda98f2 settings tab for changing devices/interface/source/bridge added
TODO: handle multiple interfaces
2016-01-14 16:59:50 +01:00
Jan Krcmar
1499af1eef upgrade websockify 0.6.0 to 0.7.0
older version contains bug that closes novnc connection after 30 seconds. this commit puts the right version into conf/requirements.txt used by pip install
2015-12-28 13:57:17 +01:00
Jan Krcmar
0eb60b1aa4 ignore novnc certificate (console/cert.pem) 2015-12-28 13:53:40 +01:00
Jan Krcmar
dac974ddab request.user.is_authenticated() substitued for @login_required decorator
settings.LOGIN_URL = /accounts/login
2015-12-22 15:09:02 +01:00
Jan Krcmar
ae4fdcec92 added class MyRemoteUserBackend(RemoteUserBackend) giving all authenticated users superuser flag 2015-12-22 15:06:19 +01:00
Jan Krcmar
f8c08cb719 wsgi.py customized for use mod_wsgi with apache module 2015-12-22 15:04:23 +01:00
Jan Krcmar
decd5ab4a6 add remoteuser auth middleware classes 2015-12-22 09:48:54 +01:00
Jan Krcmar
6dbd6e558c pip installing packages need gcc and pkg-config on debian/ubuntu 2015-12-21 10:28:03 +00:00
Anatoliy Guskov
35369edf28 Fixed Checking KVM 2015-12-21 10:02:50 +00:00
Anatoliy Guskov
e6f0116908 Fixed Checking KVM 2015-11-27 09:38:04 +02:00
Jan Krcmar
cd787c3eaf Merge branch 'master' of github.com:honza801/webvirtcloud
Conflicts:
	README.md
	computes/views.py
	gunicorn.conf.py
	instances/templates/instance.html
	instances/views.py
	vrtManager/create.py
	vrtManager/instance.py
2015-11-24 09:37:31 +00:00
Jan Krcmar
04f3a76c05 resize disk image option added 2015-11-24 08:53:13 +00:00
Jan Krcmar
8f2f95e128 cloning instance with lvm disk also clones the disk and sets the correct target in XML configuration 2015-11-13 09:15:38 +00:00
Jan Krcmar
164c9a9145 order Compute objects by name 2015-11-13 09:13:36 +00:00
Anatoliy Guskov
eac356b50a Merge pull request #57 from jcppkkk/patch-1
Require libsasl2-modules for TCP Connection
2015-10-29 13:00:33 +02:00
Anatoliy Guskov
80a27a4a6a Merge pull request #66 from savichev/patch-9
Update README.md
2015-10-29 11:41:50 +02:00
Anatoliy Guskov
b12930c070 Merge pull request #67 from savichev/patch-10
Update README.md
2015-10-29 11:41:39 +02:00
Anatoliy Guskov
7d3ef56c08 Merge pull request #68 from savichev/patch-11
Update gunicorn.conf.py
2015-10-29 11:41:08 +02:00
savichev
f4f058fdcc Update gunicorn.conf.py 2015-10-29 13:37:39 +05:00
savichev
bb5e5815c0 Update README.md 2015-10-29 13:19:10 +05:00
savichev
e2ead89fd1 Update README.md 2015-10-29 11:25:37 +05:00
Anatoliy Guskov
2a3a33d746 Fixed #61 2015-10-22 08:01:43 +03:00
Anatoliy Guskov
1e6d177bba Merge pull request #58 from jcppkkk/patch-2
Dockerization
2015-10-19 22:29:32 +03:00
Jethro Yu
83ac06782a Merge branch 'patch-2' 2015-10-15 20:55:14 +08:00
Jethro Yu
6f0c3dc5ac Dockerization 2015-10-15 20:47:10 +08:00
Jethro Yu
225db15d9e Required libsasl2-modules for TCP Connection 2015-10-15 20:26:36 +08:00
Anatoliy Guskov
bccfbb0010 Fixed generate password for VNC 2015-10-08 09:32:38 +03:00
1078 changed files with 329648 additions and 46969 deletions

25
.dockerignore Normal file
View file

@ -0,0 +1,25 @@
**/__pycache__
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
README.md

61
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,61 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
branches-ignore: [master]
schedule:
- cron: '0 21 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['javascript', 'python']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

89
.github/workflows/linter.yml vendored Normal file
View file

@ -0,0 +1,89 @@
###########################
###########################
## Linter GitHub Actions ##
###########################
###########################
name: Lint Code Base
#
# Documentation:
# https://help.github.com/en/articles/workflow-syntax-for-github-actions
#
#############################
# Start the job on all push #
#############################
on:
push:
branches: [master]
pull_request:
branches-ignore: [master]
###############
# Set the Job #
###############
jobs:
build:
name: Lint Code Base
# Set the agent to run on
runs-on: ubuntu-latest
##################
# Load all steps #
###################
steps:
##########################
# Checkout the code base #
##########################
- name: Checkout Code
uses: actions/checkout@v2
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install Required packages
run: |
sudo apt-get install -y python3-virtualenv libvirt-dev python3-lxml zlib1g-dev libxslt1-dev
- name: Create & Activate VENV
run: |
python3 -m venv venv
source venv/bin/activate
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install wheel
if [ -f dev/requirements.txt ]; then pip3 install -r dev/requirements.txt; else pip3 install -r conf/requirements.txt; fi
################################
# Run Linter against code base #
################################
- name: Lint Code Base
uses: docker://github/super-linter:latest
env:
FILTER_REGEX_EXCLUDE: .*(static|scss|venv|locale)/.*
DEFAULT_BRANCH: master
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_ALL_CODEBASE: false
VALIDATE_ANSIBLE: false
VALIDATE_CLOJURE: false
VALIDATE_COFFEE: false
VALIDATE_DART: false
VALIDATE_GO: false
VALIDATE_JSX: false
VALIDATE_KOTLIN: false
VALIDATE_POWERSHELL: false
VALIDATE_PERL: false
VALIDATE_PHP: false
VALIDATE_RAKU: false
VALIDATE_RUBY: false
VALIDATE_TSX: false
VALIDATE_TERRAFORM: false

15
.gitignore vendored
View file

@ -1,6 +1,19 @@
.vagrant
.venv
venv
venv2
.vscode
.idea
.DS_*
.webvirtcloud
*.pyc
db.sqlite3
db.sqlite3*
console/cert.pem*
tags
dhcpd.*
webvirtcloud/settings.py
*migrations/*
.coverage
htmlcov
*.log
templates/webvirtcloud.code-workspace

5
.gitpod.yml Normal file
View file

@ -0,0 +1,5 @@
image: gitpod/workspace-full
tasks:
- init: 'echo "TODO: Replace with init/build command"'
command: 'echo "TODO: Replace with command to start project"'

View file

@ -1,15 +1,17 @@
---
language: python
python:
- "2.7"
- "3.9"
env:
- DJANGO=1.8
- DJANGO=4.2.4
install:
- pip install -r dev/requirements.txt --use-mirrors
- pip install -r dev/requirements.txt
script:
- pep8 --exclude=IPy.py --ignore=E501 vrtManager accounts computes \
console create instances interfaces \
networks secrets storages
- pyflakes vrtManager accounts computes console create instances interfaces \
networks secrets storages
- pep8 --exclude=IPy.py --ignore=E501 vrtManager accounts admin appsettings \
computesconsole create datasource instances \
interfaceslogs networks nwfilters storages \
virtsecrets
- pyflakes vrtManager accounts admin appsettings computes console create datasource \
instances interfaces logs networks nwfilters storages virtsecrets
- python manage.py migrate
- python manage.py test --settings=webvirtcloud.settings-dev

69
Dockerfile Normal file
View file

@ -0,0 +1,69 @@
FROM phusion/baseimage:noble-1.0.2
EXPOSE 80
EXPOSE 6080
# Use baseimage-docker's init system.
CMD ["/sbin/my_init"]
RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean
RUN apt-get update -qqy \
&& DEBIAN_FRONTEND=noninteractive apt-get -qyy install \
--no-install-recommends \
git \
python3-venv \
python3-dev \
python3-lxml \
libvirt-dev \
zlib1g-dev \
nginx \
pkg-config \
gcc \
libldap2-dev \
libssl-dev \
libsasl2-dev \
libsasl2-modules \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY . /srv/webvirtcloud
RUN chown -R www-data:www-data /srv/webvirtcloud
# Setup webvirtcloud
WORKDIR /srv/webvirtcloud
RUN python3 -m venv venv && \
. venv/bin/activate && \
pip3 install -U pip && \
pip3 install wheel && \
pip3 install -r conf/requirements.txt && \
pip3 cache purge && \
chown -R www-data:www-data /srv/webvirtcloud
RUN . venv/bin/activate && \
python3 manage.py makemigrations && \
python3 manage.py migrate && \
python3 manage.py collectstatic --noinput && \
chown -R www-data:www-data /srv/webvirtcloud
# Setup Nginx
RUN printf "\n%s" "daemon off;" >> /etc/nginx/nginx.conf && \
rm /etc/nginx/sites-enabled/default && \
chown -R www-data:www-data /var/lib/nginx
COPY 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
COPY conf/runit/nginx /etc/service/nginx/run
COPY conf/runit/nginx-log-forwarder /etc/service/nginx-log-forwarder/run
COPY conf/runit/novncd.sh /etc/service/novnc/run
COPY conf/runit/webvirtcloud.sh /etc/service/webvirtcloud/run
# Define mountable directories.
#VOLUME []
WORKDIR /srv/webvirtcloud

344
README.md
View file

@ -1,40 +1,78 @@
## WebVirtCloud Beta
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/retspen/webvirtcloud)
# WebVirtCloud
###### Python >=3.11 & Django 4.2 LTS
## Features
* QEMU/KVM Hypervisor Management
* QEMU/KVM Instance Management - Create, Delete, Update
* Hypervisor & Instance web based stats
* Manage Multiple QEMU/KVM Hypervisor
* Manage Hypervisor Datastore pools
* Manage Hypervisor Networks
* Instance Console Access with Browsers
* Libvirt API based web management UI
* User Based Authorization and Authentication
* User can add SSH public key to root in Instance (Tested only Ubuntu)
* User can change root password in Instance (Tested only Ubuntu)
* Supports cloud-init datasource interface
### Warning!!!
How to update <code>gstfsd</code> daemon on hypervisor:
```bash
wget -O - https://clck.ru/9VMRH | sudo tee -a /usr/local/bin/gstfsd
wget -O - https://bit.ly/2NAaWXG | sudo tee -a /usr/local/bin/gstfsd
sudo service supervisor restart
```
### Description
## 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.
### Install WebVirtCloud panel (Ubuntu)
## Quick Install with Installer (Beta)
Install an OS and run specified commands. Installer supported OSes: Ubuntu 20.04/22.04, Debian 10/11, Rocky/Alma/OEL/RHEL 10.
It can be installed on a virtual machine, physical host or on a KVM host.
```bash
sudo apt-get -y install git python-virtualenv python-dev libxml2-dev libvirt-dev zlib1g-dev nginx supervisor
wget https://raw.githubusercontent.com/retspen/webvirtcloud/master/install.sh
chmod 744 install.sh
# run with sudo or root user
./install.sh
```
## Manual Installation
### Generate secret key
You should generate SECRET_KEY after cloning repository. Then put it into webvirtcloud/settings.py.
```python3
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 18.04+ LTS)
```bash
sudo apt-get -y install git virtualenv python3-virtualenv python3-dev python3-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs libsasl2-dev libldap2-dev libssl-dev
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
virtualenv -p python3 venv
source venv/bin/activate
pip install -r conf/requirements.txt
python manage.py migrate
python3 manage.py migrate
python3 manage.py collectstatic --noinput
sudo chown -R www-data:www-data /srv/webvirtcloud
sudo rm /etc/nginx/sites-enabled/default
```
@ -49,33 +87,45 @@ sudo service supervisor restart
Setup libvirt and KVM on server
```bash
wget -O - https://clck.ru/9V9fH | sudo sh
wget -O - https://bit.ly/36baWUu | sudo sh
```
### Install WebVirtCloud panel (CentOS)
Done!!
Go to http://serverip and you should see the login screen.
### Install WebVirtCloud panel (RHEL Based OS 8/9/10)
```bash
sudo yum -y install python-virtualenv python-devel libvirt-devel glibc gcc nginx supervisor libxml2 libxml2-devel git
sudo yum -y install epel-release
sudo yum -y install python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml git python3-libguestfs iproute-tc cyrus-sasl-md5 python3-libguestfs libsasl2-dev libldap2-dev libssl-dev
```
#### Creating directories and cloning repo
#### Creating directories and cloning repository
```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
# create secret key manually or use that command
sudo sed -r "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
```
#### Start installation webvirtcloud
```
sudo virtualenv venv
sudo source venv/bin/activate
sudo pip install -r conf/requirements.txt
sudo cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
sudo python manage.py migrate
```bash
virtualenv-3 venv
source venv/bin/activate
pip3 install -r conf/requirements.txt
cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
python3 manage.py migrate
python3 manage.py collectstatic --noinput
```
#### Configure the supervisor for CentOS
Add the following after the [include] line (after **files = ... ** actually):
#### Configure the supervisor for RHEL Based OS
Add the following after the [include] line (after **files = ...** actually):
```bash
sudo vim /etc/supervisord.conf
@ -88,7 +138,7 @@ autorestart=true
redirect_stderr=true
[program:novncd]
command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd
command=/srv/webvirtcloud/venv/bin/python3 /srv/webvirtcloud/console/novncd
directory=/srv/webvirtcloud
user=nginx
autostart=true
@ -97,9 +147,10 @@ redirect_stderr=true
```
#### Edit the nginx.conf file
You will need to edit the main nginx.conf file as the one that comes from the rpm's will not work. Comment the following lines:
```
```bash
# server {
# listen 80 default_server;
# listen [::]:80 default_server;
@ -124,7 +175,12 @@ You will need to edit the main nginx.conf file as the one that comes from the rp
```
Also make sure file in **/etc/nginx/conf.d/webvirtcloud.conf** has the proper paths:
```
```bash
upstream gunicorn_server {
#server unix:/srv/webvirtcloud/venv/wvcloud.socket fail_timeout=0;
server 127.0.0.1:8000 fail_timeout=0;
}
server {
listen 80;
@ -137,14 +193,14 @@ server {
}
location / {
proxy_pass http://127.0.0.1:8000;
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;
proxy_connect_timeout 1800;
proxy_read_timeout 1800;
proxy_send_timeout 1800;
client_max_body_size 1024M;
}
}
@ -160,28 +216,48 @@ Change permission for selinux:
```bash
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webvirtcloud(/.*)"
sudo setsebool -P httpd_can_network_connect on -P
```
Add required user to the kvm group:
Add required user to the kvm group(if you not install with root):
```bash
sudo usermod -G kvm -a webvirtmgr
sudo usermod -G kvm -a <username>
```
Allow http ports on firewall:
```bash
sudo firewall-cmd --add-service=http
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-port=6080/tcp
sudo firewall-cmd --add-port=6080/tcp --permanent
```
Let's restart nginx and the supervisord services:
```bash
sudo systemctl restart nginx && systemctl restart supervisord
```
And finally, check everything is running:
```bash
sudo supervisorctl status
gstfsd RUNNING pid 24662, uptime 6:01:40
novncd RUNNING pid 24661, uptime 6:01:40
webvirtcloud RUNNING pid 24660, uptime 6:01:40
```
novncd RUNNING pid 24186, uptime 2:59:14
webvirtcloud RUNNING pid 24185, uptime 2:59:14
#### Apache mod_wsgi configuration
```bash
WSGIDaemonProcess webvirtcloud threads=2 maximum-requests=1000 display-name=webvirtcloud
WSGIScriptAlias / /srv/webvirtcloud/webvirtcloud/wsgi_custom.py
```
#### Install final required packages for libvirtd and others on Host Server
```bash
wget -O - https://clck.ru/9V9fH | sudo sh
```
@ -190,19 +266,215 @@ Done!!
Go to http://serverip and you should see the login screen.
### Alternative running novncd via runit(Debian)
Alternative to running nonvcd via supervisor is runit.
On Debian systems install runit and configure novncd service:
```bash
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
<pre>
```html
login: admin
password: admin
</pre>
```
### Configuring Compute SSH connection
This is a short example of configuring cloud and compute side of the ssh connection.
On the webvirtcloud machine you need to generate ssh keys and optionally disable StrictHostKeyChecking.
### How To Update
```bash
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.
```bash
sudo -u www-data ssh-copy-id root@compute1
```
### Host SMBIOS information is not available
If you see warning
```bash
Unsupported configuration: Host SMBIOS information is not available
```
Then you need to install `dmidecode` package on your host using your package manager and restart libvirt daemon.
Debian/Ubuntu like:
```bash
sudo apt-get install dmidecode
sudo service libvirt-bin restart
```
Arch Linux
```bash
sudo pacman -S dmidecode
systemctl restart libvirtd
```
### Cloud-init
Currently supports only root ssh authorized keys and hostname. Example configuration of the cloud-init client follows.
```bash
datasource:
OpenStack:
metadata_urls: [ "http://webvirtcloud.domain.com/datasource" ]
```
### Reverse-Proxy
Edit WS_PUBLIC_PORT at settings.py file to expose redirect to 80 or 443. Default: 6080
```bash
WS_PUBLIC_PORT = 80
```
## How To Update
```bash
# Go to Installation Directory
cd /srv/webvirtcloud
source venv/bin/activate
git pull
python manage.py migrate
pip3 install -U -r conf/requirements.txt
python3 manage.py migrate
python3 manage.py collectstatic --noinput
sudo service supervisor restart
```
### License
### Running tests
Server on which tests will be performed must have libvirt up and running.
It must not contain vms.
It must have `default` storage which not contain any disk images.
It must have `default` network which must be on.
Setup venv
```bash
python -m venv venv
source venv/bin/activate
pip install -r conf/requirements.txt
```
Run tests
```bash
python manage.py test
```
## LDAP Configuration
The config options below can be changed in `webvirtcloud/settings.py` file. Variants for Active Directory and OpenLDAP are shown. This is a minimal config to get LDAP running, for further info read the [django-auth-ldap documentation](https://django-auth-ldap.readthedocs.io).
Enable LDAP
```bash
sudo sed -i "s~#\"django_auth_ldap.backend.LDAPBackend\",~\"django_auth_ldap.backend.LDAPBackend\",~g" /srv/webvirtcloud/webvirtcloud/settings.py
```
Set the LDAP server name and bind DN
```python
# Active Directory
AUTH_LDAP_SERVER_URI = "ldap://example.com"
AUTH_LDAP_BIND_DN = "username@example.com"
AUTH_LDAP_BIND_PASSWORD = "password"
# OpenLDAP
AUTH_LDAP_SERVER_URI = "ldap://example.com"
AUTH_LDAP_BIND_DN = "CN=username,CN=Users,OU=example,OU=com"
AUTH_LDAP_BIND_PASSWORD = "password"
```
Set the user filter and user and group search base and filter
```python
# Active Directory
AUTH_LDAP_USER_SEARCH = LDAPSearch(
"CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)"
)
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
"CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(objectClass=group)"
)
AUTH_LDAP_GROUP_TYPE = NestedActiveDirectoryGroupType()
# OpenLDAP
AUTH_LDAP_USER_SEARCH = LDAPSearch(
"CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(cn=%(user)s)"
)
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
"CN=Users,DC=example,DC=com", ldap.SCOPE_SUBTREE, "(objectClass=groupOfUniqueNames)"
)
AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType() # import needs to be changed at the top of settings.py
```
Set group which is required to access WebVirtCloud. You may set this to `False` to disable this filter.
```python
AUTH_LDAP_REQUIRE_GROUP = "CN=WebVirtCloud Access,CN=Users,DC=example,DC=com"
```
Populate user fields with values from LDAP
```python
AUTH_LDAP_USER_FLAGS_BY_GROUP = {
"is_staff": "CN=WebVirtCloud Staff,CN=Users,DC=example,DC=com",
"is_superuser": "CN=WebVirtCloud Admins,CN=Users,DC=example,DC=com",
}
AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}
```
Now when you login with an LDAP user it will be assigned the rights defined. The user will be authenticated then with LDAP and authorized through the WebVirtCloud permissions.
If you'd like to move a user from ldap to WebVirtCloud, just change its password from the UI and (eventually) remove from the group in LDAP.
## REST API / BETA
Webvirtcloud provides a REST API for programmatic access.
To access API methods open your browser and check them with Swagger interface
```bash
http://<webvirtloud-address:port>/swagger
```
```bash
http://<webvirtloud-address:port>/redoc
```
## Screenshots
Instance Detail:
<img src="doc/images/instance.PNG" width="96%" align="center"/>
Instance List:</br>
<img src="doc/images/grouped.PNG" width="43%"/>
<img src="doc/images/nongrouped.PNG" width="53%"/>
Other: </br>
<img src="doc/images/hosts.PNG" width="47%"/>
<img src="doc/images/log.PNG" width="49%"/>
## License
WebVirtCloud is licensed under the [Apache Licence, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).

54
Vagrantfile vendored
View file

@ -2,17 +2,55 @@
# 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
# Default machine, if name not specified...
config.vm.define "dev", primary: true do |dev|
dev.vm.box = "ubuntu/bionic64"
dev.vm.hostname = "webvirtcloud"
dev.vm.network "private_network", ip: "192.168.33.10"
dev.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 libxml2-dev libvirt-dev zlib1g-dev
virtualenv /vagrant/venv
sudo apt-get -y install python3-virtualenv virtualenv python3-pip python3-dev python3-lxml libvirt-dev zlib1g-dev python3-guestfs
virtualenv -p python3 /vagrant/venv
source /vagrant/venv/bin/activate
pip install -r /vagrant/dev/requirements.txt
SHELL
pip3 install -r /vagrant/dev/requirements.txt
SHELL
end
# To start this machine run "vagrant up prod"
# To enter this machine run "vagrant ssh prod"
config.vm.define "prod", autostart: false do |prod|
prod.vm.box = "ubuntu/bionic64"
prod.vm.hostname = "webvirtcloud"
prod.vm.network "private_network", ip: "192.168.33.11"
prod.vm.network "forwarded_port", guest: 80, host: 8081
#prod.vm.synced_folder ".", "/srv/webvirtcloud"
prod.vm.provision "shell", inline: <<-SHELL
sudo mkdir /srv/webvirtcloud
sudo cp -R /vagrant/* /srv/webvirtcloud
sudo sh /srv/webvirtcloud/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 chown -R vagrant:vagrant /srv/webvirtcloud
sudo apt-get -y install python3-virtualenv python3-dev python3-lxml python3-pip virtualenv libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs
virtualenv -p python3 /srv/webvirtcloud/venv
source /srv/webvirtcloud/venv/bin/activate
pip3 install -r /srv/webvirtcloud/requirements.txt
sudo cp /srv/webvirtcloud/conf/supervisor/webvirtcloud.conf /etc/supervisor/conf.d
sudo cp /srv/webvirtcloud/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d
sudo cp /srv/webvirtcloud/webvirtcloud/settings.py.template /srv/webvirtcloud/webvirtcloud/settings.py
sudo sed "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
python3 /srv/webvirtcloud/manage.py makemigrations
python3 /srv/webvirtcloud/manage.py migrate
python3 /srv/webvirtcloud/manage.py collectstatic --noinput
sudo rm /etc/nginx/sites-enabled/default
sudo chown -R www-data:www-data /srv/webvirtcloud
sudo service nginx restart
sudo service supervisor restart
SHELL
end
end

1
_config.yml Normal file
View file

@ -0,0 +1 @@
theme: jekyll-theme-cayman

View file

@ -0,0 +1 @@
default_app_config = 'accounts.apps.AccountsConfig'

View file

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

63
accounts/apps.py Normal file
View file

@ -0,0 +1,63 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
def apply_change_password(sender, **kwargs):
"""
Apply new change_password permission for all users
Depending on settings SHOW_PROFILE_EDIT_PASSWORD
"""
from django.conf import settings
from django.contrib.auth.models import Permission, User
if hasattr(settings, "SHOW_PROFILE_EDIT_PASSWORD"):
print("\033[1m! \033[92mSHOW_PROFILE_EDIT_PASSWORD is found inside settings.py\033[0m")
print("\033[1m* \033[92mApplying permission can_change_password for all users\033[0m")
users = User.objects.all()
permission = Permission.objects.get(codename="change_password")
if settings.SHOW_PROFILE_EDIT_PASSWORD:
print("\033[1m! \033[91mWarning!!! Setting to True for all users\033[0m")
for user in users:
user.user_permissions.add(permission)
else:
print("\033[1m* \033[91mWarning!!! Setting to False for all users\033[0m")
for user in users:
user.user_permissions.remove(permission)
print("\033[1m! Don`t forget to remove the option from settings.py\033[0m")
def create_admin(sender, **kwargs):
"""
Create initial admin user
"""
from django.contrib.auth.models import User
from accounts.models import UserAttributes
plan = kwargs.get("plan", [])
for migration, rolled_back in plan:
if (
migration.app_label == "accounts"
and migration.name == "0001_initial"
and not rolled_back
):
if User.objects.count() == 0:
print("\033[1m* \033[92mCreating default admin user\033[0m")
admin = User.objects.create_superuser("admin", None, "admin")
UserAttributes(
user=admin,
max_instances=-1,
max_cpus=-1,
max_memory=-1,
max_disk_size=-1,
).save()
break
class AccountsConfig(AppConfig):
name = "accounts"
verbose_name = "Accounts"
def ready(self):
post_migrate.connect(create_admin, sender=self)
post_migrate.connect(apply_change_password, sender=self)

View file

@ -1,24 +1,75 @@
import re
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from appsettings.settings import app_settings
from django.contrib.auth import get_user_model
from django.forms import EmailField, Form, ModelForm, ValidationError
from django.utils.translation import gettext_lazy as _
from .models import UserInstance, UserSSHKey
from .utils import validate_ssh_key
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=True, error_messages={'required': _('No password has been entered')},)
class UserInstanceForm(ModelForm):
def __init__(self, *args, **kwargs):
super(UserInstanceForm, self).__init__(*args, **kwargs)
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'))
# Make user and instance fields not editable after creation
instance = getattr(self, "instance", None)
if instance and instance.id is not None:
self.fields["user"].disabled = True
self.fields["instance"].disabled = True
def clean_instance(self):
instance = self.cleaned_data["instance"]
if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == "False":
exists = UserInstance.objects.filter(instance=instance)
if exists:
raise ValidationError(_("Instance owned by another user"))
return instance
class Meta:
model = UserInstance
fields = "__all__"
class ProfileForm(ModelForm):
class Meta:
model = get_user_model()
fields = ("first_name", "last_name", "email")
class UserSSHKeyForm(ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
self.publickeys = UserSSHKey.objects.filter(user=self.user)
super().__init__(*args, **kwargs)
def clean_keyname(self):
for key in self.publickeys:
if self.cleaned_data["keyname"] == key.keyname:
raise ValidationError(_("Key name already exist"))
return self.cleaned_data["keyname"]
def clean_keypublic(self):
for key in self.publickeys:
if self.cleaned_data["keypublic"] == key.keypublic:
raise ValidationError(_("Public key already exist"))
if not validate_ssh_key(self.cleaned_data["keypublic"]):
raise ValidationError(_("Invalid key"))
return self.cleaned_data["keypublic"]
def save(self, commit=True):
ssh_key = super().save(commit=False)
ssh_key.user = self.user
if commit:
ssh_key.save()
return ssh_key
class Meta:
model = UserSSHKey
fields = ("keyname", "keypublic")
class EmailOTPForm(Form):
email = EmailField(label=_("Email"))

View file

@ -1,29 +1,50 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
# Generated by Django 2.2.10 on 2020-01-28 07:01
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('instances', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserSSHKey',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('keyname', models.CharField(max_length=25)),
('keypublic', models.CharField(max_length=500)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserInstance',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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)),
('is_vnc', models.BooleanField(default=False)),
('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='instances.Instance')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserAttributes',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('can_clone_instances', models.BooleanField(default=True)),
('max_instances', models.IntegerField(default=1, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('max_cpus', models.IntegerField(default=1, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('max_memory', models.IntegerField(default=2048, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('max_disk_size', models.IntegerField(default=20, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -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),
]

View file

@ -0,0 +1,24 @@
# Generated by Django 2.2.12 on 2020-05-27 12:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='PermissionSet',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'permissions': (('change_password', 'Can change password'), ),
'managed': False,
'default_permissions': (),
},
),
]

View file

@ -0,0 +1,24 @@
# Generated by Django 2.2.12 on 2020-06-04 09:30
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0002_permissionset'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=2, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=2, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
]

View file

@ -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)),
],
),
]

View file

@ -0,0 +1,44 @@
# Generated by Django 2.2.13 on 2020-06-15 06:37
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0003_auto_20200604_0930'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=2, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)], verbose_name='max CPUs'),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)], verbose_name='max disk size'),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=2, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)], verbose_name='max instances'),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)], verbose_name='max memory'),
),
migrations.AlterField(
model_name='usersshkey',
name='keyname',
field=models.CharField(max_length=25, verbose_name='key name'),
),
migrations.AlterField(
model_name='usersshkey',
name='keypublic',
field=models.CharField(max_length=500, verbose_name='public key'),
),
]

View file

@ -0,0 +1,20 @@
# Generated by Django 2.2.13 on 2020-06-16 10:39
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('instances', '0003_auto_20200615_0637'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0004_auto_20200615_0637'),
]
operations = [
migrations.AlterUniqueTogether(
name='userinstance',
unique_together={('user', 'instance')},
),
]

View file

@ -1,22 +1,82 @@
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from instances.models import Instance
class UserInstanceManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related("instance", "user")
class UserInstance(models.Model):
user = models.ForeignKey(User)
instance = models.ForeignKey(Instance)
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
objects = UserInstanceManager()
def __str__(self):
return _('Instance "%(inst)s" of user %(user)s') % {
"inst": self.instance,
"user": self.user,
}
class Meta:
unique_together = ["user", "instance"]
class UserSSHKey(models.Model):
user = models.ForeignKey(User)
keyname = models.CharField(max_length=25)
keypublic = models.CharField(max_length=500)
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
keyname = models.CharField(_("key name"), max_length=25)
keypublic = models.CharField(_("public key"), max_length=500)
def __unicode__(self):
def __str__(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(
_("max instances"),
default=2,
help_text=_("-1 for unlimited. Any integer value"),
validators=[MinValueValidator(-1)],
)
max_cpus = models.IntegerField(
_("max CPUs"),
default=2,
help_text=_("-1 for unlimited. Any integer value"),
validators=[MinValueValidator(-1)],
)
max_memory = models.IntegerField(
_("max memory"),
default=2048,
help_text=_("-1 for unlimited. Any integer value"),
validators=[MinValueValidator(-1)],
)
max_disk_size = models.IntegerField(
_("max disk size"),
default=20,
help_text=_("-1 for unlimited. Any integer value"),
validators=[MinValueValidator(-1)],
)
def __str__(self):
return self.user.username
class PermissionSet(models.Model):
"""
Dummy model for holding set of permissions we need to be automatically added by Django
"""
class Meta:
default_permissions = ()
permissions = (("change_password", _("Can change password")),)
managed = False

View file

@ -1,104 +1,93 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "User" %} - {{ user }}{% endblock %}
{% load bootstrap_icons %}
{% load qr_code %}
{% block title %}{% trans "User Profile" %} - {{ user }}{% endblock %}
{% block page_heading %}{% trans "User Profile" %}: {{ user }}{% endblock page_heading %}
{% block page_heading_extra %}
{% if otp_enabled %}
<a href="{% url 'accounts:admin_email_otp' user.id %}" class="btn btn-secondary" title="{% trans "Email OTP QR code" %}">
{% bs_icon 'qr-code' %}
</a>
{% endif %}
<a href="{% url 'admin:user_update' user.id %}?next={% url 'accounts:account' user.id %}" class="btn btn-primary" title="{% trans "Edit user" %}">
{% bs_icon 'pencil' %}
</a>
<a href="{% url 'accounts:user_instance_create' user.id %}" class="btn btn-success" title="{% trans "Create user instance" %}">
{% bs_icon 'plus-circle-fill' %}
</a>
{% endblock page_heading_extra %}
{% block content %}
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
{% include 'create_user_inst_block.html' %}
<h1 class="page-header">{{ user }}</h1>
</div>
</div>
<!-- /.row -->
<ul class="nav nav-tabs">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#instances" type="button" role="tab" aria-controls="instances" aria-selected="true">
{% trans "Instances" %}
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#public-keys" type="button" role="tab" aria-controls="public-keys" aria-selected="false">
{% trans "Public Keys" %}
</button>
</li>
</ul>
{% include 'errors_block.html' %}
<div class="row">
<div class="col-lg-12">
{% if not user_insts %}
<div class="col-lg-12">
<div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "User doesn't have any Instace" %}
</div>
</div>
{% else %}
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>{% trans "Instance" %}</th>
<th>{% trans "Resize" %}</th>
<th>{% trans "Delete" %}</th>
<th colspan="2">{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for inst in user_insts %}
<tr>
<td>{{ forloop.counter }}</td>
<td><a href="{% url 'instance' inst.instance.compute.id inst.instance.name %}">{{ inst.instance.name }}</a></td>
<td>{{ inst.is_change }}</td>
<td>{{ inst.is_delete }}</td>
<td style="width:5px;">
<a href="#editPriv{{ forloop.counter }}" type="button" class="btn btn-xs btn-default" data-toggle="modal">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
</a>
<!-- Modal pool -->
<div class="modal fade" id="editPriv{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="editPrivLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Edit privilegies for" %} {{ inst.instance.name }}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<input type="hidden" name="user_inst" value="{{ inst.id }}">
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Resize" %}</label>
<div class="col-sm-6">
<select type="text" class="form-control" name="inst_change">
<option value="">False</option>
<option value="1" {% if inst.is_change %}selected{% endif %}>True</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Delete" %}</label>
<div class="col-sm-6">
<select type="text" class="form-control" name="inst_delete">
<option value="">False</option>
<option value="1" {% if inst.is_delete %}selected{% endif %}>True</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary" name="permission">{% trans "Edit" %}</button>
</div>
</form>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->
</td>
<td style="width:5px;">
<form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="user_inst" value="{{ inst.id }}">
<button type="submit" class="btn btn-xs btn-default" name="delete" tittle="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
{% endblock %}
<div class="tab-content">
<div class="tab-pane active" id="instances">
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">{% trans "Instance" %}</th>
<th scope="col">{% trans "VNC" %}</th>
<th scope="col">{% trans "Resize" %}</th>
<th scope="col">{% trans "Delete" %}</th>
<th scope="colgroup" colspan="2">{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for inst in user_insts %}
<tr>
<td>{{ forloop.counter }}</td>
<td><a href="{% url 'instances:instance' inst.instance.id %}">{{ inst.instance.name }}</a></td>
<td>{{ inst.is_vnc }}</td>
<td>{{ inst.is_change }}</td>
<td>{{ inst.is_delete }}</td>
<td style="width:5px;">
<a href="{% url 'accounts:user_instance_update' inst.id %}" class="btn btn-sm btn-secondary" title="{% trans "edit" %}">
{% bs_icon 'pencil' %}
</a>
</td>
<td style="width:5px;">
<a class="btn btn-sm btn-secondary" href="{% url 'accounts:user_instance_delete' inst.id %}" title="{% trans "Delete" %}">
{% bs_icon 'trash' %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="tab-pane fade" id="public-keys">
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">{% trans "Key name" %}</th>
<th scope="col">{% trans "Public key" %}</th>
</tr>
</thead>
<tbody>
{% for publickey in publickeys %}
<tr>
<td>{{ publickey.keyname }}</td>
<td title="{{ publickey.keypublic }}">{{ publickey.keypublic|truncatechars:64 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}

View file

@ -1,102 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Users" %}{% endblock %}
{% block content %}
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
{% include 'create_user_block.html' %}
<h1 class="page-header">{% trans "Users" %}</h1>
</div>
</div>
<!-- /.row -->
{% include 'errors_block.html' %}
<div class="row">
{% if not users %}
<div class="col-lg-12">
<div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "You don't have any User" %}
</div>
</div>
{% else %}
{% for user in users %}
<div id="{{ user.username }}" class="col-xs-12 col-sm-4">
<div class="panel {% if user.is_active %}panel-success{% else %}panel-danger{% endif %} panel-data">
<div class="panel-heading">
<h3 class="panel-title">
<a href="{% url 'account' user.id %}"><strong>{{ user.username }}</strong></a>
<a data-toggle="modal" href="#editUser{{ user.id }}" class="pull-right" title="{% trans "Edit" %}">
<span class="glyphicon glyphicon-cog"></span>
</a>
</h3>
</div>
<div class="panel-body">
<div class="col-xs-4 col-sm-4">
<p><strong>{% trans "Status:" %}</strong></p>
</div>
<div class="col-xs-4 col-sm-6">
{% if user.is_active %}
<p>{% trans "Active" %}</p>
{% else %}
<p>{% trans "Blocked" %}</p>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Modal Edit -->
<div class="modal fade" id="editUser{{ user.id }}" tabindex="-1" role="dialog" aria-labelledby="editUserLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Edit user info" %}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Name" %}</label>
<div class="col-sm-6">
<input type="hidden" name="user_id" value="{{ user.id }}">
<input type="text" name="name" class="form-control" value="{{ user.username }}" disabled>
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" name="user_pass" class="form-control" value="">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="pull-left btn btn-danger" name="delete">
{% trans "Delete" %}
</button>
{% if user.is_active %}
<button type="submit" class="pull-left btn btn-warning" name="block">
{% trans "Block" %}
</button>
{% else %}
<button type="submit" class="pull-left btn btn-success" name="unblock">
{% trans "Unblock" %}
</button>
{% endif %}
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="edit">
{% trans "Edit" %}
</button>
</form>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endfor %}
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% load django_bootstrap5 %}
{% load i18n %}
{% load bootstrap_icons %}
{% block title %}{%trans "Change Password" %}{% endblock title %}
{% block content %}
<div class="row">
<div class="offset-2 col-lg-8">
<div class="card">
<div class="card-header">
<h4 class="card-title">{%trans "Change Password" %}: {{ user }}</h4>
</div>
<div class="card-body">
<form method="post" id="password-change">
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
</form>
</div>
<div class="card-footer">
<div class="float-end">
<a class="btn btn-primary" href="javascript:history.back()">{% bs_icon 'x' %}
{% trans "Cancel" %}</a>
<button type="submit" form="password-change" class="btn btn-success">
{% bs_icon 'check2' %} {% trans "Change" %}
</button>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,9 @@
{% load i18n %}
{% load qr_code %}
{% blocktrans %}
Scan this QR code to get OTP for account '{{ user }}'
{% endblocktrans %}
<br>
{% qr_from_text totp_url %}
<p class="small">{% trans 'Some e-mail clients does not render SVG, also generating PNG.' %}</p>
{% qr_from_text totp_url size="s" image_format="png" error_correction="M" %}

View file

@ -0,0 +1,32 @@
{% extends "base.html" %}
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% load i18n %}
{% block title %}{{ title }}{% endblock %}
{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block content %}
<div class="alert alert-info">
{% blocktrans %}
Enter email address OTP QR code will be sent to.
{% endblocktrans %}
</div>
<div class="card">
<div class="card-body">
<form id="create-update" action="" method="post">
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
</form>
</div>
<div class="card-footer">
<div class="mb-0 float-end">
<a class="btn btn-primary" href="javascript:history.back()">{% bs_icon 'arrow-left' %} {% trans "Cancel" %}</a>
<button type="submit" form="create-update" class="btn btn-success">
{% bs_icon 'envelope-open' %} {% trans "Send" %}
</button>
</div>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,57 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load django_bootstrap5 %}
{% block title %}{% trans "WebVirtCloud" %} - {% trans "Sign In with OTP" %}{% endblock title %}
{% block style %}
<link href="{% static "css/signin.css" %}" rel="stylesheet">
{% endblock style %}
{% block content %}
<div class="login-box">
<div class="page-header">
<a href="/"><h1>WebVirtCloud</h1></a>
</div>
<hr>
<div class="col-12" role="main">
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% bootstrap_form_errors form %}
</div>
{% endif %}
<form id="form-signin" class="form-signin" method="post" role="form" aria-label="Login form">{% csrf_token %}
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
{% bootstrap_field form.username layout='inline' %}
{% bootstrap_field form.password layout='inline' %}
{% bootstrap_field form.otp_token layout='inline'%}
<a href="{% url 'accounts:email_otp' %}" class="float-end">{% trans "I do not have/lost my OTP!" %}</a>
<br>
<div class="d-grid">
<button id="btn-signin" class="btn btn-lg btn-success" type="submit">{% trans "Sign In" %}</button>
</div>
</form>
</div>
</div>
{% endblock content %}
{% block script %}
<script>
$(document).ready(function() {
$("#btn-signin").click(function() {
// disable button
$(this).prop("disabled", true);
// add spinner to button
$(this).html(
`
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
{% trans "Loading" %}...
`
);
$("#form-signin").submit();
});
});
</script>
{% endblock script%}

View file

@ -1,41 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="WebVirtMgr panel for manage virtual machine">
<meta name="author" content="anatoliy.guskov@gmail.com">
<title>{% block title %}{% endblock %}</title>
<!-- Bootstrap Core CSS -->
<link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">
<!-- SB admin CSS -->
<link href="{% static "css/signin.css" %}" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
<!-- jQuery -->
<script src="{% static "js/jquery.js" %}"></script>
<!-- Bootstrap Core JavaScript -->
<script src="{% static "js/bootstrap.min.js" %}"></script>
</body>
</html>

View file

@ -1,38 +0,0 @@
{% load i18n %}
{% if request.user.is_superuser %}
<a href="#AddUser" type="button" class="btn btn-success pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
<!-- Modal pool -->
<div class="modal fade" id="AddUser" tabindex="-1" role="dialog" aria-labelledby="AddUserLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Add New User" %}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Name" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="name" placeholder="john" required pattern="[a-z0-9]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" class="form-control" name="password" placeholder="*******" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary" name="create">{% trans "Create" %}</button>
</div>
</form>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->
{% endif %}

View file

@ -1,36 +0,0 @@
{% load i18n %}
{% if request.user.is_superuser %}
<a href="#addUserInst" type="button" class="btn btn-success pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
<!-- Modal pool -->
<div class="modal fade" id="addUserInst" tabindex="-1" role="dialog" aria-labelledby="addUserInstLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Add Instance for User" %}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Host / Instance" %}</label>
<div class="col-sm-6">
<select class="form-control" name="inst_id">
{% for inst in instances %}
<option value="{{ inst.id }}">{{ inst.compute.name }} / {{ inst.name }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary" name="add">{% trans "Add" %}</button>
</div>
</form>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->
{% endif %}

View file

@ -1,25 +1,55 @@
{% extends "base_auth.html" %}
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "WebVirtCloud - Sign In" %}{% endblock %}
{% load static %}
{% block title %}{% trans "WebVirtCloud" %} - {% trans "Sign In" %}{% endblock title %}
{% block style %}
<link href="{% static "css/signin.css" %}" rel="stylesheet">
{% endblock style %}
{% block content %}
<div class="row">
<div class="page-header">
<a href="/"><h1>WebVirtCloud</h1></a>
</div>
<div class="col-xs-12" role="main">
{% if form.errors %}
<div class="alert alert-danger">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{% trans "Incorrect username or password." %}
</div>
{% endif %}
<form class="form-signin" method="post" role="form">{% csrf_token %}
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
<input type="text" class="form-control" name="username" placeholder="Login" autocapitalize="none" autocorrect="off" autofocus>
<input type="password" class="form-control" name="password" placeholder="Password">
<input name="next" id="next" type="hidden" value="{% url 'instances' %}">
<button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button>
</form>
</div>
<div class="login-box">
<div class="page-header">
<a href="/"><h1>WebVirtCloud</h1></a>
</div>
{% endblock %}
<hr>
<div class="col-12" role="main">
{% if form.errors %}
<div class="alert alert-danger" role="alert">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
{% trans "Incorrect username or password." %}
</div>
{% endif %}
<form id="form-signin" class="form-signin" method="post" role="form" aria-label="Login form">{% csrf_token %}
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
<input type="text" class="form-control" name="username" placeholder="{% trans 'User' %}" autocapitalize="none" autocorrect="off" autofocus required>
<input type="password" class="form-control" name="password" placeholder="{% trans 'Password' %}" required>
<input type="hidden" name="next" value="{{ next }}">
<div class="d-grid">
<button id="btn-signin" class="btn btn-lg btn-success" type="submit">{% trans "Sign In" %}</button>
</div>
</form>
</div>
</div>
{% endblock %}
{% block script %}
<script>
$(document).ready(function() {
$("#btn-signin").click(function() {
// disable button
$(this).prop("disabled", true);
// add spinner to button
$(this).html(
`
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
{% trans "Loading" %}...
`
);
$("#form-signin").submit();
});
});
</script>
{% endblock script%}

View file

@ -1,15 +1,17 @@
{% extends "base_auth.html" %}
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "WebVirtCloud - Sign Out" %}{% endblock %}
{% block title %}
{% trans "WebVirtCloud" %} - {% trans "Sign Out"%}
{% endblock %}
{% block content %}
<div class="row">
<div>
<div class="page-header">
<a href="/"><h1>WebVirtCloud</h1></a>
</div>
<div class="col-xs-12" role="main">
<div class="col-12" role="main">
<div class="logout">
<h1>{% trans "Successful log out" %}</h1>
<h2>{% trans "Successful log out" %}</h2>
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View file

@ -1,115 +1,84 @@
{% extends "base.html" %}
{% load i18n %}
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% load tags_fingerprint %}
{% block title %}{% trans "Profile" %}{% endblock %}
{% block title %}{% trans "Profile" %}: {{ request.user.first_name }} {{ request.user.last_name}}{% endblock %}
{% block page_heading %}{% trans "Profile" %}: {{ request.user.first_name }} {{ request.user.last_name}}{% endblock page_heading %}
{% block content %}
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">{% trans "Profile" %}</h1>
</div>
</div>
<!-- /.row -->
{% include 'errors_block.html' %}
<div class="row">
<div class="col-lg-12">
<h3 class="page-header">{% trans "Edit Profile" %}</h3>
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">{% trans "Login" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" value="{{ request.user.username }}" disabled>
</div>
</div>
<div class="form-group bridge_name_form_group_dhcp">
<label class="col-sm-2 control-label">{% trans "Username" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="username" value="{{ request.user.first_name }}" pattern="[0-9a-zA-Z]+">
</div>
</div>
<div class="form-group bridge_name_form_group_dhcp">
<label class="col-sm-2 control-label">{% trans "Email" %}</label>
<div class="col-sm-4">
<input type="email" class="form-control" name="email" value="{{ request.user.email }}">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">{% trans "Change" %}</button>
</div>
</div>
</form>
<h3 class="page-header">{% trans "Edit Password" %}</h3>
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">{% trans "Old" %}</label>
<div class="col-sm-4">
<input type="password" class="form-control" name="oldpasswd" value="">
</div>
</div>
<div class="form-group bridge_name_form_group_dhcp">
<label class="col-sm-2 control-label">{% trans "New" %}</label>
<div class="col-sm-4">
<input type="password" class="form-control" name="passwd1" value="">
</div>
</div>
<div class="form-group bridge_name_form_group_dhcp">
<label class="col-sm-2 control-label">{% trans "Retry" %}</label>
<div class="col-sm-4">
<input type="password" class="form-control" name="passwd2" value="">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">{% trans "Change" %}</button>
</div>
</div>
</form>
<h3 class="page-header">{% trans "SSH Keys" %}</h3>
{% if publickeys %}
<div class="col-lg-12">
<div class="table-responsive">
<table class="table table-hover">
<tbody style="text-align: center;">
{% for key in publickeys %}
<tr>
<td>{{ key.keyname }} ({% ssh_to_fingerprint key.keypublic %})</td>
<td>
<form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="keyid" value="{{ key.id }}"/>
<button type="submit" class="btn btn-sm btn-default" name="keydelete" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<span class="glyphicon glyphicon-trash"></span>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<ul class="nav nav-tabs">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#editprofile" type="button" role="tab" aria-controls="editprofile" aria-selected="true">
{% trans "Edit Profile" %}
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#sshkeys" type="button" role="tab" aria-controls="sshkeys" aria-selected="false">
{% trans "SSH Keys" %}
</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane tab-pane-bordered active" id="editprofile">
<div class="card">
<div class="card-body">
<form method="post" action="" role="form" aria-label="Edit user info form">
{% csrf_token %}
{% bootstrap_form profile_form layout='horizontal' %}
{% if perms.accounts.change_password %}
<a href="{% url 'accounts:change_password' %}" class="btn btn-primary">
{% bs_icon 'lock' %} {% trans "Change Password" %}
</a>
{% endif %}
<form class="form-horizontal" method="post" action="" role="form">{% csrf_token %}
<div class="form-group bridge_name_form_group_dhcp">
<label class="col-sm-2 control-label">{% trans "Retry" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="keyname" placeholder="{% trans "Enter Name" %}">
</div>
</div>
<div class="form-group bridge_name_form_group_dhcp">
<label class="col-sm-2 control-label">{% trans "Retry" %}</label>
<div class="col-sm-8">
<textarea name="keypublic" class="form-control" rows="6" placeholder="{% trans "Enter Public Key" %}"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">{% trans "Create" %}</button>
</div>
</div>
</form>
</div>
<div class="mb-0 float-end">
<button type="submit" class="btn btn-primary">
{% bs_icon 'pencil' %} {% trans "Update" %}
</button>
</div>
</form>
</div>
</div>
</div>
<div class="tab-pane tab-pane-bordered fade" id="sshkeys">
{% if publickeys %}
<div class="col-lg-12">
<div class="table-responsive">
<table class="table table-hover">
<tbody class="text-center">
{% for key in publickeys %}
<tr>
<td>{{ key.keyname }} ({% ssh_to_fingerprint key.keypublic %})</td>
<td>
<a href="{% url 'accounts:ssh_key_delete' key.id %}" title="{% trans "Delete" %}" class="btn btn-sm btn-secondary">
{% bs_icon 'trash' %}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<div class="card">
<div class="card-header">
{%trans "Add SSH Key" %}
</div>
<div class="card-body">
<form method="post" action="{% url 'accounts:ssh_key_create' %}" role="form" aria-label="Add key to user form">
{% csrf_token %}
{% bootstrap_form ssh_key_form layout='horizontal' %}
<div class="mb-0 float-end">
<button type="submit" class="btn btn-primary">
{% bs_icon 'plus-circle-fill' %} {% trans "Add" %}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,12 +1,16 @@
from django import template
import base64
import hashlib
from django import template
register = template.Library()
@register.simple_tag
def ssh_to_fingerprint(line):
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]))
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'

View file

@ -1,3 +1,308 @@
from django.test import TestCase
from appsettings.settings import app_settings
from computes.models import Compute
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.shortcuts import reverse
from django.test import Client, TestCase
from instances.models import Instance
from instances.utils import refr
from libvirt import VIR_DOMAIN_UNDEFINE_NVRAM
from vrtManager.create import wvmCreate
# Create your tests here.
from accounts.forms import UserInstanceForm, UserSSHKeyForm
from accounts.models import UserInstance, UserSSHKey
from accounts.utils import validate_ssh_key
class AccountsTestCase(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Add users for testing purposes
User = get_user_model()
cls.admin_user = User.objects.get(pk=1)
cls.test_user = User.objects.create_user(username="test", password="test")
# Add localhost compute
cls.compute = Compute(
name="test-compute",
hostname="localhost",
login="",
password="",
details="local",
type=4,
)
cls.compute.save()
cls.connection = wvmCreate(
cls.compute.hostname,
cls.compute.login,
cls.compute.password,
cls.compute.type,
)
# Add disks for testing
cls.connection.create_volume(
"default",
"test-volume",
1,
"qcow2",
False,
0,
0,
)
# XML for testing vm
with open("conf/test-vm.xml", "r") as f:
cls.xml = f.read()
# Create testing vm from XML
cls.connection._defineXML(cls.xml)
refr(cls.compute)
cls.instance = Instance.objects.get(pk=1)
@classmethod
def tearDownClass(cls):
# Destroy testing vm
cls.instance.proxy.delete_all_disks()
cls.instance.proxy.delete(VIR_DOMAIN_UNDEFINE_NVRAM)
super().tearDownClass()
def setUp(self):
self.client.login(username="admin", password="admin")
permission = Permission.objects.get(codename="change_password")
self.test_user.user_permissions.add(permission)
self.rsa_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6OOdbfv27QVnSC6sKxGaHb6YFc+3gxCkyVR3cTSXE/n5BEGf8aOgBpepULWa1RZfxYHY14PlKULDygdXSdrrR2kNSwoKz/Oo4d+3EE92L7ocl1+djZbptzgWgtw1OseLwbFik+iKlIdqPsH+IUQvX7yV545ZQtAP8Qj1R+uCqkw== test@test"
self.ecdsa_key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJc5xpT3R0iFJYNZbmWgAiDlHquX/BcV1kVTsnBfiMsZgU3lGaqz2eb7IBcir/dxGnsVENTTmPQ6sNcxLxT9kkQ= realgecko@archlinux"
def test_profile(self):
response = self.client.get(reverse("accounts:profile"))
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse("accounts:account", args=[self.test_user.id])
)
self.assertEqual(response.status_code, 200)
def test_account_with_otp(self):
settings.OTP_ENABLED = True
response = self.client.get(
reverse("accounts:account", args=[self.test_user.id])
)
self.assertEqual(response.status_code, 200)
def test_login_logout(self):
client = Client()
response = client.post(
reverse("accounts:login"), {"username": "test", "password": "test"}
)
self.assertRedirects(response, reverse("accounts:profile"))
response = client.get(reverse("accounts:logout"))
self.assertRedirects(response, reverse("accounts:login"))
def test_change_password(self):
self.client.force_login(self.test_user)
response = self.client.get(reverse("accounts:change_password"))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("accounts:change_password"),
{
"old_password": "wrongpass",
"new_password1": "newpw",
"new_password2": "newpw",
},
)
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("accounts:change_password"),
{
"old_password": "test",
"new_password1": "newpw",
"new_password2": "newpw",
},
)
self.assertRedirects(response, reverse("accounts:profile"))
self.client.logout()
logged_in = self.client.login(username="test", password="newpw")
self.assertTrue(logged_in)
def test_user_instance_create_update_delete(self):
# create
response = self.client.get(
reverse("accounts:user_instance_create", args=[self.test_user.id])
)
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("accounts:user_instance_create", args=[self.test_user.id]),
{
"user": self.test_user.id,
"instance": self.instance.id,
"is_change": False,
"is_delete": False,
"is_vnc": False,
},
)
self.assertRedirects(
response, reverse("accounts:account", args=[self.test_user.id])
)
user_instance: UserInstance = UserInstance.objects.get(pk=1)
self.assertEqual(user_instance.user, self.test_user)
self.assertEqual(user_instance.instance, self.instance)
self.assertEqual(user_instance.is_change, False)
self.assertEqual(user_instance.is_delete, False)
self.assertEqual(user_instance.is_vnc, False)
# update
response = self.client.get(
reverse("accounts:user_instance_update", args=[user_instance.id])
)
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("accounts:user_instance_update", args=[user_instance.id]),
{
"user": self.test_user.id,
"instance": self.instance.id,
"is_change": True,
"is_delete": True,
"is_vnc": True,
},
)
self.assertRedirects(
response, reverse("accounts:account", args=[self.test_user.id])
)
user_instance: UserInstance = UserInstance.objects.get(pk=1)
self.assertEqual(user_instance.user, self.test_user)
self.assertEqual(user_instance.instance, self.instance)
self.assertEqual(user_instance.is_change, True)
self.assertEqual(user_instance.is_delete, True)
self.assertEqual(user_instance.is_vnc, True)
# delete
response = self.client.get(
reverse("accounts:user_instance_delete", args=[user_instance.id])
)
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("accounts:user_instance_delete", args=[user_instance.id])
)
self.assertRedirects(
response, reverse("accounts:account", args=[self.test_user.id])
)
# test 'next' redirect during deletion
user_instance = UserInstance.objects.create(
user=self.test_user, instance=self.instance
)
response = self.client.post(
reverse("accounts:user_instance_delete", args=[user_instance.id])
+ "?next="
+ reverse("index")
)
self.assertRedirects(response, reverse("index"))
def test_update_user_profile(self):
self.client.force_login(self.test_user)
user = get_user_model().objects.get(username="test")
self.assertEqual(user.first_name, "")
self.assertEqual(user.last_name, "")
self.assertEqual(user.email, "")
response = self.client.post(
reverse("accounts:profile"),
{
"first_name": "first name",
"last_name": "last name",
"email": "email@mail.mail",
},
)
self.assertRedirects(response, reverse("accounts:profile"))
user = get_user_model().objects.get(username="test")
self.assertEqual(user.first_name, "first name")
self.assertEqual(user.last_name, "last name")
self.assertEqual(user.email, "email@mail.mail")
def test_create_delete_ssh_key(self):
response = self.client.get(reverse("accounts:ssh_key_create"))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("accounts:ssh_key_create"),
{
"keyname": "keyname",
"keypublic": self.rsa_key,
},
)
self.assertRedirects(response, reverse("accounts:profile"))
key = UserSSHKey.objects.get(pk=1)
self.assertEqual(key.keyname, "keyname")
self.assertEqual(key.keypublic, self.rsa_key)
response = self.client.get(reverse("accounts:ssh_key_delete", args=[1]))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse("accounts:ssh_key_delete", args=[1]))
self.assertRedirects(response, reverse("accounts:profile"))
def test_validate_ssh_key(self):
self.assertFalse(validate_ssh_key(""))
self.assertFalse(validate_ssh_key("ssh-rsa ABBA test@test"))
self.assertFalse(validate_ssh_key("ssh-rsa AAAABwdzZGY= test@test"))
self.assertFalse(validate_ssh_key("ssh-rsa AAA test@test"))
# validate ecdsa key
self.assertTrue(validate_ssh_key(self.ecdsa_key))
def test_forms(self):
# raise available validation errors for maximum coverage
form = UserSSHKeyForm(
{"keyname": "keyname", "keypublic": self.rsa_key}, user=self.test_user
)
form.save()
form = UserSSHKeyForm(
{"keyname": "keyname", "keypublic": self.rsa_key}, user=self.test_user
)
self.assertFalse(form.is_valid())
form = UserSSHKeyForm(
{"keyname": "keyname", "keypublic": "invalid key"}, user=self.test_user
)
self.assertFalse(form.is_valid())
app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER = "False"
form = UserInstanceForm(
{
"user": self.admin_user.id,
"instance": self.instance.id,
"is_change": False,
"is_delete": False,
"is_vnc": False,
}
)
form.save()
form = UserInstanceForm(
{
"user": self.test_user.id,
"instance": self.instance.id,
"is_change": False,
"is_delete": False,
"is_vnc": False,
}
)
self.assertFalse(form.is_valid())

View file

@ -1,12 +1,55 @@
from django.conf.urls import url
from django.conf import settings
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from django_otp.forms import OTPAuthenticationForm
from . import views
from .views import CustomLoginView
app_name = "accounts"
urlpatterns = [
url(r'^login/$', 'django.contrib.auth.views.login',
{'template_name': 'login.html'}, name='login'),
url(r'^logout/$', 'django.contrib.auth.views.logout',
{'template_name': 'logout.html'}, name='logout'),
url(r'^profile/$', views.profile, name='profile'),
url(r'^$', views.accounts, name='accounts'),
url(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
path("logout/", LogoutView.as_view(template_name="logout.html"), name="logout"),
path("profile/", views.profile, name="profile"),
path("profile/<int:user_id>/", views.account, name="account"),
path("change_password/", views.change_password, name="change_password"),
path(
"user_instance/create/<int:user_id>/",
views.user_instance_create,
name="user_instance_create",
),
path(
"user_instance/<int:pk>/update/",
views.user_instance_update,
name="user_instance_update",
),
path(
"user_instance/<int:pk>/delete/",
views.user_instance_delete,
name="user_instance_delete",
),
path("ssh_key/create/", views.ssh_key_create, name="ssh_key_create"),
path("ssh_key/<int:pk>/delete/", views.ssh_key_delete, name="ssh_key_delete"),
]
if settings.OTP_ENABLED:
urlpatterns += [
path(
"login/",
LoginView.as_view(
template_name="accounts/otp_login.html",
authentication_form=OTPAuthenticationForm,
),
name="login",
),
path("email_otp/", views.email_otp, name="email_otp"),
path(
"admin_email_otp/<int:user_id>/",
views.admin_email_otp,
name="admin_email_otp",
),
]
else:
urlpatterns += (
path("login/", CustomLoginView.as_view(template_name="login.html"), name="login"),
)

61
accounts/utils.py Normal file
View file

@ -0,0 +1,61 @@
import base64
import binascii
import struct
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.translation import gettext as _
from django_otp import devices_for_user
from django_otp.plugins.otp_totp.models import TOTPDevice
def get_user_totp_device(user):
devices = devices_for_user(user)
for device in devices:
if isinstance(device, TOTPDevice):
return device
device = user.totpdevice_set.create()
return device
def validate_ssh_key(key):
array = key.encode().split()
# Each rsa-ssh key has 3 different strings in it, first one being
# typeofkey second one being keystring third one being username .
if len(array) != 3:
return False
typeofkey = array[0]
string = array[1]
# must have only valid rsa-ssh key characters ie binascii characters
try:
data = base64.decodebytes(string)
except binascii.Error:
return False
# unpack the contents of data, from data[:4] , property of ssh key .
try:
str_len = struct.unpack(">I", data[:4])[0]
except struct.error:
return False
# data[4:str_len] must have string which matches with the typeofkey, another ssh key property.
if data[4 : 4 + str_len] != typeofkey:
return False
return True
def send_email_with_otp(user, device):
send_mail(
_("OTP QR Code"),
_("Please view HTML version of this message."),
None,
[user.email],
html_message=render_to_string(
"accounts/email/otp.html",
{
"totp_url": device.config_url,
"user": user,
},
),
fail_silently=False,
)

View file

@ -1,172 +1,215 @@
from django.shortcuts import render
from admin.decorators import superuser_only
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model, update_session_auth_hash, login as auth_login
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.forms import PasswordChangeForm
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
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 accounts.models import UserInstance, UserSSHKey
from django.utils.translation import gettext_lazy as _
from instances.models import Instance
from accounts.forms import UserAddForm
from accounts.forms import EmailOTPForm, ProfileForm, UserSSHKeyForm
from accounts.models import *
from . import forms
from .utils import get_user_totp_device, send_email_with_otp
from django.contrib.auth.views import LoginView
from logs.views import addlogmsg
class CustomLoginView(LoginView):
def form_valid(self, form):
username = form.cleaned_data['username']
addlogmsg(username, "-", "-", "Logged In")
auth_login(self.request, form.get_user())
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
username = form.cleaned_data['username']
addlogmsg(username, "-", "-", "Failed Login Attempt")
return self.render_to_response(self.get_context_data(form=form))
def profile(request):
"""
:param request:
:return:
"""
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
error_messages = []
user = User.objects.get(id=request.user.id)
publickeys = UserSSHKey.objects.filter(user_id=request.user.id)
profile_form = ProfileForm(request.POST or None, instance=request.user)
ssh_key_form = UserSSHKeyForm()
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 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())
if profile_form.is_valid():
profile_form.save()
messages.success(request, _("Profile updated"))
return redirect("accounts:profile")
return render(
request,
"profile.html",
{
"publickeys": publickeys,
"profile_form": profile_form,
"ssh_key_form": ssh_key_form,
},
)
def accounts(request):
"""
:param request:
:return:
"""
def ssh_key_create(request):
key_form = UserSSHKeyForm(request.POST or None, user=request.user)
if key_form.is_valid():
key_form.save()
messages.success(request, _("SSH key added"))
return redirect("accounts:profile")
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
error_messages = []
users = User.objects.filter(is_staff=False, is_superuser=False)
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()
return HttpResponseRedirect(request.get_full_path())
if 'edit' in request.POST:
user_id = request.POST.get('user_id', '')
user_pass = request.POST.get('user_pass', '')
user_edit = User.objects.get(id=user_id)
user_edit.set_password(user_pass)
user_edit.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())
return render(request, 'accounts.html', locals())
return render(
request,
"common/form.html",
{
"form": key_form,
"title": _("Add SSH key"),
},
)
def ssh_key_delete(request, pk):
ssh_key = get_object_or_404(UserSSHKey, pk=pk, user=request.user)
if request.method == "POST":
ssh_key.delete()
messages.success(request, _("SSH key deleted"))
return redirect("accounts:profile")
return render(
request,
"common/confirm_delete.html",
{
"object": ssh_key,
"title": _("Delete SSH key"),
},
)
@superuser_only
def account(request, user_id):
"""
:param request:
:return:
"""
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
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()
instances = Instance.objects.all().order_by("name")
publickeys = UserSSHKey.objects.filter(user_id=user_id)
if user.username == request.user.username:
return HttpResponseRedirect(reverse('profile'))
return render(
request,
"account.html",
{
"user": user,
"user_insts": user_insts,
"instances": instances,
"publickeys": publickeys,
"otp_enabled": settings.OTP_ENABLED,
},
)
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_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.save()
return HttpResponseRedirect(request.get_full_path())
if 'add' in request.POST:
inst_id = request.POST.get('inst_id', '')
try:
check_inst = UserInstance.objects.get(instance_id=int(inst_id))
msg = _("Instance already added")
error_messages.append(msg)
except UserInstance.DoesNotExist:
add_user_inst = UserInstance(instance_id=int(inst_id), user_id=user_id)
add_user_inst.save()
return HttpResponseRedirect(request.get_full_path())
return render(request, 'account.html', locals())
@permission_required("accounts.change_password", raise_exception=True)
def change_password(request):
form = PasswordChangeForm(request.user, request.POST or None)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Important!
messages.success(request, _("Password Changed"))
return redirect("accounts:profile")
return render(request, "accounts/change_password_form.html", {"form": form})
@superuser_only
def user_instance_create(request, user_id):
user = get_object_or_404(User, pk=user_id)
form = forms.UserInstanceForm(request.POST or None, initial={"user": user})
if form.is_valid():
form.save()
return redirect(reverse("accounts:account", args=[user.id]))
return render(
request,
"common/form.html",
{
"form": form,
"title": _("Create User Instance"),
},
)
@superuser_only
def user_instance_update(request, pk):
user_instance = get_object_or_404(UserInstance, pk=pk)
form = forms.UserInstanceForm(request.POST or None, instance=user_instance)
if form.is_valid():
form.save()
return redirect(reverse("accounts:account", args=[user_instance.user.id]))
return render(
request,
"common/form.html",
{
"form": form,
"title": _("Update User Instance"),
},
)
@superuser_only
def user_instance_delete(request, pk):
user_instance = get_object_or_404(UserInstance, pk=pk)
if request.method == "POST":
user = user_instance.user
user_instance.delete()
next = request.GET.get("next", None)
if next:
return redirect(next)
else:
return redirect(reverse("accounts:account", args=[user.id]))
return render(
request,
"common/confirm_delete.html",
{"object": user_instance},
)
def email_otp(request):
form = EmailOTPForm(request.POST or None)
if form.is_valid():
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=form.cleaned_data["email"])
except UserModel.DoesNotExist:
pass
else:
device = get_user_totp_device(user)
send_email_with_otp(user, device)
messages.success(
request, _("OTP Sent to %(email)s") % {"email": form.cleaned_data["email"]}
)
return redirect("accounts:login")
return render(
request,
"accounts/email_otp_form.html",
{
"form": form,
"title": _("Email OTP"),
},
)
@superuser_only
def admin_email_otp(request, user_id):
user = get_object_or_404(get_user_model(), pk=user_id)
device = get_user_totp_device(user)
if user.email != "":
send_email_with_otp(user, device)
messages.success(
request, _("OTP QR code was emailed to user %(user)s") % {"user": user}
)
else:
messages.error(request, _("User email not set, failed to send QR code"))
return redirect("accounts:account", user.id)

1
admin/__init__.py Normal file
View file

@ -0,0 +1 @@
default_app_config = 'admin.apps.AdminConfig'

5
admin/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class AdminConfig(AppConfig):
name = "admin"

10
admin/decorators.py Normal file
View file

@ -0,0 +1,10 @@
from django.core.exceptions import PermissionDenied
def superuser_only(function):
def _inner(request, *args, **kwargs):
if not request.user.is_superuser:
raise PermissionDenied
return function(request, *args, **kwargs)
return _inner

117
admin/forms.py Normal file
View file

@ -0,0 +1,117 @@
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.models import Group, User
from django.urls import reverse_lazy
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from accounts.models import UserAttributes
from .models import Permission
class GroupForm(forms.ModelForm):
permissions = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
queryset=Permission.objects.filter(content_type__model="permissionset"),
required=False,
)
users = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
queryset=User.objects.all(),
required=False,
)
def __init__(self, *args, **kwargs):
super(GroupForm, self).__init__(*args, **kwargs)
instance = getattr(self, "instance", None)
if instance and instance.id:
self.fields["users"].initial = self.instance.user_set.all()
def save_m2m(self):
self.instance.user_set.set(self.cleaned_data["users"])
def save(self, *args, **kwargs):
instance = super(GroupForm, self).save()
self.save_m2m()
return instance
class Meta:
model = Group
fields = "__all__"
class UserForm(forms.ModelForm):
user_permissions = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
queryset=Permission.objects.filter(content_type__model="permissionset"),
label=_("Permissions"),
required=False,
)
groups = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple,
queryset=Group.objects.all(),
label=_("Groups"),
required=False,
)
class Meta:
model = User
fields = [
"username",
"groups",
"first_name",
"last_name",
"email",
"user_permissions",
"is_staff",
"is_active",
"is_superuser",
]
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
if self.instance.id:
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=format_lazy(
_(
"""Raw passwords are not stored, so there is no way to see this user's password,
but you can change the password using <a href='{}'>this form</a>."""
),
reverse_lazy(
"admin:user_update_password",
args=[
self.instance.id,
],
),
),
)
self.fields["Password"] = password
class UserCreateForm(UserForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = [
"username",
"password",
"groups",
"first_name",
"last_name",
"email",
"user_permissions",
"is_staff",
"is_active",
"is_superuser",
]
class UserAttributesForm(forms.ModelForm):
class Meta:
model = UserAttributes
exclude = ["user", "can_clone_instances"]

View file

@ -0,0 +1,29 @@
# Generated by Django 2.2.12 on 2020-05-27 07:01
import django.contrib.auth.models
from django.db import migrations
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Permission',
fields=[
],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('auth.permission',),
managers=[
('objects', django.contrib.auth.models.PermissionManager()),
],
),
]

View file

@ -0,0 +1,14 @@
# Generated by Django 2.2.12 on 2020-06-09 08:30
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('admin', '0001_initial'),
('auth', '0011_update_proxy_permissions'),
]
operations = [
]

View file

@ -0,0 +1,15 @@
from django.db import models, migrations
def apply_migration(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
Group.objects.create(name='Technicians')
class Migration(migrations.Migration):
dependencies = [
('admin', '0002_auto_20200609_0830'),
]
operations = [
migrations.RunPython(apply_migration)
]

13
admin/models.py Normal file
View file

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

View file

@ -0,0 +1,59 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load bootstrap_icons %}
{% block title %}{% trans "Users" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<a href="{% url 'admin:group_create' %}" class="btn btn-success btn-header float-end">
{% bs_icon 'plus-circle-fill' %}
</a>
{% include 'search_block.html' %}
<h1 class="page-header">{% trans "Groups" %}</h1>
</div>
</div>
<div class="row">
{% if not groups %}
<div class="col-lg-12">
<div class="alert alert-warning shadow-sm">
{% bs_icon 'exclamation-triangle '%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any groups" %}
</div>
</div>
{% else %}
<div class="col-lg-12">
<table class="table table-striped table-hover">
<thead>
<tr>
<th span="col">{% trans "Group Name" %}</th>
<th span="col">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for group in groups %}
<tr>
<td>
<a href=""><strong>{{ group.name }}</strong></a>
</td>
<td>
<div class="float-end btn-group">
<a class="btn btn-primary" href="{% url 'admin:group_update' group.id %}" title="{%trans "Edit" %}">
{% bs_icon 'pencil' %}
</a>
<a class="btn btn-danger" href="{% url 'admin:group_delete' group.id %}" title="{%trans "Delete" %}">
{% bs_icon 'x' %}
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock content %}
{% block script %}
<script src="{% static 'js/filter-table.js' %}"></script>
{% endblock script %}

View file

@ -0,0 +1,59 @@
{% extends "base.html" %}
{% load i18n %}
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% block title %}{% trans "Logs" %}{% endblock %}
{% block page_heading %}{% trans "Logs" %}{% endblock page_heading %}
{% block page_heading_extra %}
{% include 'search_block.html' %}
{% endblock page_heading_extra %}
{% block content %}
<div class="row">
<div class="col-lg-12">
{% if not logs %}
<div class="col-lg-12">
<div class="alert alert-warning shadow-sm">
{% bs_icon 'exclamation-triangle'%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any Logs" %}
</div>
</div>
{% else %}
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">{% trans "Date" %}</th>
<th scope="col">{% trans "User" %}</th>
<th scope="col">{% trans "Host" %}</th>
<th scope="col">{% trans "Instance" %}</th>
<th scope="col">{% trans "Message" %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for log in logs %}
<tr>
<td>{{ log.id }}</td>
<td style="width:130px;">{{ log.date|date:"M d H:i:s" }}</td>
<td>{{ log.user }}</td>
<td>{{ log.host }}</td>
<td>{{ log.instance }}</td>
<td>{{ log.message }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% bootstrap_pagination logs %}
{% endif %}
</div>
</div>
{% endblock %}
{% block script %}
<script src="/static/js/filter-table.js"></script>
{% endblock script %}

View file

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% load i18n %}
{% block title %}{{ title }}{% endblock %}
{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block content %}
<div class="card col-sm-10 offset-1">
<div class="card-body">
<form id="create-update" action="" method="post">
{% csrf_token %}
{% bootstrap_form user_form layout='horizontal' %}
{% bootstrap_form attributes_form layout='horizontal' %}
</form>
<div class="float-end">
<a class="btn btn-primary" href="javascript:history.back()">
{% bs_icon 'x-square-fill' %} {% trans "Cancel" %}</a>
<button type="submit" form="create-update" class="btn btn-success">
{% bs_icon 'check2' %} {% trans "Save" %}
</button>
</div>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,81 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% load bootstrap_icons %}
{% block title %}{{ title }}{% endblock %}
{% block page_heading %}{{ title }}{% endblock page_heading %}
{% block page_heading_extra %}
<a href="{% url 'admin:user_create' %}" class="btn btn-success btn-header float-end">
{% bs_icon 'plus-circle-fill' %}
</a>
{% include 'search_block.html' %}
{% endblock page_heading_extra %}
{% block content %}
<div class="row">
{% if not users %}
<div class="col-lg-12">
<div class="alert alert-warning shadow-sm">
{% bs_icon 'exclamation-triangle '%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any user" %}
</div>
</div>
{% else %}
<div class="col-lg-12">
<table class="table table-striped table-hover">
<thead>
<tr>
<th span="col">{% trans "Username" %}</th>
<th span="col">{% trans "Status" %}</th>
<th span="col">{% trans "Last Login" %}</th>
<th span="col">{% trans "Staff" %}</th>
<th span="col">{% trans "Superuser" %}</th>
<th span="col">{% trans "Can Clone" %}</th>
<th span="col">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for user in users %}
{% has_perm user 'instances.clone_instances' as can_clone %}
<tr class="{% if not user.is_active %}danger{% endif %}">
<td>
{{ user.username }}
</td>
<td>
{% if user.is_active %}
{% trans "Active" %}
{% else %}
{% trans "Blocked" %}
{% endif %}
</td>
<td>{{ user.last_login }}</td>
<td>{% if user.is_staff %}{% bs_icon 'check' %}{% endif %}</td>
<td>{% if user.is_superuser %}{% bs_icon 'check' %}</span>{% endif %}</td>
<td>{% if can_clone %}{% bs_icon 'check' %}{% endif %}</td>
<td>
<div class="float-end btn-group">
<a class="btn btn-success" title="{%trans "View Profile" %}" href="{% url 'accounts:account' user.id %}">{% bs_icon 'eye-fill' %}</a>
<a class="btn btn-primary" title="{%trans "Edit" %}" href="{% url 'admin:user_update' user.id %}">{% bs_icon 'pencil-fill' %}</a>
{% if user.is_active %}
<a class="btn btn-warning" title="{%trans "Block" %}" href="{% url 'admin:user_block' user.id %}">{% bs_icon 'stop-fill' %}</a>
{% else %}
<a class="btn btn-success" title="{%trans "Unblock" %}" href="{% url 'admin:user_unblock' user.id %}">{% bs_icon 'play-fill' %}</a>
{% endif %}
<a class="btn btn-danger" title="{%trans "Delete" %}" href="{% url 'admin:user_delete' user.id %}">{% bs_icon 'x-circle-fill' %}</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock content %}
{% block script %}
<script src="{% static "js/filter-table.js" %}"></script>
{% endblock script %}

124
admin/tests.py Normal file
View file

@ -0,0 +1,124 @@
from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import reverse
from django.test import TestCase
from accounts.models import UserAttributes
class AdminTestCase(TestCase):
def setUp(self):
self.client.login(username="admin", password="admin")
def test_group_list(self):
response = self.client.get(reverse("admin:group_list"))
self.assertEqual(response.status_code, 200)
def test_groups(self):
response = self.client.get(reverse("admin:group_create"))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("admin:group_create"), {"name": "Test Group"}
)
self.assertRedirects(response, reverse("admin:group_list"))
group = Group.objects.get(name="Test Group")
self.assertEqual(group.id, 1)
response = self.client.get(reverse("admin:group_update", args=[1]))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("admin:group_update", args=[1]), {"name": "Updated Group Test"}
)
self.assertRedirects(response, reverse("admin:group_list"))
group = Group.objects.get(id=1)
self.assertEqual(group.name, "Updated Group Test")
response = self.client.get(reverse("admin:group_delete", args=[1]))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse("admin:group_delete", args=[1]))
self.assertRedirects(response, reverse("admin:group_list"))
with self.assertRaises(ObjectDoesNotExist):
Group.objects.get(id=1)
def test_user_list(self):
response = self.client.get(reverse("admin:user_list"))
self.assertEqual(response.status_code, 200)
def test_users(self):
response = self.client.get(reverse("admin:user_create"))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("admin:user_create"),
{
"username": "test",
"password": "test",
"max_instances": 1,
"max_cpus": 1,
"max_memory": 1024,
"max_disk_size": 4,
},
)
self.assertRedirects(response, reverse("admin:user_list"))
user = User.objects.get(username="test")
self.assertEqual(user.id, 2)
ua: UserAttributes = UserAttributes.objects.get(id=2)
self.assertEqual(ua.user_id, 2)
self.assertEqual(ua.max_instances, 1)
self.assertEqual(ua.max_cpus, 1)
self.assertEqual(ua.max_memory, 1024)
self.assertEqual(ua.max_disk_size, 4)
response = self.client.get(reverse("admin:user_update", args=[2]))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("admin:user_update", args=[2]),
{
"username": "utest",
"max_instances": 2,
"max_cpus": 2,
"max_memory": 2048,
"max_disk_size": 8,
},
)
self.assertRedirects(response, reverse("admin:user_list"))
user = User.objects.get(id=2)
self.assertEqual(user.username, "utest")
ua: UserAttributes = UserAttributes.objects.get(id=2)
self.assertEqual(ua.user_id, 2)
self.assertEqual(ua.max_instances, 2)
self.assertEqual(ua.max_cpus, 2)
self.assertEqual(ua.max_memory, 2048)
self.assertEqual(ua.max_disk_size, 8)
response = self.client.get(reverse("admin:user_block", args=[2]))
user = User.objects.get(id=2)
self.assertFalse(user.is_active)
response = self.client.get(reverse("admin:user_unblock", args=[2]))
user = User.objects.get(id=2)
self.assertTrue(user.is_active)
response = self.client.get(reverse("admin:user_delete", args=[2]))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse("admin:user_delete", args=[2]))
self.assertRedirects(response, reverse("admin:user_list"))
with self.assertRaises(ObjectDoesNotExist):
User.objects.get(id=2)
def test_logs(self):
response = self.client.get(reverse("admin:logs"))
self.assertEqual(response.status_code, 200)

18
admin/urls.py Normal file
View file

@ -0,0 +1,18 @@
from django.urls import path
from . import views
urlpatterns = [
path("groups/", views.group_list, name="group_list"),
path("groups/create/", views.group_create, name="group_create"),
path("groups/<int:pk>/update/", views.group_update, name="group_update"),
path("groups/<int:pk>/delete/", views.group_delete, name="group_delete"),
path("users/", views.user_list, name="user_list"),
path("users/create/", views.user_create, name="user_create"),
path("users/<int:pk>/update_password/", views.user_update_password, name="user_update_password"),
path("users/<int:pk>/update/", views.user_update, name="user_update"),
path("users/<int:pk>/delete/", views.user_delete, name="user_delete"),
path("users/<int:pk>/block/", views.user_block, name="user_block"),
path("users/<int:pk>/unblock/", views.user_unblock, name="user_unblock"),
path("logs/", views.logs, name="logs"),
]

217
admin/views.py Normal file
View file

@ -0,0 +1,217 @@
from accounts.models import Instance, UserAttributes, UserInstance
from appsettings.settings import app_settings
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import AdminPasswordChangeForm
from django.contrib.auth.models import Group, User
from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext_lazy as _
from logs.models import Logs
from . import forms
from .decorators import superuser_only
@superuser_only
def group_list(request):
groups = Group.objects.all()
return render(
request,
"admin/group_list.html",
{
"groups": groups,
},
)
@superuser_only
def group_create(request):
form = forms.GroupForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("admin:group_list")
return render(
request,
"common/form.html",
{
"form": form,
"title": _("Create Group"),
},
)
@superuser_only
def group_update(request, pk):
group = get_object_or_404(Group, pk=pk)
form = forms.GroupForm(request.POST or None, instance=group)
if form.is_valid():
form.save()
return redirect("admin:group_list")
return render(
request,
"common/form.html",
{
"form": form,
"title": _("Update Group"),
},
)
@superuser_only
def group_delete(request, pk):
group = get_object_or_404(Group, pk=pk)
if request.method == "POST":
group.delete()
return redirect("admin:group_list")
return render(
request,
"common/confirm_delete.html",
{"object": group},
)
@superuser_only
def user_list(request):
users = User.objects.all()
return render(
request,
"admin/user_list.html",
{
"users": users,
"title": _("Users"),
},
)
@superuser_only
def user_create(request):
user_form = forms.UserCreateForm(request.POST or None)
attributes_form = forms.UserAttributesForm(request.POST or None)
if user_form.is_valid() and attributes_form.is_valid():
user = user_form.save()
password = user_form.cleaned_data["password"]
user.set_password(password)
user.save()
attributes = attributes_form.save(commit=False)
attributes.user = user
attributes.save()
add_default_instances(user)
return redirect("admin:user_list")
return render(
request,
"admin/user_form.html",
{
"user_form": user_form,
"attributes_form": attributes_form,
"title": _("Create User"),
},
)
@superuser_only
def user_update(request, pk):
user = get_object_or_404(User, pk=pk)
attributes, attributes_created = UserAttributes.objects.get_or_create(user=user)
user_form = forms.UserForm(request.POST or None, instance=user)
attributes_form = forms.UserAttributesForm(
request.POST or None, instance=attributes
)
if user_form.is_valid() and attributes_form.is_valid():
user_form.save()
attributes_form.save()
next = request.GET.get("next")
return redirect(next or "admin:user_list")
return render(
request,
"admin/user_form.html",
{
"user_form": user_form,
"attributes_form": attributes_form,
"title": _("Update User"),
},
)
@superuser_only
def user_update_password(request, pk):
user = get_object_or_404(User, pk=pk)
if request.method == "POST":
form = AdminPasswordChangeForm(user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user) # Important!
messages.success(
request, _("Password changed for %(user)s") % {"user": user.username}
)
return redirect("admin:user_list")
else:
messages.error(request, _("Wrong Data Provided"))
else:
form = AdminPasswordChangeForm(user)
return render(
request,
"accounts/change_password_form.html",
{
"form": form,
"user": user.username,
},
)
@superuser_only
def user_delete(request, pk):
user = get_object_or_404(User, pk=pk)
if request.method == "POST":
user.delete()
return redirect("admin:user_list")
return render(
request,
"common/confirm_delete.html",
{"object": user},
)
@superuser_only
def user_block(request, pk):
user: User = get_object_or_404(User, pk=pk)
user.is_active = False
user.save()
return redirect("admin:user_list")
@superuser_only
def user_unblock(request, pk):
user: User = get_object_or_404(User, pk=pk)
user.is_active = True
user.save()
return redirect("admin:user_list")
@superuser_only
def logs(request):
l = Logs.objects.order_by("-date")
paginator = Paginator(l, int(app_settings.LOGS_PER_PAGE))
page = request.GET.get("page", 1)
logs = paginator.page(page)
return render(request, "admin/logs.html", {"logs": logs})
def add_default_instances(user):
"""
Adds instances listed in NEW_USER_DEFAULT_INSTANCES to 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()

5
appsettings/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class AppsettingsConfig(AppConfig):
name = "appsettings"

View file

@ -0,0 +1,13 @@
from .settings import app_settings as settings
def app_settings(request):
"""
Simple context processor that puts the config into every\
RequestContext. Just make sure you have a setting like this::
TEMPLATE_CONTEXT_PROCESSORS = (
# ...
'appsettings.context_processors.app_settings',
)
"""
return {"app_settings": settings}

10
appsettings/middleware.py Normal file
View file

@ -0,0 +1,10 @@
from .settings import app_settings, get_settings
class AppSettingsMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
get_settings()
return self.get_response(request)

View file

@ -0,0 +1,25 @@
# Generated by Django 2.2.12 on 2020-05-27 16:03
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='AppSettings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=25)),
('key', models.CharField(db_index=True, max_length=50, unique=True)),
('value', models.CharField(max_length=25)),
('choices', models.CharField(max_length=70)),
('description', models.CharField(max_length=100, null=True)),
],
),
]

View file

@ -0,0 +1,79 @@
# Generated by Django 2.2.12 on 2020-05-23 12:05
from django.db import migrations
from django.utils.translation import gettext_lazy as _
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(1, _("Theme"), "BOOTSTRAP_THEME", "flaty", "", _("Bootstrap CSS & Bootswatch Theme")),
setting(2, _("Theme SASS Path"), "SASS_DIR", "dev/scss/", "", _("Bootstrap SASS & Bootswatch SASS Directory")),
setting(3, _("All Instances View Style"), "VIEW_INSTANCES_LIST_STYLE", "grouped", "grouped,nongrouped", _("All instances list style")),
setting(4, _("Logs per Page"), "LOGS_PER_PAGE", "100", "", _("Pagination for logs")),
setting(5, _("Multiple Owner for VM"), "ALLOW_INSTANCE_MULTIPLE_OWNER", "True", "True,False", _("Allow to have multiple owner for instance")),
setting(6, _("Quota Debug"), "QUOTA_DEBUG", "True", "True,False", _("Debug for user quotas")),
setting(7, _("Disk Format"), "INSTANCE_VOLUME_DEFAULT_FORMAT", "qcow2", "raw,qcow,qcow2", _("Instance disk format")),
setting(8, _("Disk Bus"), "INSTANCE_VOLUME_DEFAULT_BUS", "virtio", "virtio,scsi,ide,usb,sata", _("Instance disk bus type")),
setting(9, _("Disk SCSI Controller"), "INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER", "virtio-scsi", "virtio-scsi, lsilogic, virtio-blk", _("SCSI controller type")),
setting(10, _("Disk Cache"), "INSTANCE_VOLUME_DEFAULT_CACHE", "directsync", "default,directsync,none,unsafe,writeback,writethrough", _("Disk volume cache type")),
setting(11, _("Disk IO Type"), "INSTANCE_VOLUME_DEFAULT_IO", "default", "default,native,threads", _("Volume io modes")),
setting(12, _("Disk Detect Zeroes"), "INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES", "default", "default,on,off,unmap", _("Volume detect zeroes mode")),
setting(13, _("Disk Discard"), "INSTANCE_VOLUME_DEFAULT_DISCARD", "default", "default,unmap,ignore", _("Volume discard mode")),
setting(14, _("Disk Owner UID"), "INSTANCE_VOLUME_DEFAULT_OWNER_UID", "0", "", _("Owner UID: up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)")),
setting(15, _("Disk Owner GID"), "INSTANCE_VOLUME_DEFAULT_OWNER_GID", "0", "", _("Owner GID: up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)")),
setting(16, _("VM CPU Mode"), "INSTANCE_CPU_DEFAULT_MODE", "host-model", "no-model,host-model,host-passthrough,custom", _("Cpu modes")),
setting(17, _("VM Machine Type"), "INSTANCE_MACHINE_DEFAULT_TYPE", "q35", "q35,x86_64", _("Chipset/Machine type")),
setting(18, _("VM Firmware Type"), "INSTANCE_FIRMWARE_DEFAULT_TYPE", "BIOS", "BIOS,UEFI", _("Firmware type for x86_64")),
setting(19, _("VM Architecture Type"), "INSTANCE_ARCH_DEFAULT_TYPE", "x86_64", "x86_64,i686", _("Architecture type: x86_64, i686, etc")),
setting(20, _("VM Console Type"), "QEMU_CONSOLE_DEFAULT_TYPE", "vnc", "vnc,spice", _("Default console type")),
setting(21, _("VM Clone Name Prefix"), "CLONE_INSTANCE_DEFAULT_PREFIX", "instance", "True,False", _("Prefix for cloned instance name")),
setting(22, _("VM Clone Auto Name"), "CLONE_INSTANCE_AUTO_NAME", "False", "True,False", _("Generated name for cloned instance")),
setting(23, _("VM Clone Auto Migrate"), "CLONE_INSTANCE_AUTO_MIGRATE", "False", "True,False", _("Auto migrate instance after clone")),
setting(24, _("VM Bottom Bar"), "VIEW_INSTANCE_DETAIL_BOTTOM_BAR", "True", "True,False", _("Bottom navbar for instance details")),
setting(25, _("Show Access Root Pass"), "SHOW_ACCESS_ROOT_PASSWORD", "False", "True,False", _("Show access root password")),
setting(26, _("Show Access SSH Keys"), "SHOW_ACCESS_SSH_KEYS", "False", "True,False", _("Show access ssh keys")),
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="QEMU_CONSOLE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="ALLOW_INSTANCE_MULTIPLE_OWNER").delete()
setting.objects.using(db_alias).filter(key="CLONE_INSTANCE_DEFAULT_PREFIX").delete()
setting.objects.using(db_alias).filter(key="CLONE_INSTANCE_AUTO_NAME").delete()
setting.objects.using(db_alias).filter(key="CLONE_INSTANCE_AUTO_MIGRATE").delete()
setting.objects.using(db_alias).filter(key="LOGS_PER_PAGE").delete()
setting.objects.using(db_alias).filter(key="QUOTA_DEBUG").delete()
setting.objects.using(db_alias).filter(key="VIEW_INSTANCES_LIST_STYLE").delete()
setting.objects.using(db_alias).filter(key="VIEW_INSTANCE_DETAIL_BOTTOM_BAR").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_FORMAT").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_BUS").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_CACHE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_IO").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_DISCARD").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_OWNER_UID").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VOLUME_DEFAULT_OWNER_GID").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_CPU_DEFAULT_MODE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_MACHINE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_FIRMWARE_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_ARCH_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="BOOTSTRAP_THEME").delete()
setting.objects.using(db_alias).filter(key="SASS_DIR").delete()
setting.objects.using(db_alias).filter(key="SHOW_ACCESS_ROOT_PASSWORD").delete()
setting.objects.using(db_alias).filter(key="SHOW_ACCESS_SSH_KEYS").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0001_initial'),
]
operations = [
migrations.RunPython(add_default_settings, del_default_settings),
]

View file

@ -0,0 +1,38 @@
# Generated by Django 2.2.13 on 2020-06-15 06:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0002_auto_20200527_1603'),
]
operations = [
migrations.AlterField(
model_name='appsettings',
name='choices',
field=models.CharField(max_length=70, verbose_name='choices'),
),
migrations.AlterField(
model_name='appsettings',
name='description',
field=models.CharField(max_length=100, null=True, verbose_name='description'),
),
migrations.AlterField(
model_name='appsettings',
name='key',
field=models.CharField(db_index=True, max_length=50, unique=True, verbose_name='key'),
),
migrations.AlterField(
model_name='appsettings',
name='name',
field=models.CharField(max_length=25, verbose_name='name'),
),
migrations.AlterField(
model_name='appsettings',
name='value',
field=models.CharField(max_length=25, verbose_name='value'),
),
]

View file

@ -0,0 +1,35 @@
# Generated by Django 2.2.13 on 2020-07-16 06:37
from django.db import migrations
from django.utils.translation import gettext_lazy as _
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(27, _("Console Scale"), "CONSOLE_SCALE", "False", "True,False", _("Allow console to scaling view")),
setting(28, _("Console View-Only"), "CONSOLE_VIEW_ONLY", "False", "True,False", _("Allow only view not modify")),
setting(29, _("Console Resize Session"), "CONSOLE_RESIZE_SESSION", "False", "True,False", _("Allow to resize session for console")),
setting(30, _("Console Clip Viewport"), "CONSOLE_CLIP_VIEWPORT", "False", "True,False", _("Clip console viewport")),
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="CONSOLE_SCALE").delete()
setting.objects.using(db_alias).filter(key="CONSOLE_VIEW_ONLY").delete()
setting.objects.using(db_alias).filter(key="CONSOLE_RESIZE_SESSION").delete()
setting.objects.using(db_alias).filter(key="CONSOLE_CLIP_VIEWPORT").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0003_auto_20200615_0637'),
]
operations = [
migrations.RunPython(add_default_settings, del_default_settings),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.2.14 on 2020-09-11 12:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0004_auto_20200716_0637'),
]
operations = [
migrations.AlterField(
model_name='appsettings',
name='choices',
field=models.CharField(max_length=70, verbose_name='choices'),
),
]

View file

@ -0,0 +1,28 @@
# Generated by Django 3.2.13 on 2022-06-30 07:17
from django.db import migrations
from django.utils.translation import gettext_lazy as _
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(31, _("VM DRBD Status"), "VM_DRBD_STATUS", "False", "True,False", _("Show VM DRBD Status")),
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="VM_DRBD_STATUS").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0005_auto_20200911_1233'),
]
operations = [
migrations.RunPython(add_default_settings, del_default_settings),
]

View file

@ -0,0 +1,21 @@
# Generated by Django 3.2.13 on 2022-06-30 07:17
from django.db import migrations
from django.utils.translation import gettext_lazy as _
def update_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="INSTANCE_MACHINE_DEFAULT_TYPE").update(choices="q35,x86_64,virt"),
setting.objects.using(db_alias).filter(key="INSTANCE_ARCH_DEFAULT_TYPE").update(choices="x86_64,i686,aarch64"),
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0006_auto_20220630_0717'),
]
operations = [
migrations.RunPython(update_default_settings, None)
]

View file

@ -0,0 +1,32 @@
# Generated by Django 3.2.13 on 2022-06-30 07:17
from django.db import migrations
from django.utils.translation import gettext_lazy as _
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(32, _("VM CD-ROM Device"), "INSTANCE_CDROM_ADD", "sata", "None,ide,sata,scsi,virtio", _("Add or not cdrom device while instance creating")),
setting(33, _("VM Video Type"), "INSTANCE_VIDEO_DEFAULT_TYPE", "vga", "None,virtio,vga,cirrus,vmvga,bochs,ramfb", _("Change instance default video type")),
setting(34, _("VM Input Device"), "INSTANCE_INPUT_DEFAULT_DEVICE", "default", "None,default,virtio,usb", _("Add or not input device with specify its type")),
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="INSTANCE_CDROM_ADD").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_VIDEO_DEFAULT_TYPE").delete()
setting.objects.using(db_alias).filter(key="INSTANCE_INPUT_DEFAULT_DEVICE").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0007_auto_20220905_0918'),
]
operations = [
migrations.RunPython(add_default_settings, del_default_settings),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2023-10-30 17:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0008_auto_20220905_1459'),
]
operations = [
migrations.AlterField(
model_name='appsettings',
name='id',
field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
]

View file

@ -0,0 +1,28 @@
# Generated by Django 4.2.5 on 2023-10-30 17:05
from django.db import migrations
from django.utils.translation import gettext_lazy as _
def add_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).bulk_create([
setting(35, _("VM NIC Type"), "INSTANCE_NIC_DEFAULT_TYPE", "default", "default,e1000,e1000e,rt18139,virtio", _("Change instance default NIC type"))
])
def del_default_settings(apps, schema_editor):
setting = apps.get_model("appsettings", "AppSettings")
db_alias = schema_editor.connection.alias
setting.objects.using(db_alias).filter(key="INSTANCE_NIC_DEFAULT_TYPE").delete()
class Migration(migrations.Migration):
dependencies = [
('appsettings', '0009_alter_appsettings_id')
]
operations = [
migrations.RunPython(add_default_settings,del_default_settings)
]

View file

@ -0,0 +1,20 @@
# Generated by Django 4.2.10 on 2024-02-14 11:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("appsettings", "0010_auto_20231030_1305"),
]
operations = [
migrations.AlterField(
model_name="appsettings",
name="id",
field=models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
]

13
appsettings/models.py Normal file
View file

@ -0,0 +1,13 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class AppSettings(models.Model):
def choices_as_list(self):
return self.choices.split(",")
name = models.CharField(_("name"), max_length=25, null=False)
key = models.CharField(_("key"), db_index=True, max_length=50, unique=True)
value = models.CharField(_("value"), max_length=25)
choices = models.CharField(_("choices"), max_length=70)
description = models.CharField(_("description"), max_length=100, null=True)

18
appsettings/settings.py Normal file
View file

@ -0,0 +1,18 @@
from .models import AppSettings
class Settings:
pass
app_settings = Settings()
def get_settings():
try:
entries = AppSettings.objects.all()
except:
pass
for entry in entries:
setattr(app_settings, entry.key, entry.value)

View file

@ -0,0 +1,74 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Edit Settings" %}{% endblock title %}
{% block page_heading %}{% trans "Edit Settings" %}{% endblock page_heading %}
{% block content %}
<div class="">
<div class="col-lg-12">
<h3 class="page-header">{% trans "App Settings" %}</h3>
<form action="{% url 'set_language' %}" method="post" style="display:inline" aria-label="Edit language.name_local settings form">{% csrf_token %}
<div class="row mb-1">
<input name="next" type="hidden" value="{{ redirect_to }}">
<label class="col-sm-3 col-form-label">{% trans "Language" %}</label>
<div class="col-sm-6">
<select name="language" class="form-control" onchange="this.form.submit()">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</div>
</div>
</form>
{% if request.user.is_superuser %}
<form method="post" action="" role="form" aria-label="Edit sass directory settings form">{% csrf_token %}
<div class="row mb-1">
<label class="col-sm-3 col-form-label">{% trans sass_dir.name %}</label>
<div class="col-sm-6">
<input class="form-control" name="{{ sass_dir.key }}" value="{{ sass_dir.value }}" onchange="this.form.submit()" title="{% trans sass_dir.description %}"/>
</div>
</div>
</form>
<form method="post" action="" role="form" aria-label="Edit theme settings form">{% csrf_token %}
<div class="row mb-1">
<label class="col-sm-3 col-form-label">{% trans bootstrap_theme.name %}</label>
<div class="col-sm-6">
<select class="form-select" name="{{ bootstrap_theme.key }}" onchange="this.form.submit()" title="{% trans bootstrap_theme.description %}">
{% for theme in themes_list %}
<option {% if bootstrap_theme.value == theme %}selected{% endif %} value="{{ theme }}">{{ theme }}</option>
{% endfor %}
</select>
<span class="text-muted">{% trans "After change please full refresh page with 'Ctrl + F5' "%}</span>
</div>
</div>
</form>
{% endif %}
<h3 class="page-header">{% trans "Other Settings" %}</h3>
{% for setting in appsettings %}
<form method="post" action="" role="form" aria-label="{{setting.name}} form">{% csrf_token %}
<div class="row mb-1">
<label class="col-sm-3 col-form-label">{% trans setting.name %}</label>
<div class="col-sm-6">
{% if setting.choices %}
<select class="form-select" name="{{ setting.key }}" onchange="this.form.submit()" title="{% trans setting.description %}">
{% for choice in setting.choices_as_list %}
<option {% if setting.value == choice %} selected {% endif %} value={{ choice }}>{% trans choice %}</option>
{% endfor %}
</select>
{% else %}
<input class="form-control" name="{{ setting.key }}" value="{{ setting.value }}" title="{% trans setting.description %}" onchange="this.form.submit()"/>
{% endif%}
</div>
</div>
</form>
{% endfor %}
</div>
</div>
{% endblock %}

103
appsettings/views.py Normal file
View file

@ -0,0 +1,103 @@
import os
import sass
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.translation import gettext_noop as _
from logs.views import addlogmsg
from appsettings.models import AppSettings
@login_required
def appsettings(request):
"""
:param request:
:return:
"""
main_css = "wvc-main.min.css"
sass_dir = AppSettings.objects.get(key="SASS_DIR")
bootstrap_theme = AppSettings.objects.get(key="BOOTSTRAP_THEME")
try:
themes_list = os.listdir(sass_dir.value + "/wvc-themes")
except FileNotFoundError as err:
messages.error(request, err)
addlogmsg(request.user.username, "-", "", err)
# Bootstrap settings related with filesystems, because of that they are excluded from other settings
appsettings = AppSettings.objects.exclude(
description__startswith="Bootstrap"
).order_by("name")
if request.method == "POST":
if "SASS_DIR" in request.POST:
try:
sass_dir.value = request.POST.get("SASS_DIR", "")
sass_dir.save()
msg = _("SASS directory path is changed. Now: %(dir)s") % {
"dir": sass_dir.value
}
messages.success(request, msg)
except Exception as err:
msg = err
messages.error(request, msg)
addlogmsg(request.user.username, "-", "", msg)
return HttpResponseRedirect(request.get_full_path())
if "BOOTSTRAP_THEME" in request.POST:
theme = request.POST.get("BOOTSTRAP_THEME", "")
scss_var = f"@import '{sass_dir.value}/wvc-themes/{theme}/variables';"
# scss_boot = f"@import '{sass_dir.value}/bootstrap/bootstrap.scss';"
scss_boot = f"@import '{sass_dir.value}/bootstrap-overrides.scss';"
scss_bootswatch = (
f"@import '{sass_dir.value}/wvc-themes/{theme}/bootswatch';"
)
try:
with open(sass_dir.value + "/wvc-main.scss", "w") as main:
main.write(
scss_var + "\n" + scss_boot + "\n" + scss_bootswatch + "\n"
)
css_compressed = sass.compile(
string=scss_var + "\n" + scss_boot + "\n" + scss_bootswatch,
output_style="compressed",
)
with open("static/css/" + main_css, "w") as css:
css.write(css_compressed)
bootstrap_theme.value = theme
bootstrap_theme.save()
msg = _("Theme is changed. Now: %(theme)s") % {"theme": theme}
messages.success(request, msg)
except Exception as err:
msg = err
messages.error(request, msg)
addlogmsg(request.user.username, "-", "", msg)
return HttpResponseRedirect(request.get_full_path())
for setting in appsettings:
if setting.key in request.POST:
try:
setting.value = request.POST.get(setting.key, "")
setting.save()
msg = _("%(setting)s is changed. Now: %(value)s") % {
"setting": setting.name,
"value": setting.value,
}
messages.success(request, msg)
except Exception as err:
msg = err
messages.error(request, msg)
addlogmsg(request.user.username, "-", "", msg)
return HttpResponseRedirect(request.get_full_path())
return render(request, "appsettings.html", locals())

View file

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View file

@ -0,0 +1,20 @@
from computes.models import Compute
from rest_framework import serializers
from vrtManager.connection import CONN_SOCKET, CONN_SSH, CONN_TCP, CONN_TLS
class ComputeSerializer(serializers.ModelSerializer):
# Use <input type="password"> for the input.
password = serializers.CharField(style={"input_type": "password"})
# Use a radio input instead of a select input.
conn_types = (
(CONN_SSH, "SSH"),
(CONN_TCP, "TCP"),
(CONN_TLS, "TLS"),
(CONN_SOCKET, "SOCK"),
)
type = serializers.ChoiceField(choices=conn_types)
class Meta:
model = Compute
fields = ["id", "name", "hostname", "login", "password", "type", "details"]

56
computes/api/viewsets.py Normal file
View file

@ -0,0 +1,56 @@
from computes.models import Compute
from rest_framework import permissions, viewsets
from rest_framework.response import Response
from vrtManager.create import wvmCreate
from .serializers import ComputeSerializer
class ComputeViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows computes to be viewed or edited.
"""
queryset = Compute.objects.all().order_by("name")
serializer_class = ComputeSerializer
permission_classes = [permissions.IsAuthenticated]
class ComputeArchitecturesView(viewsets.ViewSet):
def list(self, request, compute_pk=None):
"""
Return a list of supported host architectures.
"""
compute = Compute.objects.get(pk=compute_pk)
conn = wvmCreate(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
return Response(conn.get_hypervisors_machines())
def retrieve(self, request, compute_pk=None, pk=None):
compute = Compute.objects.get(pk=compute_pk)
conn = wvmCreate(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
return Response(conn.get_machine_types(pk))
class ComputeMachinesView(viewsets.ViewSet):
def list(self, request, compute_pk=None, archs_pk=None):
"""
Return a list of supported host architectures.
"""
compute = Compute.objects.get(pk=compute_pk)
conn = wvmCreate(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
return Response(conn.get_machine_types(archs_pk))

View file

@ -1,164 +1,45 @@
import re
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from vrtManager.connection import CONN_SOCKET, CONN_SSH, CONN_TCP, CONN_TLS
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)
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'))
from .validators import validate_hostname
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)
class TcpComputeForm(forms.ModelForm):
hostname = forms.CharField(validators=[validate_hostname])
type = forms.IntegerField(widget=forms.HiddenInput, initial=CONN_TCP)
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 Meta:
model = Compute
widgets = {"password": forms.PasswordInput()}
fields = "__all__"
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)
class SshComputeForm(forms.ModelForm):
hostname = forms.CharField(validators=[validate_hostname], label=_("FQDN/IP"))
type = forms.IntegerField(widget=forms.HiddenInput, initial=CONN_SSH)
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 Meta:
model = Compute
exclude = ["password"]
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)
class TlsComputeForm(forms.ModelForm):
hostname = forms.CharField(validators=[validate_hostname])
type = forms.IntegerField(widget=forms.HiddenInput, initial=CONN_TLS)
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 Meta:
model = Compute
widgets = {"password": forms.PasswordInput()}
fields = "__all__"
class ComputeAddSocketForm(forms.Form):
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
max_length=20)
class SocketComputeForm(forms.ModelForm):
hostname = forms.CharField(widget=forms.HiddenInput, initial="localhost")
type = forms.IntegerField(widget=forms.HiddenInput, initial=CONN_SOCKET)
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'))
class Meta:
model = Compute
fields = ["name", "details", "hostname", "type"]

View file

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
@ -13,15 +14,13 @@ class Migration(migrations.Migration):
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)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('hostname', models.CharField(max_length=64)),
('login', models.CharField(max_length=20)),
('password', models.CharField(max_length=14, null=True, blank=True)),
('password', models.CharField(blank=True, max_length=14, null=True)),
('details', models.CharField(blank=True, max_length=64, null=True)),
('type', models.IntegerField()),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.2.12 on 2020-05-29 13:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('computes', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='compute',
name='name',
field=models.CharField(max_length=64, unique=True),
),
]

View file

@ -0,0 +1,38 @@
# Generated by Django 2.2.13 on 2020-06-15 06:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('computes', '0002_auto_20200529_1320'),
]
operations = [
migrations.AlterField(
model_name='compute',
name='details',
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='details'),
),
migrations.AlterField(
model_name='compute',
name='hostname',
field=models.CharField(max_length=64, verbose_name='hostname'),
),
migrations.AlterField(
model_name='compute',
name='login',
field=models.CharField(max_length=20, verbose_name='login'),
),
migrations.AlterField(
model_name='compute',
name='name',
field=models.CharField(max_length=64, unique=True, verbose_name='name'),
),
migrations.AlterField(
model_name='compute',
name='password',
field=models.CharField(blank=True, max_length=14, null=True, verbose_name='password'),
),
]

View file

@ -1,12 +1,65 @@
from django.db import models
from django.db.models import CharField, IntegerField, Model
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from libvirt import virConnect
from vrtManager.connection import connection_manager
from vrtManager.hostdetails import wvmHostDetails
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)
type = models.IntegerField()
class Compute(Model):
name = CharField(_("name"), max_length=64, unique=True)
hostname = CharField(_("hostname"), max_length=64)
login = CharField(_("login"), max_length=20)
password = CharField(_("password"), max_length=14, blank=True, null=True)
details = CharField(_("details"), max_length=64, null=True, blank=True)
type = IntegerField()
def __unicode__(self):
return self.hostname
@cached_property
def status(self):
# return connection_manager.host_is_up(self.type, self.hostname)
# TODO: looks like socket has problems connecting via VPN
if isinstance(self.connection, virConnect):
return True
else:
return self.connection
@cached_property
def connection(self):
try:
return connection_manager.get_connection(
self.hostname,
self.login,
self.password,
self.type,
)
except Exception as e:
return e
@cached_property
def proxy(self):
return wvmHostDetails(
self.hostname,
self.login,
self.password,
self.type,
)
@cached_property
def cpu_count(self):
return self.proxy.get_node_info()[3]
@cached_property
def cpu_usage(self):
return round(self.proxy.get_cpu_usage(diff=False).get('usage'))
@cached_property
def ram_size(self):
return self.proxy.get_node_info()[2]
@cached_property
def ram_usage(self):
return self.proxy.get_memory_usage()["percent"]
def __str__(self):
return self.name

View file

@ -1,221 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Computes" %}{% endblock %}
{% block content %}
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
{% include 'create_comp_block.html' %}
<h1 class="page-header">{% trans "Computes" %}</h1>
</div>
</div>
<!-- /.row -->
{% include 'errors_block.html' %}
<div class="row">
{% if computes_info %}
{% for compute in computes_info %}
<div id="{{ compute.name }}" class="col-xs-12 col-sm-4">
<div class="panel {% if compute.status %}panel-success{% else %}panel-danger{% endif %} panel-data">
<div class="panel-heading">
{% ifequal compute.status 1 %}
<h3 class="panel-title">
<a href="{% url 'overview' compute.id %}"><strong>{{ compute.name }}</strong></a>
<a data-toggle="modal" href="#editHost{{ compute.id }}" class="pull-right" title="{% trans "Edit" %}">
<i class="fa fa-cog"></i>
</a>
</h3>
{% else %}
<h3 class="panel-title"><strong>{{ compute.name }}</strong>
<a data-toggle="modal" href="#editHost{{ compute.id }}" class="pull-right" title="{% trans "Edit" %}">
<span class="glyphicon glyphicon-cog"></span>
</a>
</h3>
{% endifequal %}
</div>
<div class="panel-body">
<div class="row">
<div class="col-xs-4 col-sm-4">
<p><strong>{% trans "Status:" %}</strong></p>
</div>
<div class="col-xs-4 col-sm-6">
{% if compute.status %}
<p>{% trans "Connected" %}</p>
{% else %}
<p>{% trans "Not Connected" %}</p>
{% endif %}
</div>
</div>
<!-- Modal Edit -->
<div class="modal fade" id="editHost{{ compute.id }}" tabindex="-1" role="dialog" aria-labelledby="editHostLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Edit connection" %}</h4>
</div>
{% ifequal compute.type 1 %}
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="hidden" name="host_id" value="{{ compute.id }}">
<input type="text" name="name" class="form-control" value="{{ compute.name }}" maxlength="20" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "FQDN / IP" %}</label>
<div class="col-sm-6">
<input type="text" name="hostname" class="form-control" value="{{ compute.hostname }}" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input type="text" name="login" class="form-control" value="{{ compute.login }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" name="password" class="form-control" value="{{ compute.password }}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="pull-left btn btn-danger" name="host_del">
{% trans "Delete" %}
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_edit">
{% trans "Change" %}
</button>
</form>
</div>
{% endifequal %}
{% ifequal compute.type 2 %}
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<p class="modal-body">{% trans "Need create ssh <a href='https://github.com/retspen/webvirtmgr/wiki/Setup-SSH-Authorization'>authorization key</a>. If you have another SSH port on your server, you can add IP:PORT like '192.168.1.1:2222'." %}</p>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="hidden" name="host_id" value="{{ compute.id }}">
<input type="text" name="name" class="form-control" value="{{ compute.name }}" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "FQDN / IP" %}</label>
<div class="col-sm-6">
<input type="text" name="hostname" class="form-control" value="{{ compute.hostname }}" required pattern="[a-z0-9\:\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input type="text" name="login" class="form-control" value="{{ compute.login }}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="pull-left btn btn-danger" name="host_del">
{% trans "Delete" %}
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_edit">
{% trans "Change" %}
</button>
</form>
</div>
{% endifequal %}
{% ifequal compute.type 3 %}
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="hidden" name="host_id" value="{{ compute.id }}">
<input type="text" name="name" class="form-control" value="{{ compute.name }}" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "FQDN / IP" %}</label>
<div class="col-sm-6">
<input type="text" name="hostname" class="form-control" value="{{ compute.hostname }}" required pattern="[a-z0-9\:\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input type="text" name="login" class="form-control" placeholder="{% trans "Name" %}">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" name="password" class="form-control" value="{{ compute.password }}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="pull-left btn btn-danger" name="host_del">
{% trans "Delete" %}
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_edit">
{% trans "Change" %}
</button>
</form>
</div>
{% endifequal %}
{% ifequal compute.type 4 %}
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="hidden" name="host_id" value="{{ compute.id }}">
<input type="text" name="name" class="form-control" value="{{ compute.name }}" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="pull-left btn btn-danger" name="host_del">
{% trans "Delete" %}
</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_edit">
{% trans "Change" %}
</button>
</form>
</div>
{% endifequal %}
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="col-lg-12">
<div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any Computes" %}
</div>
</div>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
{% load i18n %}
{% block title %}{% trans "Add Compute" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<h2 class="page-header">{% trans "Create Compute" %}</h2>
</div>
</div>
<div class="row">
<div class="thumbnail col-sm-10 offset-1">
<form id="create-update" action="" method="post">
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
</form>
<div class="float-end">
<a class="btn btn-primary" href="javascript:history.back()">
{% bs_icon 'x-square-fill' %} {% trans "Cancel" %}</a>
<button type="submit" form="create-update" class="btn btn-success">
{% bs_icon 'check-circle-fill' %} {% trans "Save" %}
</button>
</div>
</div>
</div>
{% endblock content %}

View file

@ -0,0 +1,116 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load bootstrap_icons %}
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
{% block style %}
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
{% endblock %}
{% block page_heading %}{{ compute.name }} - {% trans "Instances" %}{% endblock page_heading %}
{% block page_heading_extra %}
<a href="{% url 'instances:create_instance_select_type' compute.id %}"
class="btn btn-success btn-header float-end">
{% bs_icon 'plus-circle-fill' %}
</a>
{% if instances %}
{% include 'search_block.html' %}
{% endif %}
{% endblock page_heading_extra %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<nav aria-label="breadcrumb">
<ol class="breadcrumb shadow-sm">
<li class="breadcrumb-item">
<a href="{% url 'overview' compute.id %}">{% bs_icon 'laptop' %} {% trans "Overview" %}</a>
</li>
<li class="breadcrumb-item">
<span class="fw-bold">{% bs_icon 'server' %} {% trans "Instances" %}</span>
</li>
<li class="breadcrumb-item">
<a href="{% url 'storages' compute.id %}">{% bs_icon 'device-hdd' %} {% trans "Storages" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'networks' compute.id %}">{% bs_icon 'hdd-network' %} {% trans "Networks" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'interfaces' compute.id %}">{% bs_icon 'wifi' %} {% trans "Interfaces" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'nwfilters' compute.id %}">{% bs_icon 'filter' %} {% trans "NWFilters" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'virtsecrets' compute.id %}">{% bs_icon 'key' %} {% trans "Secrets" %}</a>
</li>
</ol>
</nav>
</div>
</div>
<div class="row">
<div class="col-lg-12">
{% if not instances %}
<div class="alert alert-warning shadow-sm fade show">
{% bs_icon 'exclamation-triangle' %} <strong>{% trans "Warning" %}:</strong>
{% trans "Hypervisor doesn't have any Instances" %}
</div>
</div>
{% else %}
<table class="table table-hover sortable-theme-bootstrap" data-sortable>
<thead>
<tr>
<th scope="col">{% trans 'Name' %}<br>{% trans 'Description' %}</th>
<th scope="col">{% trans 'User' %}</th>
<th scope="col">{% trans 'Status' %}</th>
<th scope="col">{% trans 'VCPU' %}</th>
<th scope="col" class="text-end">{% trans 'Memory' %}</th>
<th scope="col" data-sortable="false">{% trans 'Actions' %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for instance in instances %}
<tr>
<td>
<a class="link-primary" href="{% url 'instances:instance' instance.id %}">{{ instance.name }}</a>
<br>
<p class="m-0 small fst-italic">{{ instance.title }}</p>
</td>
<td>
<em>
{% if instance.userinstance_set.all.count > 0 %}
{{ instance.userinstance_set.all.0.user }}
{% if instance.userinstance_set.all.count > 1 %}
(+{{ instance.userinstance_set.all.count|add:"-1" }})
{% endif %}
{% endif %}
</em>
</td>
<td>
{% if instance.proxy.instance.info.0 == 1 %}
<span class="text-success">{% trans "Active" %}</span>
{% endif %}
{% if instance.proxy.instance.info.0 == 5 %}
<span class="text-danger">{% trans "Off" %}</span>
{% endif %}
{% if instance.proxy.instance.info.0 == 3 %}
<span class="text-warning">{% trans "Suspended" %}</span>
{% endif %}
</td>
<td>{{ instance.proxy.instance.info.3 }}</td>
<td><p class="text-end">{% widthratio instance.proxy.instance.info.1 1024 1 %} MiB</p></td>
<td class="text-nowrap">
{% include 'instance_actions.html' %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static "js/sortable.min.js" %}"></script>
<script src="{% static 'js/filter-table.js' %}"></script>
{% endblock %}

View file

@ -0,0 +1,68 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% load bootstrap_icons %}
{% block title %}{% trans "Computes" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
{% include 'create_comp_block.html' %}
{% include 'search_block.html' %}
<h3 class="page-header">{% trans "Computes" %}</h3>
</div>
</div>
<div class="row">
{% if not computes %}
<div class="col-lg-12">
<div class="alert alert-warning shadow-sm">
{% bs_icon 'exclamation-triangle'%} <strong>{% trans "Warning" %}:</strong> {% trans "You don't have any computes" %}
</div>
</div>
{% else %}
<div class="col-lg-12">
<table class="table table-striped table-hover sortable-theme-bootstrap" data-sortable>
<thead>
<tr>
<th span="col" class="col-sm-3">{% trans "Name" %}</th>
<th span="col" class="col-sm-2">{% trans "Status" %}</th>
<th span="col" class="col-sm-5">{% trans "Details" %}</th>
<th span="col" class="col-sm-2 text-center">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="searchable">
{% for compute in computes %}
<tr>
<td class="col-sm-3">
{{ compute.name }}
</td>
<td class="col-sm-2">
{% if compute.status is True %}{% trans "Connected" %}{% else %}{% trans "Not Connected" %}{% endif %}
</td>
<td class="col-sm-5">
{{ compute.details|default:"" }}
</td>
<td class="col-sm-2">
<div class="float-end btn-group">
{% if compute.status is True %}
<a class="btn btn-success" title="{%trans "Overview" %}" href="{% url 'overview' compute.id %}">{% bs_icon 'eye-fill' %}</a>
{% else %}
<a class="btn btn-light" title="{%trans "Overview" %}">{% bs_icon 'eye' %}</a>
{% endif %}
<a class="btn btn-primary" title="{%trans "Edit" %}" href="{% url 'compute_update' compute.id %}">{% bs_icon 'pencil-fill' %}</a>
<a class="btn btn-danger" title="{%trans "Delete" %}" href="{% url 'compute_delete' compute.id %}">{% bs_icon 'x-circle-fill' %}</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{% endblock content %}
{% block script %}
<script src="{% static 'js/sortable.min.js' %}"></script>
<script src="{% static 'js/filter-table.js' %}"></script>
{% endblock script %}

View file

@ -1,159 +1,11 @@
{% load i18n %}
{% if request.user.is_superuser %}
<a href="#addHost" type="button" class="btn btn-success pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
{% load django_bootstrap5 %}
{% load bootstrap_icons %}
<!-- Modal -->
<div class="modal fade" id="addHost" tabindex="-1" role="dialog" aria-labelledby="addHostLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Add Connection" %}</h4>
</div>
<div class="tabbable">
<ul class="nav nav-tabs">
<li class="active">
<a href="#1" data-toggle="tab">{% trans "TCP Connections" %}</a>
</li>
<li><a href="#2" data-toggle="tab">{% trans "SSH Connections" %}</a></li>
<li><a href="#3" data-toggle="tab">{% trans "TLS Connection" %}</a></li>
<li><a href="#4" data-toggle="tab">{% trans "Local Socket" %}</a></li>
</ul>
</div>
<div class="tab-content">
<div class="tab-pane active" id="1">
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="text" name="name" class="form-control" placeholder="Label Name" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "FQDN / IP" %}</label>
<div class="col-sm-6">
<input type="text" name="hostname" class="form-control" placeholder="{% trans "FQDN or IP Address" %}" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input type="text" name="login" class="form-control" placeholder="{% trans "Username" %}">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" name="password" class="form-control" placeholder="{% trans "Password" %}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_tcp_add">
{% trans "Add" %}
</button>
</div>
</form>
</div>
<div class="tab-pane" id="2">
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<p class="modal-body">{% trans "You must create ssh <a href='https://github.com/retspen/webvirtmgr/wiki/Setup-SSH-Authorization'>authorization key</a>. If you have another SSH port on your server, you can add IP:PORT like '192.168.1.1:2222'." %}</p>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="text" name="name" class="form-control" placeholder="Label Name" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "FQDN / IP" %}</label>
<div class="col-sm-6">
<input type="text" name="hostname" class="form-control" placeholder="{% trans "FQDN or IP Address" %}" required pattern="[a-z0-9\:\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input type="text" name="login" class="form-control" placeholder="{% trans "Username" %}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_ssh_add">
{% trans "Add" %}
</button>
</div>
</form>
</div>
<div class="tab-pane" id="3">
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="text" name="name" class="form-control" placeholder="Label Name" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "FQDN / IP" %}</label>
<div class="col-sm-6">
<input type="text" name="hostname" class="form-control" placeholder="{% trans "FQDN or IP Address" %}" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Username" %}</label>
<div class="col-sm-6">
<input type="text" name="login" class="form-control" placeholder="{% trans "Username" %}">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Password" %}</label>
<div class="col-sm-6">
<input type="password" name="password" class="form-control" placeholder="{% trans "Password" %}">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_tls_add">
{% trans "Add" %}
</button>
</div>
</form>
</div>
<div class="tab-pane" id="4">
<div class="modal-body">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Label" %}</label>
<div class="col-sm-6">
<input type="text" name="name" class="form-control" placeholder="Label Name" maxlength="20" required pattern="[a-z0-9\.\-_]+">
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
{% trans "Close" %}
</button>
<button type="submit" class="btn btn-primary" name="host_socket_add">
{% trans "Add" %}
</button>
</div>
</form>
</div>
</div> <!-- /.tab-content -->
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div><!-- /.modal -->
{% endif %}
<div class="btn-group float-end mt-1" role="group" aria-label="Add host button group">
<a href="{% url 'add_tcp_host' %}" class="btn btn-success">{% trans "TCP" %}</a>
<a href="{% url 'add_ssh_host' %}" class="btn btn-success">{% trans "SSH" %}</a>
<a href="{% url 'add_tls_host' %}" class="btn btn-success">{% trans "TLS" %}</a>
<a href="{% url 'add_socket_host' %}" class="btn btn-success">{% trans "Local" %}</a>
<a href="#" class="btn btn-success disabled" title="{% trans "Add new host" %}">{% bs_icon "plus-circle-fill" %}</a>
</div>

View file

@ -1,150 +1,247 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
{% load static %}
{% load bootstrap_icons %}
{% block title %}{% trans "Overview" %} - {{ compute.name }}{% endblock %}
{% block page_heading %}{{ compute.name }}{% endblock page_heading %}
{% block content %}
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">{{ compute.name }}</h1>
<ol class="breadcrumb">
<li class="active">
<i class="fa fa-dashboard"></i> {% trans "Overview" %}
</li>
<li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li>
<li>
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
</li>
<li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li>
<li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li>
</ol>
</div>
</div>
<!-- /.row -->
<div class="row">
<nav aria-label="breadcrumb">
<ol class="breadcrumb shadow-sm">
<li class="breadcrumb-item" aria-current="page">
<span class="fw-bold">{% bs_icon 'laptop' %} {% trans "Overview" %}</span>
</li>
<li class="breadcrumb-item">
<a href="{% url 'instances' compute.id %}">{% bs_icon 'server' %} {% trans "Instances" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'storages' compute.id %}">{% bs_icon 'device-hdd' %} {% trans "Storages" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'networks' compute.id %}">{% bs_icon 'hdd-network' %} {% trans "Networks" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'interfaces' compute.id %}">{% bs_icon 'wifi' %} {% trans "Interfaces" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'nwfilters' compute.id %}">{% bs_icon 'filter' %} {% trans "NWFilters" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'virtsecrets' compute.id %}">{% bs_icon 'key' %} {% trans "Secrets" %}</a>
</li>
</ol>
</nav>
</div>
{% include 'errors_block.html' %}
<div class="row" id="max-width-page">
<h3 class="page-header">{% trans "Basic details" %}</h3>
<div class="col-xs-4 col-sm-3">
<p>{% trans "Hostname" %}</p>
<p>{% trans "Hypervisor" %}</p>
<p>{% trans "Memory" %}</p>
<p>{% trans "Architecture" %}</p>
<p>{% trans "Logical CPUs" %}</p>
<p>{% trans "Processor" %}</p>
<p>{% trans "Connection" %}</p>
</div>
<div class="col-xs-8 col-sm-7">
<p>{{ hostname }}</p>
<p>{{ hypervisor }}</p>
<p>{{ host_memory|filesizeformat }}</p>
<p>{{ host_arch }}</p>
<p>{{ logical_cpu }}</p>
<p>{{ model_cpu }}</p>
<p>{{ uri_conn }}</p>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<h3 class="page-header">{% trans "Performance" %}</h3>
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-long-arrow-right"></i> {% trans "CPU utilization" %}</h3>
</div>
<div class="panel-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart" style="padding: 0px; position: relative;">
<canvas id="cpuChart" width="735" height="160"></canvas>
</div>
</div>
</div>
<div class="shadow-sm">
<h3 class="page-header">{% trans "Basic details" %}</h3>
<dl class="mx-3 row">
<dt class="col-3">{% trans "Hostname" %}</dt>
<dd class="col-9">{{ hostname }}</dd>
<dt class="col-3">{% trans "Hypervisors" %}</dt>
<dd class="col-9">
<div class="dropdown">
{% for arch, hpv in hypervisor.items|slice:":4" %}
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="hpvArchDrop{{ forloop.counter0 }}" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{{ arch }}
</button>
<div class="dropdown-menu" aria-labelledby="hpvArchDrop{{ forloop.counter0 }}">
{% for h in hpv %}
<a class="dropdown-item" href="#">{{ h }}</a>
{% endfor %}
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-long-arrow-right"></i> {% trans "RAM utilization" %}</h3>
</div>
<div class="panel-body">
<div class="flot-chart">
<div class="flot-chart-content" id="flot-moving-line-chart" style="padding: 0px; position: relative;">
<canvas id="memChart" width="735" height="160"></canvas>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="dropdown">
{% if hypervisor.items|length > 4 %}
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="hpvDrop{{ forloop.counter0 }}" data-bs-toggle="dropdown">
{{ hypervisor.items|slice:"4:"|length }} {% trans 'more' %}...
</button>
<div class="dropdown-menu" aria-labelledby="hpvDrop{{ forloop.counter0 }}" role="menu">
{% for arc in hypervisor.keys|slice:"4:" %}
<a class="dropdown-item" tabindex="-1" href="#">{{ arc }}</a>
{% endfor %}
</div>
{% endif %}
</div>
</dd>
<dt class="col-3">{% trans "Emulator" %}</dt>
<dd class="col-9">{{ emulator }}</dd>
<dt class="col-3">{% trans "Version" %}</dt>
<dd class="col-9">
<span class="badge bg-secondary">{% trans 'Qemu' %} </span>
<span class="badge bg-primary">{{ version }}</span> &nbsp;
<span class="badge bg-secondary">{% trans 'Libvirt' %} </span>
<span class="badge bg-primary">{{ lib_version }}</span> &nbsp;
</dd>
<dt class="col-3">{% trans "Memory" %}</dt>
<dd class="col-9">{{ host_memory|filesizeformat }}</dd>
<dt class="col-3">{% trans "Architecture" %}</dt>
<dd class="col-9">{{ host_arch }}</dd>
<dt class="col-3">{% trans "Logical CPUs" %}</dt>
<dd class="col-9">{{ logical_cpu }}</dd>
<dt class="col-3">{% trans "Processor" %}</dt>
<dd class="col-9">{{ model_cpu }}</dd>
<dt class="col-3">{% trans "Connection" %}</dt>
<dd class="col-9">{{ uri_conn }}</dd>
<dt class="col-3">{% trans "Details" %}</dt>
<dd class="col-9">{{ compute.details }}</dd>
</dl>
<h3 class="page-header">{% trans "Performance" %}</h3>
<div class="mx-3 shadow-sm">
<div class="my-3 card border-success">
<div class="card-body">
<h5 class="card-title">
{% bs_icon 'arrow-right' %}
{% trans "CPU Utilization" %}
</h5>
<canvas id="cpuChart" width="735" height="160"></canvas>
</div>
</div>
<div class="my-3 card border-primary">
<div class="card-body">
<h5 class="card-title ">
{% bs_icon 'arrow-right'%} {% trans "RAM Utilization" %}
</h5>
<canvas id="memChart" width="735" height="160"></canvas>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static "js/Chart.min.js" %}"></script>
<script src="{% static "js/Chart.bundle.min.js" %}"></script>
<script>
var cpuLineData = {
labels : [0, 0, 0, 0, 0],
datasets : [
{
fillColor: "rgba(241,72,70,0.5)",
strokeColor: "rgba(241,72,70,1)",
pointColor : "rgba(241,72,70,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(220,220,220,1)",
data : [0, 0, 0, 0, 0]
}
]
}
var cpu_ctx = document.getElementById("cpuChart").getContext("2d");
var cpuChart = new Chart(cpu_ctx).Line(cpuLineData, {
animation: false,
pointDotRadius: 2,
scaleLabel: "<%=value%> %",
scaleOverride: true,
scaleSteps: 5,
scaleStepWidth: 20,
scaleStartValue: 0,
responsive: true
var cpuChart = new Chart(cpu_ctx, {
type: 'line',
data: {
datasets : [{
label: 'Usage',
backgroundColor: "rgba(241,72,70,0.5)",
pointRadius: 2,
}]
},
options: {
responsive: true,
legend: {
display: false
},
scales: {
xAxes:[{
offset: false,
ticks: {
beginAtZero: false,
autoSkip: true,
maxTicksLimit: 10,
maxRotation: 0,
minRotation: 0,
stepSize: 10,
},
}],
yAxes: [{
ticks: {
max: 100,
min: 0,
stepSize: 20,
callback: function(value, index, values) {
return value + ' %';
}
},
}],
},
tooltips: {
callbacks: {
label: function (tooltipItem, chart) {
var label = chart.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
return label += tooltipItem.yLabel + ' %';
}
}
}
}
});
var memLineData = {
labels : [0, 0, 0, 0, 0],
datasets : [
{
fillColor : "rgba(249,134,33,0.5)",
strokeColor : "rgba(249,134,33,1)",
pointColor : "rgba(249,134,33,1)",
pointStrokeColor : "#fff",
pointHighlightFill : "#fff",
pointHighlightStroke : "rgba(151,187,205,1)",
data : [0, 0, 0, 0, 0]
var mem_ctx = document.getElementById("memChart").getContext("2d");
var memChart = new Chart(mem_ctx, {
type: 'line',
data: {
datasets: [{
pointRadius: 2,
}]
},
options: {
responsive: true,
legend: {
display: false
},
scales: {
xAxes:[{
offset: false,
ticks: {
beginAtZero: false,
autoSkip: true,
maxTicksLimit: 10,
maxRotation: 0,
minRotation: 0
}
}],
yAxes: [{
ticks:{
suggestedMin: 0,
suggestedMax: 100,
callback: function(value, index, values) {
return value + ' MB';
}
},
}],
},
tooltips: {
callbacks: {
label: function (tooltipItem, chart) {
var label = chart.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
return label += tooltipItem.yLabel + ' MB';
}
}
}
]
}
});
if (Boolean({{ status }}) === true) {
window.setInterval(function graph_usage() {
$.getJSON('{% url 'compute_graph' compute_id %}', function (data) {
cpuChart.data.labels.push(data.timeline);
memChart.data.labels.push(data.timeline);
cpuChart.data.datasets[0].data.push(data.cpudata);
if (cpuChart.data.datasets[0].data.length > 10){
cpuChart.data.labels.shift();
cpuChart.data.datasets[0].data.shift();
}
memChart.options.scales.yAxes[0].ticks.max = parseInt(data.memdata.total / 1048576);
memChart.options.scales.yAxes[0].ticks.stepSize = parseInt(data.memdata.total / (1048576 * 5));
memChart.data.datasets[0].data.push(parseInt(data.memdata.usage / 1048576));
if (memChart.data.datasets[0].data.length > 10){
memChart.data.labels.shift();
memChart.data.datasets[0].data.shift();
}
cpuChart.update();
memChart.update();
});
}, 5000);
}
var mem_ctx = $("#memChart").get(0).getContext("2d");
var memChart = new Chart(mem_ctx).Line(memLineData, {
animation: false,
pointDotRadius: 2,
scaleLabel: "<%=value%> Mb",
responsive: true
});
window.setInterval(function graph_usage() {
$.getJSON('{% url 'compute_graph' compute_id %}', function (data) {
cpuChart.scale.xLabels = data.timeline;
memChart.scale.xLabels = data.timeline;
for (var i = 0; i < 5; i++) {
cpuChart.datasets[0].points[i].value = data.cpudata[i];
memChart.datasets[0].points[i].value = data.memdata[i];
}
cpuChart.update();
memChart.update();
});
}, 5000);
</script>
{% endblock %}

View file

@ -1,3 +1,159 @@
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import reverse
from django.test import TestCase
# Create your tests here.
from .models import Compute
class ComputesTestCase(TestCase):
def setUp(self):
self.client.login(username="admin", password="admin")
Compute(
name="local",
hostname="localhost",
login="",
password="",
details="local",
type=4,
).save()
def test_index(self):
response = self.client.get(reverse("computes"))
self.assertEqual(response.status_code, 200)
def test_create_update_delete(self):
response = self.client.get(reverse("add_socket_host"))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("add_socket_host"),
{
"name": "l1",
"details": "Created",
"hostname": "localhost",
"type": 4,
},
)
self.assertRedirects(response, reverse("computes"))
compute = Compute.objects.get(pk=2)
self.assertEqual(compute.name, "l1")
self.assertEqual(compute.details, "Created")
response = self.client.get(reverse("compute_update", args=[2]))
self.assertEqual(response.status_code, 200)
response = self.client.post(
reverse("compute_update", args=[2]),
{
"name": "l2",
"details": "Updated",
"hostname": "localhost",
"type": 4,
},
)
self.assertRedirects(response, reverse("computes"))
compute = Compute.objects.get(pk=2)
self.assertEqual(compute.name, "l2")
self.assertEqual(compute.details, "Updated")
response = self.client.get(reverse("compute_delete", args=[2]))
self.assertEqual(response.status_code, 200)
response = self.client.post(reverse("compute_delete", args=[2]))
self.assertRedirects(response, reverse("computes"))
with self.assertRaises(ObjectDoesNotExist):
Compute.objects.get(id=2)
def test_overview(self):
response = self.client.get(reverse("overview", args=[1]))
self.assertEqual(response.status_code, 200)
def test_graph(self):
response = self.client.get(reverse("compute_graph", args=[1]))
self.assertEqual(response.status_code, 200)
def test_instances(self):
response = self.client.get(reverse("instances", args=[1]))
self.assertEqual(response.status_code, 200)
def test_storages(self):
response = self.client.get(reverse("storages", args=[1]))
self.assertEqual(response.status_code, 200)
def test_storage(self):
pass
def test_default_storage_volumes(self):
response = self.client.get(
reverse("volumes", kwargs={"compute_id": 1, "pool": "default"})
)
self.assertEqual(response.status_code, 200)
def test_default_storage(self):
response = self.client.get(
reverse("storage", kwargs={"compute_id": 1, "pool": "default"})
)
self.assertEqual(response.status_code, 200)
def test_networks(self):
response = self.client.get(reverse("networks", args=[1]))
self.assertEqual(response.status_code, 200)
def test_default_network(self):
response = self.client.get(
reverse("network", kwargs={"compute_id": 1, "pool": "default"})
)
self.assertEqual(response.status_code, 200)
def test_interfaces(self):
response = self.client.get(reverse("interfaces", args=[1]))
self.assertEqual(response.status_code, 200)
# TODO: add test for single interface
def test_nwfilters(self):
response = self.client.get(reverse("nwfilters", args=[1]))
self.assertEqual(response.status_code, 200)
# TODO: add test for single nwfilter
def test_secrets(self):
response = self.client.get(reverse("virtsecrets", args=[1]))
self.assertEqual(response.status_code, 200)
# def test_create_instance_select_type(self):
# response = self.client.get(reverse('create_instance_select_type', args=[1]))
# self.assertEqual(response.status_code, 200)
# TODO: create_instance
def test_machines(self):
response = self.client.get(
reverse("machines", kwargs={"compute_id": 1, "arch": "x86_64"})
)
self.assertEqual(response.status_code, 200)
def test_compute_disk_buses(self):
response = self.client.get(
reverse(
"buses",
kwargs={
"compute_id": 1,
"arch": "x86_64",
"machine": "pc",
"disk": "disk",
},
)
)
self.assertEqual(response.status_code, 200)
def test_dom_capabilities(self):
response = self.client.get(
reverse(
"domcaps", kwargs={"compute_id": 1, "arch": "x86_64", "machine": "pc"}
)
)
self.assertEqual(response.status_code, 200)

View file

@ -1,9 +1,79 @@
from django.conf.urls import url
from . import views
from virtsecrets.views import secrets
from django.urls import include, path
from interfaces.views import interface, interfaces
from networks.views import network, networks
from nwfilters.views import nwfilter, nwfilters
from storages.views import create_volume, get_volumes, storage, storages
from . import forms, views
urlpatterns = [
url(r'^$', views.computes, name='computes'),
url(r'^overview/(?P<compute_id>[0-9]+)/$', views.overview, name='overview'),
url(r'^statistics/(?P<compute_id>[0-9]+)/$',
views.compute_graph, name='compute_graph'),
path("", views.computes, name="computes"),
path(
"add_tcp_host/",
views.compute_create,
{"FormClass": forms.TcpComputeForm},
name="add_tcp_host",
),
path(
"add_ssh_host/",
views.compute_create,
{"FormClass": forms.SshComputeForm},
name="add_ssh_host",
),
path(
"add_tls_host/",
views.compute_create,
{"FormClass": forms.TlsComputeForm},
name="add_tls_host",
),
path(
"add_socket_host/",
views.compute_create,
{"FormClass": forms.SocketComputeForm},
name="add_socket_host",
),
path(
"<int:compute_id>/",
include(
[
path("", views.overview, name="overview"),
path("update/", views.compute_update, name="compute_update"),
path("delete/", views.compute_delete, name="compute_delete"),
path("statistics", views.compute_graph, name="compute_graph"),
path("instances/", views.instances, name="instances"),
path("storages/", storages, name="storages"),
path("storage/<str:pool>/volumes/", get_volumes, name="volumes"),
path("storage/<str:pool>/", storage, name="storage"),
path(
"storage/<str:pool>/create_volume/",
create_volume,
name="create_volume",
),
path("networks/", networks, name="networks"),
path("network/<str:pool>/", network, name="network"),
path("interfaces/", interfaces, name="interfaces"),
path("interface/<str:iface>/", interface, name="interface"),
path("nwfilters/", nwfilters, name="nwfilters"),
path("nwfilter/<str:nwfltr>/", nwfilter, name="nwfilter"),
path("virtsecrets/", secrets, name="virtsecrets"),
path(
"archs/<str:arch>/machines",
views.get_compute_machine_types,
name="machines",
),
path(
"archs/<str:arch>/machines/<str:machine>/disks/<str:disk>/buses",
views.get_compute_disk_buses,
name="buses",
),
path(
"archs/<str:arch>/machines/<str:machine>/capabilities",
views.get_dom_capabilities,
name="domcaps",
),
]
),
),
]

17
computes/utils.py Normal file
View file

@ -0,0 +1,17 @@
from instances.models import Instance
def refresh_instance_database(compute):
domains = compute.proxy.wvm.listAllDomains()
domain_names = [d.name() for d in domains]
domain_uuids = [d.UUIDString() for d in domains]
# Delete instances that're not on host from DB
Instance.objects.filter(compute=compute).exclude(name__in=domain_names).delete()
Instance.objects.filter(compute=compute).exclude(uuid__in=domain_uuids).delete()
# Create instances that're on host but not in DB
names = Instance.objects.filter(compute=compute).values_list("name", flat=True)
for domain in domains:
if domain.name() not in names:
Instance(
compute=compute, name=domain.name(), uuid=domain.UUIDString()
).save()

26
computes/validators.py Normal file
View file

@ -0,0 +1,26 @@
import re
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
have_symbol = re.compile("[^a-zA-Z0-9._-]+")
wrong_ip = re.compile("^0.|^255.")
wrong_name = re.compile("[^a-zA-Z0-9._-]+")
def validate_hostname(value):
sym = have_symbol.match(value)
wip = wrong_ip.match(value)
if sym:
raise ValidationError(
_('Hostname must contain only numbers, or the domain name separated by "."')
)
elif wip:
raise ValidationError(_("Wrong IP address"))
def validate_name(value):
have_symbol = wrong_name.match("[^a-zA-Z0-9._-]+")
if have_symbol:
raise ValidationError(_("The hostname must not contain any special characters"))

View file

@ -1,225 +1,278 @@
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 computes.models import Compute
from admin.decorators import superuser_only
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
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
from vrtManager.connection import (
CONN_SOCKET,
CONN_SSH,
CONN_TCP,
CONN_TLS,
connection_manager,
wvmConnect,
)
from vrtManager.hostdetails import wvmHostDetails
from computes.forms import (
SocketComputeForm,
SshComputeForm,
TcpComputeForm,
TlsComputeForm,
)
from computes.models import Compute
from . import utils
@superuser_only
def computes(request):
"""
:param request:
:return:
"""
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
computes = Compute.objects.filter().order_by("name")
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
})
return compute_data
error_messages = []
computes = Compute.objects.filter()
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'])
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'])
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'])
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'],
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.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())
return render(request, "computes/list.html", {"computes": computes})
@superuser_only
def overview(request, compute_id):
"""
:param request:
:return:
"""
compute = get_object_or_404(Compute, pk=compute_id)
status = (
"true"
if connection_manager.host_is_up(compute.type, compute.hostname) is True
else "false"
)
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('index'))
conn = wvmHostDetails(
compute.hostname,
compute.login,
compute.password,
compute.type,
)
(
hostname,
host_arch,
host_memory,
logical_cpu,
model_cpu,
uri_conn,
) = conn.get_node_info()
hypervisor = conn.get_hypervisors_domain_types()
mem_usage = conn.get_memory_usage()
emulator = conn.get_emulator(host_arch)
version = conn.get_version()
lib_version = conn.get_lib_version()
conn.close()
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
return render(request, "overview.html", locals())
error_messages = []
@superuser_only
def instances(request, compute_id):
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()
conn.close()
except libvirtError as lib_err:
error_messages.append(lib_err)
utils.refresh_instance_database(compute)
instances = Instance.objects.filter(compute=compute).prefetch_related(
"userinstance_set"
)
return render(request, 'overview.html', locals())
return render(
request, "computes/instances.html", {"compute": compute, "instances": instances}
)
@superuser_only
def compute_create(request, FormClass):
form = FormClass(request.POST or None)
if form.is_valid():
form.save()
return redirect(reverse("computes"))
return render(request, "computes/form.html", {"form": form})
@superuser_only
def compute_update(request, compute_id):
compute = get_object_or_404(Compute, pk=compute_id)
if compute.type == 1:
FormClass = TcpComputeForm
elif compute.type == 2:
FormClass = SshComputeForm
elif compute.type == 3:
FormClass = TlsComputeForm
elif compute.type == 4:
FormClass = SocketComputeForm
form = FormClass(request.POST or None, instance=compute)
if form.is_valid():
form.save()
return redirect(reverse("computes"))
return render(request, "computes/form.html", {"form": form})
@superuser_only
def compute_delete(request, compute_id):
compute = get_object_or_404(Compute, pk=compute_id)
if request.method == "POST":
compute.delete()
return redirect("computes")
return render(
request,
"common/confirm_delete.html",
{"object": compute},
)
def compute_graph(request, compute_id):
"""
:param request:
:param compute_id:
:return:
"""
comp_mgr = ComputeManager(compute_id)
data = comp_mgr.compute_graph()
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('login'))
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["Content-Type"] = "text/javascript"
response.write(data)
return response
def get_compute_disk_buses(request, compute_id, arch, machine, disk):
"""
:param request:
:param compute_id:
:param arch:
:param machine:
:param disk:
:return:
"""
comp_mgr = ComputeManager(compute_id)
return HttpResponse(comp_mgr.get_disk_buses(arch, machine, disk))
def get_compute_machine_types(request, compute_id, arch):
"""
:param request:
:param compute_id:
:param arch:
:return:
"""
comp_mgr = ComputeManager(compute_id)
return HttpResponse(comp_mgr.get_machine_types(arch))
def get_compute_video_models(request, compute_id, arch, machine):
"""
:param request:
:param compute_id:
:param arch:
:param machine:
:return:
"""
comp_mgr = ComputeManager(compute_id)
return HttpResponse(comp_mgr.get_video_models(arch, machine))
def get_dom_capabilities(request, compute_id, arch, machine):
"""
:param request:
:param compute_id:
:param arch:
:param machine:
:return:
"""
comp_mgr = ComputeManager(compute_id)
return HttpResponse(comp_mgr.get_dom_capabilities(arch, machine))
class ComputeManager:
def __init__(self, compute_id):
self.compute = get_object_or_404(Compute, pk=compute_id)
self.conn = wvmConnect(
self.compute.hostname,
self.compute.login,
self.compute.password,
self.compute.type,
)
def get_video_models(self, arch, machine):
data = dict()
try:
data["videos"] = self.conn.get_video_models(arch, machine)
except libvirtError:
pass
return json.dumps(data)
def get_dom_capabilities(self, arch, machine):
data = dict()
try:
data["videos"] = self.conn.get_disk_device_types(arch, machine)
data["bus"] = self.conn.get_disk_device_types(arch, machine)
except libvirtError:
pass
return json.dumps(data)
def get_machine_types(self, arch):
data = dict()
try:
data["machines"] = self.conn.get_machine_types(arch)
except libvirtError:
pass
return json.dumps(data)
def get_disk_buses(self, arch, machine, disk):
data = dict()
try:
disk_device_types = self.conn.get_disk_device_types(arch, machine)
if disk in disk_device_types:
if disk == "disk":
data["bus"] = sorted(disk_device_types)
elif disk == "cdrom":
data["bus"] = ["ide", "sata", "scsi"]
elif disk == "floppy":
data["bus"] = ["fdc"]
elif disk == "lun":
data["bus"] = ["scsi"]
except libvirtError:
pass
return json.dumps(data)
def compute_graph(self):
try:
conn = wvmHostDetails(
self.compute.hostname,
self.compute.login,
self.compute.password,
self.compute.type,
)
current_time = timezone.now().strftime("%H:%M:%S")
cpu_usage = conn.get_cpu_usage()
mem_usage = conn.get_memory_usage()
conn.close()
except libvirtError:
cpu_usage = {"usage": 0}
mem_usage = {"usage": 0}
current_time = 0
return json.dumps(
{
"cpudata": cpu_usage["usage"],
"memdata": mem_usage,
"timeline": current_time,
}
)

100
conf/daemon/consolecallback Executable file
View file

@ -0,0 +1,100 @@
#!/usr/bin/env python3
# consolecallback - provide a persistent console that survives guest reboots
import os
import logging
import libvirt
import tty
import termios
import atexit
from argparse import ArgumentParser
from typing import Optional # noqa F401
def reset_term() -> None:
termios.tcsetattr(0, termios.TCSADRAIN, attrs)
def error_handler(unused, error) -> None:
# The console stream errors on VM shutdown; we don't care
if error[0] == libvirt.VIR_ERR_RPC and error[1] == libvirt.VIR_FROM_STREAMS:
return
logging.warning(error)
class Console(object):
def __init__(self, uri: str, uuid: str) -> None:
self.uri = uri
self.uuid = uuid
self.connection = libvirt.open(uri)
self.domain = self.connection.lookupByUUIDString(uuid)
self.state = self.domain.state(0)
self.connection.domainEventRegister(lifecycle_callback, self)
self.stream = None # type: Optional[libvirt.virStream]
self.run_console = True
self.stdin_watch = -1
logging.info("%s initial state %d, reason %d",
self.uuid, self.state[0], self.state[1])
def check_console(console: Console) -> bool:
if (console.state[0] == libvirt.VIR_DOMAIN_RUNNING or console.state[0] == libvirt.VIR_DOMAIN_PAUSED):
if console.stream is None:
console.stream = console.connection.newStream(libvirt.VIR_STREAM_NONBLOCK)
console.domain.openConsole(None, console.stream, 0)
console.stream.eventAddCallback(libvirt.VIR_STREAM_EVENT_READABLE, stream_callback, console)
else:
if console.stream:
console.stream.eventRemoveCallback()
console.stream = None
return console.run_console
def stdin_callback(watch: int, fd: int, events: int, console: Console) -> None:
readbuf = os.read(fd, 1024)
if readbuf.startswith(b""):
console.run_console = False
return
if console.stream:
console.stream.send(readbuf)
def stream_callback(stream: libvirt.virStream, events: int, console: Console) -> None:
try:
assert console.stream
received_data = console.stream.recv(1024)
except Exception:
return
os.write(0, received_data)
def lifecycle_callback(connection: libvirt.virConnect, domain: libvirt.virDomain, event: int, detail: int, console: Console) -> None:
console.state = console.domain.state(0)
logging.info("%s transitioned to state %d, reason %d",
console.uuid, console.state[0], console.state[1])
# main
parser = ArgumentParser(epilog="Example: %(prog)s 'qemu:///system' '32ad945f-7e78-c33a-e96d-39f25e025d81'")
parser.add_argument("uri")
parser.add_argument("uuid")
args = parser.parse_args()
print("Escape character is ^]")
logging.basicConfig(filename='msg.log', level=logging.DEBUG)
logging.info("URI: %s", args.uri)
logging.info("UUID: %s", args.uuid)
libvirt.virEventRegisterDefaultImpl()
libvirt.registerErrorHandler(error_handler, None)
atexit.register(reset_term)
attrs = termios.tcgetattr(0)
tty.setraw(0)
console = Console(args.uri, args.uuid)
console.stdin_watch = libvirt.virEventAddHandle(0, libvirt.VIR_EVENT_HANDLE_READABLE, stdin_callback, console)
while check_console(console):
libvirt.virEventRunDefaultImpl()

View file

@ -3,24 +3,24 @@
# gstfsd - WebVirtCloud daemon for managing VM's filesystem
#
import SocketServer
import socketserver
import json
import guestfs
import re
PORT = 16510
ADDRESS = "0.0.0.0"
class MyTCPServer(SocketServer.ThreadingTCPServer):
class MyTCPServer(socketserver.ThreadingTCPServer):
allow_reuse_address = True
class MyTCPServerHandler(SocketServer.BaseRequestHandler):
class MyTCPServerHandler(socketserver.BaseRequestHandler):
def handle(self):
# recive data
data = json.loads(self.request.recv(1024).strip())
d = self.request.recv(1024).strip()
data = json.loads(d)
# GuestFS
gfs = guestfs.GuestFS(python_return_dict=True)
@ -42,17 +42,18 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
if data['action'] == 'publickey':
if not gfs.is_dir('/root/.ssh'):
gfs.mkdir('/root/.ssh')
gfs.chmod(0700, "/root/.ssh")
gfs.chmod(700, "/root/.ssh")
gfs.write('/root/.ssh/authorized_keys', data['key'])
gfs.chmod(0600, '/root/.ssh/authorized_keys')
gfs.chmod(600, '/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}))
except Exception as err:
self.request.sendall(bytes(json.dumps({'return': 'error', 'message': str(err)}).encode()))
server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)
server.serve_forever()

View file

@ -0,0 +1,37 @@
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
}

View file

@ -0,0 +1,85 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP5rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}

View file

@ -0,0 +1,38 @@
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
}

Some files were not shown because too many files have changed in this diff Show more