mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-02-14 22:11: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
|
||||||
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_USER = environ.get('PROXSTAR_PROXMOX_USER', '')
|
||||||
PROXMOX_PASS = environ.get('PROXSTAR_PROXMOX_PASS', '')
|
PROXMOX_PASS = environ.get('PROXSTAR_PROXMOX_PASS', '')
|
||||||
PROXMOX_ISO_STORAGE = environ.get('PROXSTAR_PROXMOX_ISO_STORAGE', 'nfs-iso')
|
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_pyoidc.flask_pyoidc import OIDCAuthentication
|
||||||
from flask import Flask, render_template, request, redirect, send_from_directory, session
|
from flask import Flask, render_template, request, redirect, send_from_directory, session
|
||||||
from proxstar.db import *
|
from proxstar.db import *
|
||||||
|
from proxstar.util import *
|
||||||
from proxstar.tasks import *
|
from proxstar.tasks import *
|
||||||
from proxstar.starrs import *
|
from proxstar.starrs import *
|
||||||
from proxstar.ldapdb import *
|
from proxstar.ldapdb import *
|
||||||
|
@ -294,6 +295,7 @@ def create():
|
||||||
percents = get_user_usage_percent(proxmox, user, usage, limits)
|
percents = get_user_usage_percent(proxmox, user, usage, limits)
|
||||||
isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE'])
|
isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE'])
|
||||||
pools = get_pools(proxmox, db)
|
pools = get_pools(proxmox, db)
|
||||||
|
templates = get_templates(db)
|
||||||
return render_template(
|
return render_template(
|
||||||
'create.html',
|
'create.html',
|
||||||
username=user,
|
username=user,
|
||||||
|
@ -303,11 +305,13 @@ def create():
|
||||||
limits=limits,
|
limits=limits,
|
||||||
percents=percents,
|
percents=percents,
|
||||||
isos=isos,
|
isos=isos,
|
||||||
pools=pools)
|
pools=pools,
|
||||||
|
templates=templates)
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
name = request.form['name']
|
name = request.form['name']
|
||||||
cores = request.form['cores']
|
cores = request.form['cores']
|
||||||
memory = request.form['mem']
|
memory = request.form['mem']
|
||||||
|
template = request.form['template']
|
||||||
disk = request.form['disk']
|
disk = request.form['disk']
|
||||||
iso = request.form['iso']
|
iso = request.form['iso']
|
||||||
if iso != 'none':
|
if iso != 'none':
|
||||||
|
@ -323,8 +327,28 @@ def create():
|
||||||
else:
|
else:
|
||||||
valid, available = check_hostname(starrs, name)
|
valid, available = check_hostname(starrs, name)
|
||||||
if valid and available:
|
if valid and available:
|
||||||
q.enqueue(create_vm_task, user, name, cores, memory, disk,
|
if template == 'none':
|
||||||
iso)
|
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
|
return '', 200
|
||||||
else:
|
else:
|
||||||
return '', 403
|
return '', 403
|
||||||
|
@ -362,12 +386,14 @@ def settings():
|
||||||
rtp = 'rtp' in session['userinfo']['groups']
|
rtp = 'rtp' in session['userinfo']['groups']
|
||||||
active = 'active' in session['userinfo']['groups']
|
active = 'active' in session['userinfo']['groups']
|
||||||
if rtp:
|
if rtp:
|
||||||
|
templates = get_templates(db)
|
||||||
ignored_pools = get_ignored_pools(db)
|
ignored_pools = get_ignored_pools(db)
|
||||||
return render_template(
|
return render_template(
|
||||||
'settings.html',
|
'settings.html',
|
||||||
username=user,
|
username=user,
|
||||||
rtp=rtp,
|
rtp=rtp,
|
||||||
active=active,
|
active=active,
|
||||||
|
templates=templates,
|
||||||
ignored_pools=ignored_pools)
|
ignored_pools=ignored_pools)
|
||||||
else:
|
else:
|
||||||
return '', 403
|
return '', 403
|
||||||
|
@ -386,6 +412,14 @@ def ignored_pools(pool):
|
||||||
return '', 403
|
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>')
|
@app.route('/vm/<string:vmid>/rrd/<path:path>')
|
||||||
@auth.oidc_auth
|
@auth.oidc_auth
|
||||||
def send_rrd(vmid, path):
|
def send_rrd(vmid, path):
|
||||||
|
|
|
@ -2,7 +2,7 @@ import datetime
|
||||||
from sqlalchemy import exists
|
from sqlalchemy import exists
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from proxstar.ldapdb import *
|
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):
|
def get_vm_expire(db, vmid, months):
|
||||||
|
@ -135,7 +135,8 @@ def get_ignored_pools(db):
|
||||||
|
|
||||||
def delete_ignored_pool(db, pool):
|
def delete_ignored_pool(db, pool):
|
||||||
if db.query(exists().where(Ignored_Pools.id == pool)).scalar():
|
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.delete(ignored_pool)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
@ -145,3 +146,37 @@ def add_ignored_pool(db, pool):
|
||||||
ignored_pool = Ignored_Pools(id=pool)
|
ignored_pool = Ignored_Pools(id=pool)
|
||||||
db.add(ignored_pool)
|
db.add(ignored_pool)
|
||||||
db.commit()
|
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"
|
body = "The following VMs in Proxstar are expiring soon:\n\n"
|
||||||
for vm in vms:
|
for vm in vms:
|
||||||
if vm[1] == 0:
|
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])
|
vm[0], vm[1])
|
||||||
if vm[1] == 1:
|
elif vm[1] == 1:
|
||||||
body += " - {} in 1 day\n".format(vm[0])
|
body += " - {} expires in 1 day\n".format(vm[0])
|
||||||
else:
|
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."
|
body += "\nPlease login to Proxstar and renew any VMs you would like to keep."
|
||||||
send_email(toaddr, subject, body)
|
send_email(toaddr, subject, body)
|
||||||
|
|
|
@ -35,6 +35,9 @@ class Template(Base):
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
name = Column(String(32), nullable=False)
|
name = Column(String(32), nullable=False)
|
||||||
desc = Column(Text)
|
desc = Column(Text)
|
||||||
|
username = Column(Text, nullable=False)
|
||||||
|
password = Column(Text, nullable=False)
|
||||||
|
disk = Column(Integer, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Ignored_Pools(Base):
|
class Ignored_Pools(Base):
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
/*jshint esversion: 6 */
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#delete-vm").click(function(){
|
$("#delete-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to delete ${vmname}?`,
|
title: `Are you sure you want to delete ${vmname}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -19,7 +21,7 @@ $("#delete-vm").click(function(){
|
||||||
})
|
})
|
||||||
.then((willDelete) => {
|
.then((willDelete) => {
|
||||||
if (willDelete) {
|
if (willDelete) {
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/delete`, {
|
fetch(`/vm/${vmid}/delete`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -42,7 +44,7 @@ $("#delete-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#stop-vm").click(function(){
|
$("#stop-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to stop ${vmname}?`,
|
title: `Are you sure you want to stop ${vmname}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -81,7 +83,7 @@ $("#stop-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#reset-vm").click(function(){
|
$("#reset-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to reset ${vmname}?`,
|
title: `Are you sure you want to reset ${vmname}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -97,7 +99,7 @@ $("#reset-vm").click(function(){
|
||||||
})
|
})
|
||||||
.then((willReset) => {
|
.then((willReset) => {
|
||||||
if (willReset) {
|
if (willReset) {
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/power/reset`, {
|
fetch(`/vm/${vmid}/power/reset`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -120,7 +122,7 @@ $("#reset-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#shutdown-vm").click(function(){
|
$("#shutdown-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to shutdown ${vmname}?`,
|
title: `Are you sure you want to shutdown ${vmname}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -136,7 +138,7 @@ $("#shutdown-vm").click(function(){
|
||||||
})
|
})
|
||||||
.then((willShutdown) => {
|
.then((willShutdown) => {
|
||||||
if (willShutdown) {
|
if (willShutdown) {
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/power/shutdown`, {
|
fetch(`/vm/${vmid}/power/shutdown`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -159,7 +161,7 @@ $("#shutdown-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#suspend-vm").click(function(){
|
$("#suspend-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to suspend ${vmname}?`,
|
title: `Are you sure you want to suspend ${vmname}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -175,7 +177,7 @@ $("#suspend-vm").click(function(){
|
||||||
})
|
})
|
||||||
.then((willSuspend) => {
|
.then((willSuspend) => {
|
||||||
if (willSuspend) {
|
if (willSuspend) {
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/power/suspend`, {
|
fetch(`/vm/${vmid}/power/suspend`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -198,8 +200,8 @@ $("#suspend-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#start-vm").click(function(){
|
$("#start-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/power/start`, {
|
fetch(`/vm/${vmid}/power/start`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -220,8 +222,8 @@ $("#start-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#resume-vm").click(function(){
|
$("#resume-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/power/resume`, {
|
fetch(`/vm/${vmid}/power/resume`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -242,8 +244,7 @@ $("#resume-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#eject-iso").click(function(){
|
$("#eject-iso").click(function(){
|
||||||
const vmid = $(this).data('vmid')
|
const iso = $(this).data('iso');
|
||||||
const iso = $(this).data('iso')
|
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to eject ${iso}?`,
|
title: `Are you sure you want to eject ${iso}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -264,7 +265,7 @@ $("#eject-iso").click(function(){
|
||||||
})
|
})
|
||||||
.then((willEject) => {
|
.then((willEject) => {
|
||||||
if (willEject) {
|
if (willEject) {
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/eject`, {
|
fetch(`/vm/${vmid}/eject`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -295,7 +296,6 @@ $("#eject-iso").click(function(){
|
||||||
|
|
||||||
|
|
||||||
$("#change-iso").click(function(){
|
$("#change-iso").click(function(){
|
||||||
const vmid = $(this).data('vmid')
|
|
||||||
fetch(`/isos`, {
|
fetch(`/isos`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
|
@ -326,8 +326,8 @@ $("#change-iso").click(function(){
|
||||||
})
|
})
|
||||||
.then((willChange) => {
|
.then((willChange) => {
|
||||||
if (willChange) {
|
if (willChange) {
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
const iso = $(iso_list).val()
|
const iso = $(iso_list).val();
|
||||||
fetch(`/vm/${vmid}/mount/${iso}`, {
|
fetch(`/vm/${vmid}/mount/${iso}`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -365,8 +365,8 @@ $("#change-iso").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#renew-vm").click(function(){
|
$("#renew-vm").click(function(){
|
||||||
const vmname = $(this).data('vmname')
|
const vmname = $(this).data('vmname');
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
fetch(`/vm/${vmid}/renew`, {
|
fetch(`/vm/${vmid}/renew`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -387,13 +387,24 @@ $("#renew-vm").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#create-vm").click(function(){
|
$("#create-vm").click(function(){
|
||||||
const name = document.getElementById('name').value
|
const name = document.getElementById('name').value;
|
||||||
const cores = document.getElementById('cores').value
|
const cores = document.getElementById('cores').value;
|
||||||
const mem = document.getElementById('mem').value
|
const mem = document.getElementById('mem').value;
|
||||||
const disk = document.getElementById('disk').value
|
const template = document.getElementById('template').value;
|
||||||
const iso = document.getElementById('iso').value
|
const iso = document.getElementById('iso').value;
|
||||||
const user = document.getElementById('user')
|
const user = document.getElementById('user');
|
||||||
const max_disk = $(this).data('max_disk')
|
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 (name && disk) {
|
||||||
if (disk > max_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");
|
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');
|
var loader = document.createElement('div');
|
||||||
loader.setAttribute('class', 'loader');
|
loader.setAttribute('class', 'loader');
|
||||||
var info = document.createElement('span');
|
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({
|
swal({
|
||||||
title: `Are you sure you want to create ${name}?`,
|
title: `Are you sure you want to create ${name}?`,
|
||||||
content: info,
|
content: info,
|
||||||
|
@ -427,6 +444,7 @@ $("#create-vm").click(function(){
|
||||||
data.append('name', name);
|
data.append('name', name);
|
||||||
data.append('cores', cores);
|
data.append('cores', cores);
|
||||||
data.append('mem', mem);
|
data.append('mem', mem);
|
||||||
|
data.append('template', template);
|
||||||
data.append('disk', disk);
|
data.append('disk', disk);
|
||||||
data.append('iso', iso);
|
data.append('iso', iso);
|
||||||
if (user) {
|
if (user) {
|
||||||
|
@ -437,7 +455,14 @@ $("#create-vm").click(function(){
|
||||||
method: 'post',
|
method: 'post',
|
||||||
body: data
|
body: data
|
||||||
}).then((response) => {
|
}).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",
|
icon: "success",
|
||||||
buttons: {
|
buttons: {
|
||||||
ok: {
|
ok: {
|
||||||
|
@ -474,12 +499,13 @@ $("#create-vm").click(function(){
|
||||||
swal("Uh oh...", `You must enter a disk size for your VM!`, "error");
|
swal("Uh oh...", `You must enter a disk size for your VM!`, "error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$("#change-cores").click(function(){
|
$("#change-cores").click(function(){
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
const cur_cores = $(this).data('cores')
|
const cur_cores = $(this).data('cores');
|
||||||
const usage = $(this).data('usage')
|
const usage = $(this).data('usage');
|
||||||
const limit = $(this).data('limit')
|
const limit = $(this).data('limit');
|
||||||
var core_list = document.createElement('select');
|
var core_list = document.createElement('select');
|
||||||
core_list.setAttribute('style', 'width: 25px');
|
core_list.setAttribute('style', 'width: 25px');
|
||||||
for (i = 1; i < limit - usage + 1; i++) {
|
for (i = 1; i < limit - usage + 1; i++) {
|
||||||
|
@ -504,7 +530,7 @@ $("#change-cores").click(function(){
|
||||||
})
|
})
|
||||||
.then((willChange) => {
|
.then((willChange) => {
|
||||||
if (willChange) {
|
if (willChange) {
|
||||||
const cores = $(core_list).val()
|
const cores = $(core_list).val();
|
||||||
fetch(`/vm/${vmid}/cpu/${cores}`, {
|
fetch(`/vm/${vmid}/cpu/${cores}`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -534,10 +560,10 @@ $("#change-cores").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#change-mem").click(function(){
|
$("#change-mem").click(function(){
|
||||||
const vmid = $(this).data('vmid')
|
const vmid = $(this).data('vmid');
|
||||||
const cur_mem = $(this).data('mem')
|
const cur_mem = $(this).data('mem');
|
||||||
const usage = $(this).data('usage')
|
const usage = $(this).data('usage');
|
||||||
const limit = $(this).data('limit')
|
const limit = $(this).data('limit');
|
||||||
var mem_list = document.createElement('select');
|
var mem_list = document.createElement('select');
|
||||||
mem_list.setAttribute('style', 'width: 45px');
|
mem_list.setAttribute('style', 'width: 45px');
|
||||||
for (i = 1; i < limit - usage + 1; i++) {
|
for (i = 1; i < limit - usage + 1; i++) {
|
||||||
|
@ -562,7 +588,7 @@ $("#change-mem").click(function(){
|
||||||
})
|
})
|
||||||
.then((willChange) => {
|
.then((willChange) => {
|
||||||
if (willChange) {
|
if (willChange) {
|
||||||
const mem = $(mem_list).val()
|
const mem = $(mem_list).val();
|
||||||
fetch(`/vm/${vmid}/mem/${mem}`, {
|
fetch(`/vm/${vmid}/mem/${mem}`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
|
@ -592,10 +618,10 @@ $("#change-mem").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".edit-limit").click(function(){
|
$(".edit-limit").click(function(){
|
||||||
const user = $(this).data('user')
|
const user = $(this).data('user');
|
||||||
const cur_cpu = $(this).data('cpu')
|
const cur_cpu = $(this).data('cpu');
|
||||||
const cur_mem = $(this).data('mem')
|
const cur_mem = $(this).data('mem');
|
||||||
const cur_disk = $(this).data('disk')
|
const cur_disk = $(this).data('disk');
|
||||||
var options = document.createElement('div');
|
var options = document.createElement('div');
|
||||||
cpu_text = document.createElement('p');
|
cpu_text = document.createElement('p');
|
||||||
cpu_text.innerHTML = 'CPU';
|
cpu_text.innerHTML = 'CPU';
|
||||||
|
@ -671,7 +697,7 @@ $(".edit-limit").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".reset-limit").click(function(){
|
$(".reset-limit").click(function(){
|
||||||
const user = $(this).data('user')
|
const user = $(this).data('user');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to reset the usage limits for ${user} to the defaults?`,
|
title: `Are you sure you want to reset the usage limits for ${user} to the defaults?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -709,7 +735,7 @@ $(".reset-limit").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".delete-user").click(function(){
|
$(".delete-user").click(function(){
|
||||||
const user = $(this).data('user')
|
const user = $(this).data('user');
|
||||||
swal({
|
swal({
|
||||||
title: `Are you sure you want to delete the pool for ${user}?`,
|
title: `Are you sure you want to delete the pool for ${user}?`,
|
||||||
icon: "warning",
|
icon: "warning",
|
||||||
|
@ -747,7 +773,7 @@ $(".delete-user").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".delete-ignored-pool").click(function(){
|
$(".delete-ignored-pool").click(function(){
|
||||||
const pool = $(this).data('pool')
|
const pool = $(this).data('pool');
|
||||||
fetch(`/pool/${pool}/ignore`, {
|
fetch(`/pool/${pool}/ignore`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
|
@ -756,10 +782,23 @@ $(".delete-ignored-pool").click(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".add-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`, {
|
fetch(`/pool/${pool}/ignore`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
method: 'post'
|
method: 'post'
|
||||||
});
|
});
|
||||||
location.reload();
|
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 import create_engine
|
||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
from proxstar.db import *
|
from proxstar.db import *
|
||||||
|
from proxstar.util import *
|
||||||
from proxstar.mail import *
|
from proxstar.mail import *
|
||||||
from proxstar.starrs import *
|
from proxstar.starrs import *
|
||||||
from proxstar.proxmox import *
|
from proxstar.proxmox import *
|
||||||
|
@ -103,30 +104,45 @@ def generate_pool_cache_task():
|
||||||
store_pool_cache(db, pools)
|
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():
|
with app.app_context():
|
||||||
proxmox = connect_proxmox()
|
proxmox = connect_proxmox()
|
||||||
starrs = connect_starrs()
|
starrs = connect_starrs()
|
||||||
db = connect_db()
|
db = connect_db()
|
||||||
|
template = get_template(db, template_id)
|
||||||
vmid, mac = clone_vm(proxmox, template_id, name, user)
|
vmid, mac = clone_vm(proxmox, template_id, name, user)
|
||||||
ip = get_next_ip(starrs, app.config['STARRS_IP_RANGE'])
|
ip = get_next_ip(starrs, app.config['STARRS_IP_RANGE'])
|
||||||
register_starrs(starrs, name, app.config['STARRS_USER'], mac, ip)
|
register_starrs(starrs, name, app.config['STARRS_USER'], mac, ip)
|
||||||
get_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
|
get_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
|
||||||
change_vm_cpu(proxmox, vmid, cores)
|
change_vm_cpu(proxmox, vmid, cores)
|
||||||
change_vm_mem(proxmox, vmid, memory)
|
change_vm_mem(proxmox, vmid, memory)
|
||||||
time.sleep(60)
|
time.sleep(90)
|
||||||
change_vm_power(proxmox, vmid, 'start')
|
change_vm_power(proxmox, vmid, 'start')
|
||||||
|
time.sleep(20)
|
||||||
client = paramiko.SSHClient()
|
client = paramiko.SSHClient()
|
||||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
retry = 0
|
retry = 0
|
||||||
while retry < 30:
|
while retry < 30:
|
||||||
try:
|
try:
|
||||||
client.connect(ip, username='root', password='')
|
client.connect(
|
||||||
|
ip,
|
||||||
|
username=template['username'],
|
||||||
|
password=template['password'])
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
retry += 1
|
retry += 1
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
stdin, stdout, stderr = client.exec_command('ls')
|
stdin, stdout, stderr = client.exec_command("useradd {}".format(user))
|
||||||
for line in stdout:
|
exit_status = stdout.channel.recv_exit_status()
|
||||||
print('... ' + line.strip('\n'))
|
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()
|
client.close()
|
||||||
|
|
|
@ -37,6 +37,16 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</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">
|
<div class="form-group">
|
||||||
<label for="disk" class="pull-left">Disk (GB)</label>
|
<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'] }}">
|
<input type="number" value=32 name="disk" id="disk" class="form-control" min="1" max="{{ limits['disk'] - usage['disk'] }}">
|
||||||
|
@ -50,6 +60,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% if rtp %}
|
{% if rtp %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="user" class="pull-left">User</label>
|
<label for="user" class="pull-left">User</label>
|
||||||
|
|
|
@ -9,6 +9,34 @@
|
||||||
<h3 class="panel-title">Templates</h3>
|
<h3 class="panel-title">Templates</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<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>
|
</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
|
python-dateutil
|
||||||
csh_ldap
|
csh_ldap
|
||||||
rq
|
rq
|
||||||
rq-scheduler
|
rq-scheduler==0.7.0
|
||||||
gunicorn
|
gunicorn
|
||||||
raven
|
raven
|
||||||
paramiko
|
paramiko
|
||||||
|
|
Loading…
Reference in a new issue