convert all vm and user operations to objects, lazy load some attributes, clean up some unneeded code/imports, sort rtp view before displaying

This commit is contained in:
Jordan Rodgers 2018-02-28 03:12:18 -05:00
parent bea806e964
commit 06920e1216
11 changed files with 391 additions and 350 deletions

View file

@ -2,16 +2,17 @@ import os
import time import time
import psutil import psutil
import atexit import atexit
import psycopg2
import subprocess import subprocess
from rq import Queue from rq import Queue
from redis import Redis from redis import Redis
from rq_scheduler import Scheduler from rq_scheduler import Scheduler
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
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, session
from proxstar.db import * from proxstar.db import *
from proxstar.vm import VM
from proxstar.vnc import * from proxstar.vnc import *
from proxstar.util import * from proxstar.util import *
from proxstar.tasks import * from proxstar.tasks import *
@ -48,8 +49,6 @@ while retry < 5:
retry += 1 retry += 1
time.sleep(2) time.sleep(2)
cache = SimpleCache()
redis_conn = Redis(app.config['REDIS_HOST'], app.config['REDIS_PORT']) redis_conn = Redis(app.config['REDIS_HOST'], app.config['REDIS_PORT'])
q = Queue(connection=redis_conn) q = Queue(connection=redis_conn)
scheduler = Scheduler(connection=redis_conn) scheduler = Scheduler(connection=redis_conn)
@ -82,25 +81,27 @@ if 'cleanup_vnc' not in scheduler:
func=cleanup_vnc_task, func=cleanup_vnc_task,
interval=3600) interval=3600)
from proxstar.user import User
@app.route("/") @app.route("/")
@app.route("/user/<string:user_view>") @app.route("/user/<string:user_view>")
@auth.oidc_auth @auth.oidc_auth
def list_vms(user_view=None): def list_vms(user_view=None):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
rtp_view = False rtp_view = False
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user_view and not user['rtp']: if user_view and not user.rtp:
return '', 403 return '', 403
elif user_view and user['rtp']: elif user_view and user.rtp:
vms = get_vms_for_user(proxmox, db, user_view) vms = User(user_view).vms
rtp_view = user_view rtp_view = user_view
elif user['rtp']: elif user.rtp:
vms = get_pool_cache(db) vms = get_pool_cache(db)
rtp_view = True rtp_view = True
else: else:
if user['active']: if user.active:
vms = get_vms_for_user(proxmox, db, user['username']) vms = user.vms
else: else:
vms = 'INACTIVE' vms = 'INACTIVE'
return render_template( return render_template(
@ -130,35 +131,31 @@ def hostname(name):
@app.route("/vm/<string:vmid>") @app.route("/vm/<string:vmid>")
@auth.oidc_auth @auth.oidc_auth
def vm_details(vmid): def vm_details(vmid):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
vm = get_vm(proxmox, vmid) vm_dict = vm.info
vm['vmid'] = vmid vm_dict['vmid'] = vmid
vm['config'] = get_vm_config(proxmox, vmid) vm_dict['config'] = vm.config
vm['node'] = get_vm_node(proxmox, vmid) vm_dict['node'] = vm.node
vm['disks'] = get_vm_disks(proxmox, vmid, config=vm['config']) vm_dict['disks'] = vm.get_disks()
vm['iso'] = get_vm_iso(proxmox, vmid, config=vm['config']) vm_dict['iso'] = vm.get_iso()
vm['interfaces'] = [] vm_dict['interfaces'] = []
for interface in get_vm_interfaces( for interface in vm.get_interfaces():
proxmox, vm['vmid'], config=vm['config']): vm_dict['interfaces'].append(
vm['interfaces'].append(
[interface[0], [interface[0],
get_ip_for_mac(starrs, interface[1])]) get_ip_for_mac(starrs, interface[1])])
vm['expire'] = get_vm_expire( vm_dict['expire'] = get_vm_expire(
db, vmid, app.config['VM_EXPIRE_MONTHS']).strftime('%m/%d/%Y') db, vmid, app.config['VM_EXPIRE_MONTHS']).strftime('%m/%d/%Y')
usage = get_user_usage(proxmox, db, user['username']) usage_check = user.check_usage(vm_dict['config']['cores'],
limits = get_user_usage_limits(db, user['username']) vm_dict['config']['memory'], 0)
usage_check = check_user_usage(proxmox, db, user['username'],
vm['config']['cores'],
vm['config']['memory'], 0)
return render_template( return render_template(
'vm_details.html', 'vm_details.html',
user=user, user=user,
vm=vm, vm=vm_dict,
usage=usage, usage=user.usage,
limits=limits, limits=user.limits,
usage_check=usage_check) usage_check=usage_check)
else: else:
return '', 403 return '', 403
@ -167,18 +164,27 @@ def vm_details(vmid):
@app.route("/vm/<string:vmid>/power/<string:action>", methods=['POST']) @app.route("/vm/<string:vmid>/power/<string:action>", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_power(vmid, action): def vm_power(vmid, action):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
if action == 'start': if action == 'start':
config = get_vm_config(proxmox, vmid) config = vm.config
usage_check = check_user_usage(proxmox, db, user['username'], usage_check = user.check_usage(config['cores'], config['memory'],
config['cores'], config['memory'],
0) 0)
if usage_check: if usage_check:
return usage_check return usage_check
change_vm_power(proxmox, vmid, action) vm.start()
elif action == 'stop':
vm.stop()
elif action == 'shutdown':
vm.shutdown()
elif action == 'reset':
vm.reset()
elif action == 'suspend':
vm.suspend()
elif action == 'resume':
vm.resume()
return '', 200 return '', 200
else: else:
return '', 403 return '', 403
@ -187,13 +193,13 @@ def vm_power(vmid, action):
@app.route("/console/vm/<string:vmid>", methods=['POST']) @app.route("/console/vm/<string:vmid>", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_console(vmid): def vm_console(vmid):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
port = str(5900 + int(vmid)) port = str(5900 + int(vmid))
token = add_vnc_target(port) token = add_vnc_target(port)
node = "{}.csh.rit.edu".format(get_vm_node(proxmox, vmid)) node = "{}.csh.rit.edu".format(vm.node)
tunnel = next((tunnel for tunnel in ssh_tunnels tunnel = next((tunnel for tunnel in ssh_tunnels
if tunnel.local_bind_port == int(port)), None) if tunnel.local_bind_port == int(port)), None)
if tunnel: if tunnel:
@ -207,7 +213,7 @@ def vm_console(vmid):
node, vmid)) node, vmid))
tunnel = start_ssh_tunnel(node, port) tunnel = start_ssh_tunnel(node, port)
ssh_tunnels.append(tunnel) ssh_tunnels.append(tunnel)
start_vm_vnc(proxmox, vmid, port) vm.start_vnc(port)
else: else:
print("Tunnel already exists to {} for VM {}.".format( print("Tunnel already exists to {} for VM {}.".format(
node, vmid)) node, vmid))
@ -215,7 +221,7 @@ def vm_console(vmid):
print("Creating SSH tunnel to {} for VM {}.".format(node, vmid)) print("Creating SSH tunnel to {} for VM {}.".format(node, vmid))
tunnel = start_ssh_tunnel(node, port) tunnel = start_ssh_tunnel(node, port)
ssh_tunnels.append(tunnel) ssh_tunnels.append(tunnel)
start_vm_vnc(proxmox, vmid, port) vm.start_vnc(port)
return token, 200 return token, 200
else: else:
return '', 403 return '', 403
@ -224,22 +230,19 @@ def vm_console(vmid):
@app.route("/vm/<string:vmid>/cpu/<int:cores>", methods=['POST']) @app.route("/vm/<string:vmid>/cpu/<int:cores>", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_cpu(vmid, cores): def vm_cpu(vmid, cores):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
cur_cores = get_vm_config(proxmox, vmid)['cores'] cur_cores = vm.cpu
if cores >= cur_cores: if cores >= cur_cores:
status = get_vm(proxmox, vmid)['qmpstatus'] if vm.qmpstatus == 'running' or vm.qmpstatus == 'paused':
if status == 'running' or status == 'paused': usage_check = user.check_usage(cores - cur_cores, 0, 0)
usage_check = check_user_usage(proxmox, db, user['username'],
cores - cur_cores, 0, 0)
else: else:
usage_check = check_user_usage(proxmox, db, user['username'], usage_check = user.check_usage(cores, 0, 0)
cores, 0, 0)
if usage_check: if usage_check:
return usage_check return usage_check
change_vm_cpu(proxmox, vmid, cores) vm.set_cpu(cores)
return '', 200 return '', 200
else: else:
return '', 403 return '', 403
@ -248,22 +251,19 @@ def vm_cpu(vmid, cores):
@app.route("/vm/<string:vmid>/mem/<int:mem>", methods=['POST']) @app.route("/vm/<string:vmid>/mem/<int:mem>", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_mem(vmid, mem): def vm_mem(vmid, mem):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
cur_mem = get_vm_config(proxmox, vmid)['memory'] // 1024 cur_mem = vm.mem // 1024
if mem >= cur_mem: if mem >= cur_mem:
status = get_vm(proxmox, vmid)['qmpstatus'] if vm.qmpstatus == 'running' or vm.qmpstatus == 'paused':
if status == 'running' or status == 'paused': usage_check = user.check_usage(0, mem - cur_mem, 0)
usage_check = check_user_usage(proxmox, db, user['username'],
0, mem - cur_mem, 0)
else: else:
usage_check = check_user_usage(proxmox, db, user['username'], usage_check = user.check_usage(0, mem, 0)
0, mem, 0)
if usage_check: if usage_check:
return usage_check return usage_check
change_vm_mem(proxmox, vmid, mem * 1024) vm.set_mem(mem * 1024)
return '', 200 return '', 200
else: else:
return '', 403 return '', 403
@ -272,15 +272,15 @@ def vm_mem(vmid, mem):
@app.route("/vm/<string:vmid>/disk/<string:disk>/<int:size>", methods=['POST']) @app.route("/vm/<string:vmid>/disk/<string:disk>/<int:size>", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_disk(vmid, disk, size): def vm_disk(vmid, disk, size):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
cur_cores = get_vm_config(proxmox, vmid)['cores'] cur_cores = vm.cpu
usage_check = check_user_usage(proxmox, db, user['username'], 0, 0, size) usage_check = user.check_usage(0, 0, size)
if usage_check: if usage_check:
return usage_check return usage_check
resize_vm_disk(proxmox, vmid, disk, size) vm.resize_disk(disk, size)
return '', 200 return '', 200
else: else:
return '', 403 return '', 403
@ -289,12 +289,12 @@ def vm_disk(vmid, disk, size):
@app.route("/vm/<string:vmid>/renew", methods=['POST']) @app.route("/vm/<string:vmid>/renew", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_renew(vmid): def vm_renew(vmid):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']): vm = VM(vmid)
renew_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS']) renew_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
for interface in get_vm_interfaces(proxmox, vmid): for interface in vm.get_interfaces():
renew_ip(starrs, get_ip_for_mac(starrs, interface[1])) renew_ip(starrs, get_ip_for_mac(starrs, interface[1]))
return '', 200 return '', 200
else: else:
@ -304,10 +304,11 @@ def vm_renew(vmid):
@app.route("/vm/<string:vmid>/eject", methods=['POST']) @app.route("/vm/<string:vmid>/eject", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def iso_eject(vmid): def iso_eject(vmid):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms(proxmox, db, user): if user.rtp or int(vmid) in user.allowed_vms:
eject_vm_iso(proxmox, vmid) vm = VM(vmid)
vm.eject_iso()
return '', 200 return '', 200
else: else:
return '', 403 return '', 403
@ -316,12 +317,12 @@ def iso_eject(vmid):
@app.route("/vm/<string:vmid>/mount/<string:iso>", methods=['POST']) @app.route("/vm/<string:vmid>/mount/<string:iso>", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def iso_mount(vmid, iso): def iso_mount(vmid, iso):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db, user['username']):
iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'], iso) iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'], iso)
mount_vm_iso(proxmox, vmid, iso) vm = VM(vmid)
vm.mount_iso(iso)
return '', 200 return '', 200
else: else:
return '', 403 return '', 403
@ -330,11 +331,9 @@ def iso_mount(vmid, iso):
@app.route("/vm/<string:vmid>/delete", methods=['POST']) @app.route("/vm/<string:vmid>/delete", methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def delete(vmid): def delete(vmid):
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['rtp'] or int(vmid) in get_user_allowed_vms( if user.rtp or int(vmid) in user.allowed_vms:
proxmox, db,
user['username']) or 'rtp' in session['userinfo']['groups']:
q.enqueue(delete_vm_task, vmid) q.enqueue(delete_vm_task, vmid)
return '', 200 return '', 200
else: else:
@ -344,23 +343,19 @@ def delete(vmid):
@app.route("/vm/create", methods=['GET', 'POST']) @app.route("/vm/create", methods=['GET', 'POST'])
@auth.oidc_auth @auth.oidc_auth
def create(): def create():
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
proxmox = connect_proxmox() proxmox = connect_proxmox()
if user['active']: if user.active:
if request.method == 'GET': if request.method == 'GET':
usage = get_user_usage(proxmox, db, user['username'])
limits = get_user_usage_limits(db, user['username'])
percents = get_user_usage_percent(proxmox, user['username'], 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) templates = get_templates(db)
return render_template( return render_template(
'create.html', 'create.html',
user=user, user=user,
usage=usage, usage=user.usage,
limits=limits, limits=user.limits,
percents=percents, percents=user.usage_percent,
isos=isos, isos=isos,
pools=pools, pools=pools,
templates=templates) templates=templates)
@ -374,12 +369,12 @@ def create():
if iso != 'none': if iso != 'none':
iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'], iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'],
iso) iso)
if not user['rtp']: if not user.rtp:
usage_check = check_user_usage(proxmox, db, user['username'], usage_check = user.check_usage(0, 0, disk)
0, 0, disk) username = user.name
else: else:
usage_check = None usage_check = None
user['username'] = request.form['user'] username = request.form['user']
if usage_check: if usage_check:
return usage_check return usage_check
else: else:
@ -388,7 +383,7 @@ def create():
if template == 'none': if template == 'none':
q.enqueue( q.enqueue(
create_vm_task, create_vm_task,
user['username'], username,
name, name,
cores, cores,
memory, memory,
@ -401,7 +396,7 @@ def create():
setup_template, setup_template,
template, template,
name, name,
user['username'], username,
password, password,
cores, cores,
memory, memory,
@ -440,8 +435,8 @@ def delete_user(user):
@app.route("/settings") @app.route("/settings")
@auth.oidc_auth @auth.oidc_auth
def settings(): def settings():
user = build_user_dict(session, db) user = User(session['userinfo']['preferred_username'])
if user['rtp']: if user.rtp:
templates = get_templates(db) templates = get_templates(db)
ignored_pools = get_ignored_pools(db) ignored_pools = get_ignored_pools(db)
allowed_users = get_allowed_users(db) allowed_users = get_allowed_users(db)

View file

@ -123,6 +123,7 @@ def get_pool_cache(db):
pool_dict['limits'] = pool.limits pool_dict['limits'] = pool.limits
pool_dict['percents'] = pool.percents pool_dict['percents'] = pool.percents
pools.append(pool_dict) pools.append(pool_dict)
pools = sorted(pools, key=lambda x: x['user'])
return pools return pools
@ -204,8 +205,9 @@ def delete_allowed_user(db, user):
def set_template_info(db, template_id, name, username, password, disk): def set_template_info(db, template_id, name, username, password, disk):
if db.query(exists().where(Template.id == template_id,)).scalar(): if db.query(exists().where(Template.id == template_id, )).scalar():
template = db.query(Template).filter(Template.id == template_id,).one() template = db.query(Template).filter(Template.id == template_id,
).one()
template.name = name template.name = name
template.username = username template.username = username
if password: if password:

View file

@ -19,8 +19,14 @@ def is_rtp(user):
def is_active(user): def is_active(user):
ldap = connect_ldap() ldap = connect_ldap()
rtp_group = ldap.get_group('active') active_group = ldap.get_group('active')
return rtp_group.check_member(ldap.get_member(user, uid=True)) return active_group.check_member(ldap.get_member(user, uid=True))
def is_current_student(user):
ldap = connect_ldap()
current_student_group = ldap.get_group('current_student')
return current_student_group.check_member(ldap.get_member(user, uid=True))
def is_user(user): def is_user(user):

View file

@ -21,21 +21,6 @@ def connect_proxmox():
raise raise
def get_vms_for_user(proxmox, db, user):
pools = get_pools(proxmox, db)
if user not in pools:
if is_user(user) and not is_rtp(user):
proxmox.pools.post(poolid=user, comment='Managed by Proxstar')
else:
return []
vms = proxmox.pools(user).get()['members']
for vm in vms:
if 'name' not in vm:
vms.remove(vm)
vms = sorted(vms, key=lambda k: k['name'])
return vms
def get_vms_for_rtp(proxmox, db): def get_vms_for_rtp(proxmox, db):
pools = [] pools = []
for pool in get_pools(proxmox, db): for pool in get_pools(proxmox, db):
@ -51,13 +36,6 @@ def get_vms_for_rtp(proxmox, db):
return pools return pools
def get_user_allowed_vms(proxmox, db, user):
allowed_vms = []
for vm in get_vms_for_user(proxmox, db, user):
allowed_vms.append(vm['vmid'])
return allowed_vms
def get_node_least_mem(proxmox): def get_node_least_mem(proxmox):
nodes = proxmox.nodes.get() nodes = proxmox.nodes.get()
sorted_nodes = sorted(nodes, key=lambda x: x['mem']) sorted_nodes = sorted(nodes, key=lambda x: x['mem'])
@ -74,129 +52,6 @@ def get_vm_node(proxmox, vmid):
return vm['node'] return vm['node']
def get_vm(proxmox, vmid):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
return node.qemu(vmid).status.current.get()
def get_vm_config(proxmox, vmid):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
return node.qemu(vmid).config.get()
def get_vm_mac(proxmox, vmid, config=None, interface='net0'):
if not config:
config = get_vm_config(proxmox, vmid)
mac = config[interface].split(',')
if 'virtio' in mac[0]:
mac = mac[0].split('=')[1]
else:
mac = mac[1].split('=')[1]
return mac
def get_vm_interfaces(proxmox, vmid, config=None):
if not config:
config = get_vm_config(proxmox, vmid)
interfaces = []
for key, val in config.items():
if 'net' in key:
mac = config[key].split(',')
valid_int_types = ['virtio', 'e1000', 'rtl8139', 'vmxnet3']
if any(int_type in mac[0] for int_type in valid_int_types):
mac = mac[0].split('=')[1]
else:
mac = mac[1].split('=')[1]
interfaces.append([key, mac])
interfaces = sorted(interfaces, key=lambda x: x[0])
return interfaces
def get_vm_disk_size(proxmox, vmid, config=None, name='virtio0'):
if not config:
config = get_vm_config(proxmox, vmid)
disk_size = config[name].split(',')
for split in disk_size:
if 'size' in split:
disk_size = split.split('=')[1].rstrip('G')
return disk_size
def get_vm_disks(proxmox, vmid, config=None):
if not config:
config = get_vm_config(proxmox, vmid)
disks = []
for key, val in config.items():
valid_disk_types = ['virtio', 'ide', 'sata', 'scsi']
if any(disk_type in key for disk_type in valid_disk_types):
if 'scsihw' not in key and 'cdrom' not in val:
disk_size = val.split(',')
for split in disk_size:
if 'size' in split:
disk_size = split.split('=')[1].rstrip('G')
disks.append([key, disk_size])
disks = sorted(disks, key=lambda x: x[0])
return disks
def get_vm_iso(proxmox, vmid, config=None):
if not config:
config = get_vm_config(proxmox, vmid)
if config.get('ide2'):
if config['ide2'].split(',')[0] == 'none':
iso = 'None'
else:
iso = config['ide2'].split(',')[0].split('/')[1]
else:
iso = 'None'
return iso
def get_user_usage(proxmox, db, user):
usage = dict()
usage['cpu'] = 0
usage['mem'] = 0
usage['disk'] = 0
if is_rtp(user):
return usage
vms = get_vms_for_user(proxmox, db, user)
for vm in vms:
config = get_vm_config(proxmox, vm['vmid'])
if 'status' in vm:
if vm['status'] == 'running' or vm['status'] == 'paused':
usage['cpu'] += int(config['cores'] * config.get('sockets', 1))
usage['mem'] += (int(config['memory']) / 1024)
for disk in get_vm_disks(proxmox, vm['vmid'], config):
usage['disk'] += int(disk[1])
return usage
def check_user_usage(proxmox, db, user, vm_cpu, vm_mem, vm_disk):
limits = get_user_usage_limits(db, user)
cur_usage = get_user_usage(proxmox, db, user)
if int(cur_usage['cpu']) + int(vm_cpu) > int(limits['cpu']):
return 'exceeds_cpu_limit'
elif int(cur_usage['mem']) + (int(vm_mem) / 1024) > int(limits['mem']):
return 'exceeds_memory_limit'
elif int(cur_usage['disk']) + int(vm_disk) > int(limits['disk']):
return 'exceeds_disk_limit'
def get_user_usage_percent(proxmox, user, usage=None, limits=None):
percents = dict()
if not usage:
usage = get_user_usage(proxmox, db, user)
if not limits:
limits = get_user_usage_limits(user)
percents['cpu'] = round(usage['cpu'] / limits['cpu'] * 100)
percents['mem'] = round(usage['mem'] / limits['mem'] * 100)
percents['disk'] = round(usage['disk'] / limits['disk'] * 100)
for resource in percents:
if percents[resource] > 100:
percents[resource] = 100
return percents
def create_vm(proxmox, user, name, cores, memory, disk, iso): def create_vm(proxmox, user, name, cores, memory, disk, iso):
node = proxmox.nodes(get_node_least_mem(proxmox)) node = proxmox.nodes(get_node_least_mem(proxmox))
vmid = get_free_vmid(proxmox) vmid = get_free_vmid(proxmox)
@ -214,7 +69,7 @@ def create_vm(proxmox, user, name, cores, memory, disk, iso):
retry = 0 retry = 0
while retry < 5: while retry < 5:
try: try:
mac = get_vm_mac(proxmox, vmid) mac = VM(vmid).get_mac()
break break
except: except:
retry += 1 retry += 1
@ -222,44 +77,6 @@ def create_vm(proxmox, user, name, cores, memory, disk, iso):
return vmid, mac return vmid, mac
def delete_vm(proxmox, vmid):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
node.qemu(vmid).delete()
def change_vm_power(proxmox, vmid, action):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
if action == 'start':
node.qemu(vmid).status.start.post()
elif action == 'stop':
node.qemu(vmid).status.stop.post()
elif action == 'shutdown':
node.qemu(vmid).status.shutdown.post()
elif action == 'reset':
node.qemu(vmid).status.reset.post()
elif action == 'suspend':
node.qemu(vmid).status.suspend.post()
elif action == 'resume':
node.qemu(vmid).status.resume.post()
def change_vm_cpu(proxmox, vmid, cores):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
node.qemu(vmid).config.put(cores=cores)
def change_vm_mem(proxmox, vmid, mem):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
node.qemu(vmid).config.put(memory=mem)
def start_vm_vnc(proxmox, vmid, port):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
port = str(int(port) - 5900)
node.qemu(vmid).monitor.post(
command="change vnc 127.0.0.1:{}".format(port))
def get_isos(proxmox, storage): def get_isos(proxmox, storage):
isos = [] isos = []
for iso in proxmox.nodes('proxmox01').storage(storage).content.get(): for iso in proxmox.nodes('proxmox01').storage(storage).content.get():
@ -267,16 +84,6 @@ def get_isos(proxmox, storage):
return isos return isos
def eject_vm_iso(proxmox, vmid):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
node.qemu(vmid).config.post(ide2='none,media=cdrom')
def mount_vm_iso(proxmox, vmid, iso):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
node.qemu(vmid).config.post(ide2="{},media=cdrom".format(iso))
def get_pools(proxmox, db): def get_pools(proxmox, db):
ignored_pools = get_ignored_pools(db) ignored_pools = get_ignored_pools(db)
pools = [] pools = []
@ -288,15 +95,6 @@ def get_pools(proxmox, db):
return pools return pools
def delete_user_pool(proxmox, pool):
proxmox.pools(pool).delete()
users = proxmox.access.users.get()
if any(user['userid'] == "{}@csh.rit.edu".format(pool) for user in users):
if 'rtp' not in proxmox.access.users(
"{}@csh.rit.edu".format(pool)).get()['groups']:
proxmox.access.users("{}@csh.rit.edu".format(pool)).delete()
def clone_vm(proxmox, template_id, name, pool): def clone_vm(proxmox, template_id, name, pool):
node = proxmox.nodes(get_vm_node(proxmox, template_id)) node = proxmox.nodes(get_vm_node(proxmox, template_id))
newid = get_free_vmid(proxmox) newid = get_free_vmid(proxmox)
@ -311,14 +109,9 @@ def clone_vm(proxmox, template_id, name, pool):
retry = 0 retry = 0
while retry < 60: while retry < 60:
try: try:
mac = get_vm_mac(proxmox, newid) mac = VM(newid).get_mac()
break break
except: except:
retry += 1 retry += 1
time.sleep(3) time.sleep(3)
return newid, mac return newid, mac
def resize_vm_disk(proxmox, vmid, disk, size):
node = proxmox.nodes(get_vm_node(proxmox, vmid))
node.qemu(vmid).resize.put(disk=disk, size="+{}G".format(size))

View file

@ -1,7 +1,3 @@
import psycopg2
from flask import current_app as app
def get_next_ip(starrs, range_name): def get_next_ip(starrs, range_name):
c = starrs.cursor() c = starrs.cursor()
try: try:

View file

@ -1004,7 +1004,7 @@
-o-transform: rotate(360deg); -o-transform: rotate(360deg);
transform: rotate(360deg); transform: rotate(360deg);
} }
.c100:hover { /*.c100:hover {
cursor: default; cursor: default;
} }
.c100:hover > span { .c100:hover > span {
@ -1018,7 +1018,7 @@
left: 0.04em; left: 0.04em;
width: 0.92em; width: 0.92em;
height: 0.92em; height: 0.92em;
} }*/
.c100.dark { .c100.dark {
background-color: #777777; background-color: #777777;
} }

View file

@ -1,10 +1,12 @@
import os import os
import requests import requests
import paramiko import paramiko
import psycopg2
from flask import Flask 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.vm import VM
from proxstar.util import * from proxstar.util import *
from proxstar.mail import * from proxstar.mail import *
from proxstar.starrs import * from proxstar.starrs import *
@ -50,12 +52,11 @@ def create_vm_task(user, name, cores, memory, disk, iso):
def delete_vm_task(vmid): def delete_vm_task(vmid):
with app.app_context(): with app.app_context():
proxmox = connect_proxmox()
db = connect_db() db = connect_db()
starrs = connect_starrs() starrs = connect_starrs()
vmname = get_vm_config(proxmox, vmid)['name'] vm = VM(vmid)
delete_vm(proxmox, vmid) vm.delete()
delete_starrs(starrs, vmname) delete_starrs(starrs, vm.name)
delete_vm_expire(db, vmid) delete_vm_expire(db, vmid)
@ -67,9 +68,10 @@ def process_expired_vms_task():
print(expired_vms) print(expired_vms)
# for vmid in expired_vms: # for vmid in expired_vms:
# vmname = get_vm_config(proxmox, vmid)['name']
# vm = VM(vmid)
# delete_vm(proxmox, starrs, vmid) # delete_vm(proxmox, starrs, vmid)
# delete_starrs(starrs, vmname) # delete_starrs(starrs, vm.name)
# delete_vm_expire(vmid) # delete_vm_expire(vmid)
@ -88,10 +90,10 @@ def process_expiring_vms_task():
app.config['VM_EXPIRE_MONTHS']) app.config['VM_EXPIRE_MONTHS'])
days = (expire - datetime.date.today()).days days = (expire - datetime.date.today()).days
if days in [10, 7, 3, 1, 0]: if days in [10, 7, 3, 1, 0]:
name = get_vm_config(proxmox, vmid)['name'] name = VM(vmid).config['name']
expiring_vms.append([name, days]) expiring_vms.append([name, days])
if days == 0: if days == 0:
change_vm_power(proxmox, vmid, 'stop') VM(vmid).stop()
if expiring_vms: if expiring_vms:
send_vm_expire_email('com6056', expiring_vms) send_vm_expire_email('com6056', expiring_vms)
@ -119,14 +121,15 @@ def setup_template(template_id, name, user, password, cores, memory):
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'])
print("[{}] Setting CPU and memory.".format(name)) print("[{}] Setting CPU and memory.".format(name))
change_vm_cpu(proxmox, vmid, cores) vm = VM(vmid)
change_vm_mem(proxmox, vmid, memory) vm.set_cpu(cores)
vm.set_mem(memory)
print( print(
"[{}] Waiting for STARRS to propogate before starting VM.".format( "[{}] Waiting for STARRS to propogate before starting VM.".format(
name)) name))
time.sleep(90) time.sleep(90)
print("[{}] Starting VM.".format(name)) print("[{}] Starting VM.".format(name))
change_vm_power(proxmox, vmid, 'start') vm.start()
print("[{}] Waiting for VM to start before SSHing.".format(name)) print("[{}] Waiting for VM to start before SSHing.".format(name))
time.sleep(20) time.sleep(20)
print("[{}] Creating SSH session.".format(name)) print("[{}] Creating SSH session.".format(name))

View file

@ -27,7 +27,7 @@
</div> </div>
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
{% if user['active'] %} {% if user.active %}
<li> <li>
<a href="/"> <a href="/">
<span class="glyphicon glyphicon-th-list"></span> <span class="glyphicon glyphicon-th-list"></span>
@ -40,7 +40,7 @@
Create VM Create VM
</a> </a>
</li> </li>
{% if user['rtp'] %} {% if user.rtp %}
<li> <li>
<a href="/settings"> <a href="/settings">
<span class="glyphicon glyphicon-cog"></span> <span class="glyphicon glyphicon-cog"></span>
@ -54,14 +54,14 @@
<li class="dropdown navbar-user"> <li class="dropdown navbar-user">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" <a href="#" class="dropdown-toggle" data-toggle="dropdown"
role="button" aria-expanded="false"> role="button" aria-expanded="false">
<img src="https://profiles.csh.rit.edu/image/{{ user['username'] }}" <img src="https://profiles.csh.rit.edu/image/{{ user.name }}"
title="{{ user['username'] }}"> title="{{ user.name }}">
{{ user['username'] }} {{ user.name }}
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li> <li>
<a href="https://profiles.csh.rit.edu/user/{{ user['username'] }}"> <a href="https://profiles.csh.rit.edu/user/{{ user.name }}">
<span class="glyphicon glyphicon-user"></span> <span class="glyphicon glyphicon-user"></span>
Profile Profile
</a> </a>

87
proxstar/user.py Normal file
View file

@ -0,0 +1,87 @@
from proxstar import db
from proxstar.db import *
from proxstar.util import *
from proxstar.proxmox import *
class User(object):
def __init__(self, username):
self.name = username
self.active = is_active(self.name) or is_current_student(
self.name) or self.name in get_allowed_users(db)
self.rtp = is_rtp(self.name)
self.limits = get_user_usage_limits(db, self.name)
@lazy_property
def vms(self):
proxmox = connect_proxmox()
pools = get_pools(proxmox, db)
if self.name not in pools:
if is_user(self.name) and not is_rtp(self.name):
proxmox.pools.post(
poolid=self.name, comment='Managed by Proxstar')
else:
return []
vms = proxmox.pools(self.name).get()['members']
for vm in vms:
if 'name' not in vm:
vms.remove(vm)
vms = sorted(vms, key=lambda k: k['name'])
return vms
@lazy_property
def allowed_vms(self):
allowed_vms = []
for vm in self.vms:
allowed_vms.append(vm['vmid'])
return allowed_vms
@lazy_property
def usage(self):
usage = dict()
usage['cpu'] = 0
usage['mem'] = 0
usage['disk'] = 0
if self.rtp:
return usage
vms = self.vms
for vm in vms:
if 'status' in vm:
vm = VM(vm['vmid'])
if vm.status == 'running' or vm.status == 'paused':
usage['cpu'] += int(vm.cpu * vm.config.get('sockets', 1))
usage['mem'] += (int(vm.mem) / 1024)
for disk in vm.get_disks():
usage['disk'] += int(disk[1])
return usage
@lazy_property
def usage_percent(self):
percents = dict()
percents['cpu'] = round(self.usage['cpu'] / self.limits['cpu'] * 100)
percents['mem'] = round(self.usage['mem'] / self.limits['mem'] * 100)
percents['disk'] = round(
self.usage['disk'] / self.limits['disk'] * 100)
for resource in percents:
if percents[resource] > 100:
percents[resource] = 100
return percents
def check_usage(self, cpu, mem, disk):
if int(self.usage['cpu']) + int(cpu) > int(self.limits['cpu']):
return 'exceeds_cpu_limit'
elif int(self.usage['mem']) + (int(mem) / 1024) > int(
self.limits['mem']):
return 'exceeds_memory_limit'
elif int(self.usage['disk']) + int(disk) > int(self.limits['disk']):
return 'exceeds_disk_limit'
def delete(self):
proxmox.pools(self.name).delete()
users = proxmox.access.users.get()
if any(user['userid'] == "{}@csh.rit.edu".format(self.name)
for user in users):
if 'rtp' not in proxmox.access.users("{}@csh.rit.edu".format(
self.name)).get()['groups']:
proxmox.access.users("{}@csh.rit.edu".format(
self.name)).delete()

View file

@ -11,11 +11,14 @@ def gen_password(
return ''.join(random.choice(charset) for x in range(length)) return ''.join(random.choice(charset) for x in range(length))
def build_user_dict(session, db): def lazy_property(fn):
user_dict = dict() # Decorator that makes a property lazy-evaluated (https://stevenloria.com/lazy-properties/)
user_dict['username'] = session['userinfo']['preferred_username'] attr_name = '_lazy_' + fn.__name__
user_dict[
'active'] = 'active' in session['userinfo']['groups'] or 'current_student' in session['userinfo']['groups'] or user_dict['username'] in get_allowed_users( @property
db) def _lazy_property(self):
user_dict['rtp'] = 'rtp' in session['userinfo']['groups'] if not hasattr(self, attr_name):
return user_dict setattr(self, attr_name, fn(self))
return getattr(self, attr_name)
return _lazy_property

156
proxstar/vm.py Normal file
View file

@ -0,0 +1,156 @@
from proxstar.util import *
from proxstar.proxmox import *
class VM(object):
def __init__(self, vmid):
self.id = vmid
@lazy_property
def name(self):
return self.config['name']
@lazy_property
def cpu(self):
return self.config['cores']
@lazy_property
def mem(self):
return self.config['memory']
@lazy_property
def status(self):
return self.info['status']
@lazy_property
def qmpstatus(self):
return self.info['qmpstatus']
@lazy_property
def node(self):
proxmox = connect_proxmox()
for vm in proxmox.cluster.resources.get(type='vm'):
if vm['vmid'] == int(self.id):
return vm['node']
def delete(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).delete()
def set_cpu(self, cores):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.put(cores=cores)
self.cpu = cores
def set_mem(self, mem):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.put(memory=mem)
self.mem = mem
def start(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).status.start.post()
def stop(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).status.stop.post()
def shutdown(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).status.shutdown.post()
def reset(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).status.reset.post()
def suspend(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).status.suspend.post()
def resume(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).status.resume.post()
@lazy_property
def info(self):
proxmox = connect_proxmox()
return proxmox.nodes(self.node).qemu(self.id).status.current.get()
@lazy_property
def config(self):
proxmox = connect_proxmox()
return proxmox.nodes(self.node).qemu(self.id).config.get()
def get_interfaces(self):
interfaces = []
for key, val in self.config.items():
if 'net' in key:
mac = self.config[key].split(',')
valid_int_types = ['virtio', 'e1000', 'rtl8139', 'vmxnet3']
if any(int_type in mac[0] for int_type in valid_int_types):
mac = mac[0].split('=')[1]
else:
mac = mac[1].split('=')[1]
interfaces.append([key, mac])
interfaces = sorted(interfaces, key=lambda x: x[0])
return interfaces
def get_mac(self, interface='net0'):
mac = self.config[interface].split(',')
if 'virtio' in mac[0]:
mac = mac[0].split('=')[1]
else:
mac = mac[1].split('=')[1]
return mac
def get_disk_size(self, name='virtio0'):
disk_size = self.config[name].split(',')
for split in disk_size:
if 'size' in split:
disk_size = split.split('=')[1].rstrip('G')
return disk_size
def get_disks(self):
disks = []
for key, val in self.config.items():
valid_disk_types = ['virtio', 'ide', 'sata', 'scsi']
if any(disk_type in key for disk_type in valid_disk_types):
if 'scsihw' not in key and 'cdrom' not in val:
disk_size = val.split(',')
for split in disk_size:
if 'size' in split:
disk_size = split.split('=')[1].rstrip('G')
disks.append([key, disk_size])
disks = sorted(disks, key=lambda x: x[0])
return disks
def get_iso(self):
if self.config.get('ide2'):
if self.config['ide2'].split(',')[0] == 'none':
iso = 'None'
else:
iso = self.config['ide2'].split(',')[0].split('/')[1]
else:
iso = 'None'
return iso
def start_vnc(self, port):
proxmox = connect_proxmox()
port = str(int(port) - 5900)
proxmox.nodes(self.node).qemu(self.id).monitor.post(
command="change vnc 127.0.0.1:{}".format(port))
def eject_iso(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(
self.id).config.post(ide2='none,media=cdrom')
def mount_iso(self, iso):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(
self.id).config.post(ide2="{},media=cdrom".format(iso))
def resize_disk(self, disk, size):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).resize.put(
disk=disk, size="+{}G".format(size))