mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-02-14 14:01:51 +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,99 +387,125 @@ $("#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')
|
||||
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");
|
||||
} else {
|
||||
fetch(`/hostname/${name}`, {
|
||||
credentials: 'same-origin',
|
||||
}).then((response) => {
|
||||
return response.text()
|
||||
}).then((text) => {
|
||||
if (text == 'ok') {
|
||||
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}`,
|
||||
swal({
|
||||
title: `Are you sure you want to create ${name}?`,
|
||||
content: info,
|
||||
icon: "info",
|
||||
buttons: {
|
||||
cancel: true,
|
||||
create: {
|
||||
text: "Create",
|
||||
closeModal: false,
|
||||
className: "swal-button",
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((willCreate) => {
|
||||
if (willCreate) {
|
||||
var data = new FormData();
|
||||
data.append('name', name);
|
||||
data.append('cores', cores);
|
||||
data.append('mem', mem);
|
||||
data.append('disk', disk);
|
||||
data.append('iso', iso);
|
||||
if (user) {
|
||||
data.append('user', user.value);
|
||||
}
|
||||
fetch('/vm/create', {
|
||||
credentials: 'same-origin',
|
||||
method: 'post',
|
||||
body: data
|
||||
}).then((response) => {
|
||||
return swal(`${name} is now being created. Check back soon and it should be good to go.`, {
|
||||
icon: "success",
|
||||
buttons: {
|
||||
ok: {
|
||||
text: "OK",
|
||||
closeModal: true,
|
||||
className: "",
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
window.location = "/";
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (text == 'invalid') {
|
||||
swal("Uh oh...", `That name is not a valid name! Please try another name.`, "error");
|
||||
} else if (text == 'taken') {
|
||||
swal("Uh oh...", `That name is not available! Please try another name.`, "error");
|
||||
}
|
||||
}).catch(err => {
|
||||
if (err) {
|
||||
swal("Uh oh...", `Unable to verify name! Please try again later.`, "error");
|
||||
} else {
|
||||
swal.stopLoading();
|
||||
swal.close();
|
||||
}
|
||||
});
|
||||
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
|
||||
}
|
||||
} else if (!name && !disk) {
|
||||
swal("Uh oh...", `You must enter a name and disk size for your VM!`, "error");
|
||||
} else if (!name) {
|
||||
swal("Uh oh...", `You must enter a name for your VM!`, "error");
|
||||
} else if (!disk) {
|
||||
swal("Uh oh...", `You must enter a disk size for your VM!`, "error");
|
||||
}
|
||||
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");
|
||||
} else {
|
||||
fetch(`/hostname/${name}`, {
|
||||
credentials: 'same-origin',
|
||||
}).then((response) => {
|
||||
return response.text()
|
||||
}).then((text) => {
|
||||
if (text == 'ok') {
|
||||
var loader = document.createElement('div');
|
||||
loader.setAttribute('class', 'loader');
|
||||
var info = document.createElement('span');
|
||||
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,
|
||||
icon: "info",
|
||||
buttons: {
|
||||
cancel: true,
|
||||
create: {
|
||||
text: "Create",
|
||||
closeModal: false,
|
||||
className: "swal-button",
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((willCreate) => {
|
||||
if (willCreate) {
|
||||
var data = new FormData();
|
||||
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) {
|
||||
data.append('user', user.value);
|
||||
}
|
||||
fetch('/vm/create', {
|
||||
credentials: 'same-origin',
|
||||
method: 'post',
|
||||
body: data
|
||||
}).then((response) => {
|
||||
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: {
|
||||
text: "OK",
|
||||
closeModal: true,
|
||||
className: "",
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
window.location = "/";
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (text == 'invalid') {
|
||||
swal("Uh oh...", `That name is not a valid name! Please try another name.`, "error");
|
||||
} else if (text == 'taken') {
|
||||
swal("Uh oh...", `That name is not available! Please try another name.`, "error");
|
||||
}
|
||||
}).catch(err => {
|
||||
if (err) {
|
||||
swal("Uh oh...", `Unable to verify name! Please try again later.`, "error");
|
||||
} else {
|
||||
swal.stopLoading();
|
||||
swal.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (!name && !disk) {
|
||||
swal("Uh oh...", `You must enter a name and disk size for your VM!`, "error");
|
||||
} else if (!name) {
|
||||
swal("Uh oh...", `You must enter a name for your VM!`, "error");
|
||||
} else if (!disk) {
|
||||
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()
|
||||
|
|
|
@ -38,18 +38,29 @@
|
|||
</select>
|
||||
</div>
|
||||
<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'] }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="iso" class="pull-left">ISO</label>
|
||||
<select name="iso" id="iso" class="form-control">
|
||||
<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 iso in isos %}
|
||||
<option value="{{ iso }}">{{ iso }}</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'] }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="iso" class="pull-left">ISO</label>
|
||||
<select name="iso" id="iso" class="form-control">
|
||||
<option value="none"></option>
|
||||
{% for iso in isos %}
|
||||
<option value="{{ iso }}">{{ iso }}</option>
|
||||
{% 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…
Reference in a new issue