├── .gitignore ├── LICENSE ├── README.md ├── __init__.py ├── cron └── hoststat.py ├── dashboard ├── __init__.py ├── forms.py └── views.py ├── interfaces ├── __init__.py └── views.py ├── locale └── ru │ └── LC_MESSAGES │ ├── django.mo │ └── django.po ├── logs ├── __init__.py └── views.py ├── manage.py ├── media ├── admin ├── css │ ├── bootstrap-responsive.css │ └── bootstrap.css ├── img │ ├── facebook.png │ ├── favicon.ico │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── twitter.png │ ├── virtmgr1.jpg │ ├── virtmgr2.jpg │ ├── virtmgr3.jpg │ ├── virtmgr4.jpg │ ├── virtmgr5.jpg │ ├── virtmgr6.jpg │ ├── virtmgr7.jpg │ ├── virtmgr8.jpg │ └── virtmgr9.jpg ├── java │ ├── .DS_Store │ └── vncviewer.jar └── js │ ├── bootstrap-alert.js │ ├── bootstrap-button.js │ ├── bootstrap-carousel.js │ ├── bootstrap-collapse.js │ ├── bootstrap-dropdown.js │ ├── bootstrap-modal.js │ ├── bootstrap-popover.js │ ├── bootstrap-scrollspy.js │ ├── bootstrap-tab.js │ ├── bootstrap-tooltip.js │ ├── bootstrap-transition.js │ ├── bootstrap-typeahead.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ └── jquery.js ├── model ├── __init__.py ├── admin.py ├── models.py └── views.py ├── network ├── IPy.py ├── __init__.py └── views.py ├── newvm ├── __init__.py └── views.py ├── overview ├── __init__.py └── views.py ├── pages ├── __init__.py └── views.py ├── robots.txt ├── settings.py ├── snapshot ├── __init__.py └── views.py ├── storage ├── __init__.py └── views.py ├── templates ├── 404.html ├── 500.html ├── base.html ├── dashboard.html ├── docs.html ├── features.html ├── index.html ├── logs.html ├── network.html ├── newvm.html ├── overview.html ├── registration │ ├── activate.html │ ├── activation_complete.html │ ├── activation_email.txt │ ├── activation_email_subject.txt │ ├── login.html │ ├── logout.html │ ├── password_change_done.html │ ├── password_change_form.html │ ├── password_reset_complete.html │ ├── password_reset_confirm.html │ ├── password_reset_done.html │ ├── password_reset_email.html │ ├── password_reset_form.html │ ├── registration_complete.html │ └── registration_form.html ├── screenshot.html ├── snapshot.html ├── storage.html ├── support.html ├── vm.html └── vnc.html ├── urls.py ├── vm ├── __init__.py └── views.py └── vnc ├── __init__.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | hoststat.py 2 | urls.py 3 | settings.py 4 | *.pyc 5 | .DS_Store 6 | ._* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Version 2.6: 2 | - bugfix 3 | - manual authtorization when manage host 4 | - fix templates 5 | 6 | Version 2.5: 7 | - bugfix 8 | - add template for new VM 9 | - new design control VM 10 | - fix translate 11 | 12 | Version 2.4: 13 | - bugfix 14 | - add gettext (russian) 15 | - change template 16 | 17 | Version 2.3: 18 | - bugfix 19 | - add logs 20 | - change templates 21 | 22 | Version 2.2: 23 | - fix template 24 | - add exceptions for all operation 25 | - add support qemu without kvm 26 | 27 | Version 2.1: 28 | - fix bootstrap height (text input) 29 | 30 | Version 2.0: 31 | - new design build on bootstrap 32 | - delete interfaces for ubuntu support 33 | - optimized code 34 | 35 | Version 1.0.1a: 36 | 37 | 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 over a SSH tunnel presents a full graphical console to the guest domain. KVM is currently the only hypervisor supported 38 | 39 | Technology: 40 | 41 | The application logic is written in Python & Django. The LIBVIRT Python bindings 42 | are used to interacting with the underlying hypervisor. KVM primary supported platform. 43 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/__init__.py -------------------------------------------------------------------------------- /cron/hoststat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # heststat.py - Get server state 5 | # 6 | # Copyright © 2012 - X-systems 7 | # 8 | # Authors: 9 | # Anatoliy Guskov 10 | 11 | import MySQLdb 12 | import smtplib 13 | import socket 14 | import sys 15 | from time import strftime 16 | from email.mime.multipart import MIMEMultipart 17 | from email.mime.text import MIMEText 18 | 19 | try: 20 | conn = MySQLdb.connect (host = "localhost", 21 | user = "root", 22 | passwd = "root", 23 | db = "virtmgr") 24 | except MySQLdb.Error, e: 25 | print "Error %d: %s" % (e.args[0], e.args[1]) 26 | sys.exit(1) 27 | 28 | cursor = conn.cursor() 29 | cursor.execute("SELECT id, ipaddr, state, user_id FROM model_host") 30 | rows = cursor.fetchall() 31 | 32 | for row in rows: 33 | try: 34 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 35 | s.settimeout(1) 36 | s.connect((row[1], 16509)) 37 | s.close() 38 | status = 1 39 | if row[2] == 2: 40 | cursor.execute("UPDATE model_host SET state=%s WHERE id=%s", (status, row[0])) 41 | datetime = strftime("%y-%m-%d %H:%M:%S") 42 | cursor.execute("INSERT INTO model_log (host_id, type, message, date, user_id) VALUE (%s, 'scheduler', 'The server is available', %s, %s)", (row[0], datetime, row[3])) 43 | else: 44 | cursor.execute("UPDATE model_host SET state=%s WHERE id=%s", (status, row[0])) 45 | except: 46 | status = 2 47 | if row[2] == 1: 48 | cursor.execute("UPDATE model_host SET state=%s WHERE id=%s", (status, row[0])) 49 | datetime = strftime("%y-%m-%d %H:%M:%S") 50 | cursor.execute("INSERT INTO model_log (host_id, type, message, date, user_id) VALUE (%s, 'scheduler', 'The server is not available', %s, %s)", (row[0], datetime, row[3])) 51 | 52 | cursor.close() 53 | conn.commit() 54 | conn.close() -------------------------------------------------------------------------------- /dashboard/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/dashboard/__init__.py -------------------------------------------------------------------------------- /dashboard/forms.py: -------------------------------------------------------------------------------- 1 | import registration 2 | from django import forms 3 | from virtmgr.model.models import * 4 | 5 | class AddNewHost(forms.Form): 6 | name = forms.CharField(required=False) 7 | ipaddr = forms.CharField(required=False) 8 | sshusr = forms.CharField(required=False) 9 | passw = forms.CharField(required=False) 10 | 11 | def clean_name(self): 12 | name = self.cleaned_data['name'] 13 | hostname = Host.objects.filter(user=request.user, hostname=name) 14 | if hostname: 15 | raise forms.ValidationError("Hostname alredy exist!") 16 | return name 17 | 18 | def clean_ipaddr(self): 19 | ip = self.cleaned_data['ipaddr'] 20 | ipaddr = Host.objects.filter(user=request.user, ipaddr=ip) 21 | if ipaddr: 22 | raise forms.ValidationError("IP adress alredy exist!") 23 | return ip 24 | 25 | 26 | -------------------------------------------------------------------------------- /dashboard/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re, socket 3 | from django.utils.translation import ugettext_lazy as _ 4 | from django.shortcuts import render_to_response 5 | from django.http import HttpResponseRedirect 6 | from virtmgr.model.models import * 7 | 8 | def index(request): 9 | if not request.user.is_authenticated(): 10 | return HttpResponseRedirect('/user/login') 11 | 12 | def get_hosts_status(): 13 | kvm_host = Host.objects.filter(user=request.user.id).order_by('-id') 14 | name_ipddr = {} 15 | for host in kvm_host: 16 | try: 17 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 18 | s.settimeout(1) 19 | s.connect((host.ipaddr, 16509)) 20 | s.close() 21 | status = 1 22 | except: 23 | status = 2 24 | name_ipddr[host.id] = (host.hostname, 25 | host.ipaddr, 26 | host.login, 27 | host.passwd, 28 | status 29 | ) 30 | return name_ipddr 31 | 32 | def del_host(host): 33 | hosts = Host.objects.get(user=request.user.id, hostname=host) 34 | hosts.delete() 35 | 36 | def add_host(host, ip, usr, passw): 37 | hosts = Host(user_id=request.user.id, 38 | hostname=host, 39 | ipaddr=ip, 40 | login=usr, 41 | passwd=passw, 42 | state=0 43 | ) 44 | hosts.save() 45 | kvm_host = Host.objects.get(user=request.user.id, hostname=host) 46 | msg = _('Add server: ') 47 | msg = msg + host 48 | error_msg = Log(host_id=kvm_host.id, 49 | type='user', 50 | message=msg, 51 | user_id=request.user.id 52 | ) 53 | error_msg.save() 54 | 55 | def get_host_status(hosts): 56 | for host, info in hosts.items(): 57 | print host, info 58 | 59 | # if request.session['login_kvm'] or request.session['passwd_kvm']: 60 | # del request.session['login_kvm'] 61 | # del request.session['passwd_kvm'] 62 | 63 | host_info = get_hosts_status() 64 | errors = [] 65 | 66 | if request.method == 'POST': 67 | if request.POST.get('delete',''): 68 | host = request.POST.get('host','') 69 | del_host(host) 70 | return HttpResponseRedirect('/dashboard/') 71 | if request.POST.get('add',''): 72 | name = request.POST.get('name','') 73 | ipaddr = request.POST.get('ipaddr','') 74 | login = request.POST.get('sshusr','') 75 | passw = request.POST.get('passw','') 76 | simbol = re.search('[^a-zA-Z0-9\_]+', name) 77 | ipsimbol = re.search('[^a-z0-9\.\-]+', ipaddr) 78 | domain = re.search('[\.]+', ipaddr) 79 | privat_ip1 = re.search('^172\.16\.', ipaddr) 80 | privat_ip2 = re.search('^192\.168\.', ipaddr) 81 | privat_ip3 = re.search('^10\.', ipaddr) 82 | privat_ip4 = re.search('^127\.', ipaddr) 83 | if privat_ip1 or privat_ip2 or privat_ip3 or privat_ip4: 84 | msg = _('IP address can not be a private address space') 85 | errors.append(msg) 86 | if len(name) > 20: 87 | msg = _('The host name must not exceed 20 characters') 88 | errors.append(msg) 89 | if ipsimbol or not domain: 90 | msg = _('Hostname must contain only numbers, or the domain name separated by "."') 91 | errors.append(msg) 92 | if simbol: 93 | msg = _('The host name must not contain any characters and Russian characters') 94 | errors.append(msg) 95 | else: 96 | have_host = Host.objects.filter(user=request.user, hostname=name) 97 | have_ip = Host.objects.filter(user=request.user, ipaddr=ipaddr) 98 | if have_host or have_ip: 99 | msg = _('This host is already connected') 100 | errors.append(msg) 101 | if not name: 102 | msg = _('No hostname has been entered') 103 | errors.append(msg) 104 | if not ipaddr: 105 | msg = _('No IP address has been entered') 106 | errors.append(msg) 107 | #if not login: 108 | # msg = _('No KVM login was been entered') 109 | # errors.append(msg) 110 | #if not passw: 111 | # msg = _('No KVM password was been entered ') 112 | # errors.append(msg) 113 | if not errors: 114 | add_host(name, ipaddr, login, passw) 115 | return HttpResponseRedirect('/dashboard/') 116 | return render_to_response('dashboard.html', locals()) 117 | -------------------------------------------------------------------------------- /interfaces/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/interfaces/__init__.py -------------------------------------------------------------------------------- /interfaces/views.py: -------------------------------------------------------------------------------- 1 | import libvirt 2 | import virtinst.util as util 3 | from django.shortcuts import render_to_response 4 | from django.http import Http404, HttpResponse, HttpResponseRedirect 5 | from virtmgr.model.models import * 6 | 7 | def vm_conn(host_ip, creds): 8 | try: 9 | flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE] 10 | auth = [flags, creds, None] 11 | uri = 'qemu+tcp://' + host_ip + '/system' 12 | conn = libvirt.openAuth(uri, auth, 0) 13 | return conn 14 | except: 15 | print "Not connected" 16 | 17 | def get_interfaces(conn): 18 | try: 19 | interfaces = [] 20 | for name in conn.listInterfaces(): 21 | interfaces.append(name) 22 | for name in conn.listDefinedInterfaces(): 23 | interfaces.append(name) 24 | return interfaces 25 | except: 26 | print "Get interface failed" 27 | 28 | def index(request, host): 29 | 30 | if not request.user.is_authenticated(): 31 | return HttpResponseRedirect('/') 32 | 33 | usr_id = request.user.id 34 | kvm_host = Host.objects.get(user=usr_id,hostname=host) 35 | usr_name = request.user 36 | host_ip = kvm_host.ipaddr 37 | 38 | def creds(credentials, user_data): 39 | for credential in credentials: 40 | if credential[0] == libvirt.VIR_CRED_AUTHNAME: 41 | credential[4] = kvm_host.login 42 | if len(credential[4]) == 0: 43 | credential[4] = credential[3] 44 | elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: 45 | credential[4] = kvm_host.passwd 46 | else: 47 | return -1 48 | return 0 49 | 50 | conn = vm_conn(host_ip, creds) 51 | interfaces = get_interfaces(conn) 52 | 53 | if interfaces == None: 54 | return HttpResponseRedirect('/overview/' + host + '/') 55 | else: 56 | return HttpResponseRedirect('/interfaces/' + host + '/' + interfaces[0] + '/') 57 | 58 | def ifcfg(request, host, face): 59 | 60 | if not request.user.is_authenticated(): 61 | return HttpResponseRedirect('/') 62 | 63 | usr_id = request.user.id 64 | usr_name = request.user 65 | kvm_host = Host.objects.get(user=usr_id,hostname=host) 66 | 67 | def creds(credentials, user_data): 68 | for credential in credentials: 69 | if credential[0] == libvirt.VIR_CRED_AUTHNAME: 70 | credential[4] = kvm_host.login 71 | if len(credential[4]) == 0: 72 | credential[4] = credential[3] 73 | elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: 74 | credential[4] = kvm_host.passwd 75 | else: 76 | return -1 77 | return 0 78 | 79 | def get_ifcfg(face): 80 | ifcfg = conn.interfaceLookupByName(face) 81 | return ifcfg 82 | 83 | def get_status(): 84 | return ifcfg.isActive() 85 | 86 | def get_mac(): 87 | return ifcfg.MACString() 88 | 89 | def get_ip(): 90 | xml = ifcfg.XMLDesc(0) 91 | ip = util.get_xml_path(xml, "/interface/protocol/ip/@address") 92 | return ip 93 | 94 | def get_eth(): 95 | xml = ifcfg.XMLDesc(0) 96 | eth = util.get_xml_path(xml, "/interface/bridge/interface/@name") 97 | return eth 98 | 99 | conn = vm_conn(host_ip, creds) 100 | 101 | if conn == None: 102 | return HttpResponseRedirect('/overview/' + host + '/') 103 | 104 | interfaces = get_interfaces(conn) 105 | ifcfg = get_ifcfg(face) 106 | status = get_status() 107 | mac = get_mac() 108 | ipaddr = get_ip() 109 | bridge = get_eth() 110 | conn.close() 111 | 112 | return render_to_response('interfaces.html', locals()) 113 | 114 | def redir(request): 115 | if not request.user.is_authenticated(): 116 | return HttpResponseRedirect('/') 117 | else: 118 | return HttpResponseRedirect('/hosts') 119 | -------------------------------------------------------------------------------- /locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /logs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/logs/__init__.py -------------------------------------------------------------------------------- /logs/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import libvirt 3 | from django.shortcuts import render_to_response 4 | from django.http import HttpResponseRedirect 5 | from virtmgr.model.models import * 6 | 7 | def redir(request): 8 | if not request.user.is_authenticated(): 9 | return HttpResponseRedirect('/user/login/') 10 | else: 11 | return HttpResponseRedirect('/dashboard/') 12 | 13 | def logs(request, host_id): 14 | if not request.user.is_authenticated(): 15 | return HttpResponseRedirect('/user/login/') 16 | 17 | kvm_host = Host.objects.get(user=request.user.id, id=host_id) 18 | all_logs = Log.objects.filter(host=host_id, user=request.user.id).order_by('-date')[:50] 19 | 20 | def add_error(msg): 21 | error_msg = Log(host_id=host_id, 22 | type='libvirt', 23 | message=msg, 24 | user_id=request.user.id 25 | ) 26 | error_msg.save() 27 | 28 | def get_vms(): 29 | try: 30 | vname = {} 31 | for id in conn.listDomainsID(): 32 | id = int(id) 33 | dom = conn.lookupByID(id) 34 | vname[dom.name()] = dom.info()[0] 35 | for id in conn.listDefinedDomains(): 36 | dom = conn.lookupByName(id) 37 | vname[dom.name()] = dom.info()[0] 38 | return vname 39 | except libvirt.libvirtError as e: 40 | add_error(e) 41 | return "error" 42 | 43 | def vm_conn(): 44 | try: 45 | flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE] 46 | auth = [flags, creds, None] 47 | uri = 'qemu+tcp://' + kvm_host.ipaddr + '/system' 48 | conn = libvirt.openAuth(uri, auth, 0) 49 | return conn 50 | except libvirt.libvirtError as e: 51 | add_error(e) 52 | return "error" 53 | 54 | if not kvm_host.login or not kvm_host.passwd: 55 | def creds(credentials, user_data): 56 | for credential in credentials: 57 | if credential[0] == libvirt.VIR_CRED_AUTHNAME: 58 | credential[4] = request.session['login_kvm'] 59 | if len(credential[4]) == 0: 60 | credential[4] = credential[3] 61 | elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: 62 | credential[4] = request.session['passwd_kvm'] 63 | else: 64 | return -1 65 | return 0 66 | else: 67 | def creds(credentials, user_data): 68 | for credential in credentials: 69 | if credential[0] == libvirt.VIR_CRED_AUTHNAME: 70 | credential[4] = kvm_host.login 71 | if len(credential[4]) == 0: 72 | credential[4] = credential[3] 73 | elif credential[0] == libvirt.VIR_CRED_PASSPHRASE: 74 | credential[4] = kvm_host.passwd 75 | else: 76 | return -1 77 | return 0 78 | 79 | conn = vm_conn() 80 | 81 | if conn != "error": 82 | all_vm = get_vms() 83 | 84 | return render_to_response('logs.html', locals()) -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | try: 4 | import settings # Assumed to be in the same directory. 5 | except ImportError: 6 | import sys 7 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) 8 | sys.exit(1) 9 | 10 | if __name__ == "__main__": 11 | execute_manager(settings) 12 | -------------------------------------------------------------------------------- /media/admin: -------------------------------------------------------------------------------- 1 | /usr/lib/python2.7/site-packages/django/contrib/admin/media -------------------------------------------------------------------------------- /media/img/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/facebook.png -------------------------------------------------------------------------------- /media/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/favicon.ico -------------------------------------------------------------------------------- /media/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /media/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /media/img/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/twitter.png -------------------------------------------------------------------------------- /media/img/virtmgr1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr1.jpg -------------------------------------------------------------------------------- /media/img/virtmgr2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr2.jpg -------------------------------------------------------------------------------- /media/img/virtmgr3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr3.jpg -------------------------------------------------------------------------------- /media/img/virtmgr4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr4.jpg -------------------------------------------------------------------------------- /media/img/virtmgr5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr5.jpg -------------------------------------------------------------------------------- /media/img/virtmgr6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr6.jpg -------------------------------------------------------------------------------- /media/img/virtmgr7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr7.jpg -------------------------------------------------------------------------------- /media/img/virtmgr8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr8.jpg -------------------------------------------------------------------------------- /media/img/virtmgr9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/img/virtmgr9.jpg -------------------------------------------------------------------------------- /media/java/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/java/.DS_Store -------------------------------------------------------------------------------- /media/java/vncviewer.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/retspen/virtmgr/1b1932b584351af48c4911e0ba0f9e67b644d330/media/java/vncviewer.jar -------------------------------------------------------------------------------- /media/js/bootstrap-alert.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-alert.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#alerts 4 | * ========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* ALERT CLASS DEFINITION 26 | * ====================== */ 27 | 28 | var dismiss = '[data-dismiss="alert"]' 29 | , Alert = function ( el ) { 30 | $(el).on('click', dismiss, this.close) 31 | } 32 | 33 | Alert.prototype = { 34 | 35 | constructor: Alert 36 | 37 | , close: function ( e ) { 38 | var $this = $(this) 39 | , selector = $this.attr('data-target') 40 | , $parent 41 | 42 | if (!selector) { 43 | selector = $this.attr('href') 44 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 45 | } 46 | 47 | $parent = $(selector) 48 | $parent.trigger('close') 49 | 50 | e && e.preventDefault() 51 | 52 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 53 | 54 | $parent 55 | .trigger('close') 56 | .removeClass('in') 57 | 58 | function removeElement() { 59 | $parent 60 | .trigger('closed') 61 | .remove() 62 | } 63 | 64 | $.support.transition && $parent.hasClass('fade') ? 65 | $parent.on($.support.transition.end, removeElement) : 66 | removeElement() 67 | } 68 | 69 | } 70 | 71 | 72 | /* ALERT PLUGIN DEFINITION 73 | * ======================= */ 74 | 75 | $.fn.alert = function ( option ) { 76 | return this.each(function () { 77 | var $this = $(this) 78 | , data = $this.data('alert') 79 | if (!data) $this.data('alert', (data = new Alert(this))) 80 | if (typeof option == 'string') data[option].call($this) 81 | }) 82 | } 83 | 84 | $.fn.alert.Constructor = Alert 85 | 86 | 87 | /* ALERT DATA-API 88 | * ============== */ 89 | 90 | $(function () { 91 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) 92 | }) 93 | 94 | }( window.jQuery ); -------------------------------------------------------------------------------- /media/js/bootstrap-button.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-button.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#buttons 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | !function( $ ){ 21 | 22 | "use strict" 23 | 24 | /* BUTTON PUBLIC CLASS DEFINITION 25 | * ============================== */ 26 | 27 | var Button = function ( element, options ) { 28 | this.$element = $(element) 29 | this.options = $.extend({}, $.fn.button.defaults, options) 30 | } 31 | 32 | Button.prototype = { 33 | 34 | constructor: Button 35 | 36 | , setState: function ( state ) { 37 | var d = 'disabled' 38 | , $el = this.$element 39 | , data = $el.data() 40 | , val = $el.is('input') ? 'val' : 'html' 41 | 42 | state = state + 'Text' 43 | data.resetText || $el.data('resetText', $el[val]()) 44 | 45 | $el[val](data[state] || this.options[state]) 46 | 47 | // push to event loop to allow forms to submit 48 | setTimeout(function () { 49 | state == 'loadingText' ? 50 | $el.addClass(d).attr(d, d) : 51 | $el.removeClass(d).removeAttr(d) 52 | }, 0) 53 | } 54 | 55 | , toggle: function () { 56 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]') 57 | 58 | $parent && $parent 59 | .find('.active') 60 | .removeClass('active') 61 | 62 | this.$element.toggleClass('active') 63 | } 64 | 65 | } 66 | 67 | 68 | /* BUTTON PLUGIN DEFINITION 69 | * ======================== */ 70 | 71 | $.fn.button = function ( option ) { 72 | return this.each(function () { 73 | var $this = $(this) 74 | , data = $this.data('button') 75 | , options = typeof option == 'object' && option 76 | if (!data) $this.data('button', (data = new Button(this, options))) 77 | if (option == 'toggle') data.toggle() 78 | else if (option) data.setState(option) 79 | }) 80 | } 81 | 82 | $.fn.button.defaults = { 83 | loadingText: 'loading...' 84 | } 85 | 86 | $.fn.button.Constructor = Button 87 | 88 | 89 | /* BUTTON DATA-API 90 | * =============== */ 91 | 92 | $(function () { 93 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { 94 | var $btn = $(e.target) 95 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 96 | $btn.button('toggle') 97 | }) 98 | }) 99 | 100 | }( window.jQuery ); -------------------------------------------------------------------------------- /media/js/bootstrap-carousel.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-carousel.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#carousel 4 | * ========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* CAROUSEL CLASS DEFINITION 26 | * ========================= */ 27 | 28 | var Carousel = function (element, options) { 29 | this.$element = $(element) 30 | this.options = $.extend({}, $.fn.carousel.defaults, options) 31 | this.options.slide && this.slide(this.options.slide) 32 | this.options.pause == 'hover' && this.$element 33 | .on('mouseenter', $.proxy(this.pause, this)) 34 | .on('mouseleave', $.proxy(this.cycle, this)) 35 | } 36 | 37 | Carousel.prototype = { 38 | 39 | cycle: function () { 40 | this.interval = setInterval($.proxy(this.next, this), this.options.interval) 41 | return this 42 | } 43 | 44 | , to: function (pos) { 45 | var $active = this.$element.find('.active') 46 | , children = $active.parent().children() 47 | , activePos = children.index($active) 48 | , that = this 49 | 50 | if (pos > (children.length - 1) || pos < 0) return 51 | 52 | if (this.sliding) { 53 | return this.$element.one('slid', function () { 54 | that.to(pos) 55 | }) 56 | } 57 | 58 | if (activePos == pos) { 59 | return this.pause().cycle() 60 | } 61 | 62 | return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) 63 | } 64 | 65 | , pause: function () { 66 | clearInterval(this.interval) 67 | this.interval = null 68 | return this 69 | } 70 | 71 | , next: function () { 72 | if (this.sliding) return 73 | return this.slide('next') 74 | } 75 | 76 | , prev: function () { 77 | if (this.sliding) return 78 | return this.slide('prev') 79 | } 80 | 81 | , slide: function (type, next) { 82 | var $active = this.$element.find('.active') 83 | , $next = next || $active[type]() 84 | , isCycling = this.interval 85 | , direction = type == 'next' ? 'left' : 'right' 86 | , fallback = type == 'next' ? 'first' : 'last' 87 | , that = this 88 | 89 | this.sliding = true 90 | 91 | isCycling && this.pause() 92 | 93 | $next = $next.length ? $next : this.$element.find('.item')[fallback]() 94 | 95 | if ($next.hasClass('active')) return 96 | 97 | if (!$.support.transition && this.$element.hasClass('slide')) { 98 | this.$element.trigger('slide') 99 | $active.removeClass('active') 100 | $next.addClass('active') 101 | this.sliding = false 102 | this.$element.trigger('slid') 103 | } else { 104 | $next.addClass(type) 105 | $next[0].offsetWidth // force reflow 106 | $active.addClass(direction) 107 | $next.addClass(direction) 108 | this.$element.trigger('slide') 109 | this.$element.one($.support.transition.end, function () { 110 | $next.removeClass([type, direction].join(' ')).addClass('active') 111 | $active.removeClass(['active', direction].join(' ')) 112 | that.sliding = false 113 | setTimeout(function () { that.$element.trigger('slid') }, 0) 114 | }) 115 | } 116 | 117 | isCycling && this.cycle() 118 | 119 | return this 120 | } 121 | 122 | } 123 | 124 | 125 | /* CAROUSEL PLUGIN DEFINITION 126 | * ========================== */ 127 | 128 | $.fn.carousel = function ( option ) { 129 | return this.each(function () { 130 | var $this = $(this) 131 | , data = $this.data('carousel') 132 | , options = typeof option == 'object' && option 133 | if (!data) $this.data('carousel', (data = new Carousel(this, options))) 134 | if (typeof option == 'number') data.to(option) 135 | else if (typeof option == 'string' || (option = options.slide)) data[option]() 136 | else data.cycle() 137 | }) 138 | } 139 | 140 | $.fn.carousel.defaults = { 141 | interval: 5000 142 | , pause: 'hover' 143 | } 144 | 145 | $.fn.carousel.Constructor = Carousel 146 | 147 | 148 | /* CAROUSEL DATA-API 149 | * ================= */ 150 | 151 | $(function () { 152 | $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { 153 | var $this = $(this), href 154 | , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 155 | , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) 156 | $target.carousel(options) 157 | e.preventDefault() 158 | }) 159 | }) 160 | 161 | }( window.jQuery ); -------------------------------------------------------------------------------- /media/js/bootstrap-collapse.js: -------------------------------------------------------------------------------- 1 | /* ============================================================= 2 | * bootstrap-collapse.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#collapse 4 | * ============================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | !function( $ ){ 21 | 22 | "use strict" 23 | 24 | var Collapse = function ( element, options ) { 25 | this.$element = $(element) 26 | this.options = $.extend({}, $.fn.collapse.defaults, options) 27 | 28 | if (this.options["parent"]) { 29 | this.$parent = $(this.options["parent"]) 30 | } 31 | 32 | this.options.toggle && this.toggle() 33 | } 34 | 35 | Collapse.prototype = { 36 | 37 | constructor: Collapse 38 | 39 | , dimension: function () { 40 | var hasWidth = this.$element.hasClass('width') 41 | return hasWidth ? 'width' : 'height' 42 | } 43 | 44 | , show: function () { 45 | var dimension = this.dimension() 46 | , scroll = $.camelCase(['scroll', dimension].join('-')) 47 | , actives = this.$parent && this.$parent.find('.in') 48 | , hasData 49 | 50 | if (actives && actives.length) { 51 | hasData = actives.data('collapse') 52 | actives.collapse('hide') 53 | hasData || actives.data('collapse', null) 54 | } 55 | 56 | this.$element[dimension](0) 57 | this.transition('addClass', 'show', 'shown') 58 | this.$element[dimension](this.$element[0][scroll]) 59 | 60 | } 61 | 62 | , hide: function () { 63 | var dimension = this.dimension() 64 | this.reset(this.$element[dimension]()) 65 | this.transition('removeClass', 'hide', 'hidden') 66 | this.$element[dimension](0) 67 | } 68 | 69 | , reset: function ( size ) { 70 | var dimension = this.dimension() 71 | 72 | this.$element 73 | .removeClass('collapse') 74 | [dimension](size || 'auto') 75 | [0].offsetWidth 76 | 77 | this.$element[size ? 'addClass' : 'removeClass']('collapse') 78 | 79 | return this 80 | } 81 | 82 | , transition: function ( method, startEvent, completeEvent ) { 83 | var that = this 84 | , complete = function () { 85 | if (startEvent == 'show') that.reset() 86 | that.$element.trigger(completeEvent) 87 | } 88 | 89 | this.$element 90 | .trigger(startEvent) 91 | [method]('in') 92 | 93 | $.support.transition && this.$element.hasClass('collapse') ? 94 | this.$element.one($.support.transition.end, complete) : 95 | complete() 96 | } 97 | 98 | , toggle: function () { 99 | this[this.$element.hasClass('in') ? 'hide' : 'show']() 100 | } 101 | 102 | } 103 | 104 | /* COLLAPSIBLE PLUGIN DEFINITION 105 | * ============================== */ 106 | 107 | $.fn.collapse = function ( option ) { 108 | return this.each(function () { 109 | var $this = $(this) 110 | , data = $this.data('collapse') 111 | , options = typeof option == 'object' && option 112 | if (!data) $this.data('collapse', (data = new Collapse(this, options))) 113 | if (typeof option == 'string') data[option]() 114 | }) 115 | } 116 | 117 | $.fn.collapse.defaults = { 118 | toggle: true 119 | } 120 | 121 | $.fn.collapse.Constructor = Collapse 122 | 123 | 124 | /* COLLAPSIBLE DATA-API 125 | * ==================== */ 126 | 127 | $(function () { 128 | $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { 129 | var $this = $(this), href 130 | , target = $this.attr('data-target') 131 | || e.preventDefault() 132 | || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 133 | , option = $(target).data('collapse') ? 'toggle' : $this.data() 134 | $(target).collapse(option) 135 | }) 136 | }) 137 | 138 | }( window.jQuery ); -------------------------------------------------------------------------------- /media/js/bootstrap-dropdown.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-dropdown.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#dropdowns 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* DROPDOWN CLASS DEFINITION 26 | * ========================= */ 27 | 28 | var toggle = '[data-toggle="dropdown"]' 29 | , Dropdown = function ( element ) { 30 | var $el = $(element).on('click.dropdown.data-api', this.toggle) 31 | $('html').on('click.dropdown.data-api', function () { 32 | $el.parent().removeClass('open') 33 | }) 34 | } 35 | 36 | Dropdown.prototype = { 37 | 38 | constructor: Dropdown 39 | 40 | , toggle: function ( e ) { 41 | var $this = $(this) 42 | , selector = $this.attr('data-target') 43 | , $parent 44 | , isActive 45 | 46 | if (!selector) { 47 | selector = $this.attr('href') 48 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 49 | } 50 | 51 | $parent = $(selector) 52 | $parent.length || ($parent = $this.parent()) 53 | 54 | isActive = $parent.hasClass('open') 55 | 56 | clearMenus() 57 | !isActive && $parent.toggleClass('open') 58 | 59 | return false 60 | } 61 | 62 | } 63 | 64 | function clearMenus() { 65 | $(toggle).parent().removeClass('open') 66 | } 67 | 68 | 69 | /* DROPDOWN PLUGIN DEFINITION 70 | * ========================== */ 71 | 72 | $.fn.dropdown = function ( option ) { 73 | return this.each(function () { 74 | var $this = $(this) 75 | , data = $this.data('dropdown') 76 | if (!data) $this.data('dropdown', (data = new Dropdown(this))) 77 | if (typeof option == 'string') data[option].call($this) 78 | }) 79 | } 80 | 81 | $.fn.dropdown.Constructor = Dropdown 82 | 83 | 84 | /* APPLY TO STANDARD DROPDOWN ELEMENTS 85 | * =================================== */ 86 | 87 | $(function () { 88 | $('html').on('click.dropdown.data-api', clearMenus) 89 | $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle) 90 | }) 91 | 92 | }( window.jQuery ); -------------------------------------------------------------------------------- /media/js/bootstrap-modal.js: -------------------------------------------------------------------------------- 1 | /* ========================================================= 2 | * bootstrap-modal.js v2.0.2 3 | * http://twitter.github.com/bootstrap/javascript.html#modals 4 | * ========================================================= 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================= */ 19 | 20 | 21 | !function( $ ){ 22 | 23 | "use strict" 24 | 25 | /* MODAL CLASS DEFINITION 26 | * ====================== */ 27 | 28 | var Modal = function ( content, options ) { 29 | this.options = options 30 | this.$element = $(content) 31 | .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this)) 32 | } 33 | 34 | Modal.prototype = { 35 | 36 | constructor: Modal 37 | 38 | , toggle: function () { 39 | return this[!this.isShown ? 'show' : 'hide']() 40 | } 41 | 42 | , show: function () { 43 | var that = this 44 | 45 | if (this.isShown) return 46 | 47 | $('body').addClass('modal-open') 48 | 49 | this.isShown = true 50 | this.$element.trigger('show') 51 | 52 | escape.call(this) 53 | backdrop.call(this, function () { 54 | var transition = $.support.transition && that.$element.hasClass('fade') 55 | 56 | !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position 57 | 58 | that.$element 59 | .show() 60 | 61 | if (transition) { 62 | that.$element[0].offsetWidth // force reflow 63 | } 64 | 65 | that.$element.addClass('in') 66 | 67 | transition ? 68 | that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) : 69 | that.$element.trigger('shown') 70 | 71 | }) 72 | } 73 | 74 | , hide: function ( e ) { 75 | e && e.preventDefault() 76 | 77 | if (!this.isShown) return 78 | 79 | var that = this 80 | this.isShown = false 81 | 82 | $('body').removeClass('modal-open') 83 | 84 | escape.call(this) 85 | 86 | this.$element 87 | .trigger('hide') 88 | .removeClass('in') 89 | 90 | $.support.transition && this.$element.hasClass('fade') ? 91 | hideWithTransition.call(this) : 92 | hideModal.call(this) 93 | } 94 | 95 | } 96 | 97 | 98 | /* MODAL PRIVATE METHODS 99 | * ===================== */ 100 | 101 | function hideWithTransition() { 102 | var that = this 103 | , timeout = setTimeout(function () { 104 | that.$element.off($.support.transition.end) 105 | hideModal.call(that) 106 | }, 500) 107 | 108 | this.$element.one($.support.transition.end, function () { 109 | clearTimeout(timeout) 110 | hideModal.call(that) 111 | }) 112 | } 113 | 114 | function hideModal( that ) { 115 | this.$element 116 | .hide() 117 | .trigger('hidden') 118 | 119 | backdrop.call(this) 120 | } 121 | 122 | function backdrop( callback ) { 123 | var that = this 124 | , animate = this.$element.hasClass('fade') ? 'fade' : '' 125 | 126 | if (this.isShown && this.options.backdrop) { 127 | var doAnimate = $.support.transition && animate 128 | 129 | this.$backdrop = $('