from flask import jsonify, 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 import time 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('/slavetables/', methods=['GET']) def slavetables(regionid): selected_region = Region.query.filter_by(pid=int(regionid)).first() if selected_region == None: return jsonify({}) addresses = selected_region.inv_addresses.filter_by(enabled=True).all() regionlist = {} for address in addresses: regionlist[str(address.pid)] = { 'ipv4': str(address.ip), 'mac': str(address.mac) } data = { 'region_id': str(selected_region.pid), 'addresses': regionlist } return jsonify(data) @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('panel.dashboard')) form = CreateForm() if current_user.confirmed and form.validate_on_submit(): #selects the chosen region selected_region = Region.query.filter_by(pid=int(form.region.data)).first() #create new machine... data = { 'clientid': str(current_user.pid), 'clientemail': str(current_user.email), 'hostname': 'c' + str(current_user.pid) + '-' + str(form.servername.data), 'region': str(region_name), 'slave': str(slave_name), 'type': 'kvm', 'cpu': '1', 'mem': '512', 'hdd': '20', 'net0if': 'vmbr7' } try: query = contact_proxmaster(data, 'create') except: flash('Region not available! Please try again later...') return redirect(url_for('panel.dashboard')) if query['status'] == 'kvm_created': deployment = Deployment(user_id=int(current_user.pid), machine_alias=str(form.servername.data), machine_id=query['unit_id'], machine_cpu=data['cpu'], machine_mem=data['mem'], machine_hdd=data['hdd'], enabled=True, protected=False, daysleft=15, warning=True, discount=0, bridge_id=int(selected_bridge.pid)) db.session.add(deployment) db.session.commit() flash('A new deployment is created successfully in region "{}".'.format(str(selected_region.description))) return redirect(url_for('panel.dashboard')) else: flash('Deployment 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('panel.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'] tpm = cpu_cost + mem_cost + hdd_cost discount = round(( tpm * deploy.discount ) / 100) ppm = round(tpm - 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)) 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() email_content = 'Deployment {} is activated for {} month(s). It will expire at {}'.format(str(deploy.machine_alias), form.period.data, str((expiry + extradays).strftime('%c'))) send_email(current_app.config['MAIL_USERNAME'], str(email_content), 'email/adm_logger', user=owner, content=str(email_content)) send_email(str(owner.email), str(email_content), 'email/client_logger', content=str(email_content)) transaction = Transaction(user_id=int(owner.pid), description=str(email_content), 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 current_user.is_administrator(): return redirect(url_for('admin.list_deployments')) else: return redirect(url_for('panel.dashboard')) return render_template('vmanager/activate.html', form=form, deploy=deploy, cpu_cost=cpu_cost, mem_cost=mem_cost, hdd_cost=hdd_cost, tpm=tpm, ppm=ppm, discount=discount, total=total, currency=owner.currency) @vmanager.route('/vmremove/', methods=['GET', 'POST']) @login_required def remove(unit_id=0): data = { 'unit_id': int(unit_id), 'type': 'kvm' } deploy = Deployment.query.filter_by(machine_id=int(unit_id)).first() if current_user.is_administrator(): if deploy.protected is not True: try: query = contact_proxmaster(data, 'status') if query['status'] == 'running': query = contact_proxmaster(data, 'stop') flash('Machine {} force stopped'.format(unit_id)) time.sleep(7) query = contact_proxmaster(data, 'remove') flash('Machine {} terminated'.format(unit_id)) deploy.deleted = True deploy.enabled = False deploy.warning = False db.session.commit() except Exception as e: current_app.logger.error(e) flash('Cannot delete machine {}'.format(unit_id)) return redirect(url_for('admin.list_recyclebin')) else: current_app.logger.warning('Deployment id:{} is protected! Cannot be removed'.format(unit_id)) else: current_app.logger.warning('[WARNING] Unauthorized attempt to remove Deployment id:{}'.format(unit_id)) abort(404) @vmanager.route('/command//') @login_required def command(cmd=None, unit_id=0): #checks whether this is a valid command valid_commands = ['status', 'start', 'shutdown', 'stop', '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 = { 'type': 'kvm', 'unit_id': int(unit_id) } if current_user.is_administrator(): #current_app.logger.warning('[ADMIN] Access override for cube id:{}'.format(unitunit__id)) db_result = contact_proxmaster(data, cmd) if cmd == 'vmvnc': return redirect(db_result['url']) else: #checks if current user owns this unit_id if not unit_id in inventory: current_app.logger.warning('[{}] Access violation with unit id: {}'.format(current_user.email, unit_id)) #TODO: log ips else: db_result = contact_proxmaster(data, cmd) #print(db_result) if cmd == 'vmvnc': return redirect(db_result['url']) abort(404)