├── .gitignore ├── .travis.yml ├── MANIFEST.in ├── README.rst ├── Vagrantfile ├── conf ├── conf.d │ └── webvirtmgr-console ├── gunicorn.conf.py ├── init │ ├── webvirtmgr-console.service │ ├── webvirtmgr-redhat.conf │ └── webvirtmgr.service ├── initd │ ├── webvirtmgr-console-arch │ ├── webvirtmgr-console-gentoo │ ├── webvirtmgr-console-redhat │ ├── webvirtmgr-console-suse │ └── webvirtmgr-console-ubuntu ├── libvirt-bootstrap.sh └── saltstack │ └── centos_rhel │ └── salt │ ├── libvirt │ ├── init.sls │ ├── iptables │ ├── libvirtd │ ├── libvirtd.conf │ └── qemu.conf │ └── top.sls ├── console ├── __init__.py ├── cert.pem ├── tests.py ├── tunnel.py ├── views.py └── webvirtmgr-console ├── create ├── __init__.py ├── fixtures │ └── initial_data.json ├── forms.py ├── models.py ├── tests.py └── views.py ├── deploy ├── fabric │ ├── README.md │ ├── fab_requirements.txt │ ├── fabfile.py │ ├── settings.py │ ├── templates │ │ ├── README.md │ │ ├── nginx.conf │ │ ├── original.nginx.conf │ │ └── webvirtmgr.ini │ └── utils.py └── rpmbuild │ ├── README.md │ └── webvirtmgr.spec ├── dev-requirements.txt ├── hostdetail ├── __init__.py ├── tests.py └── views.py ├── images └── README.md ├── instance ├── __init__.py ├── models.py ├── templatetags │ ├── __init__.py │ └── tags_active.py ├── tests.py └── views.py ├── interfaces ├── __init__.py ├── forms.py ├── tests.py └── views.py ├── locale ├── da_DK │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── ru │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po └── zh_CN │ └── LC_MESSAGES │ ├── django.mo │ └── django.po ├── manage.py ├── networks ├── __init__.py ├── forms.py ├── tests.py └── views.py ├── requirements.txt ├── secrets ├── __init__.py ├── forms.py ├── tests.py └── views.py ├── serverlog ├── __init__.py ├── models.py ├── tests.py └── views.py ├── servers ├── __init__.py ├── forms.py ├── models.py ├── tests.py └── views.py ├── setup.py ├── storages ├── __init__.py ├── forms.py ├── tests.py └── views.py ├── templates ├── 404.html ├── 500.html ├── base.html ├── base_auth.html ├── console-base.html ├── console-spice.html ├── console-vnc.html ├── create.html ├── hostdetail.html ├── infrastructure.html ├── instance.html ├── instances.html ├── interface.html ├── interfaces.html ├── login.html ├── logout.html ├── network.html ├── networks.html ├── secrets.html ├── servers.html ├── sidebar.html ├── sidebar_close.html ├── storage.html └── storages.html ├── vrtManager ├── IPy.py ├── __init__.py ├── connection.py ├── create.py ├── hostdetails.py ├── instance.py ├── interface.py ├── network.py ├── rwlock.py ├── secrets.py ├── storage.py └── util.py └── webvirtmgr ├── __init__.py ├── local ├── __init__.py └── local_settings.py.example ├── settings-dev.py ├── settings.py ├── static ├── css │ ├── bootstrap-multiselect.css │ ├── bootstrap.min.css │ ├── signin.css │ ├── table-sort.css │ └── webvirtmgr.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ ├── asc.gif │ ├── bg.gif │ ├── desc.gif │ └── favicon.ico └── js │ ├── Chart.min.js │ ├── bootstrap-multiselect.js │ ├── bootstrap.min.js │ ├── infrastructure.js │ ├── jquery-1.10.2.js │ ├── jquery-migrate-1.2.1.js │ ├── jquery.tablesorter.js │ ├── novnc │ ├── Orbitron700.ttf │ ├── Orbitron700.woff │ ├── base.css │ ├── base64.js │ ├── black.css │ ├── blue.css │ ├── chrome-app │ │ └── tcp-client.js │ ├── des.js │ ├── display.js │ ├── input.js │ ├── jsunzip.js │ ├── logo.js │ ├── playback.js │ ├── rfb.js │ ├── ui.js │ ├── util.js │ ├── web-socket-js │ │ ├── README.txt │ │ ├── WebSocketMain.swf │ │ ├── swfobject.js │ │ └── web_socket.js │ ├── websock.js │ └── webutil.js │ └── spice-html5 │ ├── atKeynames.js │ ├── bitmap.js │ ├── cursor.js │ ├── display.js │ ├── enums.js │ ├── filexfer.js │ ├── inputs.js │ ├── jsbn.js │ ├── lz.js │ ├── main.js │ ├── playback.js │ ├── png.js │ ├── prng4.js │ ├── quic.js │ ├── resize.js │ ├── rng.js │ ├── rsa.js │ ├── sha1.js │ ├── simulatecursor.js │ ├── spicearraybuffer.js │ ├── spiceconn.js │ ├── spicedataview.js │ ├── spicemsg.js │ ├── spicetype.js │ ├── thirdparty │ ├── jsbn.js │ ├── prng4.js │ ├── rng.js │ ├── rsa.js │ └── sha1.js │ ├── ticket.js │ ├── utils.js │ ├── webm.js │ └── wire.js ├── urls.py ├── utils ├── __init__.py └── secret_key.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | webvirtmgr.sqlite3 2 | venv 3 | *.pyc 4 | .DS_Store 5 | ._* 6 | .idea 7 | .vagrant 8 | webvirtmgr/local/.secret_key_store 9 | webvirtmgr/local/local_settings.py 10 | dist 11 | webvirtmgr.egg-info 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | env: 5 | - DJANGO=1.5.5 6 | install: 7 | - pip install -r dev-requirements.txt 8 | script: 9 | - pep8 --exclude=IPy.py --ignore=E501 console hostdetail instance networks servers storages vrtManager 10 | - pyflakes console hostdetail instance networks servers storages vrtManager 11 | # - pylint console hostdetail instance networks servers storages vrtManager 12 | - python manage.py syncdb --noinput 13 | - python manage.py test --settings=webvirtmgr.settings-dev 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.rst 2 | 3 | include images/README.md 4 | include console/webvirtmgr-console 5 | 6 | recursive-include console *.pem 7 | recursive-include create *.json 8 | recursive-include locale *.po 9 | recursive-include templates *.html 10 | recursive-include webvirtmgr *.css 11 | recursive-include webvirtmgr *.eot 12 | recursive-include webvirtmgr *.example 13 | recursive-include webvirtmgr *.gif 14 | recursive-include webvirtmgr *.ico 15 | recursive-include webvirtmgr *.js 16 | recursive-include webvirtmgr *.svg 17 | recursive-include webvirtmgr *.swf 18 | recursive-include webvirtmgr *.ttf 19 | recursive-include webvirtmgr *.txt 20 | recursive-include webvirtmgr *.woff 21 | 22 | # ignore these directories 23 | prune deploy 24 | prune conf 25 | 26 | # ignore files that match these patterns 27 | exclude *.txt 28 | exclude .travis.yml 29 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | WebVirtMgr panel - v4.8.9 3 | ========================= 4 | ------- 5 | New project like WebVirtMgr with User Management and Filesystem management - `WebVirtCloud `_ 6 | ------- 7 | 8 | ------- 9 | Warning 10 | ------- 11 | 12 | In latest version app `console/webvirtmgr-novnc` move to `console/webvirtmgr-console` you need check your supervisor settings. 13 | 14 | ---------- 15 | Whats new? 16 | ---------- 17 | 18 | - Added RPM specs (Thanks: `Edoardo Spadoni `_) 19 | - Added support SPICE, SSH Tunnel, fixed some bugs (Thanks: `brenard `_) 20 | - Responsive design (Thanks: `Michel Käser `_) 21 | - Added VNC WebSocket support (Thanks: `Tolbkni Kao `_) 22 | - Added novnc proxy supporting new websockify versions (Thanks: `casell `_) 23 | - Added support `TLS `_ connection (Thanks: `junkb `_) 24 | - `Puppet module to control libvirt/kvm `_ (Thanks: `Alex Scoble `_) 25 | - `Deployment via Fabric/Fabtools `_ (Thanks: `Mohab Usama `_) 26 | 27 | Screenshots 28 | ----------- 29 | `Show `_ 30 | 31 | 32 | Introduction 33 | ------------ 34 | 35 | WebVirtMgr is a libvirt-based Web interface for managing virtual machines. It allows you to create and configure new domains, and adjust a domain's resource allocation. A VNC viewer presents a full graphical console to the guest domain. KVM is currently the only hypervisor supported. 36 | 37 | Technology: 38 | *********** 39 | 40 | The application logic is written in Python & Django. The LIBVIRT Python bindings are used to interacting with the underlying hypervisor. 41 | 42 | Installation (Only web panel) 43 | ----------------------------- 44 | 45 | `Install WebVirtMgr `_ 46 | 47 | 48 | Setup host server (Server for VM's) 49 | ----------------------------------- 50 | 51 | `Setup Host Server `_ 52 | 53 | License 54 | ******* 55 | 56 | WebVirtMgr is licensed under the `Apache Licence, Version 2.0 `_. 57 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure(2) do |config| 5 | config.vm.box = "chef/ubuntu-14.04" 6 | config.vm.hostname = "webvirtmgr" 7 | config.vm.network "private_network", ip: "192.168.33.10" 8 | config.vm.provision "shell", inline: <<-SHELL 9 | sudo sh /vagrant/conf/libvirt-bootstrap.sh 10 | sudo sed -i 's/auth_tcp = \"sasl\"/auth_tcp = \"none\"/g' /etc/libvirt/libvirtd.conf 11 | sudo service libvirt-bin restart 12 | sudo apt-get -y install python-pip python-dev python-libvirt python-libxml2 13 | sudo pip install -r /vagrant/dev-requirements.txt 14 | SHELL 15 | end 16 | -------------------------------------------------------------------------------- /conf/conf.d/webvirtmgr-console: -------------------------------------------------------------------------------- 1 | WEBVIRTMGR_DAEMON="/var/www/webvirtmgr/console/webvirtmgr-console" 2 | WEBVIRTMGR_LOCK_DIR="/var/lock/webvirtmgr" 3 | WEBVIRTMGR_USER="apache" 4 | WEBVIRTMGR_GROUP="apache" 5 | -------------------------------------------------------------------------------- /conf/init/webvirtmgr-console.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WebVirtMgr NoVNC proxy 3 | 4 | [Service] 5 | ExecStart=/usr/lib/python2.7/site-packages/webvirtmgr/console/webvirtmgr-console 6 | Type=simple 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /conf/init/webvirtmgr-redhat.conf: -------------------------------------------------------------------------------- 1 | description "WebVirtManager" 2 | author "Edoardo Spadoni " 3 | 4 | start on started libvirtd 5 | stop on stopped libvirtd 6 | 7 | script 8 | export HOME="/usr/lib/python2.6/site-packages/webvirtmgr" 9 | export PYTHONPATH="$PYTHONPATH:/usr/lib/python2.6/site-packages/Django-1.5.6-py2.6.egg" 10 | export DJANGO_SETTINGS_MODULE="webvirtmgr.settings" 11 | exec $HOME/manage.py run_gunicorn -c $HOME/conf/gunicorn.conf.py 12 | end script 13 | -------------------------------------------------------------------------------- /conf/init/webvirtmgr.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WebVirtMgr Gunicorn Server 3 | 4 | [Service] 5 | WorkingDirectory=/usr/lib/python2.7/site-packages/webvirtmgr 6 | ExecStart=/usr/bin/python /usr/lib/python2.7/site-packages/webvirtmgr/manage.py run_gunicorn -c /usr/lib/python2.7/site-packages/webvirtmgr/conf/gunicorn.conf.py 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /conf/initd/webvirtmgr-console-arch: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=WebVirtMgr NoVNC proxy 3 | 4 | [Service] 5 | EnvironmentFile=/etc/conf.d/webvirtmgr-console 6 | Environment=PYTHONPATH=/usr/share/novnc/utils/ 7 | User=http 8 | Group=http 9 | ExecStart=/var/www/webvirtmgr/console/webvirtmgr-console 10 | Type=simple 11 | 12 | [Install] 13 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /conf/initd/webvirtmgr-console-gentoo: -------------------------------------------------------------------------------- 1 | #!/sbin/runscript 2 | # Copyright 2013 Joachim Langenbach 3 | # Distributed under the terms of the GNU General Public License v2 4 | 5 | description="WebVirtMgr NoVNC proxy" 6 | NAME=webvirtmgr-console 7 | PIDFILE=/var/run/$NAME.pid 8 | 9 | checkconfig() { 10 | # Exit if the package is not installed 11 | if [ ! -x ${WEBVIRTMGR_DAEMON} ]; then 12 | eerror "Daemon ${WEBVIRTMGR_DAEMON} not found" 13 | return 1 14 | fi 15 | 16 | # create lock dir 17 | if [ ! -x ${WEBVIRTMGR_LOCK_DIR} ]; then 18 | mkdir -p ${WEBVIRTMGR_LOCK_DIR} 19 | chown ${WEBVIRTMGR_USER} ${WEBVIRTMGR_LOCK_DIR} 20 | if [ ! -x ${WEBVIRTMGR_LOCK_DIR} ]; then 21 | eerror "Could not create lock dir ${WEBVIRTMGR_LOCK_DIR}" 22 | return 1 23 | fi 24 | fi 25 | } 26 | 27 | start() { 28 | checkconfig || return 1 29 | ebegin "Starting ${description}" 30 | start-stop-daemon --start --background --quiet --user "${WEBVIRTMGR_USER}" --group "${WEBVIRTMGR_GROUP}" --make-pidfile --pidfile "${PIDFILE}" \ 31 | --name "${WEBVIRTMGR_DAEMON}" 32 | eend ${?} 33 | } 34 | 35 | stop() { 36 | checkconfig || return 1 37 | ebegin "Stopping ${description}" 38 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile ${PIDFILE} 39 | RETVAL="$?" 40 | rm -f ${PIDFILE} 41 | eend ${RETVAL} 42 | } 43 | -------------------------------------------------------------------------------- /conf/initd/webvirtmgr-console-redhat: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # webvirtmgr-console WebVirtMgr Console noVNC Proxy 4 | # 5 | # chkconfig: - 98 02 6 | # description: WebVirtMgr Console noVCN Proxy Server 7 | 8 | ### BEGIN INIT INFO 9 | # Provides: 10 | # Required-Start: $remote_fs $network $syslog 11 | # Required-Stop: $remote_fs $syslog 12 | # Default-Stop: 0 1 6 13 | # Short-Description: WebVirtMgr Console noVNC Proxy 14 | # Description: WebVirtMgr Console noVNC Proxy Server 15 | ### END INIT INFO 16 | 17 | . /etc/rc.d/init.d/functions 18 | 19 | prog="webvirtmgr-console" 20 | exec="/var/www/webvirtmgr/console/$prog" 21 | lockfile="/var/lock/subsys/$prog" 22 | pidfile="/var/run/webvirtmgr/$prog.pid" 23 | logfile="/var/log/$prog.log" 24 | username="nginx" 25 | 26 | # load webvirtmgr-console preferences 27 | if [ -f /etc/sysconfig/webvirtmgr-console ]; then 28 | . /etc/sysconfig/webvirtmgr-console 29 | fi 30 | 31 | if [ ! -d /var/run/webvirtmgr ]; then 32 | mkdir /var/run/webvirtmgr 33 | chown $username /var/run/webvirtmgr 34 | fi 35 | 36 | start() { 37 | [ -x $exec ] || exit 5 38 | 39 | echo -n $"Starting $prog: " 40 | export PYTHONPATH=$PYTHONPATH:/usr/lib/python2.6/site-packages/Django-1.5.6-py2.6.egg 41 | daemon --user $username --pidfile $pidfile "$exec &>/dev/null & echo \$! > $pidfile" 42 | retval=$? 43 | echo 44 | [ $retval -eq 0 ] && touch $lockfile 45 | return $retval 46 | } 47 | 48 | stop() { 49 | echo -n $"Stopping $prog: " 50 | killproc -p $pidfile $exec 51 | retval=$? 52 | echo 53 | [ $retval -eq 0 ] && rm -f $lockfile 54 | return $retval 55 | } 56 | 57 | restart() { 58 | stop 59 | start 60 | } 61 | 62 | reload() { 63 | restart 64 | } 65 | 66 | force_reload() { 67 | restart 68 | } 69 | 70 | rh_status() { 71 | status -p $pidfile $prog 72 | } 73 | 74 | rh_status_q() { 75 | rh_status >/dev/null 2>&1 76 | } 77 | 78 | 79 | case "$1" in 80 | start) 81 | rh_status_q && exit 0 82 | $1 83 | ;; 84 | stop) 85 | rh_status_q || exit 0 86 | $1 87 | ;; 88 | restart) 89 | $1 90 | ;; 91 | reload) 92 | rh_status_q || exit 7 93 | $1 94 | ;; 95 | force-reload) 96 | force_reload 97 | ;; 98 | status) 99 | rh_status 100 | ;; 101 | condrestart|try-restart) 102 | rh_status_q || exit 0 103 | restart 104 | ;; 105 | *) 106 | echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" 107 | exit 2 108 | esac 109 | exit $? 110 | -------------------------------------------------------------------------------- /conf/initd/webvirtmgr-console-suse: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # webvirtmgr-console WebVirtMgr Console noVNC Proxy 4 | # 5 | # chkconfig: - 98 02 6 | # description: WebVirtMgr Console noVCN Proxy Server 7 | 8 | ### BEGIN INIT INFO 9 | # Provides: 10 | # Required-Start: $remote_fs $network $syslog 11 | # Required-Stop: $remote_fs $syslog 12 | # Default-Stop: 0 1 6 13 | # Short-Description: WebVirtMgr Console noVNC Proxy 14 | # Description: WebVirtMgr Console noVNC Proxy Server 15 | ### END INIT INFO 16 | 17 | 18 | # Shell functions sourced from /etc/rc.status: 19 | # rc_check check and set local and overall rc status 20 | # rc_status check and set local and overall rc status 21 | # rc_status -v ditto but be verbose in local rc status 22 | # rc_status -v -r ditto and clear the local rc status 23 | # rc_failed set local and overall rc status to failed 24 | # rc_failed set local and overall rc status to 25 | # rc_reset clear local rc status (overall remains) 26 | # rc_exit exit appropriate to overall rc status 27 | . /etc/rc.status 28 | 29 | 30 | 31 | prog="webvirtmgr-console" 32 | exec="/srv/www/htdocs/webvirtmgr/console/$prog" 33 | lockfile="/var/lock/subsys/$prog" 34 | pidfile="/var/run/webvirtmgr/$prog.pid" 35 | logfile="/var/log/$prog.log" 36 | username="nginx" 37 | 38 | 39 | test -x $exec || exit 5 40 | PIDFILE=$pidfile 41 | 42 | if [ ! -d /var/run/webvirtmgr ]; then 43 | mkdir /var/run/webvirtmgr 44 | chown $username /var/run/webvirtmgr 45 | fi 46 | 47 | 48 | # First reset status of this service 49 | rc_reset 50 | 51 | # Return values acc. to LSB for all commands but status: 52 | # 0 - success 53 | # 1 - generic or unspecified error 54 | # 2 - invalid or excess argument(s) 55 | # 3 - unimplemented feature (e.g. "reload") 56 | # 4 - insufficient privilege 57 | # 5 - program is not installed 58 | # 6 - program is not configured 59 | # 7 - program is not running 60 | # 61 | # Note that starting an already running service, stopping 62 | # or restarting a not-running service as well as the restart 63 | # with force-reload (in case signalling is not supported) are 64 | # considered a success. 65 | 66 | start() { 67 | 68 | echo -n $"Starting $prog: " 69 | ## Start daemon with startproc(8). If this fails 70 | ## the echo return value is set appropriate. 71 | 72 | # NOTE: startproc return 0, even if service is 73 | # already running to match LSB spec. 74 | startproc -p $PIDFILE $exec 75 | 76 | #daemon --user $username --pidfile $pidfile "$exec &>/dev/null & echo \$! > $pidfile" 77 | # Remember status and be verbose 78 | rc_status -v 79 | 80 | } 81 | 82 | stop() { 83 | echo -n $"Stopping $prog: " 84 | killproc -TERM -p $PIDFILE $exec 85 | 86 | } 87 | 88 | 89 | case "$1" in 90 | start) 91 | # Remember status and be verbose 92 | start 93 | rc_status -v 94 | ;; 95 | stop) 96 | # Remember status and be verbose 97 | stop 98 | rc_status -v 99 | ;; 100 | restart) 101 | $0 stop 102 | sleep 5 103 | $0 start 104 | # Remember status and be verbose 105 | rc_status -v 106 | ;; 107 | reload) 108 | $0 restart 109 | rc_status -v 110 | ;; 111 | force-reload) 112 | $0 restart 113 | rc_status -v 114 | ;; 115 | status) 116 | rc_status -v 117 | ;; 118 | condrestart|try-restart) 119 | $0 restart 120 | rc_status -v 121 | ;; 122 | *) 123 | echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" 124 | exit 2 125 | esac 126 | exit $? 127 | -------------------------------------------------------------------------------- /conf/initd/webvirtmgr-console-ubuntu: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: nova-novncproxy 4 | # Required-Start: $network $local_fs $remote_fs $syslog 5 | # Required-Stop: $remote_fs 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Nova NoVNC proxy 9 | # Description: Nova NoVNC proxy 10 | ### END INIT INFO 11 | 12 | 13 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 15 | DESC="WebVirtMgr NoVNC proxy" 16 | NAME='webvirtmgr-console' 17 | DAEMON_PREFIX='/var/www/webvirtmgr/console' 18 | PIDFILE="/run/${NAME}.pid" 19 | SCRIPTNAME="/etc/init.d/${NAME}" 20 | LOCK_DIR="/run/lock/${NAME}" 21 | USER='www-data' 22 | GROUP='www-data' 23 | 24 | # read in defaults if available 25 | [ -f "/etc/default/${NAME}" ] && . "/etc/default/${NAME}" 26 | 27 | DAEMON="${DAEMON_PREFIX}/${NAME}" 28 | 29 | # Exit if the package is not installed 30 | [ -x $DAEMON ] || exit 0 31 | 32 | mkdir -p ${LOCK_DIR} 33 | chown "${USER}:${GROUP}" ${LOCK_DIR} 34 | 35 | . /lib/lsb/init-functions 36 | 37 | do_start() 38 | { 39 | start-stop-daemon --start --background --quiet --chuid "${USER}:${GROUP}" --make-pidfile --pidfile $PIDFILE --startas $DAEMON --test > /dev/null \ 40 | || return 1 41 | start-stop-daemon --start --background --quiet --chuid "${USER}:${GROUP}" --make-pidfile --pidfile $PIDFILE --startas $DAEMON -- \ 42 | $DAEMON_ARGS \ 43 | || return 2 44 | } 45 | 46 | do_stop() 47 | { 48 | start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE 49 | RETVAL="$?" 50 | rm -f $PIDFILE 51 | return "$RETVAL" 52 | } 53 | 54 | case "$1" in 55 | start) 56 | log_daemon_msg "Starting $DESC " "$NAME" 57 | do_start 58 | case "$?" in 59 | 0|1) log_end_msg 0 ;; 60 | 2) log_end_msg 1 ;; 61 | esac 62 | ;; 63 | stop) 64 | log_daemon_msg "Stopping $DESC" "$NAME" 65 | do_stop 66 | case "$?" in 67 | 0|1) log_end_msg 0 ;; 68 | 2) log_end_msg 1 ;; 69 | esac 70 | ;; 71 | status) 72 | status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? 73 | ;; 74 | restart|force-reload) 75 | log_daemon_msg "Restarting $DESC" "$NAME" 76 | do_stop 77 | case "$?" in 78 | 0|1) 79 | do_start 80 | case "$?" in 81 | 0) log_end_msg 0 ;; 82 | 1) log_end_msg 1 ;; # Old process is still running 83 | *) log_end_msg 1 ;; # Failed to start 84 | esac 85 | ;; 86 | *) 87 | # Failed to stop 88 | log_end_msg 1 89 | ;; 90 | esac 91 | ;; 92 | *) 93 | echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 94 | exit 3 95 | ;; 96 | esac 97 | 98 | : 99 | -------------------------------------------------------------------------------- /conf/saltstack/centos_rhel/salt/libvirt/init.sls: -------------------------------------------------------------------------------- 1 | qemu-kvm: 2 | pkg.installed 3 | 4 | libvirt: 5 | pkg.installed 6 | 7 | /etc/libvirt/libvirtd.conf: 8 | file.managed: 9 | - user: root 10 | - group: root 11 | - mode: 644 12 | - source: salt://libvirt/libvirtd.conf 13 | - require: 14 | - pkg: libvirt 15 | 16 | /etc/sysconfig/libvirtd: 17 | file.managed: 18 | - user: root 19 | - group: root 20 | - mode: 644 21 | - source: salt://libvirt/libvirtd 22 | 23 | /etc/libvirt/qemu.conf: 24 | file.managed: 25 | - user: root 26 | - group: root 27 | - mode: 644 28 | - source: salt://libvirt/qemu.conf 29 | - require: 30 | - pkg: qemu-kvm 31 | 32 | /etc/sysconfig/iptables: 33 | file.managed: 34 | - user: root 35 | - group: root 36 | - mode: 644 37 | - source: salt://libvirt/iptables 38 | 39 | iptables: 40 | service: 41 | - dead 42 | - running 43 | 44 | libvirtd: 45 | service: 46 | - running 47 | - enable: True 48 | 49 | ksm: 50 | service: 51 | - running 52 | - enable: True 53 | 54 | ksmtuned: 55 | service: 56 | - running 57 | - enable: True 58 | -------------------------------------------------------------------------------- /conf/saltstack/centos_rhel/salt/libvirt/iptables: -------------------------------------------------------------------------------- 1 | *nat 2 | :PREROUTING ACCEPT [0:0] 3 | :POSTROUTING ACCEPT [0:0] 4 | :OUTPUT ACCEPT [0:0] 5 | COMMIT 6 | *filter 7 | :INPUT ACCEPT [0:0] 8 | :FORWARD ACCEPT [0:0] 9 | :OUTPUT ACCEPT [0:0] 10 | -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 11 | -A INPUT -p icmp -j ACCEPT 12 | -A INPUT -i lo -j ACCEPT 13 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT 14 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 5900:5930 -j ACCEPT 15 | #-A INPUT -m state --state NEW -m tcp -p tcp --dport 6800 -j ACCEPT 16 | -A INPUT -m state --state NEW -m tcp -p tcp --dport 16509 -j ACCEPT 17 | -A INPUT -j REJECT --reject-with icmp-host-prohibited 18 | -A FORWARD -i br0 -o br0 -j ACCEPT 19 | -A FORWARD -j REJECT --reject-with icmp-host-prohibited 20 | COMMIT 21 | -------------------------------------------------------------------------------- /conf/saltstack/centos_rhel/salt/libvirt/libvirtd: -------------------------------------------------------------------------------- 1 | # Override the default config file 2 | # NOTE: This setting is no longer honoured if using 3 | # systemd. Set '--config /etc/libvirt/libvirtd.conf' 4 | # in LIBVIRTD_ARGS instead. 5 | #LIBVIRTD_CONFIG=/etc/libvirt/libvirtd.conf 6 | 7 | # Listen for TCP/IP connections 8 | # NB. must setup TLS/SSL keys prior to using this 9 | LIBVIRTD_ARGS="--listen" 10 | 11 | # Override Kerberos service keytab for SASL/GSSAPI 12 | #KRB5_KTNAME=/etc/libvirt/krb5.tab 13 | 14 | # Override the QEMU/SDL default audio driver probing when 15 | # starting virtual machines using SDL graphics 16 | # 17 | # NB these have no effect for VMs using VNC, unless vnc_allow_host_audio 18 | # is enabled in /etc/libvirt/qemu.conf 19 | #QEMU_AUDIO_DRV=sdl 20 | # 21 | #SDL_AUDIODRIVER=pulse 22 | 23 | # Override the maximum number of opened files 24 | #LIBVIRTD_NOFILES_LIMIT=2048 25 | -------------------------------------------------------------------------------- /conf/saltstack/centos_rhel/salt/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': 3 | - libvirt 4 | -------------------------------------------------------------------------------- /console/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/console/__init__.py -------------------------------------------------------------------------------- /console/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANC0jrLeaeBghQL8 3 | 93iU8Q/SiAKkS+qKOBT8xSu2nTkK60prhlrdPVMDx1ZbRg3MKwsCOVJX8EMj/VSX 4 | rIpUbXXOtDEakD1glbm8M8wtiYlPBA2mRdTGOfehUu3bFubBMmi67GsWzZna9+0R 5 | pbCEM7WxosS6DHxOKakvG128c1w5AgMBAAECf38th8d3wQz5xBQOEr9oA32gzUDO 6 | 3XQOxh4D6iK5JRWdkcPHwhecucHci5wUApxlzdTnhganj8FaLQVfZ8Tjrc8B36BR 7 | DJ4eGOr7kPwIrEGRfDJKJuIKhbDAucQJoT7nwwUjWC7Rxwo33JOXAREi/F+mP7U0 8 | euLY5w8HF/LFZvECQQD2TmKSCcOFAYfhuwGLXwa5YpxLKTc0mqSdUiHOKWZZDaw8 9 | glSvGa11IrS9LQardOluMlRl6Ur1NatABbGfa0Z7AkEA2OtUyQ7J/oIV0xzyrJMP 10 | USHUL9+dQIwSkHLRXLBRlHBYjjn4mP+kMx9nLcZa2KigjeelvNrWjdALLTGb7N3j 11 | 2wJBAJB0AFNt7oF/GFZG+uzmWmc3cjqSfZIYcBrkJHdoyGHKJw2F0myvPo/IPwAD 12 | /DlqY80BL4ZlgFS3mSv/KuE4ZSkCQQCYcjrx2iznarC/33ZFBk0bbbTbuEGhnU9m 13 | qOquQ1PyCEqUGfW2QOM68nTm3KH35fqAAC6gXQOwQme4aUb13rNFAkEA2BPFLqKQ 14 | C7zp2RnGzgKyMzd0Frdki4naq1zo7jk7pziGb4RFZa+xiC5qzK4E8YnJqC2zmdQ1 15 | mxxJ1iHfKROzfg== 16 | -----END PRIVATE KEY----- 17 | -----BEGIN CERTIFICATE----- 18 | MIIC3DCCAkWgAwIBAgIJAO8zN6F9KJH2MA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD 19 | VQQGEwJVQTEUMBIGA1UECAwLWmFwb3JvemhreWExEzARBgNVBAcMClphcG9yb3po 20 | eWUxEzARBgNVBAoMCldlYlZpcnRNZ3IxEDAOBgNVBAMMB1JldHNwZW4xJTAjBgkq 21 | hkiG9w0BCQEWFnN1cHBvcnRAd2VidmlydG1nci5uZXQwHhcNMTQwMTMxMDkxODIw 22 | WhcNMjQwMTI5MDkxODIwWjCBhjELMAkGA1UEBhMCVUExFDASBgNVBAgMC1phcG9y 23 | b3poa3lhMRMwEQYDVQQHDApaYXBvcm96aHllMRMwEQYDVQQKDApXZWJWaXJ0TWdy 24 | MRAwDgYDVQQDDAdSZXRzcGVuMSUwIwYJKoZIhvcNAQkBFhZzdXBwb3J0QHdlYnZp 25 | cnRtZ3IubmV0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQtI6y3mngYIUC 26 | /Pd4lPEP0ogCpEvqijgU/MUrtp05CutKa4Za3T1TA8dWW0YNzCsLAjlSV/BDI/1U 27 | l6yKVG11zrQxGpA9YJW5vDPMLYmJTwQNpkXUxjn3oVLt2xbmwTJouuxrFs2Z2vft 28 | EaWwhDO1saLEugx8TimpLxtdvHNcOQIDAQABo1AwTjAdBgNVHQ4EFgQUQTrOS0h7 29 | +5A4ERA89F3dcQbbDA8wHwYDVR0jBBgwFoAUQTrOS0h7+5A4ERA89F3dcQbbDA8w 30 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBQ29yr5Mw+9KzukiGiKtPZ 31 | H3pDncNiWreOtg8OZNpQGPF2ShOOL18oGnxs9a/+IxzCycV4fGpg4p0XYQCUhLMl 32 | MBVXrdTUvX6LWw826BzWjPAQC+tOUJZ8kri03sa9QvIsoND08ku8/USfPOva52sl 33 | z309reuxZ0ERYApv/3UdpQ== 34 | -----END CERTIFICATE----- 35 | -------------------------------------------------------------------------------- /console/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /console/tunnel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This class provide from VirtManager project, from console.py 4 | # file. 5 | # 6 | # Copyright (C) 2006-2008 Red Hat, Inc. 7 | # Copyright (C) 2006 Daniel P. Berrange 8 | # Copyright (C) 2010 Marc-André Lureau 9 | # 10 | # This program is free software; you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation; either version 2 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program; if not, write to the Free Software 22 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 23 | # MA 02110-1301 USA. 24 | # 25 | 26 | import os 27 | import socket 28 | import signal 29 | import logging 30 | 31 | 32 | class Tunnel(object): 33 | def __init__(self): 34 | self.outfd = None 35 | self.errfd = None 36 | self.pid = None 37 | 38 | def open(self, connhost, connuser, connport, gaddr, gport, gsocket): 39 | if self.outfd is not None: 40 | return -1 41 | 42 | # Build SSH cmd 43 | argv = ["ssh", "ssh"] 44 | if connport: 45 | argv += ["-p", str(connport)] 46 | 47 | if connuser: 48 | argv += ['-l', connuser] 49 | 50 | argv += [connhost] 51 | 52 | # Build 'nc' command run on the remote host 53 | # 54 | # This ugly thing is a shell script to detect availability of 55 | # the -q option for 'nc': debian and suse based distros need this 56 | # flag to ensure the remote nc will exit on EOF, so it will go away 57 | # when we close the VNC tunnel. If it doesn't go away, subsequent 58 | # VNC connection attempts will hang. 59 | # 60 | # Fedora's 'nc' doesn't have this option, and apparently defaults 61 | # to the desired behavior. 62 | # 63 | if gsocket: 64 | nc_params = "-U %s" % gsocket 65 | else: 66 | nc_params = "%s %s" % (gaddr, gport) 67 | 68 | nc_cmd = ( 69 | """nc -q 2>&1 | grep "requires an argument" >/dev/null;""" 70 | """if [ $? -eq 0 ] ; then""" 71 | """ CMD="nc -q 0 %(nc_params)s";""" 72 | """else""" 73 | """ CMD="nc %(nc_params)s";""" 74 | """fi;""" 75 | """eval "$CMD";""" % 76 | {'nc_params': nc_params}) 77 | 78 | argv.append("sh -c") 79 | argv.append("'%s'" % nc_cmd) 80 | 81 | argv_str = reduce(lambda x, y: x + " " + y, argv[1:]) 82 | logging.debug("Creating SSH tunnel: %s", argv_str) 83 | 84 | fds = socket.socketpair() 85 | errorfds = socket.socketpair() 86 | 87 | pid = os.fork() 88 | if pid == 0: 89 | fds[0].close() 90 | errorfds[0].close() 91 | 92 | os.close(0) 93 | os.close(1) 94 | os.close(2) 95 | os.dup(fds[1].fileno()) 96 | os.dup(fds[1].fileno()) 97 | os.dup(errorfds[1].fileno()) 98 | os.execlp(*argv) 99 | os._exit(1) 100 | else: 101 | fds[1].close() 102 | errorfds[1].close() 103 | 104 | logging.debug("Tunnel PID=%d OUTFD=%d ERRFD=%d", 105 | pid, fds[0].fileno(), errorfds[0].fileno()) 106 | errorfds[0].setblocking(0) 107 | 108 | self.outfd = fds[0] 109 | self.errfd = errorfds[0] 110 | self.pid = pid 111 | 112 | fd = fds[0].fileno() 113 | if fd < 0: 114 | raise SystemError("can't open a new tunnel: fd=%d" % fd) 115 | return fd 116 | 117 | def close(self): 118 | if self.outfd is None: 119 | return 120 | 121 | logging.debug("Shutting down tunnel PID=%d OUTFD=%d ERRFD=%d", 122 | self.pid, self.outfd.fileno(), 123 | self.errfd.fileno()) 124 | self.outfd.close() 125 | self.outfd = None 126 | self.errfd.close() 127 | self.errfd = None 128 | 129 | os.kill(self.pid, signal.SIGKILL) 130 | self.pid = None 131 | 132 | def get_err_output(self): 133 | errout = "" 134 | while True: 135 | try: 136 | new = self.errfd.recv(1024) 137 | except: 138 | break 139 | 140 | if not new: 141 | break 142 | 143 | errout += new 144 | 145 | return errout 146 | -------------------------------------------------------------------------------- /console/views.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.shortcuts import render_to_response 4 | from django.template import RequestContext 5 | from django.http import HttpResponseRedirect 6 | from django.core.urlresolvers import reverse 7 | 8 | from instance.models import Instance 9 | from vrtManager.instance import wvmInstance 10 | 11 | from webvirtmgr.settings import WS_PORT 12 | from webvirtmgr.settings import WS_PUBLIC_HOST 13 | 14 | 15 | def console(request): 16 | """ 17 | Console instance block 18 | """ 19 | if not request.user.is_authenticated(): 20 | return HttpResponseRedirect(reverse('login')) 21 | 22 | if request.method == 'GET': 23 | token = request.GET.get('token', '') 24 | 25 | try: 26 | temptoken = token.split('-', 1) 27 | host = int(temptoken[0]) 28 | uuid = temptoken[1] 29 | instance = Instance.objects.get(compute_id=host, uuid=uuid) 30 | conn = wvmInstance(instance.compute.hostname, 31 | instance.compute.login, 32 | instance.compute.password, 33 | instance.compute.type, 34 | instance.name) 35 | console_type = conn.get_console_type() 36 | console_websocket_port = conn.get_console_websocket_port() 37 | console_passwd = conn.get_console_passwd() 38 | except: 39 | console_type = None 40 | console_websocket_port = None 41 | console_passwd = None 42 | 43 | ws_port = console_websocket_port if console_websocket_port else WS_PORT 44 | ws_host = WS_PUBLIC_HOST if WS_PUBLIC_HOST else request.get_host() 45 | 46 | if ':' in ws_host: 47 | ws_host = re.sub(':[0-9]+', '', ws_host) 48 | 49 | if console_type == 'vnc': 50 | response = render_to_response('console-vnc.html', locals(), 51 | context_instance=RequestContext(request)) 52 | elif console_type == 'spice': 53 | response = render_to_response('console-spice.html', locals(), 54 | context_instance=RequestContext(request)) 55 | else: 56 | response = "Console type %s no support" % console_type 57 | 58 | response.set_cookie('token', token) 59 | return response 60 | -------------------------------------------------------------------------------- /create/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/create/__init__.py -------------------------------------------------------------------------------- /create/fixtures/initial_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "create.Flavor", 4 | "pk": 1, 5 | "fields": { 6 | "label": "micro", 7 | "vcpu": "1", 8 | "memory": "512", 9 | "disk": "20" 10 | } 11 | }, 12 | { 13 | "model": "create.Flavor", 14 | "pk": 2, 15 | "fields": { 16 | "label": "mini", 17 | "vcpu": "2", 18 | "memory": "1024", 19 | "disk": "30" 20 | } 21 | }, 22 | { 23 | "model": "create.Flavor", 24 | "pk": 3, 25 | "fields": { 26 | "label": "small", 27 | "vcpu": "2", 28 | "memory": "2048", 29 | "disk": "40" 30 | } 31 | }, 32 | { 33 | "model": "create.Flavor", 34 | "pk": 4, 35 | "fields": { 36 | "label": "medium", 37 | "vcpu": "2", 38 | "memory": "4096", 39 | "disk": "60" 40 | } 41 | }, 42 | { 43 | "model": "create.Flavor", 44 | "pk": 5, 45 | "fields": { 46 | "label": "large", 47 | "vcpu": "4", 48 | "memory": "8192", 49 | "disk": "80" 50 | } 51 | }, 52 | { 53 | "model": "create.Flavor", 54 | "pk": 6, 55 | "fields": { 56 | "label": "xlarge", 57 | "vcpu": "8", 58 | "memory": "16384", 59 | "disk": "160" 60 | } 61 | } 62 | ] -------------------------------------------------------------------------------- /create/forms.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django import forms 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | from create.models import Flavor 7 | 8 | 9 | class FlavorAddForm(forms.Form): 10 | label = forms.CharField(label="Name", 11 | error_messages={'required': _('No flavor name has been entered')}, 12 | max_length=20) 13 | vcpu = forms.IntegerField(label="VCPU", 14 | error_messages={'required': _('No VCPU has been entered')}, ) 15 | disk = forms.IntegerField(label="HDD", 16 | error_messages={'required': _('No HDD image has been entered')}, ) 17 | memory = forms.IntegerField(label="RAM", 18 | error_messages={'required': _('No RAM size has been entered')}, ) 19 | 20 | def clean_name(self): 21 | label = self.cleaned_data['label'] 22 | have_symbol = re.match('^[a-zA-Z0-9._-]+$', label) 23 | if not have_symbol: 24 | raise forms.ValidationError(_('The flavor name must not contain any special characters')) 25 | elif len(label) > 20: 26 | raise forms.ValidationError(_('The flavor name must not exceed 20 characters')) 27 | try: 28 | Flavor.objects.get(label=label) 29 | except Flavor.DoesNotExist: 30 | return label 31 | raise forms.ValidationError(_('Flavor name is already use')) 32 | 33 | 34 | class NewVMForm(forms.Form): 35 | name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')}, 36 | max_length=20) 37 | vcpu = forms.IntegerField(error_messages={'required': _('No VCPU has been entered')}) 38 | host_model = forms.BooleanField(required=False) 39 | disk = forms.IntegerField(required=False) 40 | memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')}) 41 | networks = forms.CharField(error_messages={'required': _('No Network pool has been choice')}) 42 | storage = forms.CharField(max_length=20, required=False) 43 | template = forms.CharField(required=False) 44 | images = forms.CharField(required=False) 45 | cache_mode = forms.CharField(error_messages={'required': _('Please select HDD cache mode')}) 46 | hdd_size = forms.IntegerField(required=False) 47 | meta_prealloc = forms.BooleanField(required=False) 48 | virtio = forms.BooleanField(required=False) 49 | mac = forms.CharField(required=False) 50 | 51 | def clean_name(self): 52 | name = self.cleaned_data['name'] 53 | have_symbol = re.match('^[a-zA-Z0-9._-]+$', name) 54 | if not have_symbol: 55 | raise forms.ValidationError(_('The name of the virtual machine must not contain any special characters')) 56 | elif len(name) > 20: 57 | raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters')) 58 | return name 59 | -------------------------------------------------------------------------------- /create/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Flavor(models.Model): 5 | label = models.CharField(max_length=12) 6 | memory = models.IntegerField() 7 | vcpu = models.IntegerField() 8 | disk = models.IntegerField() 9 | 10 | def __unicode__(self): 11 | return self.name 12 | -------------------------------------------------------------------------------- /create/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /deploy/fabric/README.md: -------------------------------------------------------------------------------- 1 | # WebVirtMgr Fabric/Fabtools Deployment 2 | 3 | ## Introduction 4 | 5 | This directory includes fabric script that helps you automate WebVirtMgr deployment on your server(s) with one command line. 6 | 7 | The fabric deployment script (fabfile.py) depends on: 8 | 9 | * [Fabric](http://www.fabfile.org/) 10 | * [Fabtools](http://fabtools.readthedocs.org/en/latest/api/index.html) 11 | 12 | ## Deployment 13 | 14 | This Deployment script has been tested on the following distributions: 15 | 16 | * Ubuntu 12.04 and above 17 | * Debian 7.5 18 | * Centos 6.4, 6.5 19 | * Fedora 20, 19 20 | 21 | ### Steps 22 | 23 | Install requirements (mainly Fabric and Fabtools) 24 | 25 | ``` 26 | $ cd deploy/fabric 27 | $ pip install -r fab_requirements.txt 28 | ``` 29 | 30 | Then invoke the deployment task via fabric (**deploy_webvirt**). 31 | 32 | Assuming deployment via **username** with **sudo** permission. 33 | 34 | ``` 35 | $ fab -H *host ip* -u *username* -p *password* deploy_webvirt 36 | ``` 37 | 38 | Deployment task will start its routine on the remote server, and will eventually prompt you with database initialization (Admin Information). 39 | 40 | It will look something similar to this: 41 | 42 | ``` 43 | [192.168.20.14] out: You just installed Django's auth system, which means you don't have any superusers defined. 44 | [192.168.20.14] out: Would you like to create one now? (yes/no): yes (*type yes*) 45 | [192.168.20.14] out: Username (leave blank to use 'root'): admin (*type username*) 46 | [192.168.20.14] out: Email address: admin@iaas.local (*type email*) 47 | [192.168.20.14] out: Password: (*type password*) 48 | [192.168.20.14] out: Password (again): (*type password again*) 49 | [192.168.20.14] out: Superuser created successfully. 50 | [192.168.20.14] out: Installing custom SQL ... 51 | [192.168.20.14] out: Installing indexes ... 52 | [192.168.20.14] out: Installed 6 object(s) from 1 fixture(s) 53 | ``` 54 | 55 | The script will continue with the rest of the steps required to finalize the deployment. 56 | 57 | A successful installation will finish with the following message: 58 | 59 | ``` 60 | Done. 61 | Disconnecting from 192.168.20.14... done. 62 | ``` 63 | 64 | Now your installation is Done. You can browse to your WebVirtMgr Host and login via the User name and Password you entered during the setup. 65 | 66 | ### Notes 67 | 68 | #### System Updates 69 | 70 | Try to update your system before WebVirtMgr installation. 71 | 72 | #### Fedora & Centos *requiretty* 73 | 74 | Fedora and Centos add a default restriction **requiretty** in **sudoers** file. 75 | 76 | This restriction can cause an error like: 77 | 78 | ``` 79 | [192.168.20.14] out: sudo: sorry, you must have a tty to run sudo 80 | ``` 81 | 82 | In order to overcome this issue, you will need to disable **requiretty** (at least temporarily) 83 | 84 | Run: 85 | 86 | ``` 87 | $ visudo 88 | ``` 89 | 90 | and comment out 91 | 92 | ``` 93 | # Defaults requiretty 94 | ``` 95 | 96 | #### Default Settings 97 | 98 | In case you want to modify the default deployment settings, you can edit **settings.py**. 99 | 100 | For example you can edit the **SERVER_NAME** that will be inserted in *nginx.conf* Nginx site. 101 | 102 | ```python 103 | # NGINX server name 104 | SERVER_NAME = "iaas.local" 105 | ``` 106 | 107 | You can also edit any of the templates found in *templates* directory 108 | 109 | - **nginx.conf**: webvirtmgr Nginx site. 110 | - **original.nginx.conf**: default Nginx configuration (Applicable only on Fedora) 111 | - **webvirtmgr.ini**: supervisord config file for WebVirtMgr Gunicorn sever. 112 | -------------------------------------------------------------------------------- /deploy/fabric/fab_requirements.txt: -------------------------------------------------------------------------------- 1 | Fabric==1.8.2 2 | fabtools==0.17.0 3 | -------------------------------------------------------------------------------- /deploy/fabric/fabfile.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fabric Deployment script for WebvirtMgr 3 | """ 4 | 5 | from fabric.api import task 6 | from fabtools.system import distrib_id 7 | 8 | from utils import install_system_packages 9 | from utils import get_webvirt 10 | from utils import configure_nginx 11 | from utils import configure_novnc 12 | from utils import configure_supervisor 13 | 14 | 15 | @task 16 | def deploy_webvirt(): 17 | """ 18 | Install Webvirt on a Remote server 19 | """ 20 | distro = distrib_id() 21 | install_system_packages(distro) 22 | get_webvirt() 23 | configure_nginx(distro) 24 | configure_novnc(distro) 25 | configure_supervisor(distro) 26 | 27 | 28 | @task 29 | def update_webvirt(): 30 | """ 31 | Update Webvirt on a Remote server 32 | Webvirt should be already installed 33 | """ 34 | distro = distrib_id() 35 | get_webvirt() 36 | configure_nginx(distro) 37 | configure_supervisor(distro) 38 | -------------------------------------------------------------------------------- /deploy/fabric/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Settings for deploying WebVirtMgr on a remote node 3 | Used by fabfile.py 4 | """ 5 | 6 | DEBIAN_PKGS = [ 7 | "git", "python-pip", "python-libvirt", "python-libxml2", "novnc", 8 | "supervisor", "nginx" 9 | ] 10 | 11 | FEDORA_PKGS = [ 12 | "git", "python-pip", "libvirt-python", "libxml2-python", "supervisor", 13 | "nginx", "python-websockify" 14 | ] 15 | 16 | CENTOS_PKGS = [ 17 | "git", "python-pip", "libvirt-python", "libxml2-python", "supervisor", 18 | "nginx", "python-websockify" 19 | ] 20 | # Extra Centos package 21 | CENTOS_EPEL = ( 22 | "epel-release-6-8.noarch", 23 | "http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm" 24 | ) 25 | 26 | REPO_URL = "https://github.com/retspen/webvirtmgr.git" 27 | INSTALL_PATH = "/var/www/" 28 | 29 | # NGINX server name 30 | SERVER_NAME = "webvirt.local" 31 | -------------------------------------------------------------------------------- /deploy/fabric/templates/README.md: -------------------------------------------------------------------------------- 1 | # Templates 2 | 3 | ## nginx.conf 4 | 5 | This is the WebVirtMgr *site* template that will be added to Nginx *conf.d* directory. 6 | 7 | ## original.nginx.conf 8 | 9 | This template will replace default *nginx.conf* (in */etc/nginx/* ) on Fedora deployment, in order to disable the default server (running on port 80)! 10 | 11 | ## webvirtmgr.ini 12 | 13 | This is the supervisord configuration for WebVirtMgr *Gunicorn* server. 14 | -------------------------------------------------------------------------------- /deploy/fabric/templates/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | 4 | server_name %(server_name)s; 5 | #access_log /var/log/nginx/webvirtmgr_access_log; 6 | 7 | location / { 8 | proxy_pass http://127.0.0.1:8000; 9 | proxy_set_header X-Real-IP $remote_addr; 10 | proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for; 11 | proxy_set_header Host $host; 12 | proxy_set_header X-Forwarded-Proto $remote_addr; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /deploy/fabric/templates/original.nginx.conf: -------------------------------------------------------------------------------- 1 | # For more information on configuration, see: 2 | # * Official English Documentation: http://nginx.org/en/docs/ 3 | # * Official Russian Documentation: http://nginx.org/ru/docs/ 4 | 5 | user nginx; 6 | worker_processes 4; 7 | 8 | error_log /var/log/nginx/error.log; 9 | #error_log /var/log/nginx/error.log notice; 10 | #error_log /var/log/nginx/error.log info; 11 | 12 | pid /run/nginx.pid; 13 | 14 | 15 | events { 16 | worker_connections 1024; 17 | } 18 | 19 | 20 | http { 21 | include /etc/nginx/mime.types; 22 | default_type application/octet-stream; 23 | 24 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 25 | '$status $body_bytes_sent "$http_referer" ' 26 | '"$http_user_agent" "$http_x_forwarded_for"'; 27 | 28 | access_log /var/log/nginx/access.log main; 29 | 30 | sendfile on; 31 | #tcp_nopush on; 32 | 33 | #keepalive_timeout 0; 34 | keepalive_timeout 65; 35 | 36 | #gzip on; 37 | 38 | # Load modular configuration files from the /etc/nginx/conf.d directory. 39 | # See http://nginx.org/en/docs/ngx_core_module.html#include 40 | # for more information. 41 | include /etc/nginx/conf.d/*.conf; 42 | 43 | index index.html index.htm; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /deploy/fabric/templates/webvirtmgr.ini: -------------------------------------------------------------------------------- 1 | [program:webvirtmgr] 2 | autorestart = True 3 | autostart = True 4 | command = /usr/bin/python /var/www/webvirtmgr/manage.py run_gunicorn -c /var/www/webvirtmgr/conf/gunicorn.conf.py 5 | directory = /var/www/webvirtmgr 6 | redirect_stderr = True 7 | stdout_logfile = /var/log/supervisor/webvirtmgr.log 8 | user = nginx 9 | -------------------------------------------------------------------------------- /deploy/fabric/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Fabric deployment script Utils 3 | """ 4 | 5 | import os 6 | 7 | import settings as fsettings # Fabric Deployment settings 8 | 9 | from fabric.api import cd, sudo 10 | from fabric.context_managers import settings 11 | from fabric.contrib.files import append, contains 12 | 13 | from fabtools import require, files 14 | from fabtools.rpm import is_installed 15 | from fabtools.supervisor import reload_config 16 | from fabtools.nginx import disable as disable_site 17 | from fabtools.python import install_requirements 18 | 19 | # Local repo path 20 | LOCAL_BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 21 | "..", "..")) 22 | 23 | 24 | def install_system_packages(distro): 25 | """ 26 | Install system packages based on the Linux distribution of the remote node 27 | """ 28 | if distro in ["Debian", "Ubuntu"]: 29 | require.deb.uptodate_index(max_age={'day': 7}) 30 | return require.deb.packages(fsettings.DEBIAN_PKGS) 31 | elif distro in ["CentOS", "RHEL"]: 32 | if not is_installed(fsettings.CENTOS_EPEL[0]): 33 | fsettings.CENTOS_PKGS.append(fsettings.CENTOS_EPEL[1]) 34 | return require.rpm.packages(fsettings.CENTOS_PKGS) 35 | elif distro in ["Fedora"]: 36 | return require.rpm.packages(fsettings.FEDORA_PKGS) 37 | 38 | raise RuntimeError("ERROR: Unrecognized OS!") 39 | 40 | 41 | def get_webvirt(): 42 | """ 43 | Clone WebVirtMgr and Add it to installation location 44 | """ 45 | require.directory(fsettings.INSTALL_PATH, use_sudo=True) 46 | with cd(fsettings.INSTALL_PATH): 47 | require.git.working_copy(fsettings.REPO_URL, use_sudo=True) 48 | 49 | webvirt_path = os.path.join(fsettings.INSTALL_PATH, "webvirtmgr") 50 | with cd(webvirt_path): 51 | install_requirements("requirements.txt", use_sudo=True) 52 | sudo("python manage.py syncdb") # --noinput and load fixtures?! 53 | sudo("python manage.py collectstatic --noinput") # just say yes! 54 | 55 | 56 | def configure_nginx(distro): 57 | """ 58 | Add Nginx configuration 59 | """ 60 | # Local webvirtmgr site template 61 | conf = os.path.join(LOCAL_BASE_DIR, "deploy", "fabric", "templates", 62 | "nginx.conf") 63 | # Remote location 64 | conf_path = os.path.join("/etc/nginx/conf.d", "webvirtmgr.conf") 65 | context = { 66 | "server_name": fsettings.SERVER_NAME 67 | } 68 | 69 | # Upload template to server 70 | files.upload_template(conf, conf_path, context=context, use_sudo=True) 71 | 72 | # Nginx, make sure `default` website is not running. 73 | if distro in ["Debian", "Ubuntu"]: 74 | disable_site("default") 75 | elif distro in ["Fedora"]: 76 | # Fedora places the default server:80 in nginx.conf! 77 | # we will replace nginx.conf 78 | default = "/etc/nginx/nginx.conf" 79 | default_bak = default + ".bak" 80 | 81 | # Local default nginx.conf template 82 | conf = os.path.join(LOCAL_BASE_DIR, "deploy", "fabric", 83 | "templates", "original.nginx.conf") 84 | 85 | if not files.is_file(default_bak): 86 | # only replace backup if required 87 | sudo("mv %s %s" % (default, default + ".bak")) 88 | 89 | # Upload new nginx.conf to server 90 | files.upload_template(conf, default, use_sudo=True) 91 | else: 92 | default = "/etc/nginx/conf.d/default.conf" 93 | if files.is_file(default): 94 | sudo("mv %s %s" % (default, default + ".bak")) 95 | 96 | # Ensure running ... 97 | # require.nginx.server() 98 | require.service.restart("nginx") 99 | 100 | 101 | def configure_novnc(distro): 102 | """ 103 | Configure Websocket proxy (noVNC) 104 | """ 105 | if distro in ["Debian", "Ubuntu"]: 106 | with settings(warn_only=True): 107 | sudo("service novnc stop") 108 | sudo("rm /etc/init.d/novnc") 109 | sudo("update-rc.d -f novnc remove") 110 | sudo("cp /var/www/webvirtmgr/conf/initd/webvirtmgr-console-ubuntu\ 111 | /etc/init.d/webvirtmgr-console") 112 | sudo("service webvirtmgr-console start") 113 | sudo("update-rc.d webvirtmgr-console defaults") 114 | sudo("chown -R www-data:www-data /var/www/webvirtmgr") 115 | elif distro in ["CentOS", "RHEL", "Fedora"]: 116 | sudo("cp /var/www/webvirtmgr/conf/initd/webvirtmgr-console-redhat\ 117 | /etc/init.d/webvirtmgr-console") 118 | sudo("service webvirtmgr-console start") 119 | sudo("chkconfig webvirtmgr-console on") 120 | sudo("chown -R nginx:nginx /var/www/webvirtmgr") 121 | 122 | 123 | def configure_supervisor(distro): 124 | """ 125 | Configure supervisor for running our WebVirtMgr Django Gunicorn Server 126 | """ 127 | if distro in ["Debian", "Ubuntu"]: 128 | user = "www-data" 129 | require.supervisor.process( 130 | "webvirtmgr", 131 | command= 132 | "/usr/bin/python /var/www/webvirtmgr/manage.py run_gunicorn -c\ 133 | /var/www/webvirtmgr/conf/gunicorn.conf.py", 134 | directory="/var/www/webvirtmgr", 135 | user=user, 136 | stdout_logfile="/var/log/supervisor/webvirtmgr.log", 137 | autostart=True, 138 | autorestart=True, 139 | redirect_stderr=True 140 | ) 141 | elif distro in ["CentOS", "RHEL", "Fedora"]: 142 | # first, ensure supervisord is running! 143 | with settings(warn_only=True): 144 | require.service.restart("supervisord") 145 | supervisord = "/etc/supervisord.conf" 146 | if not contains(supervisord, "[program:webvirtmgr]"): 147 | f = open("templates/webvirtmgr.ini") 148 | content = f.read() 149 | f.close() 150 | append(supervisord, content, use_sudo=True) 151 | reload_config() 152 | -------------------------------------------------------------------------------- /deploy/rpmbuild/README.md: -------------------------------------------------------------------------------- 1 | Installation through RPM 2 | ======================== 3 | 4 | If you want you can install webvirtmgr through a rpm package. Under `deploy/rpmbuild/webvirtmgr.spec` you find the spec file to build the rpm. 5 | 6 | Tested on `CentOS 6.5`. 7 | 8 | ### Build 9 | 10 | Using `mock` and `rpmdevtools` to create the rpm (`yum install mock rpm-build rpmdevtools` from `EPEL` ). 11 | 12 | ##### User 13 | 14 | Add new mock user: 15 | 16 | ```sh 17 | useradd 18 | passwd 19 | usermod -aG mock 20 | ``` 21 | 22 | Or editing existing users: 23 | 24 | ```sh 25 | usermod -aG mock && newgrp mock 26 | ``` 27 | 28 | ##### Get sources from repository 29 | 30 | ```sh 31 | cd deploy/rpmbuild/ 32 | spectool -g webvirtmgr.spec 33 | ``` 34 | 35 | ##### Build the sources (src.rpm) 36 | 37 | ```sh 38 | mock --resultdir=. --root epel-6-x86_64 --buildsrpm --spec webvirtmgr.spec --sources . 39 | ``` 40 | 41 | ##### Build rpm 42 | 43 | ```sh 44 | mock --resultdir=. --root=epel-6-x86_64 --rebuild webvirtmgr-4.8.8-1.el6.src.rpm 45 | ``` 46 | 47 | After this operations you have `webvirtmgr-4.8.8-1.el6.noarch.rpm`. To install it simply run: 48 | 49 | ```sh 50 | yum localinstall webvirtmgr-4.8.8-1.el6.noarch.rpm 51 | ``` 52 | 53 | ### Files 54 | 55 | After the rpm installation, you can find the webvirtmgr files under `{python_sitelib}/webvirtmgr`. 56 | 57 | Set the correct bind parameter in file `{python_sitelib}/webvirtmgr/conf/gunicorn.conf.py`. 58 | 59 | In `CentOS 6.5` the path `{python_sitelib}` is usually `/usr/lib/python2.6/site-packages`. 60 | 61 | Upstart file 62 | ============ 63 | 64 | If you want that webvirtmgr starts at boot, under `{python_sitelib}/webvirtmgr/conf/init/webvirtmgr-redhat.conf` there is the configuration file for upstart. The installation through rpm **launch** the app at boot. In fact the rpm installation copy the file `webvirtmgr-redhat.conf` in `/etc/init/`. 65 | 66 | If you don't want to start webvirtmgr at boot simply remove the `webvirtmgr-redhat.conf` file in `/etc/init/`. 67 | -------------------------------------------------------------------------------- /deploy/rpmbuild/webvirtmgr.spec: -------------------------------------------------------------------------------- 1 | %global name webvirtmgr 2 | %global version 4.8.9 3 | %global release 1 4 | 5 | Name: %{name} 6 | Version: %{version} 7 | Release: %{release}%{?dist} 8 | Summary: WebVirtMgr panel for manage virtual machine 9 | 10 | License: Apache Licence, Version 2.0 11 | URL: http://github.com/retspen/webvirtmgr 12 | Source0: https://github.com/retspen/webvirtmgr/archive/master.tar.gz 13 | 14 | %if 0%{?rhel} >= 7 || 0%{?fedora} 15 | %bcond_without systemd # enabled 16 | Requires(post): systemd 17 | Requires(preun): systemd 18 | Requires(postun): systemd 19 | BuildRequires: systemd 20 | %else 21 | %bcond_with systemd # disabled 22 | Requires(post): chkconfig 23 | Requires(postun): /sbin/service 24 | Requires(preun): /sbin/service 25 | Requires(preun): chkconfig 26 | %endif 27 | 28 | BuildArch: noarch 29 | BuildRequires: python-setuptools 30 | 31 | Requires: python-setuptools libvirt-python libxml2-python python-websockify python-gunicorn python-lockfile 32 | %if 0%{?rhel} >= 7 33 | Requires: python-django 34 | %else 35 | Requires: python-django15 36 | %endif 37 | 38 | Requires: libvirt qemu-kvm 39 | 40 | %description 41 | WebVirtMgr is a libvirt-based Web interface for managing virtual machines. 42 | It allows you to create and configure new domains, and adjust a domain resource allocation. 43 | A VNC viewer presents a full graphical console to the guest domain. 44 | KVM is currently the only hypervisor supported. 45 | 46 | %prep 47 | %setup -n %{name}-master 48 | 49 | %build 50 | %{__python} setup.py build 51 | 52 | %install 53 | %{__python} setup.py install --skip-build --install-lib=%{python_sitelib}/%{name} --root %{buildroot} 54 | cp -r templates %{buildroot}%{python_sitelib}/%{name}/ 55 | cp -r webvirtmgr/static %{buildroot}%{python_sitelib}/%{name}/ 56 | 57 | mkdir -p %{buildroot}%{python_sitelib}/%{name}/conf 58 | cp conf/gunicorn.conf.py %{buildroot}%{python_sitelib}/%{name}/conf/gunicorn.conf.py 59 | 60 | 61 | %if %{with systemd} 62 | mkdir -p %{buildroot}%{_unitdir} 63 | install -m0644 conf/init/webvirtmgr-console.service %{buildroot}%{_unitdir}/webvirtmgr-console.service 64 | install -m0644 conf/init/webvirtmgr.service %{buildroot}%{_unitdir}/webvirtmgr.service 65 | %else 66 | mkdir -p %{buildroot}%{_sysconfdir}/init 67 | cp conf/init/webvirtmgr-redhat.conf %{buildroot}%{_sysconfdir}/init/webvirtmgr.conf 68 | mkdir -p %{buildroot}%{_sysconfdir}/init.d 69 | cp conf/initd/webvirtmgr-console-redhat %{buildroot}%{_sysconfdir}/init.d/webvirtmgr-console 70 | %endif 71 | 72 | cp manage.py %{buildroot}%{python_sitelib}/%{name}/ 73 | rm -rf %{buildroot}%{_bindir}/manage.py 74 | 75 | %post 76 | %if %{with systemd} 77 | %systemd_post webvirtmgr.service 78 | %systemd_post webvirtmgr-console.service 79 | %else 80 | /sbin/chkconfig --add webvirtmgr 81 | /sbin/chkconfig --add webvirtmgr-console 82 | %endif 83 | 84 | %preun 85 | %if %{with systemd} 86 | %systemd_preun webvirtmgr.service 87 | %systemd_preun webvirtmgr-console.service 88 | %else 89 | if [ $1 -eq 0 ]; then 90 | /sbin/stop webvirtmgr >/dev/null 2>&1 || : 91 | /sbin/chkconfig --del webvirtmgr 92 | /sbin/service webvirtmgr stop >/dev/null 2>&1 || : 93 | /sbin/chkconfig --del webvirtmgr-console 94 | fi 95 | %endif 96 | 97 | %postun 98 | %if %{with systemd} 99 | %systemd_postun_with_restart webvirtmgr.service 100 | %systemd_postun_with_restart webvirtmgr-console.service 101 | %else 102 | if [ $1 -ge 1 ]; then 103 | /sbin/restart webvirtmgr >/dev/null 2>&1 || : 104 | /sbin/service webvirtmgr-console restart >/dev/null 2>&1 || : 105 | fi 106 | %endif 107 | 108 | 109 | %files 110 | %defattr(-,root,root) 111 | %{python_sitelib}/* 112 | %config %{python_sitelib}/%{name}/conf/gunicorn.conf.py 113 | %if %{with systemd} 114 | %{_unitdir}/webvirtmgr.service 115 | %{_unitdir}/webvirtmgr-console.service 116 | %else 117 | %{_sysconfdir}/init/* 118 | %{_sysconfdir}/init.d/* 119 | %endif 120 | 121 | %changelog 122 | * Mon Jan 18 2016 Giacomo Sanchietti - 4.8.9 123 | - Support rhel 7 124 | - Upgrade to 4.8.9 125 | 126 | * Wed Jan 26 2015 Edoardo Spadoni - 4.8.8 127 | - first version 128 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | 3 | django-jenkins==0.14.1 4 | pyflakes==0.7.3 5 | pylint==1.1.0 6 | pep8==1.4.6 -------------------------------------------------------------------------------- /hostdetail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/hostdetail/__init__.py -------------------------------------------------------------------------------- /hostdetail/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /hostdetail/views.py: -------------------------------------------------------------------------------- 1 | from libvirt import libvirtError 2 | 3 | from django.shortcuts import render_to_response 4 | from django.http import HttpResponse, HttpResponseRedirect 5 | from django.template import RequestContext 6 | from django.core.urlresolvers import reverse 7 | import json 8 | import time 9 | 10 | from servers.models import Compute 11 | from vrtManager.hostdetails import wvmHostDetails 12 | from webvirtmgr.settings import TIME_JS_REFRESH 13 | 14 | 15 | def hostusage(request, host_id): 16 | """ 17 | Return Memory and CPU Usage 18 | """ 19 | if not request.user.is_authenticated(): 20 | return HttpResponseRedirect(reverse('login')) 21 | 22 | points = 5 23 | datasets = {} 24 | cookies = {} 25 | compute = Compute.objects.get(id=host_id) 26 | curent_time = time.strftime("%H:%M:%S") 27 | 28 | try: 29 | conn = wvmHostDetails(compute.hostname, 30 | compute.login, 31 | compute.password, 32 | compute.type) 33 | cpu_usage = conn.get_cpu_usage() 34 | mem_usage = conn.get_memory_usage() 35 | conn.close() 36 | except libvirtError: 37 | cpu_usage = 0 38 | mem_usage = 0 39 | 40 | try: 41 | cookies['cpu'] = request._cookies['cpu'] 42 | cookies['mem'] = request._cookies['mem'] 43 | cookies['timer'] = request._cookies['timer'] 44 | except KeyError: 45 | cookies['cpu'] = None 46 | cookies['mem'] = None 47 | 48 | if not cookies['cpu'] and not cookies['mem']: 49 | datasets['cpu'] = [0] 50 | datasets['mem'] = [0] 51 | datasets['timer'] = [curent_time] 52 | else: 53 | datasets['cpu'] = eval(cookies['cpu']) 54 | datasets['mem'] = eval(cookies['mem']) 55 | datasets['timer'] = eval(cookies['timer']) 56 | 57 | datasets['timer'].append(curent_time) 58 | datasets['cpu'].append(int(cpu_usage['usage'])) 59 | datasets['mem'].append(int(mem_usage['usage']) / 1048576) 60 | 61 | if len(datasets['timer']) > points: 62 | datasets['timer'].pop(0) 63 | if len(datasets['cpu']) > points: 64 | datasets['cpu'].pop(0) 65 | if len(datasets['mem']) > points: 66 | datasets['mem'].pop(0) 67 | 68 | cpu = { 69 | 'labels': datasets['timer'], 70 | 'datasets': [ 71 | { 72 | "fillColor": "rgba(241,72,70,0.5)", 73 | "strokeColor": "rgba(241,72,70,1)", 74 | "pointColor": "rgba(241,72,70,1)", 75 | "pointStrokeColor": "#fff", 76 | "data": datasets['cpu'] 77 | } 78 | ] 79 | } 80 | 81 | memory = { 82 | 'labels': datasets['timer'], 83 | 'datasets': [ 84 | { 85 | "fillColor": "rgba(249,134,33,0.5)", 86 | "strokeColor": "rgba(249,134,33,1)", 87 | "pointColor": "rgba(249,134,33,1)", 88 | "pointStrokeColor": "#fff", 89 | "data": datasets['mem'] 90 | } 91 | ] 92 | } 93 | 94 | data = json.dumps({'cpu': cpu, 'memory': memory}) 95 | response = HttpResponse() 96 | response['Content-Type'] = "text/javascript" 97 | response.cookies['cpu'] = datasets['cpu'] 98 | response.cookies['timer'] = datasets['timer'] 99 | response.cookies['mem'] = datasets['mem'] 100 | response.write(data) 101 | return response 102 | 103 | 104 | def overview(request, host_id): 105 | """ 106 | Overview page. 107 | """ 108 | if not request.user.is_authenticated(): 109 | return HttpResponseRedirect(reverse('login')) 110 | 111 | errors = [] 112 | time_refresh = TIME_JS_REFRESH 113 | 114 | compute = Compute.objects.get(id=host_id) 115 | 116 | try: 117 | conn = wvmHostDetails(compute.hostname, 118 | compute.login, 119 | compute.password, 120 | compute.type) 121 | hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info() 122 | hypervisor = conn.hypervisor_type() 123 | mem_usage = conn.get_memory_usage() 124 | conn.close() 125 | except libvirtError as err: 126 | errors.append(err) 127 | 128 | return render_to_response('hostdetail.html', locals(), context_instance=RequestContext(request)) 129 | -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | Folder need for upload ISO images (You can delete this file) 2 | -------------------------------------------------------------------------------- /instance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/instance/__init__.py -------------------------------------------------------------------------------- /instance/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from servers.models import Compute 3 | 4 | 5 | class Instance(models.Model): 6 | compute = models.ForeignKey(Compute) 7 | name = models.CharField(max_length=20) 8 | uuid = models.CharField(max_length=36) 9 | # display_name = models.CharField(max_length=50) 10 | # display_description = models.CharField(max_length=255) 11 | 12 | def __unicode__(self): 13 | return self.name 14 | -------------------------------------------------------------------------------- /instance/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/instance/templatetags/__init__.py -------------------------------------------------------------------------------- /instance/templatetags/tags_active.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | import re 3 | 4 | register = template.Library() 5 | 6 | 7 | @register.simple_tag 8 | def active(request, pattern): 9 | if re.search(pattern, request.path): 10 | return 'selected' 11 | return '' 12 | -------------------------------------------------------------------------------- /instance/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /interfaces/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/interfaces/__init__.py -------------------------------------------------------------------------------- /interfaces/forms.py: -------------------------------------------------------------------------------- 1 | import re 2 | from django.utils.translation import ugettext_lazy as _ 3 | from django import forms 4 | 5 | 6 | class AddInterface(forms.Form): 7 | name = forms.CharField(max_length=10, required=True) 8 | itype = forms.ChoiceField(required=True, choices=(('bridge', 'bridge'), ('ethernet', 'ethernet'))) 9 | start_mode = forms.ChoiceField(required=True, 10 | choices=(('none', 'none'), ('onboot', 'onboot'), ('hotplug', 'hotplug'))) 11 | netdev = forms.CharField(max_length=15, required=True) 12 | ipv4_type = forms.ChoiceField(required=True, choices=(('dhcp', 'dhcp'), ('static', 'static'), ('none', 'none'))) 13 | ipv4_addr = forms.CharField(max_length=18, required=False) 14 | ipv4_gw = forms.CharField(max_length=15, required=False) 15 | ipv6_type = forms.ChoiceField(required=True, choices=(('dhcp', 'dhcp'), ('static', 'static'), ('none', 'none'))) 16 | ipv6_addr = forms.CharField(max_length=100, required=False) 17 | ipv6_gw = forms.CharField(max_length=100, required=False) 18 | stp = forms.ChoiceField(required=False, choices=(('on', 'on'), ('off', 'off'))) 19 | delay = forms.IntegerField(required=False) 20 | 21 | def clean_ipv4_addr(self): 22 | ipv4_addr = self.cleaned_data['ipv4_addr'] 23 | have_symbol = re.match('^[0-9./]+$', ipv4_addr) 24 | if not have_symbol: 25 | raise forms.ValidationError(_('The ipv4 must not contain any special characters')) 26 | elif len(ipv4_addr) > 20: 27 | raise forms.ValidationError(_('The ipv4 must not exceed 20 characters')) 28 | return ipv4_addr 29 | 30 | def clean_ipv4_gw(self): 31 | ipv4_gw = self.cleaned_data['ipv4_gw'] 32 | have_symbol = re.match('^[0-9.]+$', ipv4_gw) 33 | if not have_symbol: 34 | raise forms.ValidationError(_('The ipv4 gateway must not contain any special characters')) 35 | elif len(ipv4_gw) > 20: 36 | raise forms.ValidationError(_('The ipv4 gateway must not exceed 20 characters')) 37 | return ipv4_gw 38 | 39 | def clean_ipv6_addr(self): 40 | ipv6_addr = self.cleaned_data['ipv6_addr'] 41 | have_symbol = re.match('^[0-9a-f./:]+$', ipv6_addr) 42 | if not have_symbol: 43 | raise forms.ValidationError(_('The ipv6 must not contain any special characters')) 44 | elif len(ipv6_addr) > 100: 45 | raise forms.ValidationError(_('The ipv6 must not exceed 100 characters')) 46 | return ipv6_addr 47 | 48 | def clean_ipv6_gw(self): 49 | ipv6_gw = self.cleaned_data['ipv6_gw'] 50 | have_symbol = re.match('^[0-9.]+$', ipv6_gw) 51 | if not have_symbol: 52 | raise forms.ValidationError(_('The ipv6 gateway must not contain any special characters')) 53 | elif len(ipv6_gw) > 100: 54 | raise forms.ValidationError(_('The ipv6 gateway must not exceed 100 characters')) 55 | return ipv6_gw 56 | 57 | def clean_name(self): 58 | name = self.cleaned_data['name'] 59 | have_symbol = re.match('^[a-z0-9.]+$', name) 60 | if not have_symbol: 61 | raise forms.ValidationError(_('The interface must not contain any special characters')) 62 | elif len(name) > 10: 63 | raise forms.ValidationError(_('The interface must not exceed 10 characters')) 64 | return name 65 | 66 | def clean_netdev(self): 67 | netdev = self.cleaned_data['netdev'] 68 | have_symbol = re.match('^[a-z0-9.:]+$', netdev) 69 | if not have_symbol: 70 | raise forms.ValidationError(_('The interface must not contain any special characters')) 71 | elif len(netdev) > 10: 72 | raise forms.ValidationError(_('The interface must not exceed 10 characters')) 73 | return netdev 74 | -------------------------------------------------------------------------------- /interfaces/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /interfaces/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.http import HttpResponseRedirect 3 | from django.template import RequestContext 4 | from django.core.urlresolvers import reverse 5 | 6 | from servers.models import Compute 7 | from interfaces.forms import AddInterface 8 | 9 | from vrtManager.interface import wvmInterface, wvmInterfaces 10 | 11 | from libvirt import libvirtError 12 | 13 | 14 | def interfaces(request, host_id): 15 | """ 16 | Interfaces block 17 | 18 | """ 19 | if not request.user.is_authenticated(): 20 | return HttpResponseRedirect(reverse('index')) 21 | 22 | errors = [] 23 | ifaces_all = [] 24 | compute = Compute.objects.get(id=host_id) 25 | 26 | try: 27 | conn = wvmInterfaces(compute.hostname, 28 | compute.login, 29 | compute.password, 30 | compute.type) 31 | ifaces = conn.get_ifaces() 32 | try: 33 | netdevs = conn.get_net_device() 34 | except: 35 | netdevs = ['eth0', 'eth1'] 36 | 37 | for iface in ifaces: 38 | ifaces_all.append(conn.get_iface_info(iface)) 39 | 40 | if request.method == 'POST': 41 | if 'create' in request.POST: 42 | form = AddInterface(request.POST) 43 | if form.is_valid(): 44 | data = form.cleaned_data 45 | conn.create_iface(data['name'], data['itype'], data['start_mode'], data['netdev'], 46 | data['ipv4_type'], data['ipv4_addr'], data['ipv4_gw'], 47 | data['ipv6_type'], data['ipv6_addr'], data['ipv6_gw'], 48 | data['stp'], data['delay']) 49 | return HttpResponseRedirect(request.get_full_path()) 50 | conn.close() 51 | except libvirtError as err: 52 | errors.append(err) 53 | 54 | return render_to_response('interfaces.html', locals(), context_instance=RequestContext(request)) 55 | 56 | 57 | def interface(request, host_id, iface): 58 | """ 59 | Interface block 60 | 61 | """ 62 | if not request.user.is_authenticated(): 63 | return HttpResponseRedirect(reverse('index')) 64 | 65 | errors = [] 66 | ifaces_all = [] 67 | compute = Compute.objects.get(id=host_id) 68 | 69 | try: 70 | conn = wvmInterface(compute.hostname, 71 | compute.login, 72 | compute.password, 73 | compute.type, 74 | iface) 75 | start_mode = conn.get_start_mode() 76 | state = conn.is_active() 77 | mac = conn.get_mac() 78 | itype = conn.get_type() 79 | ipv4 = conn.get_ipv4() 80 | ipv4_type = conn.get_ipv4_type() 81 | ipv6 = conn.get_ipv6() 82 | ipv6_type = conn.get_ipv6_type() 83 | bridge = conn.get_bridge() 84 | 85 | if request.method == 'POST': 86 | if 'stop' in request.POST: 87 | conn.stop_iface() 88 | return HttpResponseRedirect(request.get_full_path()) 89 | if 'start' in request.POST: 90 | conn.start_iface() 91 | return HttpResponseRedirect(request.get_full_path()) 92 | if 'delete' in request.POST: 93 | conn.delete_iface() 94 | return HttpResponseRedirect(reverse('interfaces', args=[host_id])) 95 | conn.close() 96 | except libvirtError as err: 97 | errors.append(err) 98 | 99 | return render_to_response('interface.html', locals(), context_instance=RequestContext(request)) 100 | -------------------------------------------------------------------------------- /locale/da_DK/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/locale/da_DK/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtmgr.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /networks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/networks/__init__.py -------------------------------------------------------------------------------- /networks/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.utils.translation import ugettext_lazy as _ 3 | import re 4 | 5 | 6 | class AddNetPool(forms.Form): 7 | name = forms.CharField(error_messages={'required': _('No pool name has been entered')}, 8 | max_length=20) 9 | subnet = forms.CharField(error_messages={'required': _('No subnet has been entered')}, 10 | max_length=20) 11 | forward = forms.CharField(max_length=100) 12 | dhcp = forms.BooleanField(required=False) 13 | fixed = forms.BooleanField(required=False) 14 | bridge_name = forms.CharField(max_length=20, required=False) 15 | openvswitch = forms.BooleanField(required=False) 16 | 17 | def clean_name(self): 18 | name = self.cleaned_data['name'] 19 | have_symbol = re.match('^[a-zA-Z0-9\.\_\:\-]+$', name) 20 | if not have_symbol: 21 | raise forms.ValidationError(_('The pool name must not contain any special characters')) 22 | elif len(name) > 20: 23 | raise forms.ValidationError(_('The pool name must not exceed 20 characters')) 24 | return name 25 | 26 | def clean_subnet(self): 27 | subnet = self.cleaned_data['subnet'] 28 | have_symbol = re.match('^[0-9./]+$', subnet) 29 | if not have_symbol: 30 | raise forms.ValidationError(_('The pool subnet must not contain any special characters')) 31 | elif len(subnet) > 20: 32 | raise forms.ValidationError(_('The pool subnet must not exceed 20 characters')) 33 | return subnet 34 | 35 | def clean_bridge_name(self): 36 | bridge_name = self.cleaned_data['bridge_name'] 37 | if self.cleaned_data['forward'] == 'bridge': 38 | have_symbol = re.match('^[a-zA-Z0-9\.\_\:\-]+$', bridge_name) 39 | if not have_symbol: 40 | raise forms.ValidationError(_('The pool bridge name must not contain any special characters')) 41 | elif len(bridge_name) > 20: 42 | raise forms.ValidationError(_('The pool bridge name must not exceed 20 characters')) 43 | return bridge_name 44 | -------------------------------------------------------------------------------- /networks/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /networks/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.http import HttpResponseRedirect 3 | from django.template import RequestContext 4 | from django.utils.translation import ugettext_lazy as _ 5 | from django.core.urlresolvers import reverse 6 | 7 | from servers.models import Compute 8 | from networks.forms import AddNetPool 9 | 10 | from vrtManager.network import wvmNetwork, wvmNetworks 11 | from vrtManager.network import network_size 12 | 13 | from libvirt import libvirtError 14 | 15 | 16 | def networks(request, host_id): 17 | """ 18 | Networks block 19 | """ 20 | if not request.user.is_authenticated(): 21 | return HttpResponseRedirect(reverse('login')) 22 | 23 | errors = [] 24 | compute = Compute.objects.get(id=host_id) 25 | 26 | try: 27 | conn = wvmNetworks(compute.hostname, 28 | compute.login, 29 | compute.password, 30 | compute.type) 31 | networks = conn.get_networks_info() 32 | 33 | if request.method == 'POST': 34 | if 'create' in request.POST: 35 | form = AddNetPool(request.POST) 36 | if form.is_valid(): 37 | data = form.cleaned_data 38 | if data['name'] in networks: 39 | msg = _("Pool name already in use") 40 | errors.append(msg) 41 | if data['forward'] == 'bridge' and data['bridge_name'] == '': 42 | errors.append('Please enter bridge name') 43 | try: 44 | gateway, netmask, dhcp = network_size(data['subnet'], data['dhcp']) 45 | except: 46 | msg = _("Input subnet pool error") 47 | errors.append(msg) 48 | if not errors: 49 | conn.create_network(data['name'], data['forward'], gateway, netmask, 50 | dhcp, data['bridge_name'], data['openvswitch'], data['fixed']) 51 | return HttpResponseRedirect(reverse('network', args=[host_id, data['name']])) 52 | conn.close() 53 | except libvirtError as err: 54 | errors.append(err) 55 | 56 | return render_to_response('networks.html', locals(), context_instance=RequestContext(request)) 57 | 58 | 59 | def network(request, host_id, pool): 60 | """ 61 | Networks block 62 | """ 63 | if not request.user.is_authenticated(): 64 | return HttpResponseRedirect(reverse('login')) 65 | 66 | errors = [] 67 | compute = Compute.objects.get(id=host_id) 68 | 69 | try: 70 | conn = wvmNetwork(compute.hostname, 71 | compute.login, 72 | compute.password, 73 | compute.type, 74 | pool) 75 | networks = conn.get_networks() 76 | state = conn.is_active() 77 | device = conn.get_bridge_device() 78 | autostart = conn.get_autostart() 79 | ipv4_forward = conn.get_ipv4_forward() 80 | ipv4_dhcp_range_start = conn.get_ipv4_dhcp_range_start() 81 | ipv4_dhcp_range_end = conn.get_ipv4_dhcp_range_end() 82 | ipv4_network = conn.get_ipv4_network() 83 | fixed_address = conn.get_mac_ipaddr() 84 | except libvirtError as err: 85 | errors.append(err) 86 | 87 | if request.method == 'POST': 88 | if 'start' in request.POST: 89 | try: 90 | conn.start() 91 | return HttpResponseRedirect(request.get_full_path()) 92 | except libvirtError as error_msg: 93 | errors.append(error_msg.message) 94 | if 'stop' in request.POST: 95 | try: 96 | conn.stop() 97 | return HttpResponseRedirect(request.get_full_path()) 98 | except libvirtError as error_msg: 99 | errors.append(error_msg.message) 100 | if 'delete' in request.POST: 101 | try: 102 | conn.delete() 103 | return HttpResponseRedirect(reverse('networks', args=[host_id])) 104 | except libvirtError as error_msg: 105 | errors.append(error_msg.message) 106 | if 'set_autostart' in request.POST: 107 | try: 108 | conn.set_autostart(1) 109 | return HttpResponseRedirect(request.get_full_path()) 110 | except libvirtError as error_msg: 111 | errors.append(error_msg.message) 112 | if 'unset_autostart' in request.POST: 113 | try: 114 | conn.set_autostart(0) 115 | return HttpResponseRedirect(request.get_full_path()) 116 | except libvirtError as error_msg: 117 | errors.append(error_msg.message) 118 | 119 | conn.close() 120 | 121 | return render_to_response('network.html', locals(), context_instance=RequestContext(request)) 122 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==1.5.5 2 | gunicorn==19.5.0 3 | # Utility Requirements 4 | # for SECURE_KEY generation 5 | lockfile>=0.9 6 | # Uncoment for support ldap 7 | #django-auth-ldap==1.2.0 8 | -------------------------------------------------------------------------------- /secrets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/secrets/__init__.py -------------------------------------------------------------------------------- /secrets/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | 4 | class AddSecret(forms.Form): 5 | ephemeral = forms.ChoiceField(required=True, choices=(('no', 'no'), ('yes', 'yes'))) 6 | private = forms.ChoiceField(required=True, choices=(('no', 'no'), ('yes', 'yes'))) 7 | usage_type = forms.ChoiceField(required=True, choices=(('ceph', 'ceph'), ('volume', 'volume'), ('iscsi', 'iscsi'))) 8 | data = forms.CharField(max_length=100, required=True) 9 | -------------------------------------------------------------------------------- /secrets/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /secrets/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.http import HttpResponseRedirect 3 | from django.template import RequestContext 4 | from django.core.urlresolvers import reverse 5 | 6 | from servers.models import Compute 7 | from secrets.forms import AddSecret 8 | 9 | from vrtManager.secrets import wvmSecrets 10 | 11 | from libvirt import libvirtError 12 | 13 | 14 | def secrets(request, host_id): 15 | """ 16 | Networks block 17 | """ 18 | if not request.user.is_authenticated(): 19 | return HttpResponseRedirect(reverse('login')) 20 | 21 | errors = [] 22 | secrets_all = [] 23 | compute = Compute.objects.get(id=host_id) 24 | 25 | try: 26 | conn = wvmSecrets(compute.hostname, 27 | compute.login, 28 | compute.password, 29 | compute.type) 30 | secrets = conn.get_secrets() 31 | for uuid in secrets: 32 | secrt = conn.get_secret(uuid) 33 | try: 34 | secret_value = conn.get_secret_value(uuid) 35 | except: 36 | secret_value = '' 37 | secrets_all.append({'usage': secrt.usageID(), 38 | 'uuid': secrt.UUIDString(), 39 | 'usageType': secrt.usageType(), 40 | 'value': secret_value 41 | }) 42 | if request.method == 'POST': 43 | if 'create' in request.POST: 44 | form = AddSecret(request.POST) 45 | if form.is_valid(): 46 | data = form.cleaned_data 47 | conn.create_secret(data['ephemeral'], data['private'], data['usage_type'], data['data']) 48 | return HttpResponseRedirect(request.get_full_path()) 49 | if 'delete' in request.POST: 50 | uuid = request.POST.get('uuid', '') 51 | conn.delete_secret(uuid) 52 | return HttpResponseRedirect(request.get_full_path()) 53 | if 'set_value' in request.POST: 54 | uuid = request.POST.get('uuid', '') 55 | value = request.POST.get('value', '') 56 | conn.set_secret_value(uuid, value) 57 | return HttpResponseRedirect(request.get_full_path()) 58 | except libvirtError as err: 59 | errors.append(err) 60 | 61 | return render_to_response('secrets.html', locals(), context_instance=RequestContext(request)) 62 | -------------------------------------------------------------------------------- /serverlog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/serverlog/__init__.py -------------------------------------------------------------------------------- /serverlog/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class InstanceLog(models.Model): 5 | message = models.TextField() 6 | date = models.DateTimeField(auto_now_add=True) 7 | 8 | def __unicode__(self): 9 | return self.message -------------------------------------------------------------------------------- /serverlog/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /serverlog/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /servers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/servers/__init__.py -------------------------------------------------------------------------------- /servers/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Compute(models.Model): 5 | name = models.CharField(max_length=20) 6 | hostname = models.CharField(max_length=20) 7 | login = models.CharField(max_length=20) 8 | password = models.CharField(max_length=14, blank=True, null=True) 9 | type = models.IntegerField() 10 | 11 | def __unicode__(self): 12 | return self.hostname 13 | -------------------------------------------------------------------------------- /servers/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | ================= 3 | WebVirtMgr panel 4 | ================= 5 | 6 | Introduction 7 | ------------ 8 | 9 | WebVirtMgr is a libvirt-based Web interface for managing virtual machines. It allows you to create and configure new domains, and adjust a domain's resource allocation. A VNC viewer presents a full graphical console to the guest domain. KVM is currently the only hypervisor supported. 10 | 11 | Technology: 12 | *********** 13 | 14 | The application logic is written in Python & Django. The LIBVIRT Python bindings are used to interacting with the underlying hypervisor. 15 | 16 | Installation (Only web panel) 17 | ----------------------------- 18 | 19 | `Install WebVirtMgr `_ 20 | 21 | 22 | Setup host server (Server for VM's) 23 | ----------------------------------- 24 | 25 | `Setup Host Server `_ 26 | 27 | Links 28 | ------ 29 | - `Website `_ 30 | - `Screenshots `_ 31 | - `Wiki `_ 32 | """ 33 | 34 | import os 35 | from setuptools import setup, find_packages 36 | 37 | 38 | __version__ = "4.8.9" 39 | 40 | 41 | def read(fname): 42 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 43 | 44 | 45 | setup( 46 | # package name in pypi 47 | name='webvirtmgr', 48 | # extract version from module. 49 | version=__version__, 50 | description="WebVirtMgr panel for manage virtual machine", 51 | long_description=__doc__, 52 | classifiers=[], 53 | keywords='', 54 | author='Anatoliy Guskov', 55 | author_email='anatoliy.guskov@gmail.com', 56 | url='http://github.com/retspen/webvirtmgr', 57 | license='Apache Licence, Version 2.0.', 58 | # include all packages in the egg, except the test package. 59 | packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), 60 | # for avoiding conflict have one namespace for all webvirtmgr related eggs. 61 | namespace_packages=[], 62 | # include non python files 63 | include_package_data=True, 64 | zip_safe=False, 65 | # specify dependencies 66 | install_requires=[ 67 | 'setuptools', 68 | 'django>=1.5.5', 69 | 'gunicorn>=18.0', 70 | 'lockfile>=0.9', 71 | ], 72 | # mark test target to require extras. 73 | extras_require={ 74 | 'ldap': ["django-auth-ldap>=1.2.0"] 75 | }, 76 | scripts=[ 77 | 'manage.py', 78 | ], 79 | ) 80 | -------------------------------------------------------------------------------- /storages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/storages/__init__.py -------------------------------------------------------------------------------- /storages/forms.py: -------------------------------------------------------------------------------- 1 | import re 2 | from django import forms 3 | from django.utils.translation import ugettext_lazy as _ 4 | 5 | 6 | class AddStgPool(forms.Form): 7 | name = forms.CharField(error_messages={'required': _('No pool name has been entered')}, 8 | max_length=20) 9 | stg_type = forms.CharField(max_length=10) 10 | target = forms.CharField(error_messages={'required': _('No path has been entered')}, 11 | max_length=100, 12 | required=False) 13 | source = forms.CharField(max_length=100, required=False) 14 | ceph_user = forms.CharField(required=False) 15 | ceph_host = forms.CharField(required=False) 16 | ceph_pool = forms.CharField(required=False) 17 | secret = forms.CharField(required=False) 18 | netfs_host = forms.CharField(required=False) 19 | source_format = forms.CharField(required=False) 20 | 21 | def clean_name(self): 22 | name = self.cleaned_data['name'] 23 | have_symbol = re.match('^[a-zA-Z0-9._-]+$', name) 24 | if not have_symbol: 25 | raise forms.ValidationError(_('The pool name must not contain any special characters')) 26 | elif len(name) > 20: 27 | raise forms.ValidationError(_('The pool name must not exceed 20 characters')) 28 | return name 29 | 30 | def clean_target(self): 31 | storage_type = self.cleaned_data['stg_type'] 32 | target = self.cleaned_data['target'] 33 | have_symbol = re.match('^[a-zA-Z0-9/_]+$', target) 34 | if storage_type == 'dir' or storage_type == 'netfs': 35 | if not have_symbol: 36 | raise forms.ValidationError(_('The target must not contain any special characters')) 37 | if storage_type == 'dir' or storage_type == 'netfs': 38 | if not target: 39 | raise forms.ValidationError(_('No path has been entered')) 40 | return target 41 | 42 | def clean_source(self): 43 | storage_type = self.cleaned_data['stg_type'] 44 | source = self.cleaned_data['source'] 45 | have_symbol = re.match('^[a-zA-Z0-9\/_]+$', source) 46 | if storage_type == 'logical' or storage_type == 'netfs': 47 | if not source: 48 | raise forms.ValidationError(_('No device has been entered')) 49 | if not have_symbol: 50 | raise forms.ValidationError(_('The source must not contain any special characters')) 51 | return source 52 | 53 | 54 | class AddImage(forms.Form): 55 | name = forms.CharField(max_length=20) 56 | format = forms.ChoiceField(required=True, choices=(('qcow2', 'qcow2 (recommended)'), 57 | ('qcow', 'qcow'), 58 | ('qed', 'qed'), 59 | ('raw', 'raw'))) 60 | size = forms.IntegerField() 61 | meta_prealloc = forms.BooleanField(required=False) 62 | 63 | def clean_name(self): 64 | name = self.cleaned_data['name'] 65 | have_symbol = re.match('^[a-zA-Z0-9._-]+$', name) 66 | if not have_symbol: 67 | raise forms.ValidationError(_('The image name must not contain any special characters')) 68 | elif len(name) > 20: 69 | raise forms.ValidationError(_('The image name must not exceed 20 characters')) 70 | return name 71 | 72 | 73 | class CloneImage(forms.Form): 74 | name = forms.CharField(max_length=20) 75 | image = forms.CharField(max_length=20) 76 | convert = forms.BooleanField(required=False) 77 | format = forms.ChoiceField(required=False, choices=(('qcow2', 'qcow2 (recommended)'), 78 | ('qcow', 'qcow'), 79 | ('qed', 'qed'), 80 | ('raw', 'raw'))) 81 | meta_prealloc = forms.BooleanField(required=False) 82 | 83 | def clean_name(self): 84 | name = self.cleaned_data['name'] 85 | have_symbol = re.match('^[a-zA-Z0-9._-]+$', name) 86 | if not have_symbol: 87 | raise forms.ValidationError(_('The image name must not contain any special characters')) 88 | elif len(name) > 20: 89 | raise forms.ValidationError(_('The image name must not exceed 20 characters')) 90 | return name 91 | -------------------------------------------------------------------------------- /storages/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | {% load staticfiles %} 4 | {% block title %}{% trans "404" %}{% endblock %} 5 | {% block content %} 6 |
7 |
8 |

Oops!

9 | 10 |

{% trans "404 Not Found" %}

11 | 12 |

{% trans "The requested page was not found on this server." %}

13 | ← Back 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | {% load staticfiles %} 4 | {% block title %}{% trans "500" %}{% endblock %} 5 | {% block content %} 6 |
7 |
8 |

Oops!

9 | 10 |

{% trans "500 Internal Server Error" %}

11 | 12 |

{% trans "The server encountered an internal error or misconfiguration and was unable to complete you request." %}

13 | ← Back 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}{% endblock %} 8 | 9 | 10 | {% block style %}{% endblock %} 11 | 12 | 13 | 14 | 46 |
47 | {% block content %}{% endblock %} 48 |
49 | 50 | 51 | {% block script %}{% endblock %} 52 | 53 | 54 | -------------------------------------------------------------------------------- /templates/base_auth.html: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% load staticfiles %} 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}{% endblock %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 40 |
41 | {% block content %}{% endblock %} 42 |
43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /templates/console-base.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 46 | 47 | {% block head %}{% endblock %} 48 | 49 | 50 | 51 | 52 | 89 |
90 | {% block content %}{% endblock %} 91 |
92 | 93 | 94 | 95 | 121 | 122 | {% block foot %}{% endblock %} 123 | 124 | 125 | -------------------------------------------------------------------------------- /templates/hostdetail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | {% load staticfiles %} 4 | {% block title %}{% trans "Overview" %}{% endblock %} 5 | {% block content %} 6 | {% include 'sidebar.html' %} 7 |
8 | {% if errors %} 9 | {% for error in errors %} 10 |
11 | 12 | {{ error }} 13 |
14 | {% endfor %} 15 | {% endif %} 16 | 17 |

{% trans "Basic details" %}

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
{% trans "Connection" %}{{ uri_conn }}
{% trans "Hostname" %}{{ hostname }}
{% trans "Hypervisor" %}{{ hypervisor }}
{% trans "Memory" %}{{ host_memory|filesizeformat }}
{% trans "Logical CPUs" %}{{ logical_cpu }}
{% trans "Processor" %}{{ model_cpu }}
{% trans "Architecture" %}{{ host_arch }}
50 | 51 |

{% trans "Performance" %}

52 |

{% trans "CPU usage" %}

53 | 54 |

{% trans "Memory usage" %}

55 | 56 |
57 | {% include 'sidebar_close.html' %} 58 | {% endblock %} 59 | {% block script %} 60 | 61 | 94 | {% endblock %} 95 | -------------------------------------------------------------------------------- /templates/infrastructure.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Infrastructure" %}{% endblock %} 4 | {% block content %} 5 |
6 |
7 | 19 | {% if hosts_vms %} 20 |
21 |
22 | Hide: 23 | 26 | 29 | 32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {% for host, vms in hosts_vms.items %} 47 | 48 | 49 | 50 | 57 | 58 | 59 | 60 | 61 | {% if vms %} 62 | {% for vm, info in vms.items %} 63 | 64 | 65 | 67 | 74 | 75 | 76 | 77 | 78 | {% endfor %} 79 | {% endif %} 80 | {% endfor %} 81 | 82 |
#{% trans "Hostname / VMs" %}{% trans "Status" %}{% trans "VCPUs" %}{% trans "Memory" %}{% trans "Usage" %}
{{ forloop.counter }}{{ host.1 }}{% ifequal host.2 1 %}{% trans "Active" %} 51 | {% endifequal %} 52 | {% ifequal host.2 2 %}{% trans "Not Active" %} 53 | {% endifequal %} 54 | {% ifequal host.2 3 %}{% trans "Connection Failed" %} 55 | {% endifequal %} 56 | {{ host.3 }}{{ host.4|filesizeformat }}{{ host.5 }}%
{{ forloop.counter }}   {{ vm }}{% ifequal info.0 1 %}{% trans "Running" %} 68 | {% endifequal %} 69 | {% ifequal info.0 3 %}{% trans "Suspend" %} 70 | {% endifequal %} 71 | {% ifequal info.0 5 %}{% trans "Shutoff" %} 72 | {% endifequal %} 73 | {{ info.1 }}{{ info.2|filesizeformat }}{{ info.3 }}%
83 | {% else %} 84 |
85 |

{% trans "You have no connection" %}

86 |
87 | {% endif %} 88 |
89 |
90 | {% endblock %} 91 | {% block script %} 92 | 93 | {% endblock %} 94 | -------------------------------------------------------------------------------- /templates/interface.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | {% load staticfiles %} 4 | {% block title %}{% trans "Interface" %}{% endblock %} 5 | {% block content %} 6 | {% include 'sidebar.html' %} 7 |
8 | {% if errors %} 9 | {% for error in errors %} 10 |
11 | 12 | {{ error }} 13 |
14 | {% endfor %} 15 | {% endif %} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {% ifequal itype 'bridge' %} 39 | 40 | 41 | 42 | 43 | {% endifequal %} 44 | 45 | 46 | 47 | 48 | 49 | 50 | 62 | 63 | 64 |
{% trans "Interface" %}{{ iface }}
{% trans "IPv4" %} ({% ifequal ipv4 None %}None{% else %}{{ ipv4_type }}{% endifequal %}){{ ipv4 }}
{% trans "IPv6" %} ({% ifequal ipv6 None %}None{% else %}{{ ipv6_type }}{% endifequal %}){{ ipv6 }}
{% trans "MAC Adress" %}{{ mac }}
{% trans "Interface type" %}{{ itype }}
{% trans "Bridge device" %}{{ bridge }}
{% trans "Boot mode" %}{{ start_mode }}
{% trans "State" %} 51 |
{% csrf_token %} 52 | {% ifequal state 0 %} 53 | 54 | 56 | {% else %} 57 | 59 | {% endifequal %} 60 |
61 |
65 |
66 | {% include 'sidebar_close.html' %} 67 | {% endblock %} 68 | -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base_auth.html" %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Sign In" %}{% endblock %} 4 | {% block content %} 5 |
6 |
7 | {% if form.errors %} 8 |
9 | 10 | {% trans "Incorrect username or password." %} 11 |
12 | {% endif %} 13 | 21 |
22 |
23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "base_auth.html" %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Sign Out" %}{% endblock %} 4 | {% block content %} 5 |
6 |
7 |
8 |

{% trans "Successful log out" %}

9 |
10 |
11 |
12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/sidebar.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% load tags_active %} 3 |
4 | 10 |
11 |
12 | 39 | -------------------------------------------------------------------------------- /templates/sidebar_close.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /vrtManager/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/vrtManager/__init__.py -------------------------------------------------------------------------------- /vrtManager/hostdetails.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 Webvirtmgr. 3 | # 4 | import time 5 | from vrtManager.connection import wvmConnect 6 | from vrtManager.util import get_xml_path 7 | 8 | 9 | def cpu_version(ctx): 10 | for info in ctx.xpathEval('/sysinfo/processor/entry'): 11 | elem = info.xpathEval('@name')[0].content 12 | if elem == 'version': 13 | return info.content 14 | return 'Unknown' 15 | 16 | 17 | class wvmHostDetails(wvmConnect): 18 | def get_memory_usage(self): 19 | """ 20 | Function return memory usage on node. 21 | """ 22 | get_all_mem = self.wvm.getInfo()[1] * 1048576 23 | get_freemem = self.wvm.getMemoryStats(-1, 0) 24 | if type(get_freemem) == dict: 25 | free = (get_freemem.values()[0] + 26 | get_freemem.values()[2] + 27 | get_freemem.values()[3]) * 1024 28 | percent = (100 - ((free * 100) / get_all_mem)) 29 | usage = (get_all_mem - free) 30 | mem_usage = {'usage': usage, 'percent': percent} 31 | else: 32 | mem_usage = {'usage': None, 'percent': None} 33 | return mem_usage 34 | 35 | def get_cpu_usage(self): 36 | """ 37 | Function return cpu usage on node. 38 | """ 39 | prev_idle = 0 40 | prev_total = 0 41 | cpu = self.wvm.getCPUStats(-1, 0) 42 | if type(cpu) == dict: 43 | for num in range(2): 44 | idle = self.wvm.getCPUStats(-1, 0).values()[1] 45 | total = sum(self.wvm.getCPUStats(-1, 0).values()) 46 | diff_idle = idle - prev_idle 47 | diff_total = total - prev_total 48 | diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10 49 | prev_total = total 50 | prev_idle = idle 51 | if num == 0: 52 | time.sleep(1) 53 | else: 54 | if diff_usage < 0: 55 | diff_usage = 0 56 | else: 57 | return {'usage': None} 58 | return {'usage': diff_usage} 59 | 60 | def get_node_info(self): 61 | """ 62 | Function return host server information: hostname, cpu, memory, ... 63 | """ 64 | info = [] 65 | info.append(self.wvm.getHostname()) 66 | info.append(self.wvm.getInfo()[0]) 67 | info.append(self.wvm.getInfo()[1] * 1048576) 68 | info.append(self.wvm.getInfo()[2]) 69 | info.append(get_xml_path(self.wvm.getSysinfo(0), func=cpu_version)) 70 | info.append(self.wvm.getURI()) 71 | return info 72 | 73 | def hypervisor_type(self): 74 | """Return hypervisor type""" 75 | return get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/domain/@type") 76 | -------------------------------------------------------------------------------- /vrtManager/interface.py: -------------------------------------------------------------------------------- 1 | from vrtManager.connection import wvmConnect 2 | from vrtManager import util 3 | from libvirt import VIR_INTERFACE_XML_INACTIVE 4 | 5 | 6 | class wvmInterfaces(wvmConnect): 7 | def get_iface_info(self, name): 8 | iface = self.get_iface(name) 9 | xml = iface.XMLDesc(0) 10 | mac = iface.MACString() 11 | itype = util.get_xml_path(xml, "/interface/@type") 12 | state = iface.isActive() 13 | return {'name': name, 'type': itype, 'state': state, 'mac': mac} 14 | 15 | def define_iface(self, xml, flag=0): 16 | self.wvm.interfaceDefineXML(xml, flag) 17 | 18 | def create_iface(self, name, itype, mode, netdev, ipv4_type, ipv4_addr, ipv4_gw, 19 | ipv6_type, ipv6_addr, ipv6_gw, stp, delay): 20 | xml = """ 21 | """ % (itype, name, mode) 22 | if ipv4_type == 'dhcp': 23 | xml += """ 24 | 25 | """ 26 | if ipv4_type == 'static': 27 | address, prefix = ipv4_addr.split('/') 28 | xml += """ 29 | 30 | 31 | """ % (address, prefix, ipv4_gw) 32 | if ipv6_type == 'dhcp': 33 | xml += """ 34 | 35 | """ 36 | if ipv6_type == 'static': 37 | address, prefix = ipv6_addr.split('/') 38 | xml += """ 39 | 40 | 41 | """ % (address, prefix, ipv6_gw) 42 | if itype == 'bridge': 43 | xml += """ 44 | 45 | """ % (stp, delay, netdev) 46 | xml += """""" 47 | self.define_iface(xml) 48 | iface = self.get_iface(name) 49 | iface.create() 50 | 51 | 52 | class wvmInterface(wvmConnect): 53 | def __init__(self, host, login, passwd, conn, iface): 54 | wvmConnect.__init__(self, host, login, passwd, conn) 55 | self.iface = self.get_iface(iface) 56 | 57 | def _XMLDesc(self, flags=0): 58 | return self.iface.XMLDesc(flags) 59 | 60 | def get_start_mode(self): 61 | try: 62 | xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) 63 | return util.get_xml_path(xml, "/interface/start/@mode") 64 | except: 65 | return None 66 | 67 | def is_active(self): 68 | return self.iface.isActive() 69 | 70 | def get_mac(self): 71 | mac = self.iface.MACString() 72 | if mac: 73 | return mac 74 | else: 75 | return None 76 | 77 | def get_type(self): 78 | xml = self._XMLDesc() 79 | return util.get_xml_path(xml, "/interface/@type") 80 | 81 | def get_ipv4_type(self): 82 | try: 83 | xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) 84 | ipaddr = util.get_xml_path(xml, "/interface/protocol/ip/@address") 85 | if ipaddr: 86 | return 'static' 87 | else: 88 | return 'dhcp' 89 | except: 90 | return None 91 | 92 | def get_ipv4(self): 93 | xml = self._XMLDesc() 94 | int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol/ip/@address") 95 | int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol/ip/@prefix") 96 | if not int_ipv4_ip or not int_ipv4_mask: 97 | return None 98 | else: 99 | return int_ipv4_ip + '/' + int_ipv4_mask 100 | 101 | def get_ipv6_type(self): 102 | try: 103 | xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) 104 | ipaddr = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address") 105 | if ipaddr: 106 | return 'static' 107 | else: 108 | return 'dhcp' 109 | except: 110 | return None 111 | 112 | def get_ipv6(self): 113 | xml = self._XMLDesc() 114 | int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address") 115 | int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[2]/ip/@prefix") 116 | if not int_ipv6_ip or not int_ipv6_mask: 117 | return None 118 | else: 119 | return int_ipv6_ip + '/' + int_ipv6_mask 120 | 121 | def get_bridge(self): 122 | if self.get_type() == 'bridge': 123 | xml = self._XMLDesc() 124 | return util.get_xml_path(xml, "/interface/bridge/interface/@name") 125 | else: 126 | return None 127 | 128 | def stop_iface(self): 129 | self.iface.destroy() 130 | 131 | def start_iface(self): 132 | self.iface.create() 133 | 134 | def delete_iface(self): 135 | self.iface.undefine() 136 | -------------------------------------------------------------------------------- /vrtManager/secrets.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from vrtManager.connection import wvmConnect 3 | 4 | 5 | class wvmSecrets(wvmConnect): 6 | def create_secret(self, ephemeral, private, secret_type, data): 7 | xml = """ 8 | """ % (ephemeral, private, secret_type) 9 | if secret_type == 'ceph': 10 | xml += """%s""" % (data) 11 | if secret_type == 'volume': 12 | xml += """%s""" % (data) 13 | if secret_type == 'iscsi': 14 | xml += """%s""" % (data) 15 | xml += """ 16 | """ 17 | self.wvm.secretDefineXML(xml) 18 | 19 | def get_secret_value(self, uuid): 20 | secrt = self.get_secret(uuid) 21 | value = secrt.value() 22 | return base64.b64encode(value) 23 | 24 | def set_secret_value(self, uuid, value): 25 | secrt = self.get_secret(uuid) 26 | value = base64.b64decode(value) 27 | secrt.setValue(value) 28 | 29 | def delete_secret(self, uuid): 30 | secrt = self.get_secret(uuid) 31 | secrt.undefine() 32 | -------------------------------------------------------------------------------- /vrtManager/util.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 Webvirtmgr. 3 | # 4 | import random 5 | import libxml2 6 | import libvirt 7 | 8 | 9 | def is_kvm_available(xml): 10 | kvm_domains = get_xml_path(xml, "//domain/@type='kvm'") 11 | if kvm_domains > 0: 12 | return True 13 | else: 14 | return False 15 | 16 | 17 | def randomMAC(): 18 | """Generate a random MAC address.""" 19 | # qemu MAC 20 | oui = [0x52, 0x54, 0x00] 21 | 22 | mac = oui + [random.randint(0x00, 0xff), 23 | random.randint(0x00, 0xff), 24 | random.randint(0x00, 0xff)] 25 | return ':'.join(map(lambda x: "%02x" % x, mac)) 26 | 27 | 28 | def randomUUID(): 29 | """Generate a random UUID.""" 30 | 31 | u = [random.randint(0, 255) for dummy in range(0, 16)] 32 | return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u) 33 | 34 | 35 | def get_max_vcpus(conn, type=None): 36 | """@param conn: libvirt connection to poll for max possible vcpus 37 | @type type: optional guest type (kvm, etc.)""" 38 | if type is None: 39 | type = conn.getType() 40 | try: 41 | m = conn.getMaxVcpus(type.lower()) 42 | except libvirt.libvirtError: 43 | m = 32 44 | return m 45 | 46 | 47 | def xml_escape(str): 48 | """Replaces chars ' " < > & with xml safe counterparts""" 49 | if str is None: 50 | return None 51 | 52 | str = str.replace("&", "&") 53 | str = str.replace("'", "'") 54 | str = str.replace("\"", """) 55 | str = str.replace("<", "<") 56 | str = str.replace(">", ">") 57 | return str 58 | 59 | 60 | def compareMAC(p, q): 61 | """Compare two MAC addresses""" 62 | pa = p.split(":") 63 | qa = q.split(":") 64 | 65 | if len(pa) != len(qa): 66 | if p > q: 67 | return 1 68 | else: 69 | return -1 70 | 71 | for i in xrange(len(pa)): 72 | n = int(pa[i], 0x10) - int(qa[i], 0x10) 73 | if n > 0: 74 | return 1 75 | elif n < 0: 76 | return -1 77 | return 0 78 | 79 | 80 | def get_xml_path(xml, path=None, func=None): 81 | """ 82 | Return the content from the passed xml xpath, or return the result 83 | of a passed function (receives xpathContext as its only arg) 84 | """ 85 | doc = None 86 | ctx = None 87 | result = None 88 | 89 | try: 90 | doc = libxml2.parseDoc(xml) 91 | ctx = doc.xpathNewContext() 92 | 93 | if path: 94 | ret = ctx.xpathEval(path) 95 | if ret is not None: 96 | if type(ret) == list: 97 | if len(ret) >= 1: 98 | result = ret[0].content 99 | else: 100 | result = ret 101 | 102 | elif func: 103 | result = func(ctx) 104 | 105 | else: 106 | raise ValueError("'path' or 'func' is required.") 107 | finally: 108 | if doc: 109 | doc.freeDoc() 110 | if ctx: 111 | ctx.xpathFreeContext() 112 | return result 113 | 114 | 115 | def pretty_mem(val): 116 | val = int(val) 117 | if val > (10 * 1024 * 1024): 118 | return "%2.2f GB" % (val / (1024.0 * 1024.0)) 119 | else: 120 | return "%2.0f MB" % (val / 1024.0) 121 | 122 | 123 | def pretty_bytes(val): 124 | val = int(val) 125 | if val > (1024 * 1024 * 1024): 126 | return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0)) 127 | else: 128 | return "%2.2f MB" % (val / (1024.0 * 1024.0)) 129 | -------------------------------------------------------------------------------- /webvirtmgr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/__init__.py -------------------------------------------------------------------------------- /webvirtmgr/local/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/local/__init__.py -------------------------------------------------------------------------------- /webvirtmgr/local/local_settings.py.example: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # Debug settings should default to False in production Environments 4 | DEBUG = False 5 | TEMPLATE_DEBUG = DEBUG 6 | 7 | LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) 8 | 9 | # Set custom secret key: 10 | # You can either set it to a specific value or you can let horizion generate a 11 | # default secret key that is unique on this machine, e.i. regardless of the 12 | # amount of Python WSGI workers (if used behind Apache+mod_wsgi): However, there 13 | # may be situations where you would want to set this explicitly, e.g. when 14 | # multiple dashboard instances are distributed on different machines (usually 15 | # behind a load-balancer). Either you have to make sure that a session gets all 16 | # requests routed to the same dashboard instance or you set the same SECRET_KEY 17 | # for all of them. 18 | from webvirtmgr.utils import secret_key 19 | SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH, '.secret_key_store')) 20 | 21 | ADMINS = ( 22 | # ('Your Name', 'your_email@example.com'), 23 | ) 24 | 25 | MANAGERS = ADMINS 26 | 27 | # Uncomment the relevant entries for ldap authentication 28 | # import ldap 29 | # from django_auth_ldap.config import LDAPSearch,GroupOfUniqueNamesType 30 | 31 | #AUTHENTICATION_BACKENDS = ( 32 | # 'django_auth_ldap.backend.LDAPBackend', 33 | # 'django.contrib.auth.backends.ModelBackend', 34 | #) 35 | 36 | # If the system is unable to verify the directory cert then change these settings 37 | #AUTH_LDAP_GLOBAL_OPTIONS = { 38 | # ldap.OPT_X_TLS_REQUIRE_CERT: True, 39 | # ldap.OPT_X_TLS_DEMAND: True, 40 | # ldap.OPT_REFERRALS: False, 41 | # ldap.OPT_X_TLS_CACERTDIR: "/etc/pki/tls/certs/", 42 | #} 43 | 44 | #AUTH_LDAP_SERVER_URI = "ldaps://ldapserverhostname.example.com" 45 | #AUTH_LDAP_BIND_DN = "uid=binduser,ou=systemusers,dc=example,dc=com" 46 | #AUTH_LDAP_BIND_PASSWORD = "" 47 | #AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", 48 | # ldap.SCOPE_SUBTREE, "(uid=%(user)s)") 49 | #AUTH_LDAP_GROUP_SEARCH = LDAPSearch("ou=groups,dc=example,dc=com", 50 | # ldap.SCOPE_SUBTREE, "(objectClass=groupOfUniqueNames)" 51 | #) 52 | #AUTH_LDAP_GROUP_TYPE = GroupOfUniqueNamesType() 53 | # 54 | #AUTH_LDAP_USER_FLAGS_BY_GROUP = { 55 | # "is_active": ["cn=grouptopermit1,ou=groups,dc=example,dc=com", "cn=grouptopermit2,ou=groups,dc=example,dc=com"], 56 | # "is_staff": "cn=grouptopermit2,ou=groups,dc=example,dc=com", 57 | # "is_superuser": "cn=grouptopermit2,ou=groups,dc=example,dc=com" 58 | #} 59 | 60 | DATABASES = { 61 | 'default': { 62 | 'ENGINE': 'django.db.backends.sqlite3', 63 | 'NAME': os.path.join(LOCAL_PATH, '..', '..', 'webvirtmgr.sqlite3'), 64 | # The following settings are not used with sqlite3: 65 | 'USER': '', 66 | 'PASSWORD': '', 67 | 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. 68 | 'PORT': '', # Set to empty string for default. 69 | } 70 | } 71 | 72 | TIME_JS_REFRESH = 2000 73 | 74 | # Hosts/domain names that are valid for this site; required if DEBUG is False 75 | # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts 76 | ALLOWED_HOSTS = ['*'] 77 | 78 | # Local time zone for this installation. Choices can be found here: 79 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 80 | # although not all choices may be available on all operating systems. 81 | # In a Windows environment this must be set to your system time zone. 82 | TIME_ZONE = 'UTC' 83 | 84 | # Language code for this installation. All choices can be found here: 85 | # http://www.i18nguy.com/unicode/language-identifiers.html 86 | LANGUAGE_CODE = 'en-us' 87 | 88 | SITE_ID = 1 89 | 90 | # If you set this to False, Django will make some optimizations so as not 91 | # to load the internationalization machinery. 92 | USE_I18N = True 93 | 94 | # If you set this to False, Django will not format dates, numbers and 95 | # calendars according to the current locale. 96 | USE_L10N = True 97 | 98 | # If you set this to False, Django will not use timezone-aware datetimes. 99 | USE_TZ = True 100 | 101 | 102 | # Set STATIC ROOT & MEDIA ROOT to a path that your Webserver could pick up 103 | STATIC_URL = '/static/' 104 | STATIC_ROOT = os.path.join(LOCAL_PATH, '..', '..', 'static') 105 | 106 | MEDIA_ROOT = '' 107 | MEDIA_URL = '' 108 | 109 | # Keepalive interval and count for libvirt connections 110 | # 111 | # from: http://libvirt.org/html/libvirt-libvirt.html#virConnectSetKeepAlive 112 | # 113 | # Start sending keepalive messages after @interval seconds of inactivity and 114 | # consider the connection to be broken when no response is received after 115 | # @count keepalive messages sent in a row. In other words, sending count + 1 116 | # keepalive message results in closing the connection. When @interval is <= 0, 117 | # no keepalive messages will be sent. When @count is 0, the connection will be 118 | # automatically closed after @interval seconds of inactivity without sending 119 | # any keepalive messages. 120 | # 121 | # Keep in mind that by default (at least on ubuntu variants) the libvirt.d is 122 | # configured to send keepalive messages by default (interval: 5, count: 5). 123 | # If the libvirt.d keeps on sending keepalive messages form the server-side 124 | # we will never close the connection no matter what these parameters are set 125 | # to, as long as the libvirt.d is up and running at the server side. 126 | LIBVIRT_KEEPALIVE_INTERVAL = 5 127 | LIBVIRT_KEEPALIVE_COUNT = 5 128 | -------------------------------------------------------------------------------- /webvirtmgr/settings-dev.py: -------------------------------------------------------------------------------- 1 | from webvirtmgr.settings import * 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = DEBUG 5 | 6 | INSTALLED_APPS += ('django_jenkins',) 7 | 8 | JENKINS_TASKS = ( 9 | # 'django_jenkins.tasks.run_pylint', 10 | 'django_jenkins.tasks.with_coverage', 11 | 'django_jenkins.tasks.django_tests', 12 | 'django_jenkins.tasks.run_pep8', 13 | 'django_jenkins.tasks.run_pyflakes', 14 | ) 15 | -------------------------------------------------------------------------------- /webvirtmgr/static/css/bootstrap-multiselect.css: -------------------------------------------------------------------------------- 1 | 2 | .multiselect-container { 3 | position: absolute; 4 | list-style-type: none; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | .multiselect-container .input-group { 9 | margin: 5px; 10 | } 11 | .multiselect-container > li { 12 | padding: 0; 13 | } 14 | .multiselect-container > li > a.multiselect-all label { 15 | font-weight: bold; 16 | } 17 | .multiselect-container > li > label.multiselect-group { 18 | margin: 0; 19 | padding: 3px 20px 3px 20px; 20 | height: 100%; 21 | } 22 | .multiselect-container > li > a > label { 23 | margin: 0; 24 | height: 100%; 25 | cursor: pointer; 26 | font-weight: normal; 27 | } 28 | .multiselect-container > li > a > label.radio, 29 | .multiselect-container > li > a > label.checkbox { 30 | margin: 0; 31 | } 32 | .multiselect-container > li > a > label > input[type="checkbox"] { 33 | margin-bottom: 5px; 34 | } 35 | .btn-group > .btn-group:nth-child(2) > .multiselect.btn { 36 | border-top-left-radius: 4px; 37 | border-bottom-left-radius: 4px; 38 | } 39 | -------------------------------------------------------------------------------- /webvirtmgr/static/css/signin.css: -------------------------------------------------------------------------------- 1 | .form-signin { 2 | max-width: 330px; 3 | padding: 15px; 4 | margin: 0 auto; 5 | } 6 | 7 | .form-signin .form-signin-heading, 8 | .form-signin .checkbox { 9 | margin-bottom: 10px; 10 | } 11 | 12 | .form-signin .checkbox { 13 | font-weight: normal; 14 | } 15 | 16 | .form-signin .form-control { 17 | position: relative; 18 | font-size: 16px; 19 | height: auto; 20 | padding: 10px; 21 | -webkit-box-sizing: border-box; 22 | -moz-box-sizing: border-box; 23 | box-sizing: border-box; 24 | } 25 | 26 | .form-signin .form-control:focus { 27 | z-index: 2; 28 | } 29 | 30 | .form-signin input[type="text"] { 31 | margin-bottom: -1px; 32 | border-bottom-left-radius: 0; 33 | border-bottom-right-radius: 0; 34 | } 35 | 36 | .form-signin input[type="password"] { 37 | margin-bottom: 10px; 38 | border-top-left-radius: 0; 39 | border-top-right-radius: 0; 40 | } 41 | 42 | .logout { 43 | text-align: center; 44 | } -------------------------------------------------------------------------------- /webvirtmgr/static/css/table-sort.css: -------------------------------------------------------------------------------- 1 | table#sortTable th { 2 | background-image: url('../img/bg.gif'); 3 | background-repeat: no-repeat; 4 | background-position: center right; 5 | cursor: pointer; 6 | } 7 | 8 | table#sortTable th.sorter-false { 9 | background-image: none; 10 | } 11 | 12 | table#sortTable thead tr .tablesorter-headerAsc { 13 | background-image: url('../img/asc.gif'); 14 | } 15 | 16 | table#sortTable thead tr .tablesorter-headerDesc { 17 | background-image: url('../img/desc.gif'); 18 | } 19 | -------------------------------------------------------------------------------- /webvirtmgr/static/css/webvirtmgr.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #333; 3 | background-color: #fff; 4 | } 5 | 6 | pre { 7 | font-size: 12px; 8 | } 9 | 10 | input[type="radio"], input[type="checkbox"] { 11 | margin-top: 7px; 12 | } 13 | 14 | label input[type="radio"], label input[type="checkbox"] { 15 | margin: 0; 16 | } 17 | 18 | .container { 19 | max-width: 980px; 20 | } 21 | 22 | .navbar-inverse { 23 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .15); 24 | background-color: #563d7c; 25 | border-color: #463265; 26 | box-shadow: 0 1px 0 rgba(255, 255, 255, .1); 27 | } 28 | 29 | .navbar-inverse .navbar-brand, 30 | .navbar-inverse .navbar-nav > li > a { 31 | color: #f3ecff; 32 | } 33 | 34 | .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { 35 | color: #fff; 36 | background-color: transparent; 37 | } 38 | 39 | .page-header { 40 | margin-top: 0; 41 | } 42 | 43 | @media (min-width: 769px) { 44 | .page-header__filter h1 { 45 | float: left; 46 | } 47 | .page-header__filter .form-inline { 48 | float: right; 49 | } 50 | } 51 | 52 | .sidebar ul .list { 53 | display: block; 54 | padding: 8px 10px; 55 | margin: 0 0 5px 0; 56 | font-size: 14px; 57 | border-radius: 3px; 58 | text-decoration: none; 59 | color: #777; 60 | white-space: nowrap; 61 | overflow: hidden; 62 | text-overflow: ellipsis; 63 | cursor: pointer; 64 | } 65 | 66 | .sidebar ul { 67 | padding-left: 0; 68 | list-style: none; 69 | border-bottom: 1px solid #eee; 70 | margin-bottom: 25px; 71 | } 72 | @media (min-width: 981px) { 73 | .sidebar ul { 74 | border-bottom: 0; 75 | margin-bottom: 0; 76 | } 77 | } 78 | 79 | .sidebar .list.selected { 80 | color: #fff; 81 | background: #4183c4; 82 | } 83 | 84 | .sidebar .list:hover { 85 | color: #777; 86 | background: #eee; 87 | } 88 | 89 | .form-group { 90 | margin-top: 25px; 91 | } 92 | 93 | .table thead > tr > th { 94 | vertical-align: bottom; 95 | border-bottom: 1px solid #ddd; 96 | } 97 | 98 | .table-nolines > thead > tr > th, 99 | .table-nolines > tbody > tr > th, 100 | .table-nolines > tfoot > tr > th, 101 | .table-nolines > thead > tr > td, 102 | .table-nolines > tbody > tr > td, 103 | .table-nolines > tfoot > tr > td { 104 | border-top: 0; 105 | } 106 | 107 | .panel-success { 108 | margin-top: 20px; 109 | } 110 | 111 | .panel-danger { 112 | margin-top: 20px; 113 | } 114 | 115 | .panel-body:after { 116 | clear: none; 117 | } 118 | 119 | .well { 120 | margin-top: 20px; 121 | text-align: center; 122 | } 123 | 124 | .dl-horizontal dt { 125 | text-align: left; 126 | } 127 | 128 | .modal-body { 129 | padding-left: 10px; 130 | padding-bottom: 0; 131 | } 132 | 133 | #error { 134 | text-align: center; 135 | } 136 | 137 | .format-convert { 138 | display: none; 139 | } 140 | 141 | .inst-btn { 142 | width: 95px; 143 | text-align: center; 144 | } 145 | 146 | #lineLegend { 147 | margin-bottom: 20px; 148 | padding-left: 25px 149 | } 150 | 151 | .netIN { 152 | border-color: rgb(83, 191, 189); 153 | margin: 0.5em; 154 | border-style: solid; 155 | border-width: 0 0 0 1em; 156 | padding: 0 0.3em; 157 | } 158 | 159 | .netOUT { 160 | border-color: rgb(151, 187, 205); 161 | margin: 0.5em; 162 | border-style: solid; 163 | border-width: 0 0 0 1em; 164 | padding: 0 0.3em; 165 | } 166 | 167 | .diskRD { 168 | border-color: rgb(83, 191, 189); 169 | margin: 0.5em; 170 | border-style: solid; 171 | border-width: 0 0 0 1em; 172 | padding: 0 0.3em; 173 | } 174 | 175 | .diskWR { 176 | border-color: rgb(249, 134, 33); 177 | margin: 0.5em; 178 | border-style: solid; 179 | border-width: 0 0 0 1em; 180 | padding: 0 0.3em; 181 | } 182 | 183 | p { 184 | margin: 10px 10px 10px 10px; 185 | } 186 | 187 | .nav-tabs { 188 | background-color: #eee; 189 | } 190 | 191 | .nav-btn-act { 192 | height: 42px; 193 | } 194 | 195 | .tab-inst { 196 | padding: 15px; 197 | border: 1px solid #eee; 198 | border-top: 0; 199 | } 200 | 201 | #xmltextarea { 202 | font-family: Monaco, Menlo, Consolas, "Courier New", monospace; 203 | font-size: 12px; 204 | width: 100%; 205 | height: 340px; 206 | padding: 9.5px 0 0 9.5px; 207 | margin-bottom: 10px; 208 | background-color: rgb(245, 245, 245); 209 | border: 1px solid rgb(204, 204, 204); 210 | border-radius: 4px; 211 | } 212 | 213 | #viewXMLpre .pre-scrollable { 214 | width: 100%; 215 | } 216 | -------------------------------------------------------------------------------- /webvirtmgr/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /webvirtmgr/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /webvirtmgr/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /webvirtmgr/static/img/asc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/img/asc.gif -------------------------------------------------------------------------------- /webvirtmgr/static/img/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/img/bg.gif -------------------------------------------------------------------------------- /webvirtmgr/static/img/desc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/img/desc.gif -------------------------------------------------------------------------------- /webvirtmgr/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/img/favicon.ico -------------------------------------------------------------------------------- /webvirtmgr/static/js/infrastructure.js: -------------------------------------------------------------------------------- 1 | $.expr[':'].Contains = $.expr.createPseudo(function(arg) { 2 | return function( elem ) { 3 | return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0; 4 | }; 5 | }); 6 | 7 | $(document).ready(function() { 8 | // add event button labeled "filter" 9 | $('#filter_button').click(function(event) { 10 | // get value 11 | var filter_val = $('#filter_input').val(); 12 | if(filter_val == '') { 13 | // show all 14 | $('tbody tr').show(); 15 | } else { 16 | // show only matches 17 | $('tbody tr:Contains(\'' + filter_val + '\')').show(); 18 | // hide non-matching items 19 | $('tbody tr:not(:Contains(\'' + filter_val + '\'))').hide(); 20 | } 21 | }); 22 | 23 | // add event button labeled "clear" 24 | $('#filter_clear').click(function(event) { 25 | $('#filter_input').val(''); 26 | $('#filter_button').click(); 27 | }); 28 | 29 | // trigger filter when enter key pressed 30 | $('#filter_input').keyup(function(event){ 31 | if(event.keyCode == 13){ 32 | $('#filter_button').click(); 33 | } 34 | }); 35 | 36 | $('#hide_vms_bystate input[type=checkbox]').change(function () { 37 | $('tbody tr[data-status=' + $(this).data('value') + ']').toggle(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/Orbitron700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/js/novnc/Orbitron700.ttf -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/Orbitron700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/js/novnc/Orbitron700.woff -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/base64.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | // From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js 6 | 7 | /*jslint white: false, bitwise: false, plusplus: false */ 8 | /*global console */ 9 | 10 | var Base64 = { 11 | 12 | /* Convert data (an array of integers) to a Base64 string. */ 13 | toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''), 14 | base64Pad : '=', 15 | 16 | encode: function (data) { 17 | "use strict"; 18 | var result = ''; 19 | var toBase64Table = Base64.toBase64Table; 20 | var length = data.length 21 | var lengthpad = (length%3); 22 | var i = 0, j = 0; 23 | // Convert every three bytes to 4 ascii characters. 24 | /* BEGIN LOOP */ 25 | for (i = 0; i < (length - 2); i += 3) { 26 | result += toBase64Table[data[i] >> 2]; 27 | result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; 28 | result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; 29 | result += toBase64Table[data[i+2] & 0x3f]; 30 | } 31 | /* END LOOP */ 32 | 33 | // Convert the remaining 1 or 2 bytes, pad out to 4 characters. 34 | if (lengthpad === 2) { 35 | j = length - lengthpad; 36 | result += toBase64Table[data[j] >> 2]; 37 | result += toBase64Table[((data[j] & 0x03) << 4) + (data[j+1] >> 4)]; 38 | result += toBase64Table[(data[j+1] & 0x0f) << 2]; 39 | result += toBase64Table[64]; 40 | } else if (lengthpad === 1) { 41 | j = length - lengthpad; 42 | result += toBase64Table[data[j] >> 2]; 43 | result += toBase64Table[(data[j] & 0x03) << 4]; 44 | result += toBase64Table[64]; 45 | result += toBase64Table[64]; 46 | } 47 | 48 | return result; 49 | }, 50 | 51 | /* Convert Base64 data to a string */ 52 | toBinaryTable : [ 53 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 54 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 55 | -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 56 | 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, 57 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 58 | 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 59 | -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 60 | 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 61 | ], 62 | 63 | decode: function (data, offset) { 64 | "use strict"; 65 | offset = typeof(offset) !== 'undefined' ? offset : 0; 66 | var toBinaryTable = Base64.toBinaryTable; 67 | var base64Pad = Base64.base64Pad; 68 | var result, result_length, idx, i, c, padding; 69 | var leftbits = 0; // number of bits decoded, but yet to be appended 70 | var leftdata = 0; // bits decoded, but yet to be appended 71 | var data_length = data.indexOf('=') - offset; 72 | 73 | if (data_length < 0) { data_length = data.length - offset; } 74 | 75 | /* Every four characters is 3 resulting numbers */ 76 | result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5); 77 | result = new Array(result_length); 78 | 79 | // Convert one by one. 80 | /* BEGIN LOOP */ 81 | for (idx = 0, i = offset; i < data.length; i++) { 82 | c = toBinaryTable[data.charCodeAt(i) & 0x7f]; 83 | padding = (data.charAt(i) === base64Pad); 84 | // Skip illegal characters and whitespace 85 | if (c === -1) { 86 | console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i); 87 | continue; 88 | } 89 | 90 | // Collect data into leftdata, update bitcount 91 | leftdata = (leftdata << 6) | c; 92 | leftbits += 6; 93 | 94 | // If we have 8 or more bits, append 8 bits to the result 95 | if (leftbits >= 8) { 96 | leftbits -= 8; 97 | // Append if not padding. 98 | if (!padding) { 99 | result[idx++] = (leftdata >> leftbits) & 0xff; 100 | } 101 | leftdata &= (1 << leftbits) - 1; 102 | } 103 | } 104 | /* END LOOP */ 105 | 106 | // If there are any bits left, the base64 string was corrupted 107 | if (leftbits) { 108 | throw {name: 'Base64-Error', 109 | message: 'Corrupted base64 string'}; 110 | } 111 | 112 | return result; 113 | } 114 | 115 | }; /* End of Base64 namespace */ 116 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/black.css: -------------------------------------------------------------------------------- 1 | /* 2 | * noVNC black CSS 3 | * Copyright (C) 2012 Joel Martin 4 | * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) 5 | * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). 6 | */ 7 | 8 | #keyboardinput { 9 | background-color:#000; 10 | } 11 | 12 | #noVNC-control-bar { 13 | background: #4c4c4c; /* Old browsers */ 14 | background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ 15 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ 16 | background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ 17 | background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ 18 | background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ 19 | background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ 20 | } 21 | 22 | .triangle-right { 23 | border:2px solid #fff; 24 | background:#000; 25 | color:#fff; 26 | } 27 | 28 | .noVNC_status_button { 29 | font-size: 12px; 30 | vertical-align: middle; 31 | border:1px solid #4c4c4c; 32 | 33 | background: #4c4c4c; /* Old browsers */ 34 | background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ 35 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ 36 | background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ 37 | background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ 38 | background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ 39 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ 40 | background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ 41 | } 42 | 43 | .noVNC_status_button_selected { 44 | background: #9dd53a; /* Old browsers */ 45 | background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ 46 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ 47 | background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ 48 | background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ 49 | background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ 50 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ 51 | background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ 52 | } 53 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/blue.css: -------------------------------------------------------------------------------- 1 | /* 2 | * noVNC blue CSS 3 | * Copyright (C) 2012 Joel Martin 4 | * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) 5 | * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). 6 | */ 7 | 8 | #noVNC-control-bar { 9 | background-color:#04073d; 10 | background-image: -webkit-gradient( 11 | linear, 12 | left bottom, 13 | left top, 14 | color-stop(0.54, rgb(10,15,79)), 15 | color-stop(0.5, rgb(4,7,61)) 16 | ); 17 | background-image: -moz-linear-gradient( 18 | center bottom, 19 | rgb(10,15,79) 54%, 20 | rgb(4,7,61) 50% 21 | ); 22 | } 23 | 24 | .triangle-right { 25 | border:2px solid #fff; 26 | background:#04073d; 27 | color:#fff; 28 | } 29 | 30 | #keyboardinput { 31 | background-color:#04073d; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/playback.js: -------------------------------------------------------------------------------- 1 | /* 2 | * noVNC: HTML5 VNC client 3 | * Copyright (C) 2012 Joel Martin 4 | * Licensed under MPL 2.0 (see LICENSE.txt) 5 | */ 6 | 7 | "use strict"; 8 | /*jslint browser: true, white: false */ 9 | /*global Util, VNC_frame_data, finish */ 10 | 11 | var rfb, mode, test_state, frame_idx, frame_length, 12 | iteration, iterations, istart_time, 13 | 14 | // Pre-declarations for jslint 15 | send_array, next_iteration, queue_next_packet, do_packet; 16 | 17 | // Override send_array 18 | send_array = function (arr) { 19 | // Stub out send_array 20 | }; 21 | 22 | next_iteration = function () { 23 | if (iteration === 0) { 24 | frame_length = VNC_frame_data.length; 25 | test_state = 'running'; 26 | } else { 27 | rfb.disconnect(); 28 | } 29 | 30 | if (test_state !== 'running') { return; } 31 | 32 | iteration += 1; 33 | if (iteration > iterations) { 34 | finish(); 35 | return; 36 | } 37 | 38 | frame_idx = 0; 39 | istart_time = (new Date()).getTime(); 40 | rfb.connect('test', 0, "bogus"); 41 | 42 | queue_next_packet(); 43 | 44 | }; 45 | 46 | queue_next_packet = function () { 47 | var frame, foffset, toffset, delay; 48 | if (test_state !== 'running') { return; } 49 | 50 | frame = VNC_frame_data[frame_idx]; 51 | while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) { 52 | //Util.Debug("Send frame " + frame_idx); 53 | frame_idx += 1; 54 | frame = VNC_frame_data[frame_idx]; 55 | } 56 | 57 | if (frame === 'EOF') { 58 | Util.Debug("Finished, found EOF"); 59 | next_iteration(); 60 | return; 61 | } 62 | if (frame_idx >= frame_length) { 63 | Util.Debug("Finished, no more frames"); 64 | next_iteration(); 65 | return; 66 | } 67 | 68 | if (mode === 'realtime') { 69 | foffset = frame.slice(1, frame.indexOf('{', 1)); 70 | toffset = (new Date()).getTime() - istart_time; 71 | delay = foffset - toffset; 72 | if (delay < 1) { 73 | delay = 1; 74 | } 75 | 76 | setTimeout(do_packet, delay); 77 | } else { 78 | setTimeout(do_packet, 1); 79 | } 80 | }; 81 | 82 | var bytes_processed = 0; 83 | 84 | do_packet = function () { 85 | //Util.Debug("Processing frame: " + frame_idx); 86 | var frame = VNC_frame_data[frame_idx], 87 | start = frame.indexOf('{', 1) + 1; 88 | bytes_processed += frame.length - start; 89 | if (VNC_frame_encoding === 'binary') { 90 | var u8 = new Uint8Array(frame.length - start); 91 | for (var i = 0; i < frame.length - start; i++) { 92 | u8[i] = frame.charCodeAt(start + i); 93 | } 94 | rfb.recv_message({'data' : u8}); 95 | } else { 96 | rfb.recv_message({'data' : frame.slice(start)}); 97 | } 98 | frame_idx += 1; 99 | 100 | queue_next_packet(); 101 | }; 102 | 103 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/novnc/web-socket-js/WebSocketMain.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/static/js/novnc/web-socket-js/WebSocketMain.swf -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/bitmap.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2012 by Jeremy P. White 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | 22 | /*---------------------------------------------------------------------------- 23 | ** bitmap.js 24 | ** Handle SPICE_IMAGE_TYPE_BITMAP 25 | **--------------------------------------------------------------------------*/ 26 | function convert_spice_bitmap_to_web(context, spice_bitmap) 27 | { 28 | var ret; 29 | var offset, x; 30 | var u8 = new Uint8Array(spice_bitmap.data); 31 | if (spice_bitmap.format != SPICE_BITMAP_FMT_32BIT && 32 | spice_bitmap.format != SPICE_BITMAP_FMT_RGBA) 33 | return undefined; 34 | 35 | ret = context.createImageData(spice_bitmap.x, spice_bitmap.y); 36 | for (offset = 0; offset < (spice_bitmap.y * spice_bitmap.stride); ) 37 | for (x = 0; x < spice_bitmap.x; x++, offset += 4) 38 | { 39 | ret.data[offset + 0 ] = u8[offset + 2]; 40 | ret.data[offset + 1 ] = u8[offset + 1]; 41 | ret.data[offset + 2 ] = u8[offset + 0]; 42 | 43 | // FIXME - We effectively treat all images as having SPICE_IMAGE_FLAGS_HIGH_BITS_SET 44 | if (spice_bitmap.format == SPICE_BITMAP_FMT_32BIT) 45 | ret.data[offset + 3] = 255; 46 | else 47 | ret.data[offset + 3] = u8[offset]; 48 | } 49 | 50 | return ret; 51 | } 52 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/cursor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2012 by Jeremy P. White 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | 22 | /*---------------------------------------------------------------------------- 23 | ** SpiceCursorConn 24 | ** Drive the Spice Cursor Channel 25 | **--------------------------------------------------------------------------*/ 26 | function SpiceCursorConn() 27 | { 28 | SpiceConn.apply(this, arguments); 29 | } 30 | 31 | SpiceCursorConn.prototype = Object.create(SpiceConn.prototype); 32 | SpiceCursorConn.prototype.process_channel_message = function(msg) 33 | { 34 | if (msg.type == SPICE_MSG_CURSOR_INIT) 35 | { 36 | var cursor_init = new SpiceMsgCursorInit(msg.data); 37 | DEBUG > 1 && console.log("SpiceMsgCursorInit"); 38 | if (this.parent && this.parent.inputs && 39 | this.parent.inputs.mouse_mode == SPICE_MOUSE_MODE_SERVER) 40 | { 41 | // FIXME - this imagines that the server actually 42 | // provides the current cursor position, 43 | // instead of 0,0. As of May 11, 2012, 44 | // that assumption was false :-(. 45 | this.parent.inputs.mousex = cursor_init.position.x; 46 | this.parent.inputs.mousey = cursor_init.position.y; 47 | } 48 | // FIXME - We don't handle most of the parameters here... 49 | return true; 50 | } 51 | 52 | if (msg.type == SPICE_MSG_CURSOR_SET) 53 | { 54 | var cursor_set = new SpiceMsgCursorSet(msg.data); 55 | DEBUG > 1 && console.log("SpiceMsgCursorSet"); 56 | if (cursor_set.flags & SPICE_CURSOR_FLAGS_NONE) 57 | { 58 | document.getElementById(this.parent.screen_id).style.cursor = "none"; 59 | return true; 60 | } 61 | 62 | if (cursor_set.flags > 0) 63 | this.log_warn("FIXME: No support for cursor flags " + cursor_set.flags); 64 | 65 | if (cursor_set.cursor.header.type != SPICE_CURSOR_TYPE_ALPHA) 66 | { 67 | this.log_warn("FIXME: No support for cursor type " + cursor_set.cursor.header.type); 68 | return false; 69 | } 70 | 71 | this.set_cursor(cursor_set.cursor); 72 | 73 | return true; 74 | } 75 | 76 | if (msg.type == SPICE_MSG_CURSOR_HIDE) 77 | { 78 | DEBUG > 1 && console.log("SpiceMsgCursorHide"); 79 | document.getElementById(this.parent.screen_id).style.cursor = "none"; 80 | return true; 81 | } 82 | 83 | if (msg.type == SPICE_MSG_CURSOR_RESET) 84 | { 85 | DEBUG > 1 && console.log("SpiceMsgCursorReset"); 86 | document.getElementById(this.parent.screen_id).style.cursor = "auto"; 87 | return true; 88 | } 89 | 90 | if (msg.type == SPICE_MSG_CURSOR_INVAL_ALL) 91 | { 92 | DEBUG > 1 && console.log("SpiceMsgCursorInvalAll"); 93 | // FIXME - There may be something useful to do here... 94 | return true; 95 | } 96 | 97 | return false; 98 | } 99 | 100 | SpiceCursorConn.prototype.set_cursor = function(cursor) 101 | { 102 | var pngstr = create_rgba_png(cursor.header.height, cursor.header.width, cursor.data); 103 | var curstr = 'url(data:image/png,' + pngstr + ') ' + 104 | cursor.header.hot_spot_x + ' ' + cursor.header.hot_spot_y + ", default"; 105 | var screen = document.getElementById(this.parent.screen_id); 106 | screen.style.cursor = 'auto'; 107 | screen.style.cursor = curstr; 108 | if (window.getComputedStyle(screen, null).cursor == 'auto') 109 | SpiceSimulateCursor.simulate_cursor(this, cursor, screen, pngstr); 110 | } 111 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/filexfer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2014 Red Hat, Inc. 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | function SpiceFileXferTask(id, file) 22 | { 23 | this.id = id; 24 | this.file = file; 25 | } 26 | 27 | SpiceFileXferTask.prototype.create_progressbar = function() 28 | { 29 | var _this = this; 30 | var cancel = document.createElement("input"); 31 | this.progressbar_container = document.createElement("div"); 32 | this.progressbar = document.createElement("progress"); 33 | 34 | cancel.type = 'button'; 35 | cancel.value = 'Cancel'; 36 | cancel.style.float = 'right'; 37 | cancel.onclick = function() 38 | { 39 | _this.cancelled = true; 40 | _this.remove_progressbar(); 41 | }; 42 | 43 | this.progressbar.setAttribute('max', this.file.size); 44 | this.progressbar.setAttribute('value', 0); 45 | this.progressbar.style.width = '100%'; 46 | this.progressbar.style.margin = '4px auto'; 47 | this.progressbar.style.display = 'inline-block'; 48 | this.progressbar_container.style.width = '90%'; 49 | this.progressbar_container.style.margin = 'auto'; 50 | this.progressbar_container.style.padding = '4px'; 51 | this.progressbar_container.textContent = this.file.name; 52 | this.progressbar_container.appendChild(cancel); 53 | this.progressbar_container.appendChild(this.progressbar); 54 | document.getElementById('spice-xfer-area').appendChild(this.progressbar_container); 55 | } 56 | 57 | SpiceFileXferTask.prototype.update_progressbar = function(value) 58 | { 59 | this.progressbar.setAttribute('value', value); 60 | } 61 | 62 | SpiceFileXferTask.prototype.remove_progressbar = function() 63 | { 64 | if (this.progressbar_container && this.progressbar_container.parentNode) 65 | this.progressbar_container.parentNode.removeChild(this.progressbar_container); 66 | } 67 | 68 | function handle_file_dragover(e) 69 | { 70 | e.stopPropagation(); 71 | e.preventDefault(); 72 | e.dataTransfer.dropEffect = 'copy'; 73 | } 74 | 75 | function handle_file_drop(e) 76 | { 77 | var sc = window.spice_connection; 78 | var files = e.dataTransfer.files; 79 | 80 | e.stopPropagation(); 81 | e.preventDefault(); 82 | for (var i = files.length - 1; i >= 0; i--) 83 | { 84 | if (files[i].type); // do not copy a directory 85 | sc.file_xfer_start(files[i]); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/prng4.js: -------------------------------------------------------------------------------- 1 | // Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012 2 | 3 | /* 4 | * Copyright (c) 2003-2005 Tom Wu 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21 | * 22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * In addition, the following condition applies: 29 | * 30 | * All redistributions must retain an intact copy of this copyright notice 31 | * and disclaimer. 32 | */ 33 | 34 | 35 | // prng4.js - uses Arcfour as a PRNG 36 | 37 | function Arcfour() { 38 | this.i = 0; 39 | this.j = 0; 40 | this.S = new Array(); 41 | } 42 | 43 | // Initialize arcfour context from key, an array of ints, each from [0..255] 44 | function ARC4init(key) { 45 | var i, j, t; 46 | for(i = 0; i < 256; ++i) 47 | this.S[i] = i; 48 | j = 0; 49 | for(i = 0; i < 256; ++i) { 50 | j = (j + this.S[i] + key[i % key.length]) & 255; 51 | t = this.S[i]; 52 | this.S[i] = this.S[j]; 53 | this.S[j] = t; 54 | } 55 | this.i = 0; 56 | this.j = 0; 57 | } 58 | 59 | function ARC4next() { 60 | var t; 61 | this.i = (this.i + 1) & 255; 62 | this.j = (this.j + this.S[this.i]) & 255; 63 | t = this.S[this.i]; 64 | this.S[this.i] = this.S[this.j]; 65 | this.S[this.j] = t; 66 | return this.S[(t + this.S[this.i]) & 255]; 67 | } 68 | 69 | Arcfour.prototype.init = ARC4init; 70 | Arcfour.prototype.next = ARC4next; 71 | 72 | // Plug in your RNG constructor here 73 | function prng_newstate() { 74 | return new Arcfour(); 75 | } 76 | 77 | // Pool size must be a multiple of 4 and greater than 32. 78 | // An array of bytes the size of the pool will be passed to init() 79 | var rng_psize = 256; 80 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/resize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2014 by Jeremy P. White 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | /*---------------------------------------------------------------------------- 22 | ** resize.js 23 | ** This bit of Javascript is a set of logic to help with window 24 | ** resizing, using the agent channel to request screen resizes. 25 | ** 26 | ** It's a bit tricky, as we want to wait for resizing to settle down 27 | ** before sending a size. Further, while horizontal resizing to use the whole 28 | ** browser width is fairly easy to arrange with css, resizing an element to use 29 | ** the whole vertical space (or to force a middle div to consume the bulk of the browser 30 | ** window size) is tricky, and the consensus seems to be that Javascript is 31 | ** the only right way to do it. 32 | **--------------------------------------------------------------------------*/ 33 | function resize_helper(sc) 34 | { 35 | var w = document.getElementById(sc.screen_id).clientWidth; 36 | var h = document.getElementById(sc.screen_id).clientHeight; 37 | 38 | var m = document.getElementById(sc.message_id); 39 | 40 | /* Resize vertically; basically we leave a 20 pixel margin 41 | at the bottom, and use the position of the message window 42 | to figure out how to resize */ 43 | var hd = window.innerHeight - m.offsetHeight - m.offsetTop - 20; 44 | 45 | /* Xorg requires height be a multiple of 8; round up */ 46 | h = h + hd; 47 | if (h % 8 > 0) 48 | h += (8 - (h % 8)); 49 | 50 | /* Xorg requires width be a multiple of 8; round up */ 51 | if (w % 8 > 0) 52 | w += (8 - (w % 8)); 53 | 54 | 55 | sc.resize_window(0, w, h, 32, 0, 0); 56 | sc.spice_resize_timer = undefined; 57 | } 58 | 59 | function handle_resize(e) 60 | { 61 | var sc = window.spice_connection; 62 | 63 | if (sc && sc.spice_resize_timer) 64 | { 65 | window.clearTimeout(sc.spice_resize_timer); 66 | sc.spice_resize_timer = undefined; 67 | } 68 | 69 | sc.spice_resize_timer = window.setTimeout(resize_helper, 200, sc); 70 | } 71 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/rng.js: -------------------------------------------------------------------------------- 1 | // Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012 2 | 3 | /* 4 | * Copyright (c) 2003-2005 Tom Wu 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21 | * 22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * In addition, the following condition applies: 29 | * 30 | * All redistributions must retain an intact copy of this copyright notice 31 | * and disclaimer. 32 | */ 33 | 34 | 35 | // Random number generator - requires a PRNG backend, e.g. prng4.js 36 | 37 | // For best results, put code like 38 | // 39 | // in your main HTML document. 40 | 41 | var rng_state; 42 | var rng_pool; 43 | var rng_pptr; 44 | 45 | // Mix in a 32-bit integer into the pool 46 | function rng_seed_int(x) { 47 | rng_pool[rng_pptr++] ^= x & 255; 48 | rng_pool[rng_pptr++] ^= (x >> 8) & 255; 49 | rng_pool[rng_pptr++] ^= (x >> 16) & 255; 50 | rng_pool[rng_pptr++] ^= (x >> 24) & 255; 51 | if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; 52 | } 53 | 54 | // Mix in the current time (w/milliseconds) into the pool 55 | function rng_seed_time() { 56 | rng_seed_int(new Date().getTime()); 57 | } 58 | 59 | // Initialize the pool with junk if needed. 60 | if(rng_pool == null) { 61 | rng_pool = new Array(); 62 | rng_pptr = 0; 63 | var t; 64 | if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { 65 | // Extract entropy (256 bits) from NS4 RNG if available 66 | var z = window.crypto.random(32); 67 | for(t = 0; t < z.length; ++t) 68 | rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; 69 | } 70 | while(rng_pptr < rng_psize) { // extract some randomness from Math.random() 71 | t = Math.floor(65536 * Math.random()); 72 | rng_pool[rng_pptr++] = t >>> 8; 73 | rng_pool[rng_pptr++] = t & 255; 74 | } 75 | rng_pptr = 0; 76 | rng_seed_time(); 77 | //rng_seed_int(window.screenX); 78 | //rng_seed_int(window.screenY); 79 | } 80 | 81 | function rng_get_byte() { 82 | if(rng_state == null) { 83 | rng_seed_time(); 84 | rng_state = prng_newstate(); 85 | rng_state.init(rng_pool); 86 | for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) 87 | rng_pool[rng_pptr] = 0; 88 | rng_pptr = 0; 89 | //rng_pool = null; 90 | } 91 | // TODO: allow reseeding after first request 92 | return rng_state.next(); 93 | } 94 | 95 | function rng_get_bytes(ba) { 96 | var i; 97 | for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); 98 | } 99 | 100 | function SecureRandom() {} 101 | 102 | SecureRandom.prototype.nextBytes = rng_get_bytes; 103 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/rsa.js: -------------------------------------------------------------------------------- 1 | // Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012 2 | 3 | /* 4 | * Copyright (c) 2003-2005 Tom Wu 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21 | * 22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * In addition, the following condition applies: 29 | * 30 | * All redistributions must retain an intact copy of this copyright notice 31 | * and disclaimer. 32 | */ 33 | 34 | 35 | // Depends on jsbn.js and rng.js 36 | 37 | // Version 1.1: support utf-8 encoding in pkcs1pad2 38 | 39 | // convert a (hex) string to a bignum object 40 | function parseBigInt(str,r) { 41 | return new BigInteger(str,r); 42 | } 43 | 44 | function linebrk(s,n) { 45 | var ret = ""; 46 | var i = 0; 47 | while(i + n < s.length) { 48 | ret += s.substring(i,i+n) + "\n"; 49 | i += n; 50 | } 51 | return ret + s.substring(i,s.length); 52 | } 53 | 54 | function byte2Hex(b) { 55 | if(b < 0x10) 56 | return "0" + b.toString(16); 57 | else 58 | return b.toString(16); 59 | } 60 | 61 | // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint 62 | function pkcs1pad2(s,n) { 63 | if(n < s.length + 11) { // TODO: fix for utf-8 64 | alert("Message too long for RSA"); 65 | return null; 66 | } 67 | var ba = new Array(); 68 | var i = s.length - 1; 69 | while(i >= 0 && n > 0) { 70 | var c = s.charCodeAt(i--); 71 | if(c < 128) { // encode using utf-8 72 | ba[--n] = c; 73 | } 74 | else if((c > 127) && (c < 2048)) { 75 | ba[--n] = (c & 63) | 128; 76 | ba[--n] = (c >> 6) | 192; 77 | } 78 | else { 79 | ba[--n] = (c & 63) | 128; 80 | ba[--n] = ((c >> 6) & 63) | 128; 81 | ba[--n] = (c >> 12) | 224; 82 | } 83 | } 84 | ba[--n] = 0; 85 | var rng = new SecureRandom(); 86 | var x = new Array(); 87 | while(n > 2) { // random non-zero pad 88 | x[0] = 0; 89 | while(x[0] == 0) rng.nextBytes(x); 90 | ba[--n] = x[0]; 91 | } 92 | ba[--n] = 2; 93 | ba[--n] = 0; 94 | return new BigInteger(ba); 95 | } 96 | 97 | // "empty" RSA key constructor 98 | function RSAKey() { 99 | this.n = null; 100 | this.e = 0; 101 | this.d = null; 102 | this.p = null; 103 | this.q = null; 104 | this.dmp1 = null; 105 | this.dmq1 = null; 106 | this.coeff = null; 107 | } 108 | 109 | // Set the public key fields N and e from hex strings 110 | function RSASetPublic(N,E) { 111 | if(N != null && E != null && N.length > 0 && E.length > 0) { 112 | this.n = parseBigInt(N,16); 113 | this.e = parseInt(E,16); 114 | } 115 | else 116 | alert("Invalid RSA public key"); 117 | } 118 | 119 | // Perform raw public operation on "x": return x^e (mod n) 120 | function RSADoPublic(x) { 121 | return x.modPowInt(this.e, this.n); 122 | } 123 | 124 | // Return the PKCS#1 RSA encryption of "text" as an even-length hex string 125 | function RSAEncrypt(text) { 126 | var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); 127 | if(m == null) return null; 128 | var c = this.doPublic(m); 129 | if(c == null) return null; 130 | var h = c.toString(16); 131 | if((h.length & 1) == 0) return h; else return "0" + h; 132 | } 133 | 134 | // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string 135 | //function RSAEncryptB64(text) { 136 | // var h = this.encrypt(text); 137 | // if(h) return hex2b64(h); else return null; 138 | //} 139 | 140 | // protected 141 | RSAKey.prototype.doPublic = RSADoPublic; 142 | 143 | // public 144 | RSAKey.prototype.setPublic = RSASetPublic; 145 | RSAKey.prototype.encrypt = RSAEncrypt; 146 | //RSAKey.prototype.encrypt_b64 = RSAEncryptB64; 147 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/spicearraybuffer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2012 by Jeremy P. White 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | /*---------------------------------------------------------------------------- 22 | ** SpiceArrayBufferSlice 23 | ** This function is a work around for IE 10, which has no slice() 24 | ** method in it's subclass. 25 | **--------------------------------------------------------------------------*/ 26 | function SpiceArrayBufferSlice(start, end) 27 | { 28 | start = start || 0; 29 | end = end || this.byteLength; 30 | if (end < 0) 31 | end = this.byteLength + end; 32 | if (start < 0) 33 | start = this.byteLength + start; 34 | if (start < 0) 35 | start = 0; 36 | if (end < 0) 37 | end = 0; 38 | if (end > this.byteLength) 39 | end = this.byteLength; 40 | if (start > end) 41 | start = end; 42 | 43 | var ret = new ArrayBuffer(end - start); 44 | var in1 = new Uint8Array(this, start, end - start); 45 | var out = new Uint8Array(ret); 46 | var i; 47 | 48 | for (i = 0; i < end - start; i++) 49 | out[i] = in1[i]; 50 | 51 | return ret; 52 | } 53 | 54 | if (! ArrayBuffer.prototype.slice) 55 | { 56 | ArrayBuffer.prototype.slice = SpiceArrayBufferSlice; 57 | console.log("WARNING: ArrayBuffer.slice() is missing; we are extending ArrayBuffer to compensate"); 58 | } 59 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/spicedataview.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2012 by Jeremy P. White 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | /*---------------------------------------------------------------------------- 22 | ** SpiceDataView 23 | ** FIXME FIXME 24 | ** This is used because Firefox does not have DataView yet. 25 | ** We should use DataView if we have it, because it *has* to 26 | ** be faster than this code 27 | **--------------------------------------------------------------------------*/ 28 | function SpiceDataView(buffer, byteOffset, byteLength) 29 | { 30 | if (byteOffset !== undefined) 31 | { 32 | if (byteLength !== undefined) 33 | this.u8 = new Uint8Array(buffer, byteOffset, byteLength); 34 | else 35 | this.u8 = new Uint8Array(buffer, byteOffset); 36 | } 37 | else 38 | this.u8 = new Uint8Array(buffer); 39 | }; 40 | 41 | SpiceDataView.prototype = { 42 | getUint8: function(byteOffset) 43 | { 44 | return this.u8[byteOffset]; 45 | }, 46 | getUint16: function(byteOffset, littleEndian) 47 | { 48 | var low = 1, high = 0; 49 | if (littleEndian) 50 | { 51 | low = 0; 52 | high = 1; 53 | } 54 | 55 | return (this.u8[byteOffset + high] << 8) | this.u8[byteOffset + low]; 56 | }, 57 | getUint32: function(byteOffset, littleEndian) 58 | { 59 | var low = 2, high = 0; 60 | if (littleEndian) 61 | { 62 | low = 0; 63 | high = 2; 64 | } 65 | 66 | return (this.getUint16(byteOffset + high, littleEndian) << 16) | 67 | this.getUint16(byteOffset + low, littleEndian); 68 | }, 69 | getUint64: function (byteOffset, littleEndian) 70 | { 71 | var low = 4, high = 0; 72 | if (littleEndian) 73 | { 74 | low = 0; 75 | high = 4; 76 | } 77 | 78 | return (this.getUint32(byteOffset + high, littleEndian) << 32) | 79 | this.getUint32(byteOffset + low, littleEndian); 80 | }, 81 | setUint8: function(byteOffset, b) 82 | { 83 | this.u8[byteOffset] = (b & 0xff); 84 | }, 85 | setUint16: function(byteOffset, i, littleEndian) 86 | { 87 | var low = 1, high = 0; 88 | if (littleEndian) 89 | { 90 | low = 0; 91 | high = 1; 92 | } 93 | this.u8[byteOffset + high] = (i & 0xffff) >> 8; 94 | this.u8[byteOffset + low] = (i & 0x00ff); 95 | }, 96 | setUint32: function(byteOffset, w, littleEndian) 97 | { 98 | var low = 2, high = 0; 99 | if (littleEndian) 100 | { 101 | low = 0; 102 | high = 2; 103 | } 104 | 105 | this.setUint16(byteOffset + high, (w & 0xffffffff) >> 16, littleEndian); 106 | this.setUint16(byteOffset + low, (w & 0x0000ffff), littleEndian); 107 | }, 108 | setUint64: function(byteOffset, w, littleEndian) 109 | { 110 | var low = 4, high = 0; 111 | if (littleEndian) 112 | { 113 | low = 0; 114 | high = 4; 115 | } 116 | 117 | this.setUint32(byteOffset + high, (w & 0xffffffffffffffff) >> 32, littleEndian); 118 | this.setUint32(byteOffset + low, (w & 0x00000000ffffffff), littleEndian); 119 | }, 120 | } 121 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/thirdparty/prng4.js: -------------------------------------------------------------------------------- 1 | // Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012 2 | 3 | /* 4 | * Copyright (c) 2003-2005 Tom Wu 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21 | * 22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * In addition, the following condition applies: 29 | * 30 | * All redistributions must retain an intact copy of this copyright notice 31 | * and disclaimer. 32 | */ 33 | 34 | 35 | // prng4.js - uses Arcfour as a PRNG 36 | 37 | function Arcfour() { 38 | this.i = 0; 39 | this.j = 0; 40 | this.S = new Array(); 41 | } 42 | 43 | // Initialize arcfour context from key, an array of ints, each from [0..255] 44 | function ARC4init(key) { 45 | var i, j, t; 46 | for(i = 0; i < 256; ++i) 47 | this.S[i] = i; 48 | j = 0; 49 | for(i = 0; i < 256; ++i) { 50 | j = (j + this.S[i] + key[i % key.length]) & 255; 51 | t = this.S[i]; 52 | this.S[i] = this.S[j]; 53 | this.S[j] = t; 54 | } 55 | this.i = 0; 56 | this.j = 0; 57 | } 58 | 59 | function ARC4next() { 60 | var t; 61 | this.i = (this.i + 1) & 255; 62 | this.j = (this.j + this.S[this.i]) & 255; 63 | t = this.S[this.i]; 64 | this.S[this.i] = this.S[this.j]; 65 | this.S[this.j] = t; 66 | return this.S[(t + this.S[this.i]) & 255]; 67 | } 68 | 69 | Arcfour.prototype.init = ARC4init; 70 | Arcfour.prototype.next = ARC4next; 71 | 72 | // Plug in your RNG constructor here 73 | function prng_newstate() { 74 | return new Arcfour(); 75 | } 76 | 77 | // Pool size must be a multiple of 4 and greater than 32. 78 | // An array of bytes the size of the pool will be passed to init() 79 | var rng_psize = 256; 80 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/thirdparty/rng.js: -------------------------------------------------------------------------------- 1 | // Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012 2 | 3 | /* 4 | * Copyright (c) 2003-2005 Tom Wu 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21 | * 22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * In addition, the following condition applies: 29 | * 30 | * All redistributions must retain an intact copy of this copyright notice 31 | * and disclaimer. 32 | */ 33 | 34 | 35 | // Random number generator - requires a PRNG backend, e.g. prng4.js 36 | 37 | // For best results, put code like 38 | // 39 | // in your main HTML document. 40 | 41 | var rng_state; 42 | var rng_pool; 43 | var rng_pptr; 44 | 45 | // Mix in a 32-bit integer into the pool 46 | function rng_seed_int(x) { 47 | rng_pool[rng_pptr++] ^= x & 255; 48 | rng_pool[rng_pptr++] ^= (x >> 8) & 255; 49 | rng_pool[rng_pptr++] ^= (x >> 16) & 255; 50 | rng_pool[rng_pptr++] ^= (x >> 24) & 255; 51 | if(rng_pptr >= rng_psize) rng_pptr -= rng_psize; 52 | } 53 | 54 | // Mix in the current time (w/milliseconds) into the pool 55 | function rng_seed_time() { 56 | rng_seed_int(new Date().getTime()); 57 | } 58 | 59 | // Initialize the pool with junk if needed. 60 | if(rng_pool == null) { 61 | rng_pool = new Array(); 62 | rng_pptr = 0; 63 | var t; 64 | if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) { 65 | // Extract entropy (256 bits) from NS4 RNG if available 66 | var z = window.crypto.random(32); 67 | for(t = 0; t < z.length; ++t) 68 | rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; 69 | } 70 | while(rng_pptr < rng_psize) { // extract some randomness from Math.random() 71 | t = Math.floor(65536 * Math.random()); 72 | rng_pool[rng_pptr++] = t >>> 8; 73 | rng_pool[rng_pptr++] = t & 255; 74 | } 75 | rng_pptr = 0; 76 | rng_seed_time(); 77 | //rng_seed_int(window.screenX); 78 | //rng_seed_int(window.screenY); 79 | } 80 | 81 | function rng_get_byte() { 82 | if(rng_state == null) { 83 | rng_seed_time(); 84 | rng_state = prng_newstate(); 85 | rng_state.init(rng_pool); 86 | for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) 87 | rng_pool[rng_pptr] = 0; 88 | rng_pptr = 0; 89 | //rng_pool = null; 90 | } 91 | // TODO: allow reseeding after first request 92 | return rng_state.next(); 93 | } 94 | 95 | function rng_get_bytes(ba) { 96 | var i; 97 | for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte(); 98 | } 99 | 100 | function SecureRandom() {} 101 | 102 | SecureRandom.prototype.nextBytes = rng_get_bytes; 103 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/thirdparty/rsa.js: -------------------------------------------------------------------------------- 1 | // Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012 2 | 3 | /* 4 | * Copyright (c) 2003-2005 Tom Wu 5 | * All Rights Reserved. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining 8 | * a copy of this software and associated documentation files (the 9 | * "Software"), to deal in the Software without restriction, including 10 | * without limitation the rights to use, copy, modify, merge, publish, 11 | * distribute, sublicense, and/or sell copies of the Software, and to 12 | * permit persons to whom the Software is furnished to do so, subject to 13 | * the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 21 | * 22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * In addition, the following condition applies: 29 | * 30 | * All redistributions must retain an intact copy of this copyright notice 31 | * and disclaimer. 32 | */ 33 | 34 | 35 | // Depends on jsbn.js and rng.js 36 | 37 | // Version 1.1: support utf-8 encoding in pkcs1pad2 38 | 39 | // convert a (hex) string to a bignum object 40 | function parseBigInt(str,r) { 41 | return new BigInteger(str,r); 42 | } 43 | 44 | function linebrk(s,n) { 45 | var ret = ""; 46 | var i = 0; 47 | while(i + n < s.length) { 48 | ret += s.substring(i,i+n) + "\n"; 49 | i += n; 50 | } 51 | return ret + s.substring(i,s.length); 52 | } 53 | 54 | function byte2Hex(b) { 55 | if(b < 0x10) 56 | return "0" + b.toString(16); 57 | else 58 | return b.toString(16); 59 | } 60 | 61 | // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint 62 | function pkcs1pad2(s,n) { 63 | if(n < s.length + 11) { // TODO: fix for utf-8 64 | alert("Message too long for RSA"); 65 | return null; 66 | } 67 | var ba = new Array(); 68 | var i = s.length - 1; 69 | while(i >= 0 && n > 0) { 70 | var c = s.charCodeAt(i--); 71 | if(c < 128) { // encode using utf-8 72 | ba[--n] = c; 73 | } 74 | else if((c > 127) && (c < 2048)) { 75 | ba[--n] = (c & 63) | 128; 76 | ba[--n] = (c >> 6) | 192; 77 | } 78 | else { 79 | ba[--n] = (c & 63) | 128; 80 | ba[--n] = ((c >> 6) & 63) | 128; 81 | ba[--n] = (c >> 12) | 224; 82 | } 83 | } 84 | ba[--n] = 0; 85 | var rng = new SecureRandom(); 86 | var x = new Array(); 87 | while(n > 2) { // random non-zero pad 88 | x[0] = 0; 89 | while(x[0] == 0) rng.nextBytes(x); 90 | ba[--n] = x[0]; 91 | } 92 | ba[--n] = 2; 93 | ba[--n] = 0; 94 | return new BigInteger(ba); 95 | } 96 | 97 | // "empty" RSA key constructor 98 | function RSAKey() { 99 | this.n = null; 100 | this.e = 0; 101 | this.d = null; 102 | this.p = null; 103 | this.q = null; 104 | this.dmp1 = null; 105 | this.dmq1 = null; 106 | this.coeff = null; 107 | } 108 | 109 | // Set the public key fields N and e from hex strings 110 | function RSASetPublic(N,E) { 111 | if(N != null && E != null && N.length > 0 && E.length > 0) { 112 | this.n = parseBigInt(N,16); 113 | this.e = parseInt(E,16); 114 | } 115 | else 116 | alert("Invalid RSA public key"); 117 | } 118 | 119 | // Perform raw public operation on "x": return x^e (mod n) 120 | function RSADoPublic(x) { 121 | return x.modPowInt(this.e, this.n); 122 | } 123 | 124 | // Return the PKCS#1 RSA encryption of "text" as an even-length hex string 125 | function RSAEncrypt(text) { 126 | var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3); 127 | if(m == null) return null; 128 | var c = this.doPublic(m); 129 | if(c == null) return null; 130 | var h = c.toString(16); 131 | if((h.length & 1) == 0) return h; else return "0" + h; 132 | } 133 | 134 | // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string 135 | //function RSAEncryptB64(text) { 136 | // var h = this.encrypt(text); 137 | // if(h) return hex2b64(h); else return null; 138 | //} 139 | 140 | // protected 141 | RSAKey.prototype.doPublic = RSADoPublic; 142 | 143 | // public 144 | RSAKey.prototype.setPublic = RSASetPublic; 145 | RSAKey.prototype.encrypt = RSAEncrypt; 146 | //RSAKey.prototype.encrypt_b64 = RSAEncryptB64; 147 | -------------------------------------------------------------------------------- /webvirtmgr/static/js/spice-html5/wire.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright (C) 2012 by Jeremy P. White 4 | 5 | This file is part of spice-html5. 6 | 7 | spice-html5 is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | spice-html5 is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with spice-html5. If not, see . 19 | */ 20 | 21 | /*-------------------------------------------------------------------------------------- 22 | ** SpiceWireReader 23 | ** This class will receive messages from a WebSocket and relay it to a given 24 | ** callback. It will optionally save and pass along a header, useful in processing 25 | ** the mini message format. 26 | **--------------------------------------------------------------------------------------*/ 27 | function SpiceWireReader(sc, callback) 28 | { 29 | this.sc = sc; 30 | this.callback = callback; 31 | this.needed = 0; 32 | 33 | this.buffers = []; 34 | 35 | this.sc.ws.wire_reader = this; 36 | this.sc.ws.binaryType = "arraybuffer"; 37 | this.sc.ws.addEventListener('message', wire_blob_catcher); 38 | } 39 | 40 | SpiceWireReader.prototype = 41 | { 42 | 43 | /*------------------------------------------------------------------------ 44 | ** Process messages coming in from our WebSocket 45 | **----------------------------------------------------------------------*/ 46 | inbound: function (mb) 47 | { 48 | var at; 49 | 50 | /* Just buffer if we don't need anything yet */ 51 | if (this.needed == 0) 52 | { 53 | this.buffers.push(mb); 54 | return; 55 | } 56 | 57 | /* Optimization - if we have just one inbound block, and it's 58 | suitable for our needs, just use it. */ 59 | if (this.buffers.length == 0 && mb.byteLength >= this.needed) 60 | { 61 | if (mb.byteLength > this.needed) 62 | { 63 | this.buffers.push(mb.slice(this.needed)); 64 | mb = mb.slice(0, this.needed); 65 | } 66 | this.callback.call(this.sc, mb, 67 | this.saved_msg_header || undefined); 68 | } 69 | else 70 | { 71 | this.buffers.push(mb); 72 | } 73 | 74 | 75 | /* If we have fragments that add up to what we need, combine them */ 76 | /* FIXME - it would be faster to revise the processing code to handle 77 | ** multiple fragments directly. Essentially, we should be 78 | ** able to do this without any slice() or combine_array_buffers() calls */ 79 | while (this.buffers.length > 1 && this.buffers[0].byteLength < this.needed) 80 | { 81 | var mb1 = this.buffers.shift(); 82 | var mb2 = this.buffers.shift(); 83 | 84 | this.buffers.unshift(combine_array_buffers(mb1, mb2)); 85 | } 86 | 87 | 88 | while (this.buffers.length > 0 && this.buffers[0].byteLength >= this.needed) 89 | { 90 | mb = this.buffers.shift(); 91 | if (mb.byteLength > this.needed) 92 | { 93 | this.buffers.unshift(mb.slice(this.needed)); 94 | mb = mb.slice(0, this.needed); 95 | } 96 | this.callback.call(this.sc, mb, 97 | this.saved_msg_header || undefined); 98 | } 99 | 100 | }, 101 | 102 | request: function(n) 103 | { 104 | this.needed = n; 105 | }, 106 | 107 | save_header: function(h) 108 | { 109 | this.saved_msg_header = h; 110 | }, 111 | 112 | clear_header: function() 113 | { 114 | this.saved_msg_header = undefined; 115 | }, 116 | } 117 | 118 | function wire_blob_catcher(e) 119 | { 120 | DEBUG > 1 && console.log(">> WebSockets.onmessage"); 121 | DEBUG > 1 && console.log("id " + this.wire_reader.sc.connection_id +"; type " + this.wire_reader.sc.type); 122 | SpiceWireReader.prototype.inbound.call(this.wire_reader, e.data); 123 | } 124 | -------------------------------------------------------------------------------- /webvirtmgr/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from django.conf import settings 3 | 4 | urlpatterns = patterns('', 5 | url(r'^$', 'servers.views.index', name='index'), 6 | url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}, name='login'), 7 | url(r'^logout/$', 'django.contrib.auth.views.logout', {'template_name': 'logout.html'}, name='logout'), 8 | url(r'^servers/$', 'servers.views.servers_list', name='servers_list'), 9 | url(r'^infrastructure/$', 'servers.views.infrastructure', name='infrastructure'), 10 | url(r'^host/(\d+)/$', 'hostdetail.views.overview', name='overview'), 11 | url(r'^create/(\d+)/$', 'create.views.create', name='create'), 12 | url(r'^storages/(\d+)/$', 'storages.views.storages', name='storages'), 13 | url(r'^storage/(\d+)/([\w\-\.]+)/$', 'storages.views.storage', name='storage'), 14 | url(r'^networks/(\d+)/$', 'networks.views.networks', name='networks'), 15 | url(r'^network/(\d+)/([\w\-\.]+)/$', 'networks.views.network', name='network'), 16 | url(r'^interfaces/(\d+)/$', 'interfaces.views.interfaces', name='interfaces'), 17 | url(r'^interface/(\d+)/([\w\.\:]+)/$', 'interfaces.views.interface', name='interface'), 18 | url(r'^instance/(\d+)/([\w\-\.\_]+)/$', 'instance.views.instance', name='instance'), 19 | url(r'^instances/(\d+)/$', 'instance.views.instances', name='instances'), 20 | url(r'^secrets/(\d+)/$', 'secrets.views.secrets', name='secrets'), 21 | url(r'^console/$', 'console.views.console', name='console'), 22 | url(r'^info/hostusage/(\d+)/$', 'hostdetail.views.hostusage', name='hostusage'), 23 | url(r'^info/insts_status/(\d+)/$', 'instance.views.insts_status', name='insts_status'), 24 | url(r'^info/inst_status/(\d+)/([\w\-\.]+)/$', 'instance.views.inst_status', name='inst_status'), 25 | url(r'^info/instusage/(\d+)/([\w\-\.]+)/$', 'instance.views.instusage', name='instusage'), 26 | ) 27 | 28 | urlpatterns += patterns('', 29 | (r'^static/(?P.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT}), 30 | ) 31 | -------------------------------------------------------------------------------- /webvirtmgr/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/webvirtmgr/86bb20f2eb5dedf03ee6aa942cabcb39fbc74821/webvirtmgr/utils/__init__.py -------------------------------------------------------------------------------- /webvirtmgr/utils/secret_key.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 Nebula, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | from __future__ import with_statement # Python 2.5 compliance 15 | 16 | import lockfile 17 | import os 18 | import random 19 | import string 20 | 21 | 22 | class FilePermissionError(Exception): 23 | """The key file permissions are insecure.""" 24 | pass 25 | 26 | 27 | def generate_key(key_length=64): 28 | """Secret key generator. 29 | 30 | The quality of randomness depends on operating system support, 31 | see http://docs.python.org/library/random.html#random.SystemRandom. 32 | """ 33 | if hasattr(random, 'SystemRandom'): 34 | choice = random.SystemRandom().choice 35 | else: 36 | choice = random.choice 37 | return ''.join(map(lambda x: choice(string.digits + string.ascii_letters), 38 | range(key_length))) 39 | 40 | 41 | def generate_or_read_from_file(key_file='.secret_key', key_length=64): 42 | """Multiprocess-safe secret key file generator. 43 | 44 | Useful to replace the default (and thus unsafe) SECRET_KEY in settings.py 45 | upon first start. Save to use, i.e. when multiple Python interpreters 46 | serve the dashboard Django application (e.g. in a mod_wsgi + daemonized 47 | environment). Also checks if file permissions are set correctly and 48 | throws an exception if not. 49 | """ 50 | lock = lockfile.FileLock(key_file) 51 | # with lock: 52 | if not lock.is_locked(): 53 | if not os.path.exists(key_file): 54 | key = generate_key(key_length) 55 | old_umask = os.umask(0o177) # Use '0600' file permissions 56 | with open(key_file, 'w') as f: 57 | f.write(key) 58 | os.umask(old_umask) 59 | else: 60 | if oct(os.stat(key_file).st_mode & 0o777) != '0600': 61 | raise FilePermissionError("Insecure key file permissions!") 62 | with open(key_file, 'r') as f: 63 | key = f.readline() 64 | return key 65 | -------------------------------------------------------------------------------- /webvirtmgr/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for webvirtmgr project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | 18 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks 19 | # if running multiple sites in the same mod_wsgi process. To fix this, use 20 | # mod_wsgi daemon mode with each site in its own daemon process, or use 21 | # os.environ["DJANGO_SETTINGS_MODULE"] = "webvirtmgr.settings" 22 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtmgr.settings") 23 | 24 | # This application object is used by any WSGI server configured to use this 25 | # file. This includes Django's development server, if the WSGI_APPLICATION 26 | # setting points here. 27 | from django.core.wsgi import get_wsgi_application 28 | application = get_wsgi_application() 29 | 30 | # Apply WSGI middleware here. 31 | # from helloworld.wsgi import HelloWorldApplication 32 | # application = HelloWorldApplication(application) 33 | --------------------------------------------------------------------------------