mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-02-12 13:01:51 +00:00
Shared Pools
This commit is contained in:
parent
50855e3712
commit
dff517d822
12 changed files with 349 additions and 16 deletions
12
HACKING/proxstar-postgres/schema/large.sql
generated
12
HACKING/proxstar-postgres/schema/large.sql
generated
|
@ -266,6 +266,18 @@ ALTER TABLE ONLY public.vm_expiration
|
|||
ADD CONSTRAINT vm_expiration_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: allowed_users; Type: TABLE; Schema: public; Owner: proxstar
|
||||
--
|
||||
|
||||
CREATE TABLE public.shared_pools (
|
||||
name VARCHAR(32) PRIMARY KEY,
|
||||
members VARCHAR(32)[]
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.shared_pools OWNER TO proxstar;
|
||||
|
||||
--
|
||||
-- Name: DATABASE proxstar; Type: ACL; Schema: -; Owner: postgres
|
||||
--
|
||||
|
|
|
@ -44,6 +44,9 @@ from proxstar.db import (
|
|||
delete_allowed_user,
|
||||
get_template_disk,
|
||||
set_template_info,
|
||||
add_shared_pool,
|
||||
get_shared_pool,
|
||||
get_shared_pools
|
||||
)
|
||||
from proxstar.vnc import (
|
||||
add_vnc_target,
|
||||
|
@ -197,7 +200,25 @@ def list_vms(user_view=None):
|
|||
vms.append(pending_vm)
|
||||
else:
|
||||
vms = 'INACTIVE'
|
||||
return render_template('list_vms.html', user=user, user_view=user_view, vms=vms)
|
||||
return render_template('list_vms.html', user=user, external_view=user_view, vms=vms)
|
||||
|
||||
|
||||
@app.route('/pool/shared/<string:name>')
|
||||
@auth.oidc_auth
|
||||
def list_shared_vms(name=None):
|
||||
user = User(session['userinfo']['preferred_username'])
|
||||
pool = get_shared_pool(db, name)
|
||||
if pool:
|
||||
if user.name in pool.members or user.rtp:
|
||||
proxmox = connect_proxmox()
|
||||
vms = proxmox.pools(pool.name).get()['members']
|
||||
else:
|
||||
return 'Not Member of Pool', 403
|
||||
else:
|
||||
return 'Pool does not exist', 400
|
||||
if app.config['FORCE_STANDARD_USER']:
|
||||
user.rtp = False
|
||||
return render_template('list_vms.html', user=user, external_view=pool, vms=vms)
|
||||
|
||||
|
||||
@app.route('/pools')
|
||||
|
@ -205,11 +226,10 @@ def list_pools():
|
|||
user = User(session['userinfo']['preferred_username'])
|
||||
if app.config['FORCE_STANDARD_USER']:
|
||||
user.rtp = False
|
||||
if not user.rtp:
|
||||
abort(403)
|
||||
connect_proxmox()
|
||||
vms = get_pool_cache(db)
|
||||
return render_template('list_pools.html', user=user, vms=vms)
|
||||
proxmox = connect_proxmox()
|
||||
user_pools = get_pool_cache(db) if user.rtp else []
|
||||
shared_pools = map(lambda pool: {"name": pool.name, "members": pool.members, "vms": proxmox.pools(pool.name).get()['members']}, get_shared_pools(db, user.name, user.rtp))
|
||||
return render_template('list_pools.html', user=user, user_pools=user_pools, shared_pools=shared_pools)
|
||||
|
||||
|
||||
@app.route('/isos')
|
||||
|
@ -463,9 +483,11 @@ def create():
|
|||
if request.method == 'GET':
|
||||
stored_isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE'])
|
||||
pools = get_pools(proxmox, db)
|
||||
for pool in get_shared_pools(db, user.name, True):
|
||||
pools.append(pool.name)
|
||||
templates = get_templates(db)
|
||||
return render_template(
|
||||
'create.html',
|
||||
'create_vm.html',
|
||||
user=user,
|
||||
usage=user.usage,
|
||||
limits=user.limits,
|
||||
|
@ -585,6 +607,58 @@ def ignored_pools(pool):
|
|||
else:
|
||||
return '', 403
|
||||
|
||||
@app.route('/pool/shared/create', methods=['GET', 'POST'])
|
||||
@auth.oidc_auth
|
||||
def create_shared_pool():
|
||||
user = User(session['userinfo']['preferred_username'])
|
||||
if request.method == 'GET':
|
||||
return render_template('create_pool.html',user=user)
|
||||
elif request.method == 'POST':
|
||||
name = request.form['name']
|
||||
members = request.form['members'].split(';')
|
||||
description = request.form['description']
|
||||
if 'rtp' in session['userinfo']['groups']:
|
||||
try:
|
||||
proxmox = connect_proxmox()
|
||||
proxmox.pools.post(poolid=name, comment=description)
|
||||
except:
|
||||
return 'Error creating pool', 400
|
||||
add_shared_pool(db, name, members)
|
||||
return '', 200
|
||||
else:
|
||||
return '', 403
|
||||
|
||||
|
||||
@app.route('/pool/shared/<string:name>/modify', methods=['POST'])
|
||||
@auth.oidc_auth
|
||||
def modify_shared_pool(name):
|
||||
members = request.form['members'].split(',')
|
||||
if 'rtp' in session['userinfo']['groups']:
|
||||
pool = get_shared_pool(db, name)
|
||||
if pool:
|
||||
pool.members = members
|
||||
db.commit()
|
||||
return '', 200
|
||||
return 'Pool not found', 400
|
||||
else:
|
||||
return '', 403
|
||||
|
||||
|
||||
@app.route('/pool/shared/<string:name>/delete', methods=['POST'])
|
||||
@auth.oidc_auth
|
||||
def delete_shared_pool(name):
|
||||
if 'rtp' in session['userinfo']['groups']:
|
||||
pool = get_shared_pool(db, name)
|
||||
if pool:
|
||||
db.delete(pool)
|
||||
db.commit()
|
||||
proxmox = connect_proxmox()
|
||||
proxmox.pools(name).delete()
|
||||
return '', 200
|
||||
return 'Pool not found', 400
|
||||
else:
|
||||
return '', 403
|
||||
|
||||
|
||||
@app.route('/user/<string:user>/allow', methods=['POST', 'DELETE'])
|
||||
@auth.oidc_auth
|
||||
|
|
|
@ -14,6 +14,7 @@ from proxstar.models import (
|
|||
Template,
|
||||
Usage_Limit,
|
||||
VM_Expiration,
|
||||
Shared_Pools,
|
||||
)
|
||||
|
||||
|
||||
|
@ -212,3 +213,23 @@ def set_template_info(db, template_id, name, disk):
|
|||
template.name = name
|
||||
template.disk = disk
|
||||
db.commit()
|
||||
|
||||
|
||||
def add_shared_pool(db, name, members):
|
||||
if db.query(Shared_Pools).get(name):
|
||||
return "Name Already in Use"
|
||||
db.add(Shared_Pools(name=name, members=members))
|
||||
db.commit()
|
||||
|
||||
|
||||
def get_shared_pool(db, name):
|
||||
return db.query(Shared_Pools).get(name)
|
||||
|
||||
|
||||
def get_shared_pools(db, user, all_pools):
|
||||
if all_pools:
|
||||
return db.query(Shared_Pools).all()
|
||||
pools = []
|
||||
for pool in db.query(Shared_Pools).filter(Shared_Pools.members.contains(f"{{{user}}}")).all():
|
||||
pools.append(pool)
|
||||
return pools
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from sqlalchemy import Column, Date, Integer, String
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.ext.mutable import MutableList
|
||||
from sqlalchemy.types import JSON, Text
|
||||
|
||||
from proxstar.util import default_repr
|
||||
|
@ -53,3 +54,9 @@ class Ignored_Pools(Base):
|
|||
class Allowed_Users(Base):
|
||||
__tablename__ = 'allowed_users'
|
||||
id = Column(String(32), primary_key=True)
|
||||
|
||||
@default_repr
|
||||
class Shared_Pools(Base):
|
||||
__tablename__ = 'shared_pools'
|
||||
name = Column(String(32), primary_key=True)
|
||||
members = Column(MutableList.as_mutable(postgresql.ARRAY(String(32))))
|
||||
|
|
|
@ -2,7 +2,7 @@ from flask import current_app as app
|
|||
from proxmoxer import ProxmoxAPI
|
||||
|
||||
from proxstar import logging
|
||||
from proxstar.db import get_ignored_pools
|
||||
from proxstar.db import get_ignored_pools, get_shared_pool
|
||||
from proxstar.ldapdb import is_user
|
||||
|
||||
|
||||
|
|
|
@ -379,6 +379,54 @@ $("#create-vm").click(function(){
|
|||
});
|
||||
});
|
||||
|
||||
$("#create-pool").click(function(){
|
||||
console.log("bingus");
|
||||
const name = document.getElementById('name').value.toLowerCase();
|
||||
const description = document.getElementById('description').value;
|
||||
const members = document.getElementById('members').value;
|
||||
var info = document.createElement('span');
|
||||
swal({
|
||||
title: `Are you sure you want to create ${name}?`,
|
||||
content: info,
|
||||
icon: "info",
|
||||
buttons: {
|
||||
cancel: true,
|
||||
confirm: {
|
||||
text: "Create",
|
||||
closeModal: false,
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((willCreate) => {
|
||||
if (willCreate) {
|
||||
var data = new FormData();
|
||||
data.append('name', name);
|
||||
data.append('description', description);
|
||||
data.append('members', members);
|
||||
fetch('/pool/shared/create', {
|
||||
credentials: 'same-origin',
|
||||
method: 'POST',
|
||||
body: data
|
||||
}).then((response) => {
|
||||
console.log(response);
|
||||
var swal_text = `${name} is now being created. Check back soon and it should be good to go.`
|
||||
return swal(`${swal_text}`, {
|
||||
icon: "success",
|
||||
buttons: {
|
||||
ok: {
|
||||
text: "OK",
|
||||
closeModal: true,
|
||||
className: "",
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
window.location = "/";
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#change-cores").click(function(){
|
||||
const vmid = $(this).data('vmid');
|
||||
const usage = $(this).data('usage');
|
||||
|
@ -569,6 +617,67 @@ $(".edit-limit").click(function(){
|
|||
});
|
||||
});
|
||||
|
||||
$(".edit-shared-members").click(function(){
|
||||
const pool = $(this).data('pool');
|
||||
const currentMembers = $(this).data('members').slice(1,-1).split(', ');
|
||||
var currentMembersString = "";
|
||||
currentMembers.forEach(name => {
|
||||
currentMembersString += name.slice(1,-1) + ',';
|
||||
});
|
||||
var options = document.createElement('div');
|
||||
var members = document.createElement('input');
|
||||
members.type = 'text';
|
||||
members.defaultValue = currentMembersString.slice(0,-1);
|
||||
options.append(members);
|
||||
swal({
|
||||
title: `Enter the new member list for ${pool}:`,
|
||||
content: options,
|
||||
buttons: {
|
||||
cancel: {
|
||||
text: "Cancel",
|
||||
visible: true,
|
||||
closeModal: true,
|
||||
className: "",
|
||||
},
|
||||
confirm: {
|
||||
text: "Submit",
|
||||
closeModal: false,
|
||||
}
|
||||
},
|
||||
})
|
||||
.then((willChange) => {
|
||||
if (willChange) {
|
||||
var data = new FormData();
|
||||
data.append('members', $(members).val());
|
||||
fetch(`/pool/shared/${pool}/modify`, {
|
||||
credentials: 'same-origin',
|
||||
method: 'post',
|
||||
body: data
|
||||
}).then((response) => {
|
||||
return swal(`Now applying new member list to ${pool}!`, {
|
||||
icon: "success",
|
||||
buttons: {
|
||||
ok: {
|
||||
text: "OK",
|
||||
closeModal: true,
|
||||
className: "",
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
window.location = "/";
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
if (err) {
|
||||
swal("Uh oh...", `Unable to change the members of ${pool}. Please try again later.`, "error");
|
||||
} else {
|
||||
swal.stopLoading();
|
||||
swal.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".delete-user").click(function(){
|
||||
const user = $(this).data('user');
|
||||
swal({
|
||||
|
@ -607,6 +716,44 @@ $(".delete-user").click(function(){
|
|||
});
|
||||
});
|
||||
|
||||
$(".delete-pool").click(function(){
|
||||
const pool = $(this).data('pool');
|
||||
swal({
|
||||
title: `Are you sure you want to delete the pool ${pool}?`,
|
||||
icon: "warning",
|
||||
buttons: {
|
||||
cancel: true,
|
||||
delete: {
|
||||
text: "delete",
|
||||
closeModal: false,
|
||||
className: "swal-button--danger",
|
||||
}
|
||||
},
|
||||
dangerMode: true,
|
||||
})
|
||||
.then((willDelete) => {
|
||||
if (willDelete) {
|
||||
fetch(`/pool/shared/${ pool }/delete`, {
|
||||
credentials: 'same-origin',
|
||||
method: 'post'
|
||||
}).then((response) => {
|
||||
return swal(`The pool ${pool} has been deleted!`, {
|
||||
icon: "success",
|
||||
});
|
||||
}).then(() => {
|
||||
window.location = "/";
|
||||
}).catch(err => {
|
||||
if (err) {
|
||||
swal("Uh oh...", `Unable to delete the pool ${pool}. Please try again later.`, "error");
|
||||
} else {
|
||||
swal.stopLoading();
|
||||
swal.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(".delete-ignored-pool").click(function(){
|
||||
const pool = $(this).data('pool');
|
||||
fetch(`/pool/${pool}/ignore`, {
|
||||
|
|
|
@ -54,11 +54,17 @@
|
|||
Create VM
|
||||
</a>
|
||||
</li>
|
||||
{% if user.rtp %}
|
||||
<li class="nav-item navbar-user dropdown">
|
||||
<a class="nav-link" href="/pools">
|
||||
<i class="fas fa-user"></i>
|
||||
User Pools
|
||||
Pools
|
||||
</a>
|
||||
</li>
|
||||
{% if user.rtp %}
|
||||
<li class="nav-item navbar-user dropdown">
|
||||
<a class="nav-link" href="/pool/shared/create">
|
||||
<i class="fas fa-plus-circle"></i>
|
||||
Create Shared Pool
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item navbar-user dropdown">
|
||||
|
|
37
proxstar/templates/create_pool.html
Normal file
37
proxstar/templates/create_pool.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
{% extends "base.html" %}
|
||||
{% block body %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% if user['rtp'] %}
|
||||
<div class="col-md-12 col-sm-12">
|
||||
{% else %}
|
||||
<div class="col-lg-9 col-md-8 col-sm-12">
|
||||
{% endif %}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Create Shared Pool</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="name" class="pull-left">Pool Name</label>
|
||||
<input type="text" name="name" id="name" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description" class="pull-left">Description</label>
|
||||
<input type="text" name="description" id="description" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="members" class="pull-left">Members</label>
|
||||
<input type="text" name="members" id="members" class="form-control">
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button class="btn btn-success" id="create-pool" name="create">CREATE</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,9 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% block body %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% for pool in vms %}
|
||||
{% for pool in user_pools %}
|
||||
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header text-center">
|
||||
|
@ -56,7 +55,30 @@
|
|||
<button class="btn btn-info proxstar-poolbtn edit-limit" data-user="{{ pool['user'] }}" data-cpu="{{ pool['limits']['cpu'] }}" data-mem="{{ pool['limits']['mem'] }}" data-disk="{{ pool['limits']['disk'] }}">EDIT</button>
|
||||
{% if not pool['vms'] %}
|
||||
<button class="btn btn-danger proxstar-poolbtn delete-user" data-user="{{ pool['user'] }}">DELETE</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for pool in shared_pools %}
|
||||
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header text-center">
|
||||
<h5 class="card-title user-title">
|
||||
<a href="/pool/{{ pool.name }}">
|
||||
{{ pool.name }}
|
||||
</a>
|
||||
</h5>
|
||||
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center">
|
||||
<h6>{{ pool.members|length }} members</h5>
|
||||
<button class="btn btn-info proxstar-poolbtn edit-shared-members" data-pool="{{ pool.name }}" data-members="{{ pool.members }}">EDIT</button>
|
||||
{% if not pool['vms'] %}
|
||||
<button class="btn btn-danger proxstar-poolbtn delete-pool" data-pool="{{ pool.name }}">DELETE</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% if user_view %}
|
||||
{% if external_view %}
|
||||
<div class="col-md-12 col-sm-12">
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header text-center">
|
||||
<h5 class="card-title">{{ user_view.name }}</h5>
|
||||
<h5 class="card-title">{{ external_view.name }}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@ from rq.registry import StartedJobRegistry
|
|||
|
||||
from proxstar.ldapdb import is_active, is_user, is_current_student
|
||||
from proxstar import db, q, redis_conn
|
||||
from proxstar.db import get_allowed_users, get_user_usage_limits, is_rtp
|
||||
from proxstar.db import get_allowed_users, get_user_usage_limits, is_rtp, get_shared_pools
|
||||
from proxstar.proxmox import connect_proxmox, get_pools
|
||||
from proxstar.util import lazy_property, default_repr
|
||||
from proxstar.vm import VM
|
||||
|
@ -39,6 +39,7 @@ class User:
|
|||
if 'name' not in vm:
|
||||
vms.remove(vm)
|
||||
vms = sorted(vms, key=lambda k: k['name'])
|
||||
|
||||
return vms
|
||||
|
||||
@lazy_property
|
||||
|
@ -63,6 +64,12 @@ class User:
|
|||
allowed_vms = []
|
||||
for vm in self.vms:
|
||||
allowed_vms.append(vm['vmid'])
|
||||
shared_pools = get_shared_pools(db, self.name, False)
|
||||
proxmox = connect_proxmox()
|
||||
for pool in shared_pools:
|
||||
vms = proxmox.pools(pool.name).get()['members']
|
||||
for vm in vms:
|
||||
allowed_vms.append(vm['vmid'])
|
||||
return allowed_vms
|
||||
|
||||
@lazy_property
|
||||
|
|
Loading…
Reference in a new issue