diff --git a/.gitignore b/.gitignore index 1ef7cd2..62e9d4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ config.py __pycache__/* +rrd/* diff --git a/app.py b/app.py index b4a4209..32e3015 100644 --- a/app.py +++ b/app.py @@ -5,32 +5,47 @@ import subprocess from db import * from starrs import * from proxmox import * +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 app = Flask(__name__) - config = os.path.join(app.config.get('ROOT_DIR', os.getcwd()), "config.py") - app.config.from_pyfile(config) - app.config["GIT_REVISION"] = subprocess.check_output( ['git', 'rev-parse', '--short', 'HEAD']).decode('utf-8').rstrip() - auth = OIDCAuthentication( app, issuer=app.config['OIDC_ISSUER'], client_registration_info=app.config['OIDC_CLIENT_CONFIG']) +cache = SimpleCache() @app.route("/") +@app.route("/user/") @auth.oidc_auth -def list_vms(): - user = session['userinfo']['preferred_username'] +def list_vms(user=None): + rtp_view = False rtp = 'rtp' in session['userinfo']['groups'] proxmox = connect_proxmox() - vms = get_vms_for_user(proxmox, user, rtp) - return render_template('list_vms.html', username=user, rtp=rtp, vms=vms) + if user and not rtp: + return '', 403 + elif user and rtp: + vms = get_vms_for_user(proxmox, user) + rtp_view = user + user = session['userinfo']['preferred_username'] + elif rtp: + user = session['userinfo']['preferred_username'] + vms = cache.get('vms') + if vms is None: + vms = get_vms_for_rtp(proxmox) + cache.set('vms', vms, timeout=5 * 60) + rtp_view = True + else: + user = session['userinfo']['preferred_username'] + vms = get_vms_for_user(proxmox, user) + return render_template( + 'list_vms.html', username=user, rtp=rtp, rtp_view=rtp_view, vms=vms) @app.route("/isos") @@ -227,7 +242,7 @@ def create(): if request.method == 'GET': usage = get_user_usage(proxmox, user) limits = get_user_usage_limits(user) - percents = get_user_usage_percent(proxmox, usage, limits) + percents = get_user_usage_percent(proxmox, user, usage, limits) isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE']) return render_template( 'create.html', @@ -304,6 +319,12 @@ def limits(): return '', 403 +@app.route('/vm//rrd/') +@auth.oidc_auth +def send_rrd(vmid, path): + return send_from_directory("rrd/{}".format(vmid), path) + + @app.route('/novnc/') @auth.oidc_auth def send_novnc(path): diff --git a/cron.py b/cron.py index f3f62fe..50ba816 100644 --- a/cron.py +++ b/cron.py @@ -1,19 +1,47 @@ +import os from db import * -from config import * from starrs import * from proxmox import * +from flask import Flask, current_app + +app = Flask(__name__) +config = os.path.join(app.config.get('ROOT_DIR', os.getcwd()), "config.py") +app.config.from_pyfile(config) def process_expired_vms(): - proxmox = connect_proxmox(PROXMOX_HOST, PROXMOX_USER, PROXMOX_PASS) - starrs = connect_starrs(STARRS_DB_NAME, STARRS_DB_USER, STARRS_DB_HOST, - STARRS_DB_PASS) + proxmox = connect_proxmox() + starrs = connect_starrs() expired_vms = get_expired_vms() - for vmid in expired_vms: - vmname = get_vm_config(proxmox, vmid)['name'] - delete_vm(proxmox, starrs, vmid) - delete_starrs(starrs, vmname) - delete_vm_expire(vmid) + print(expired_vms) -process_expired_vms() +# for vmid in expired_vms: +# vmname = get_vm_config(proxmox, vmid)['name'] +# delete_vm(proxmox, starrs, vmid) +# delete_starrs(starrs, vmname) +# delete_vm_expire(vmid) + + +def get_rrd_graphs(): + proxmox = connect_proxmox() + pools = get_pools(proxmox) + for pool in pools: + vms = proxmox.pools(pool).get()['members'] + for vm in vms: + vm_dir = "rrd/{}".format(vm['vmid']) + if not os.path.exists(vm_dir): + os.makedirs(vm_dir) + sources = [ + 'cpu', 'mem', 'netin', 'netout', 'diskread', 'diskwrite' + ] + for source in sources: + image = get_rrd_for_vm(proxmox, vm['vmid'], source, 'day') + with open("rrd/{}/{}.png".format(vm['vmid'], source), + 'wb') as f: + f.write(image.encode('raw_unicode_escape')) + + +with app.app_context(): + process_expired_vms() + get_rrd_graphs() diff --git a/proxmox.py b/proxmox.py index e47117d..863ea02 100644 --- a/proxmox.py +++ b/proxmox.py @@ -1,40 +1,47 @@ import time -from flask import current_app as app +from functools import lru_cache from proxmoxer import ProxmoxAPI +from flask import current_app as app from db import * def connect_proxmox(): try: proxmox = ProxmoxAPI( - app.config['PROXMOX_HOST'], user=app.config['PROXMOX_USER'], password=app.config['PROXMOX_PASS'], verify_ssl=False) + app.config['PROXMOX_HOST'], + user=app.config['PROXMOX_USER'], + password=app.config['PROXMOX_PASS'], + verify_ssl=False) except: print("Unable to connect to Proxmox!") raise return proxmox -def get_vms_for_user(proxmox, user, rtp=False): +def get_vms_for_user(proxmox, user): pools = get_pools(proxmox) - if not rtp: - if user not in pools: - proxmox.pools.post(poolid=user, comment='Managed by Proxstar') - 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 - else: - pool_vms = [] - for pool in pools: - vms = proxmox.pools(pool).get()['members'] - for vm in vms: - if 'name' not in vm: - vms.remove(vm) - vms = sorted(vms, key=lambda k: k['name']) - pool_vms.append([pool, vms]) - return pool_vms + if user not in pools: + proxmox.pools.post(poolid=user, comment='Managed by Proxstar') + 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): + pools = get_pools(proxmox) + pool_vms = [] + for pool in pools: + pool_dict = dict() + pool_dict['user'] = pool + pool_dict['usage'] = get_user_usage(proxmox, pool) + pool_dict['limits'] = get_user_usage_limits(pool) + pool_dict['percents'] = get_user_usage_percent( + proxmox, pool, pool_dict['usage'], pool_dict['limits']) + pool_vms.append(pool_dict) + return pool_vms def get_user_allowed_vms(proxmox, user): @@ -102,10 +109,9 @@ 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(',') - if 'size' in disk_size[0]: - disk_size = disk_size[0].split('=')[1].rstrip('G') - else: - disk_size = disk_size[1].split('=')[1].rstrip('G') + for split in disk_size: + if 'size' in split: + disk_size = split.split('=')[1].rstrip('G') return disk_size @@ -118,10 +124,9 @@ def get_vm_disks(proxmox, vmid, config=None): 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(',') - if 'size' in disk_size[0]: - disk_size = disk_size[0].split('=')[1].rstrip('G') - else: - disk_size = disk_size[1].split('=')[1].rstrip('G') + 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 @@ -151,7 +156,7 @@ def get_user_usage(proxmox, user): 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) + usage['mem'] += (int(config['memory']) / 1024) for disk in get_vm_disks(proxmox, vm['vmid'], config): usage['disk'] += int(disk[1]) return usage @@ -168,15 +173,18 @@ def check_user_usage(proxmox, user, vm_cpu, vm_mem, vm_disk): return 'exceeds_disk_limit' -def get_user_usage_percent(proxmox, usage=None, limits=None): +def get_user_usage_percent(proxmox, user, usage=None, limits=None): percents = dict() if not usage: usage = get_user_usage(proxmox, user) if not limits: limits = get_user_usage_limits(user) - percents['cpu'] = round(int(usage['cpu']) / int(limits['cpu']) * 100) - percents['mem'] = round(int(usage['mem']) / int(limits['mem']) * 100) - percents['disk'] = round(int(usage['disk']) / int(limits['disk']) * 100) + 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 @@ -255,3 +263,9 @@ def get_pools(proxmox): pools.append(poolid) pools = sorted(pools) return pools + + +def get_rrd_for_vm(proxmox, vmid, source, time): + node = proxmox.nodes(get_vm_node(proxmox, vmid)) + image = node.qemu(vmid).rrd.get(ds=source, timeframe=time)['image'] + return image diff --git a/starrs.py b/starrs.py index d19c843..9f0fc88 100644 --- a/starrs.py +++ b/starrs.py @@ -6,7 +6,8 @@ def connect_starrs(): try: starrs = psycopg2.connect( "dbname='{}' user='{}' host='{}' password='{}'".format( - app.config['STARRS_DB_NAME'], app.config['STARRS_DB_USER'], app.config['STARRS_DB_HOST'], app.config['STARRS_DB_PASS'])) + app.config['STARRS_DB_NAME'], app.config['STARRS_DB_USER'], + app.config['STARRS_DB_HOST'], app.config['STARRS_DB_PASS'])) except: print("Unable to connect to STARRS database.") raise diff --git a/static/css/styles.css b/static/css/styles.css index f6f5b49..73f05c5 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -105,6 +105,37 @@ table, th, td { } .usage-limit { - width: 80%; margin: 0px auto; } + +.rrd-graph { + width: 800px; + height: 200px; + padding-top: 10px; + padding-bottom: 10px; +} + +.profile { + float: left; + width: 35%; + height: 60px; +} + +.profile-img { + max-width: 100%; + max-height: 100%; +} + +.progress { + margin-top: 2px; + margin-bottom: 0px; + float: right; + width: 85%; + height: 12px; +} + +.resource-bar { + width: 60%; + float: right; + padding-bottom: 2px; +} diff --git a/static/js/script.js b/static/js/script.js index c949703..5a62617 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -584,18 +584,21 @@ $(".edit-limit").click(function(){ cpu_text.innerHTML = 'CPU'; options.append(cpu_text); var cpu = document.createElement('input'); + cpu.type = 'number'; cpu.defaultValue = cur_cpu; options.append(cpu); mem_text = document.createElement('p'); mem_text.innerHTML = 'Memory (GB)'; options.append(mem_text); var mem = document.createElement('input'); + mem.type = 'number'; mem.defaultValue = cur_mem; options.append(mem) disk_text = document.createElement('p'); disk_text.innerHTML = 'Disk (GB)'; options.append(disk_text); var disk = document.createElement('input'); + disk.type = 'number'; disk.defaultValue = cur_disk; options.append(disk) swal({ diff --git a/templates/create.html b/templates/create.html index 65e582b..9965ceb 100644 --- a/templates/create.html +++ b/templates/create.html @@ -27,6 +27,7 @@