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 ActivateForm from .. import db, csrf from ..email import send_email from ..models import User, Permission, Transaction, Order, Deployment, PubVLAN, Service, Region, Server, Address, Domain, SupportTopic, contact_proxmaster from ..decorators import admin_required, permission_required import json 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=['POST']) @csrf.exempt def slavetables(): postdata = request.get_json() if postdata['passphrase'] == 'batkataisthebest1': selected_slave = Server.query.filter_by(name=str(postdata['slavename'])).first() if selected_slave == None: return jsonify({'status': 'slave_not_found'}) addrlist = {} try: deploylist = selected_slave.inv_deployments.all() for deploy in deploylist: pubvlanlist = deploy.inv_pubvlans.all() for pubvlan in pubvlanlist: if pubvlan.pubaddr != None: addrlist[str(pubvlan.pubaddr.ip)] = str(pubvlan.vlan_id) addrlist['status'] = 'ok' except: addrlist['status'] = 'tables_gen_error' return jsonify(addrlist) @vmanager.route('/vmcreate/', methods=['GET', 'POST']) @login_required @admin_required def vmcreate(orderid): from flask_wtf import FlaskForm from wtforms import validators, RadioField, SubmitField from sqlalchemy.sql.expression import func, select order = Order.query.filter_by(pid=int(orderid)).first() if order == None: abort(403) servers = Server.query.filter_by(region_id=int(order.region_id)).all() serverchoices = [] for server in servers: serverchoice = (str(server.pid), str(server.name)) serverchoices.append(serverchoice) #TODO: check api for happiness and preselect suggested slave #happyslave = '2' #warrior in our case happyslave = '1' rand_pub_addr = Address.query.filter_by(region_id=int(order.region_id)).filter_by(reserved=False).filter_by(pubvlan_id=None).order_by(func.random()).first() if rand_pub_addr == None: flash('No free addresses in the selected region') return redirect(url_for('admin.list_addresses')) class SlaveForm(FlaskForm): slaveswitcher = RadioField('Select Slave Server', [validators.Required()], choices=serverchoices, default=happyslave) submit = SubmitField('Create') form = SlaveForm() if form.validate_on_submit(): selectedslaveswitch = form.slaveswitcher.data selectedslave = Server.query.filter_by(pid=int(selectedslaveswitch)).first() data = { 'clientid': str(order.user_id), 'clientemail': str(order.owner.email), 'hostname': 'c' + str(order.user_id) + '-' + str(order.parameter1), 'slave': str(selectedslave.name), 'region': str(order.region.name), 'type': 'kvm', 'cpu': str(int(float(order.parameter2))), 'mem': str(int(float(order.parameter3))), 'hdd': str(int(float(order.parameter4))), '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': selected_slave = Server.query.filter_by(name=query['slave']).first() #create new topic new_topic = SupportTopic(hashtag='deploy-' + str(query['unit_id'])) db.session.add(new_topic) db.session.commit() deployment = Deployment(user_id=order.user_id, machine_alias=str(order.parameter1), period=1, 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, server_id=int(selected_slave.pid), topic_id=int(new_topic.pid)) db.session.add(deployment) db.session.commit() new_vlan = PubVLAN(deploy_id=int(deployment.pid), vlan_id=int(query['vlanid'])) db.session.add(new_vlan) db.session.commit() rand_pub_addr.pubvlan_id = new_vlan.pid rand_pub_addr.user_id = order.user_id db.session.commit() order.status = 'accepted' db.session.commit() flash('A new deployment is created successfully in region "{}".'.format(str(order.region.description))) return redirect(url_for('panel.dashboard')) else: flash('Deployment could not be created! Please try again later...') return redirect(url_for('panel.dashboard')) return render_template('vmanager/create.html', form=form, order=order) @vmanager.route('/vmremove/', methods=['GET', 'POST']) @login_required @admin_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'] == 'UNREACHABLE': flash('Physical machine is not reachable. Cannot be deleted from the database') return redirect(url_for('admin.list_archive')) if query['status'] == 'running': query = contact_proxmaster(data, 'stop') flash('Machine {} force stopped'.format(unit_id)) time.sleep(7) #pubvlans depends on deploy as foreign key so delete them first for pubvlan in deploy.inv_pubvlans: #clean public addr assignment if pubvlan.pubaddr != None: pubvlan.pubaddr.pubvlan_id = None pubvlan.pubaddr.user_id = None db.session.commit() db.session.delete(pubvlan) db.session.commit() 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_archive')) 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('/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), 'danger') 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('/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)