from flask import render_template, abort, redirect, url_for, abort, flash, request, current_app, make_response, g from flask_login import login_required, login_user, logout_user, current_user from flask_sqlalchemy import get_debug_queries from . import vmanager from .forms import DeployForm from .. import db from ..email import send_email from ..models import User, Permission, Deployment, Service, Region, Address, Domain, contact_proxmaster from ..decorators import admin_required, permission_required import base64 import string import random from datetime import datetime, timedelta, date, time import ast def randstr(n): return ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(n)) #@vmanager.before_app_request #def before_request(): # g.user = current_user # print('current_user: %s, g.user: %s, leaving bef_req' % (current_user, g.user)) @vmanager.after_app_request def after_request(response): for query in get_debug_queries(): if query.duration >= current_app.config['SLOW_DB_QUERY_TIME']: current_app.logger.warning('Slow query: %s\nParameters: %s\nDuration: %fs\nContext: %s\n' % (query.statement, query.parameters, query.duration, query.context)) return response #APP STORE @vmanager.route('/market/', methods=['GET']) @login_required def market(group_id=0): page = { 'title': 'Market' } allproducts = Product.get_products() allgroups = current_app.config['GROUPS'] if group_id == 0: return render_template('vmanager/market.html', groups=allgroups, products=allproducts) filtered_products = {} for key, value in allproducts.items(): if value['group'] == group_id: filtered_products[key] = value if filtered_products == {}: abort(404) return render_template('vmanager/marketgroup.html', groupname=allgroups[group_id], products=filtered_products) @vmanager.route('/deploy/', methods=['GET', 'POST']) @login_required def deploy(product_id=None): #if current_user.wallet < 20: # flash('Недостатъчно средства в сметката за тази операция') # return redirect(url_for('uinvoice.addfunds')) if current_user.name is None: flash('Please update profile info for this operation.') return redirect(url_for('uinvoice.profile')) page = { 'title': 'Deploy' } try: product = Product.get_products()[product_id] except: current_app.logger.error('unknown product {}'.format(product_id)) abort(404) product_pic = '..' + product['img'] product_name = product['name'] product_description = product['description'] product_cpu = product['cpu'] product_mem = product['mem'] product_hdd = product['hdd'] product_recipe = product['recipe'] hostname = 'deploy-{}.local'.format(randstr(6)) form = DeployForm(servername=hostname, cpu=product_cpu, mem=product_mem, hdd=product_hdd, recipe=product_recipe) form.region.choices = current_app.config['REGIONS'] form.recipe.choices = current_app.config['RECIPES'] if current_user.confirmed and form.validate_on_submit(): client_id = current_user.pid data = { 'clientid': str(client_id), 'clientname': str(current_user.name), 'clientemail': str(current_user.email), 'hostname': str(form.servername.data), 'vmpass': form.vmpassword.data, 'region': form.region.data, 'vps_type': 'kvm', 'vps_recipe': form.recipe.data, 'vps_cpu': form.cpu.data, 'vps_mem': form.mem.data, 'vps_hdd': form.hdd.data, 'vps_ipv4': form.ipv4.data } try: query = contact_proxmaster(data, 'vmcreate') except: flash('Region unreachable! Please try again later...') return redirect(url_for('vmanager.dashboard')) if query is not None: deployment = Deployment(user_id=client_id, product_id=product_id, machine_alias=form.servername.data, machine_id=query['cube'], machine_cpu=form.cpu.data, machine_mem=form.mem.data, machine_hdd=form.hdd.data, date_expire=(datetime.utcnow() + timedelta(days=30)), enabled=True) db.session.add(deployment) db.session.commit() flash('Deploy requested.') else: flash('Deploy cancelled! Please try again later...') return redirect(url_for('vmanager.dashboard')) return render_template('vmanager/deploy.html', page=page, form=form, product_id=product_id, product_pic=product_pic, product_name=product_name, product_description=product_description, product_recipe=product_recipe) #COMMAND AND CONTROL @vmanager.route("/dashboard", methods=['GET', 'POST']) @login_required def dashboard(): cuser = current_user deployments = cuser.inv_deployments.order_by(Deployment.date_created.desc()).all() inv_deploycubeids = [] inv_deploynames = [] for invcls in deployments: if invcls.user_id == cuser.pid and invcls.enabled == True: inv_deploycubeids.extend([invcls.machine_id]) inv_deploynames.extend([invcls.machine_alias]) services = cuser.inv_services.order_by(Service.date_last_charge.asc()).all() inv_services = [] for invcls in services: if invcls.user_id == cuser.pid and invcls.enabled == True: inv_services.extend([invcls.description]) domains = cuser.inv_domains.order_by(Domain.date_created.desc()).all() inv_domains = [] for invcls in domains: if invcls.user_id == cuser.pid and invcls.enabled == True: inv_domains.extend([invcls.fqdn]) addresses = cuser.inv_addresses.order_by(Address.ip.asc()).all() inv_addresses = [] for invcls in addresses: if invcls.user_id == cuser.pid and invcls.enabled == True: inv_addresses.extend([invcls.ip]) #extract rrd and status from the deployments rrd = {} statuses = {} for cubeid in inv_deploycubeids: rrd[cubeid] = {} try: query = contact_proxmaster({}, 'vmrrd', cubeid) except: flash('Deploy #{} unreachable. Support is notified'.format(str(cubeid))) send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid), 'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid) graphs_list = ['net', 'cpu', 'mem', 'hdd'] try: for graph in graphs_list: raw = query[graph]['image'].encode('raw_unicode_escape') rrd[cubeid][graph] = base64.b64encode(raw).decode() status = { cubeid : query['status'] } statuses.update(status) except Exception as e: current_app.logger.error(e) flash('Deploy #{} unreachable. Support is notified'.format(str(cubeid))) send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid), 'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid ) current_app.logger.info('[{}] deployments: {}, services: {}, domains: {}, addresses: {}'.format(current_user.email, inv_deploynames, inv_services, inv_domains, inv_addresses )) return render_template('vmanager/dashboard.html', rrd=rrd, status=statuses, inv_deployments=deployments, inv_services=services, inv_domains=domains, inv_addresses=addresses) @vmanager.route('/vmvnc/') @login_required def vnc(vmid=0): result = current_user.inv_deployments.order_by(Deployment.date_created.desc()).all() inventory = [] for invcls in result: if invcls.enabled == True: inventory.extend([invcls.machine_id]) #checks if current user owns this vmid if not vmid in inventory: current_app.logger.warning('User does not own cube id: {}'.format(str(vmid))) #TODO: log ips else: db_result = contact_proxmaster({}, 'vmvnc', vmid) #return render_template('vmanager/vnc.html', url=db_result['url']) return redirect(db_result['url']) #admin override if current_user.is_administrator(): current_app.logger.warning('Admin override for cube id: {}'.format(str(vmid))) db_result = contact_proxmaster({}, 'vmvnc', vmid) return redirect(db_result['url']) abort(404) valid_commands = ['vmstatus', 'vmstart', 'vmshutdown', 'vmstop'] @vmanager.route('//') @login_required def command(cmd=None, vmid=0): #checks whether this is a valid command if not cmd in valid_commands: current_app.logger.warning(cmd + ' is not a valid command!') abort(404) #if cmd == 'vmstart' and current_user.wallet < 3.0: # flash('Недостатъчно средства в сметката за тази операция') # return redirect(url_for('uinvoice.addfunds')) result = current_user.inv_deployments.order_by(Deployment.date_created.desc()).all() inventory = [] for invcls in result: if invcls.enabled == True: inventory.extend([invcls.machine_id]) #checks if current user owns this vmid if not vmid in inventory: current_app.logger.warning('user id: {} does not own cube id:{}'.format(current_user.pid, vmid)) #TODO: log ips else: db_result = contact_proxmaster({}, cmd, vmid) #print(db_result) #admin override if current_user.is_administrator(): current_app.logger.warning('Admin override for cube id:{}'.format(vmid)) db_result = contact_proxmaster({}, cmd, vmid) abort(404)