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 CreateForm, ActivateForm from .. import db from ..email import send_email from ..models import User, Permission, Transaction, Bridge, Router, 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 from dateutil.relativedelta import relativedelta import ast def randstr(n): return ''.join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(n)) @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 @vmanager.route('/createvm', methods=['GET', 'POST']) @login_required def createvm(): if current_user.name is None: flash('Please update profile info for this operation.') return redirect(url_for('settings.profile')) deployment_seeds = current_user.inv_deployments.filter_by(deleted=False).filter_by(protected=False).all() if deployment_seeds != []: flash('Offline deployments still exist.') return redirect(url_for('main.dashboard')) form = CreateForm() if current_user.confirmed and form.validate_on_submit(): selected_region = Region.query.filter_by(pid=int(form.region.data)).first() #TODO: Filter switches for the selected region only! selected_bridge = current_user.inv_bridges.filter_by(deleted=False).all() if selected_bridge == []: #no switches in the account. create one... data = { 'clientid': str(current_user.pid), 'clientemail': str(current_user.email), 'region': str(selected_region.name) } query = contact_proxmaster(data, 'brcreate') if query is not None: bridge = Bridge(user_id=int(current_user.pid)) db.session.add(bridge) db.session.commit() flash('New point created successfully in region "{}".'.format(str(selected_region.description))) newbridge = True else: flash('Point could not be created! Please try again later...') return redirect(url_for('main.dashboard')) else: #bridge found. lets see on which slave it is so we can create the instance on the same slave. data = {} query = contact_proxmaster(data, 'brquery', str(selected_bridge.bridge_id)) if query is not None: newbridge = False else: flash('Point found but cannot be used1') return redirect(url_for('main.dashboard')) #machine will be installed where the switch physically is slave_name = query['slave_name'] bridge_id = query['bridge_id'] #create new machine... data = { 'clientid': str(current_user.pid), 'clientemail': str(current_user.email), 'hostname': str(form.servername.data) + '-c' + str(current_user.pid), 'region': str(selected_region.name), 'slave': str(slave_name), 'type': 'kvm', 'cpu': '1', 'mem': '512', 'hdd': '20', 'eth0br': str(bridge_id), 'eth0ip': 'AUTO' } try: query = contact_proxmaster(data, 'vmcreate') except: flash('Region not available! Please try again later...') return redirect(url_for('main.dashboard')) if query is not None: deployment = Deployment(user_id=int(current_user.pid), machine_alias=query['hostname'], machine_id=query['cube'], machine_cpu=data['cpu'], machine_mem=data['mem'], machine_hdd=data['hdd'], enabled=True, protected=False, daysleft=15, warning=True, discount=0) db.session.add(deployment) db.session.commit() flash('New device created successfully in region "{}".'.format(str(selected_region.description))) return redirect(url_for('main.dashboard')) else: flash('Device could not be created! Please try again later...') #TODO: cleanup bridge if the machine is new and we were not be able to create it return redirect(url_for('main.dashboard')) return render_template('vmanager/create.html', form=form) @vmanager.route('/activate/', methods=['GET', 'POST']) @login_required def activate(itemid=0): result = current_user.inv_deployments.filter_by(deleted=False).all() inventory = [] for invcls in result: inventory.extend([invcls.machine_id]) if current_user.is_administrator(): current_app.logger.warning('[ADMIN] Access override for deployment id:{}'.format(itemid)) elif not itemid in inventory: current_app.logger.error('[{}] Access violation with deployment id: {}'.format(current_user.email, itemid)) abort(403) deploy = Deployment.query.filter_by(machine_id=itemid).first() if deploy is None: abort(404) if deploy.enabled == True and deploy.warning == False: abort(403) cpu_cost = deploy.machine_cpu * current_app.config['CPU_RATIO'] mem_cost = ( deploy.machine_mem / 1024 ) * current_app.config['MEM_RATIO'] hdd_cost = deploy.machine_hdd * current_app.config['HDD_RATIO'] ppm = cpu_cost + mem_cost + hdd_cost discount = round(( ppm * deploy.discount ) / 100) ppm = round(ppm - discount) #default period = 1 for virgin deployments if deploy.period is None: form = ActivateForm(period=1) total = ppm * 1 else: form = ActivateForm(period=int(deploy.period)) total = ppm * deploy.period owner = deploy.owner if owner.confirmed and form.validate_on_submit(): total = ppm * form.period.data if owner.wallet < total: flash('Activation costs {} {}. Insufficient Funds'.format(total, owner.currency)) if current_user.is_administrator(): return redirect(url_for('admin.list_users')) else: return redirect(url_for('uinvoice.transactions')) current_app.logger.info('[{}] Charge deployment: {}'.format(owner.email, deploy.machine_id)) router = current_user.inv_routers.filter_by(deleted=False).all() if router == []: #no router. creating... data = { 'clientid': str(current_user.pid), 'clientemail': str(current_user.email), 'hostname': 'c' + str(current_user.pid) + 'router', 'region': str(selected_region.name), 'slave': str(selected_slave), 'type': 'lxc', 'cpu': '1', 'mem': '128', 'hdd': '1', 'eth0br': str(bridge_id), 'eth0ip': '192.168.9.1', 'eth1br': 'vmbr0', 'eth1ip': str(selected_address.ip) } try: query = contact_proxmaster(data, 'vmcreate') except: flash('Region unreachable! Cannot create router. Please try again later...') return redirect(url_for('main.dashboard')) if query is not None: router = Router(user_id=int(current_user.pid), machine_id=query['cube']) db.session.add(router) db.session.commit() today = datetime.utcnow() expiry = today + relativedelta(today, months=+(form.period.data)) daysleft = expiry - today extradays = relativedelta(today, days=+(deploy.daysleft)) deploy.date_last_charge = today + extradays deploy.period = form.period.data deploy.daysleft = daysleft.days + extradays.days deploy.warning = False deploy.enabled = True deploy.protected = True db.session.commit() transaction = Transaction(user_id=int(owner.pid), description='Deployment {} activated for {} month(s)'.format(str(deploy.machine_alias), form.period.data), value=-total) db.session.add(transaction) db.session.commit() owner.wallet = owner.wallet - total db.session.commit() flash('Deployment {} activated for {} month(s)'.format(str(deploy.machine_alias), form.period.data)) if owner.is_administrator: return redirect(url_for('admin.list_deployments')) else: return redirect(url_for('main.dashboard')) return render_template('vmanager/activate.html', form=form, deploy=deploy, cpu_cost=cpu_cost, mem_cost=mem_cost, hdd_cost=hdd_cost, ppm=ppm, discount=discount, total=total, currency=owner.currency) @vmanager.route('/vmremove/', methods=['GET', 'POST']) @login_required def remove(itemid=0): data = {} deploy = Deployment.query.filter_by(machine_id=int(itemid)).first() if current_user.is_administrator(): if deploy.protected is not True: try: query = contact_proxmaster(data, 'vmremove', int(itemid)) flash('Machine {} terminated'.format(itemid)) deploy.deleted = True deploy.enabled = False deploy.warning = False db.session.commit() except: flash('Cannot delete machine {}'.format(itemid)) return redirect(url_for('admin.list_recyclebin')) else: current_app.logger.warning('Deployment id:{} is protected! Cannot be removed'.format(itemid)) else: current_app.logger.warning('[WARNING] Unauthorized attempt to remove Deployment id:{}'.format(itemid)) abort(404) @vmanager.route('/command//') @login_required def command(cmd=None, vmid=0): #checks whether this is a valid command valid_commands = ['vmstatus', 'vmstart', 'vmshutdown', 'vmstop', 'vmvnc'] if not cmd in valid_commands: current_app.logger.warning(cmd + ' is not a valid command!') abort(404) #work with enabled deploys only that you own. result = current_user.inv_deployments.filter_by(enabled=True) inventory = [] for invcls in result: inventory.extend([invcls.machine_id]) data = {} if current_user.is_administrator(): #current_app.logger.warning('[ADMIN] Access override for cube id:{}'.format(vmid)) db_result = contact_proxmaster(data, cmd, int(vmid)) if cmd == 'vmvnc': return redirect(db_result['url']) else: #checks if current user owns this vmid if not vmid in inventory: current_app.logger.warning('[{}] Access violation with cube id: {}'.format(current_user.email, vmid)) #TODO: log ips else: db_result = contact_proxmaster(data, cmd, int(vmid)) #print(db_result) if cmd == 'vmvnc': return redirect(db_result['url']) abort(404)