mirror of
				https://github.com/ComputerScienceHouse/proxstar.git
				synced 2025-03-09 15:40:09 +00:00 
			
		
		
		
	fix rq-scheduler version, most of templates is now working, fix javascript issues
This commit is contained in:
		
							parent
							
								
									5fb7f66624
								
							
						
					
					
						commit
						df39356c4d
					
				
					 11 changed files with 326 additions and 149 deletions
				
			
		| 
						 | 
				
			
			@ -24,7 +24,10 @@ OIDC_CLIENT_CONFIG = {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
# Proxmox
 | 
			
		||||
PROXMOX_HOSTS = [host.strip() for host in environ.get('PROXSTAR_PROXMOX_HOSTS', '').split(',')]
 | 
			
		||||
PROXMOX_HOSTS = [
 | 
			
		||||
    host.strip()
 | 
			
		||||
    for host in environ.get('PROXSTAR_PROXMOX_HOSTS', '').split(',')
 | 
			
		||||
]
 | 
			
		||||
PROXMOX_USER = environ.get('PROXSTAR_PROXMOX_USER', '')
 | 
			
		||||
PROXMOX_PASS = environ.get('PROXSTAR_PROXMOX_PASS', '')
 | 
			
		||||
PROXMOX_ISO_STORAGE = environ.get('PROXSTAR_PROXMOX_ISO_STORAGE', 'nfs-iso')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ from werkzeug.contrib.cache import SimpleCache
 | 
			
		|||
from flask_pyoidc.flask_pyoidc import OIDCAuthentication
 | 
			
		||||
from flask import Flask, render_template, request, redirect, send_from_directory, session
 | 
			
		||||
from proxstar.db import *
 | 
			
		||||
from proxstar.util import *
 | 
			
		||||
from proxstar.tasks import *
 | 
			
		||||
from proxstar.starrs import *
 | 
			
		||||
from proxstar.ldapdb import *
 | 
			
		||||
| 
						 | 
				
			
			@ -294,6 +295,7 @@ def create():
 | 
			
		|||
            percents = get_user_usage_percent(proxmox, user, usage, limits)
 | 
			
		||||
            isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE'])
 | 
			
		||||
            pools = get_pools(proxmox, db)
 | 
			
		||||
            templates = get_templates(db)
 | 
			
		||||
            return render_template(
 | 
			
		||||
                'create.html',
 | 
			
		||||
                username=user,
 | 
			
		||||
| 
						 | 
				
			
			@ -303,11 +305,13 @@ def create():
 | 
			
		|||
                limits=limits,
 | 
			
		||||
                percents=percents,
 | 
			
		||||
                isos=isos,
 | 
			
		||||
                pools=pools)
 | 
			
		||||
                pools=pools,
 | 
			
		||||
                templates=templates)
 | 
			
		||||
        elif request.method == 'POST':
 | 
			
		||||
            name = request.form['name']
 | 
			
		||||
            cores = request.form['cores']
 | 
			
		||||
            memory = request.form['mem']
 | 
			
		||||
            template = request.form['template']
 | 
			
		||||
            disk = request.form['disk']
 | 
			
		||||
            iso = request.form['iso']
 | 
			
		||||
            if iso != 'none':
 | 
			
		||||
| 
						 | 
				
			
			@ -323,8 +327,28 @@ def create():
 | 
			
		|||
            else:
 | 
			
		||||
                valid, available = check_hostname(starrs, name)
 | 
			
		||||
                if valid and available:
 | 
			
		||||
                    q.enqueue(create_vm_task, user, name, cores, memory, disk,
 | 
			
		||||
                              iso)
 | 
			
		||||
                    if template == 'none':
 | 
			
		||||
                        q.enqueue(
 | 
			
		||||
                            create_vm_task,
 | 
			
		||||
                            user,
 | 
			
		||||
                            name,
 | 
			
		||||
                            cores,
 | 
			
		||||
                            memory,
 | 
			
		||||
                            disk,
 | 
			
		||||
                            iso,
 | 
			
		||||
                            timeout=300)
 | 
			
		||||
                    else:
 | 
			
		||||
                        password = gen_password(16)
 | 
			
		||||
                        q.enqueue(
 | 
			
		||||
                            setup_template,
 | 
			
		||||
                            template,
 | 
			
		||||
                            name,
 | 
			
		||||
                            user,
 | 
			
		||||
                            password,
 | 
			
		||||
                            cores,
 | 
			
		||||
                            memory,
 | 
			
		||||
                            timeout=600)
 | 
			
		||||
                        return password, 200
 | 
			
		||||
            return '', 200
 | 
			
		||||
    else:
 | 
			
		||||
        return '', 403
 | 
			
		||||
| 
						 | 
				
			
			@ -362,12 +386,14 @@ def settings():
 | 
			
		|||
    rtp = 'rtp' in session['userinfo']['groups']
 | 
			
		||||
    active = 'active' in session['userinfo']['groups']
 | 
			
		||||
    if rtp:
 | 
			
		||||
        templates = get_templates(db)
 | 
			
		||||
        ignored_pools = get_ignored_pools(db)
 | 
			
		||||
        return render_template(
 | 
			
		||||
            'settings.html',
 | 
			
		||||
            username=user,
 | 
			
		||||
            rtp=rtp,
 | 
			
		||||
            active=active,
 | 
			
		||||
            templates=templates,
 | 
			
		||||
            ignored_pools=ignored_pools)
 | 
			
		||||
    else:
 | 
			
		||||
        return '', 403
 | 
			
		||||
| 
						 | 
				
			
			@ -386,6 +412,14 @@ def ignored_pools(pool):
 | 
			
		|||
        return '', 403
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.route('/template/<string:template_id>/disk')
 | 
			
		||||
@auth.oidc_auth
 | 
			
		||||
def template_disk(template_id):
 | 
			
		||||
    if template_id == 'none':
 | 
			
		||||
        return '0'
 | 
			
		||||
    return get_template_disk(db, template_id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.route('/vm/<string:vmid>/rrd/<path:path>')
 | 
			
		||||
@auth.oidc_auth
 | 
			
		||||
def send_rrd(vmid, path):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import datetime
 | 
			
		|||
from sqlalchemy import exists
 | 
			
		||||
from dateutil.relativedelta import relativedelta
 | 
			
		||||
from proxstar.ldapdb import *
 | 
			
		||||
from proxstar.models import VM_Expiration, Usage_Limit, Pool_Cache, Ignored_Pools, Base
 | 
			
		||||
from proxstar.models import VM_Expiration, Usage_Limit, Pool_Cache, Ignored_Pools, Template, Base
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_vm_expire(db, vmid, months):
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +135,8 @@ def get_ignored_pools(db):
 | 
			
		|||
 | 
			
		||||
def delete_ignored_pool(db, pool):
 | 
			
		||||
    if db.query(exists().where(Ignored_Pools.id == pool)).scalar():
 | 
			
		||||
        ignored_pool = db.query(Ignored_Pools).filter(Ignored_Pools.id == pool).one()
 | 
			
		||||
        ignored_pool = db.query(Ignored_Pools).filter(
 | 
			
		||||
            Ignored_Pools.id == pool).one()
 | 
			
		||||
        db.delete(ignored_pool)
 | 
			
		||||
        db.commit()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -145,3 +146,37 @@ def add_ignored_pool(db, pool):
 | 
			
		|||
        ignored_pool = Ignored_Pools(id=pool)
 | 
			
		||||
        db.add(ignored_pool)
 | 
			
		||||
        db.commit()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_templates(db):
 | 
			
		||||
    templates = []
 | 
			
		||||
    for template in db.query(Template).all():
 | 
			
		||||
        template_dict = dict()
 | 
			
		||||
        template_dict['id'] = template.id
 | 
			
		||||
        template_dict['name'] = template.name
 | 
			
		||||
        template_dict['desc'] = template.desc
 | 
			
		||||
        template_dict['username'] = template.username
 | 
			
		||||
        template_dict['disk'] = template.disk
 | 
			
		||||
        templates.append(template_dict)
 | 
			
		||||
    return templates
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_template(db, template_id):
 | 
			
		||||
    template_dict = dict()
 | 
			
		||||
    if db.query(exists().where(Template.id == template_id)).scalar():
 | 
			
		||||
        template = db.query(Template).filter(Template.id == template_id).one()
 | 
			
		||||
        template_dict['id'] = template.id
 | 
			
		||||
        template_dict['name'] = template.name
 | 
			
		||||
        template_dict['desc'] = template.desc
 | 
			
		||||
        template_dict['username'] = template.username
 | 
			
		||||
        template_dict['password'] = template.password
 | 
			
		||||
        template_dict['disk'] = template.disk
 | 
			
		||||
    return template_dict
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_template_disk(db, template_id):
 | 
			
		||||
    disk = 0
 | 
			
		||||
    if db.query(exists().where(Template.id == template_id)).scalar():
 | 
			
		||||
        template = db.query(Template).filter(Template.id == template_id).one()
 | 
			
		||||
        disk = template.disk
 | 
			
		||||
    return str(disk)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,11 +24,11 @@ def send_vm_expire_email(user, vms):
 | 
			
		|||
    body = "The following VMs in Proxstar are expiring soon:\n\n"
 | 
			
		||||
    for vm in vms:
 | 
			
		||||
        if vm[1] == 0:
 | 
			
		||||
            body += "    - {} today (VM has been stopped)\n".format(
 | 
			
		||||
            body += "    - {} expires today (VM has been stopped and will be deleted tomorrow)\n".format(
 | 
			
		||||
                vm[0], vm[1])
 | 
			
		||||
        if vm[1] == 1:
 | 
			
		||||
            body += "    - {} in 1 day\n".format(vm[0])
 | 
			
		||||
        elif vm[1] == 1:
 | 
			
		||||
            body += "    - {} expires in 1 day\n".format(vm[0])
 | 
			
		||||
        else:
 | 
			
		||||
            body += "    - {} in {} days\n".format(vm[0], vm[1])
 | 
			
		||||
            body += "    - {} expires in {} days\n".format(vm[0], vm[1])
 | 
			
		||||
    body += "\nPlease login to Proxstar and renew any VMs you would like to keep."
 | 
			
		||||
    send_email(toaddr, subject, body)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,9 @@ class Template(Base):
 | 
			
		|||
    id = Column(Integer, primary_key=True)
 | 
			
		||||
    name = Column(String(32), nullable=False)
 | 
			
		||||
    desc = Column(Text)
 | 
			
		||||
    username = Column(Text, nullable=False)
 | 
			
		||||
    password = Column(Text, nullable=False)
 | 
			
		||||
    disk = Column(Integer, nullable=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Ignored_Pools(Base):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,11 @@
 | 
			
		|||
/*jshint esversion: 6 */
 | 
			
		||||
 | 
			
		||||
$(document).ready(function(){
 | 
			
		||||
    $('[data-toggle="tooltip"]').tooltip();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$("#delete-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to delete ${vmname}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +21,7 @@ $("#delete-vm").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willDelete) => {
 | 
			
		||||
        if (willDelete) {
 | 
			
		||||
            const vmid = $(this).data('vmid')
 | 
			
		||||
            const vmid = $(this).data('vmid');
 | 
			
		||||
            fetch(`/vm/${vmid}/delete`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +44,7 @@ $("#delete-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#stop-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to stop ${vmname}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +83,7 @@ $("#stop-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#reset-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to reset ${vmname}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +99,7 @@ $("#reset-vm").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willReset) => {
 | 
			
		||||
        if (willReset) {
 | 
			
		||||
            const vmid = $(this).data('vmid')
 | 
			
		||||
            const vmid = $(this).data('vmid');
 | 
			
		||||
            fetch(`/vm/${vmid}/power/reset`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +122,7 @@ $("#reset-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#shutdown-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to shutdown ${vmname}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +138,7 @@ $("#shutdown-vm").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willShutdown) => {
 | 
			
		||||
        if (willShutdown) {
 | 
			
		||||
            const vmid = $(this).data('vmid')
 | 
			
		||||
            const vmid = $(this).data('vmid');
 | 
			
		||||
            fetch(`/vm/${vmid}/power/shutdown`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +161,7 @@ $("#shutdown-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#suspend-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to suspend ${vmname}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +177,7 @@ $("#suspend-vm").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willSuspend) => {
 | 
			
		||||
        if (willSuspend) {
 | 
			
		||||
            const vmid = $(this).data('vmid')
 | 
			
		||||
            const vmid = $(this).data('vmid');
 | 
			
		||||
            fetch(`/vm/${vmid}/power/suspend`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -198,8 +200,8 @@ $("#suspend-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#start-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    const vmid = $(this).data('vmid');
 | 
			
		||||
    fetch(`/vm/${vmid}/power/start`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
        method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -220,8 +222,8 @@ $("#start-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#resume-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    const vmid = $(this).data('vmid');
 | 
			
		||||
    fetch(`/vm/${vmid}/power/resume`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
        method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -242,8 +244,7 @@ $("#resume-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#eject-iso").click(function(){
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    const iso = $(this).data('iso')
 | 
			
		||||
    const iso = $(this).data('iso');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to eject ${iso}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -264,7 +265,7 @@ $("#eject-iso").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willEject) => {
 | 
			
		||||
        if (willEject) {
 | 
			
		||||
            const vmid = $(this).data('vmid')
 | 
			
		||||
            const vmid = $(this).data('vmid');
 | 
			
		||||
            fetch(`/vm/${vmid}/eject`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -295,7 +296,6 @@ $("#eject-iso").click(function(){
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
$("#change-iso").click(function(){
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    fetch(`/isos`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
    }).then((response) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -326,8 +326,8 @@ $("#change-iso").click(function(){
 | 
			
		|||
        })
 | 
			
		||||
        .then((willChange) => {
 | 
			
		||||
            if (willChange) {
 | 
			
		||||
                const vmid = $(this).data('vmid')
 | 
			
		||||
                const iso = $(iso_list).val()
 | 
			
		||||
                const vmid = $(this).data('vmid');
 | 
			
		||||
                const iso = $(iso_list).val();
 | 
			
		||||
                fetch(`/vm/${vmid}/mount/${iso}`, {
 | 
			
		||||
                    credentials: 'same-origin',
 | 
			
		||||
                    method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -365,8 +365,8 @@ $("#change-iso").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#renew-vm").click(function(){
 | 
			
		||||
    const vmname = $(this).data('vmname')
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    const vmname = $(this).data('vmname');
 | 
			
		||||
    const vmid = $(this).data('vmid');
 | 
			
		||||
    fetch(`/vm/${vmid}/renew`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
        method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -387,13 +387,24 @@ $("#renew-vm").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#create-vm").click(function(){
 | 
			
		||||
    const name = document.getElementById('name').value
 | 
			
		||||
    const cores = document.getElementById('cores').value
 | 
			
		||||
    const mem = document.getElementById('mem').value
 | 
			
		||||
    const disk = document.getElementById('disk').value
 | 
			
		||||
    const iso = document.getElementById('iso').value
 | 
			
		||||
    const user = document.getElementById('user')
 | 
			
		||||
    const max_disk = $(this).data('max_disk')
 | 
			
		||||
    const name = document.getElementById('name').value;
 | 
			
		||||
    const cores = document.getElementById('cores').value;
 | 
			
		||||
    const mem = document.getElementById('mem').value;
 | 
			
		||||
    const template = document.getElementById('template').value;
 | 
			
		||||
    const iso = document.getElementById('iso').value;
 | 
			
		||||
    const user = document.getElementById('user');
 | 
			
		||||
    const max_disk = $(this).data('max_disk');
 | 
			
		||||
    var disk = document.getElementById('disk').value;
 | 
			
		||||
    fetch(`/template/${template}/disk`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
    }).then((response) => {
 | 
			
		||||
        return response.text()
 | 
			
		||||
    }).then((template_disk) => {
 | 
			
		||||
        if (template != 'none') {
 | 
			
		||||
            disk = template_disk
 | 
			
		||||
        }
 | 
			
		||||
        return disk
 | 
			
		||||
    }).then((disk) => {
 | 
			
		||||
        if (name && disk) {
 | 
			
		||||
            if (disk > max_disk) {
 | 
			
		||||
                swal("Uh oh...", `You do not have enough disk resources available! Please lower the VM disk size to ${max_disk}GB or lower.`, "error");
 | 
			
		||||
| 
						 | 
				
			
			@ -407,7 +418,13 @@ $("#create-vm").click(function(){
 | 
			
		|||
                        var loader = document.createElement('div');
 | 
			
		||||
                        loader.setAttribute('class', 'loader');
 | 
			
		||||
                        var info = document.createElement('span');
 | 
			
		||||
                    info.innerHTML = `Cores: ${cores}<br>Memory: ${mem/1024} GB<br>Disk: ${disk} GB<br>ISO: ${iso}`,
 | 
			
		||||
                        if (template == 'none') {
 | 
			
		||||
                            info.innerHTML = `Cores: ${cores}<br>Memory: ${mem/1024}GB<br>Disk: ${disk}GB<br>ISO: ${iso}`;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            const template_select = document.getElementById('template');
 | 
			
		||||
                            const template_name = template_select.options[template_select.selectedIndex].text;
 | 
			
		||||
                            info.innerHTML = `Cores: ${cores}<br>Memory: ${mem/1024}GB<br>Template: ${template_name}`;
 | 
			
		||||
                        }
 | 
			
		||||
                        swal({
 | 
			
		||||
                            title: `Are you sure you want to create ${name}?`,
 | 
			
		||||
                            content: info,
 | 
			
		||||
| 
						 | 
				
			
			@ -427,6 +444,7 @@ $("#create-vm").click(function(){
 | 
			
		|||
                                data.append('name', name);
 | 
			
		||||
                                data.append('cores', cores);
 | 
			
		||||
                                data.append('mem', mem);
 | 
			
		||||
                                data.append('template', template);
 | 
			
		||||
                                data.append('disk', disk);
 | 
			
		||||
                                data.append('iso', iso);
 | 
			
		||||
                                if (user) {
 | 
			
		||||
| 
						 | 
				
			
			@ -437,7 +455,14 @@ $("#create-vm").click(function(){
 | 
			
		|||
                                    method: 'post',
 | 
			
		||||
                                    body: data
 | 
			
		||||
                                }).then((response) => {
 | 
			
		||||
                                return swal(`${name} is now being created. Check back soon and it should be good to go.`, {
 | 
			
		||||
                                    return response.text()
 | 
			
		||||
                                }).then((password) => {
 | 
			
		||||
                                    if (template == 'none') {
 | 
			
		||||
                                        var swal_text = `${name} is now being created. Check back soon and it should be good to go.`
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        var swal_text = `${name} is now being created. Check back soon and it should be good to go. The SSH credentials are your CSH username for the user and ${password} for the password. Save this password because you will not be able to retrieve it again!`
 | 
			
		||||
                                    }
 | 
			
		||||
                                    return swal(`${swal_text}`, {
 | 
			
		||||
                                        icon: "success",
 | 
			
		||||
                                        buttons: {
 | 
			
		||||
                                            ok: {
 | 
			
		||||
| 
						 | 
				
			
			@ -474,12 +499,13 @@ $("#create-vm").click(function(){
 | 
			
		|||
            swal("Uh oh...", `You must enter a disk size for your VM!`, "error");
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$("#change-cores").click(function(){
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    const cur_cores = $(this).data('cores')
 | 
			
		||||
    const usage = $(this).data('usage')
 | 
			
		||||
    const limit = $(this).data('limit')
 | 
			
		||||
    const vmid = $(this).data('vmid');
 | 
			
		||||
    const cur_cores = $(this).data('cores');
 | 
			
		||||
    const usage = $(this).data('usage');
 | 
			
		||||
    const limit = $(this).data('limit');
 | 
			
		||||
    var core_list = document.createElement('select');
 | 
			
		||||
    core_list.setAttribute('style', 'width: 25px');
 | 
			
		||||
    for (i = 1; i < limit - usage + 1; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -504,7 +530,7 @@ $("#change-cores").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willChange) => {
 | 
			
		||||
        if (willChange) {
 | 
			
		||||
            const cores = $(core_list).val()
 | 
			
		||||
            const cores = $(core_list).val();
 | 
			
		||||
            fetch(`/vm/${vmid}/cpu/${cores}`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -534,10 +560,10 @@ $("#change-cores").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$("#change-mem").click(function(){
 | 
			
		||||
    const vmid = $(this).data('vmid')
 | 
			
		||||
    const cur_mem = $(this).data('mem')
 | 
			
		||||
    const usage = $(this).data('usage')
 | 
			
		||||
    const limit = $(this).data('limit')
 | 
			
		||||
    const vmid = $(this).data('vmid');
 | 
			
		||||
    const cur_mem = $(this).data('mem');
 | 
			
		||||
    const usage = $(this).data('usage');
 | 
			
		||||
    const limit = $(this).data('limit');
 | 
			
		||||
    var mem_list = document.createElement('select');
 | 
			
		||||
    mem_list.setAttribute('style', 'width: 45px');
 | 
			
		||||
    for (i = 1; i < limit - usage + 1; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -562,7 +588,7 @@ $("#change-mem").click(function(){
 | 
			
		|||
    })
 | 
			
		||||
    .then((willChange) => {
 | 
			
		||||
        if (willChange) {
 | 
			
		||||
            const mem = $(mem_list).val()
 | 
			
		||||
            const mem = $(mem_list).val();
 | 
			
		||||
            fetch(`/vm/${vmid}/mem/${mem}`, {
 | 
			
		||||
                credentials: 'same-origin',
 | 
			
		||||
                method: 'post'
 | 
			
		||||
| 
						 | 
				
			
			@ -592,10 +618,10 @@ $("#change-mem").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$(".edit-limit").click(function(){
 | 
			
		||||
    const user = $(this).data('user')
 | 
			
		||||
    const cur_cpu = $(this).data('cpu')
 | 
			
		||||
    const cur_mem = $(this).data('mem')
 | 
			
		||||
    const cur_disk = $(this).data('disk')
 | 
			
		||||
    const user = $(this).data('user');
 | 
			
		||||
    const cur_cpu = $(this).data('cpu');
 | 
			
		||||
    const cur_mem = $(this).data('mem');
 | 
			
		||||
    const cur_disk = $(this).data('disk');
 | 
			
		||||
    var options = document.createElement('div');
 | 
			
		||||
    cpu_text = document.createElement('p');
 | 
			
		||||
    cpu_text.innerHTML = 'CPU';
 | 
			
		||||
| 
						 | 
				
			
			@ -671,7 +697,7 @@ $(".edit-limit").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$(".reset-limit").click(function(){
 | 
			
		||||
    const user = $(this).data('user')
 | 
			
		||||
    const user = $(this).data('user');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to reset the usage limits for ${user} to the defaults?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -709,7 +735,7 @@ $(".reset-limit").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$(".delete-user").click(function(){
 | 
			
		||||
    const user = $(this).data('user')
 | 
			
		||||
    const user = $(this).data('user');
 | 
			
		||||
    swal({
 | 
			
		||||
        title: `Are you sure you want to delete the pool for ${user}?`,
 | 
			
		||||
        icon: "warning",
 | 
			
		||||
| 
						 | 
				
			
			@ -747,7 +773,7 @@ $(".delete-user").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$(".delete-ignored-pool").click(function(){
 | 
			
		||||
    const pool = $(this).data('pool')
 | 
			
		||||
    const pool = $(this).data('pool');
 | 
			
		||||
    fetch(`/pool/${pool}/ignore`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
        method: 'delete'
 | 
			
		||||
| 
						 | 
				
			
			@ -756,10 +782,23 @@ $(".delete-ignored-pool").click(function(){
 | 
			
		|||
});
 | 
			
		||||
 | 
			
		||||
$(".add-ignored-pool").click(function(){
 | 
			
		||||
    const pool = document.getElementById('pool').value
 | 
			
		||||
    const pool = document.getElementById('pool').value;
 | 
			
		||||
    fetch(`/pool/${pool}/ignore`, {
 | 
			
		||||
        credentials: 'same-origin',
 | 
			
		||||
        method: 'post'
 | 
			
		||||
    });
 | 
			
		||||
    location.reload();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function hide_for_template(obj) {
 | 
			
		||||
    var template_element = obj;
 | 
			
		||||
    var selected = template_element.options[template_element.selectedIndex].value;
 | 
			
		||||
    var hide_area = document.getElementById('hide-for-template');
 | 
			
		||||
 | 
			
		||||
    if(selected === 'none'){
 | 
			
		||||
        hide_area.style.display = 'block';
 | 
			
		||||
    }
 | 
			
		||||
    else{
 | 
			
		||||
        hide_area.style.display = 'none';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ from flask import Flask
 | 
			
		|||
from sqlalchemy import create_engine
 | 
			
		||||
from sqlalchemy.orm import sessionmaker
 | 
			
		||||
from proxstar.db import *
 | 
			
		||||
from proxstar.util import *
 | 
			
		||||
from proxstar.mail import *
 | 
			
		||||
from proxstar.starrs import *
 | 
			
		||||
from proxstar.proxmox import *
 | 
			
		||||
| 
						 | 
				
			
			@ -103,30 +104,45 @@ def generate_pool_cache_task():
 | 
			
		|||
        store_pool_cache(db, pools)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_template(template_id, name, user, cores, memory):
 | 
			
		||||
def setup_template(template_id, name, user, password, cores, memory):
 | 
			
		||||
    with app.app_context():
 | 
			
		||||
        proxmox = connect_proxmox()
 | 
			
		||||
        starrs = connect_starrs()
 | 
			
		||||
        db = connect_db()
 | 
			
		||||
        template = get_template(db, template_id)
 | 
			
		||||
        vmid, mac = clone_vm(proxmox, template_id, name, user)
 | 
			
		||||
        ip = get_next_ip(starrs, app.config['STARRS_IP_RANGE'])
 | 
			
		||||
        register_starrs(starrs, name, app.config['STARRS_USER'], mac, ip)
 | 
			
		||||
        get_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
 | 
			
		||||
        change_vm_cpu(proxmox, vmid, cores)
 | 
			
		||||
        change_vm_mem(proxmox, vmid, memory)
 | 
			
		||||
        time.sleep(60)
 | 
			
		||||
        time.sleep(90)
 | 
			
		||||
        change_vm_power(proxmox, vmid, 'start')
 | 
			
		||||
        time.sleep(20)
 | 
			
		||||
        client = paramiko.SSHClient()
 | 
			
		||||
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 | 
			
		||||
        retry = 0
 | 
			
		||||
        while retry < 30:
 | 
			
		||||
            try:
 | 
			
		||||
                client.connect(ip, username='root', password='')
 | 
			
		||||
                client.connect(
 | 
			
		||||
                    ip,
 | 
			
		||||
                    username=template['username'],
 | 
			
		||||
                    password=template['password'])
 | 
			
		||||
                break
 | 
			
		||||
            except:
 | 
			
		||||
                retry += 1
 | 
			
		||||
                time.sleep(3)
 | 
			
		||||
        stdin, stdout, stderr = client.exec_command('ls')
 | 
			
		||||
        for line in stdout:
 | 
			
		||||
            print('... ' + line.strip('\n'))
 | 
			
		||||
        stdin, stdout, stderr = client.exec_command("useradd {}".format(user))
 | 
			
		||||
        exit_status = stdout.channel.recv_exit_status()
 | 
			
		||||
        root_password = gen_password(32)
 | 
			
		||||
        stdin, stdout, stderr = client.exec_command(
 | 
			
		||||
            "echo '{}' | passwd root --stdin".format(root_password))
 | 
			
		||||
        exit_status = stdout.channel.recv_exit_status()
 | 
			
		||||
        stdin, stdout, stderr = client.exec_command(
 | 
			
		||||
            "echo '{}' | passwd '{}' --stdin".format(password, user))
 | 
			
		||||
        exit_status = stdout.channel.recv_exit_status()
 | 
			
		||||
        stdin, stdout, stderr = client.exec_command(
 | 
			
		||||
            "echo '{} ALL=(ALL:ALL) ALL' | sudo EDITOR='tee -a' visudo".format(
 | 
			
		||||
                user))
 | 
			
		||||
        exit_status = stdout.channel.recv_exit_status()
 | 
			
		||||
        client.close()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,16 @@
 | 
			
		|||
                            {% endfor %}
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label for="template" class="pull-left">Template</label>
 | 
			
		||||
                        <select name="template" id="template" class="form-control" onchange="hide_for_template(this)">
 | 
			
		||||
                            <option value="none"></option>
 | 
			
		||||
                            {% for template in templates %}
 | 
			
		||||
                            <option value="{{ template['id'] }}">{{ template['name'] }} ({{ template['disk'] }}GB)</option>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        </select>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div id="hide-for-template">
 | 
			
		||||
                        <div class="form-group">
 | 
			
		||||
                            <label for="disk" class="pull-left">Disk (GB)</label>
 | 
			
		||||
                            <input type="number" value=32 name="disk" id="disk" class="form-control" min="1" max="{{ limits['disk'] - usage['disk'] }}">
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +60,7 @@
 | 
			
		|||
                                {% endfor %}
 | 
			
		||||
                            </select>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    {% if rtp %}
 | 
			
		||||
                    <div class="form-group">
 | 
			
		||||
                        <label for="user" class="pull-left">User</label>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,34 @@
 | 
			
		|||
                    <h3 class="panel-title">Templates</h3>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="panel-body">
 | 
			
		||||
                    <table class="table table-bordered table-striped">
 | 
			
		||||
                        <thead>
 | 
			
		||||
                            <tr role="row">
 | 
			
		||||
                                <th>Proxmox ID</th>
 | 
			
		||||
                                <th>Name</th>
 | 
			
		||||
                                <th>Username</th>
 | 
			
		||||
                                <th>Disk Size (GB)</th>
 | 
			
		||||
                                <th>Description</th>
 | 
			
		||||
                                <th>Delete</th>
 | 
			
		||||
                            </tr>
 | 
			
		||||
                        </thead>
 | 
			
		||||
                        <tbody>
 | 
			
		||||
                            {% for template in templates %}
 | 
			
		||||
                                <tr role="row">
 | 
			
		||||
                                    <td>{{ template['id'] }}</td>
 | 
			
		||||
                                    <td>{{ template['name'] }}</td>
 | 
			
		||||
                                    <td>{{ template['username'] }}</td>
 | 
			
		||||
                                    <td>{{ template['disk'] }}</td>
 | 
			
		||||
                                    <td>{{ template['desc'] }}</td>
 | 
			
		||||
                                    <td>
 | 
			
		||||
                                        <button class="btn btn-sm btn-danger delete-template" data-template_id="{{ template['id'] }}">
 | 
			
		||||
                                            <span class="glyphicon glyphicon-remove"></span>
 | 
			
		||||
                                        </button>
 | 
			
		||||
                                    </td>
 | 
			
		||||
                                </tr>
 | 
			
		||||
                            {% endfor %}
 | 
			
		||||
                        </tbody>
 | 
			
		||||
                    </table>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								proxstar/util.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								proxstar/util.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
import secrets
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gen_password(
 | 
			
		||||
        length,
 | 
			
		||||
        charset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*"
 | 
			
		||||
):
 | 
			
		||||
    return "".join([secrets.choice(charset) for _ in range(0, length)])
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ sqlalchemy
 | 
			
		|||
python-dateutil
 | 
			
		||||
csh_ldap
 | 
			
		||||
rq
 | 
			
		||||
rq-scheduler
 | 
			
		||||
rq-scheduler==0.7.0
 | 
			
		||||
gunicorn
 | 
			
		||||
raven
 | 
			
		||||
paramiko
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue