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 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('/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 bridges for the selected region only. switch should return slave name selected_bridge = current_user.inv_bridges.filter_by(deleted=False).first() if selected_bridge is None: #no bridges in the account. create one... data = { 'clientid': str(current_user.pid), 'clientemail': str(current_user.email), 'region': str(selected_region.name), 'type': 'br' } #create bridge unit query = contact_proxmaster(data, 'create') if query['status'] == 'bridge_created': newbridge = True #machine will be installed where the bridge physically is region_name = query['region'] slave_name = query['slave'] bridge_id = query['unit_id'] bridge_phy_id = query['phy_id'] selected_bridge = Bridge(user_id=int(current_user.pid), bridge_id=bridge_id) db.session.add(selected_bridge) db.session.commit() flash('New point created successfully in region "{}".'.format(str(selected_region.description))) 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 = { 'unit_id': int(selected_bridge.bridge_id), 'type': 'br' } query = contact_proxmaster(data, 'query') if query['status'] == 'query_success': newbridge = False #machine will be installed where the switch physically is region_name = query['region'] slave_name = query['slave'] bridge_phy_id = query['phy_id'] else: flash('Point found but cannot be used!') return redirect(url_for('main.dashboard')) ### #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': 'vmbr' + str(bridge_phy_id) } try: query = contact_proxmaster(data, 'create') except: flash('Region not available! Please try again later...') return redirect(url_for('main.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('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)) #TODO: Filter routers for the selected region only. switch should return slave name selected_router = owner.inv_routers.filter_by(deleted=False).first() if selected_router is None: #TODO: Filter bridges for the selected region only. switch should return slave name selected_bridge = owner.inv_bridges.filter_by(deleted=False).first() if selected_bridge is None: flash('No private network found.') else: #bridge found. lets see on which slave it is so we can create the instance on the same slave. data = { 'unit_id': int(selected_bridge.bridge_id), 'type': 'br' } query = contact_proxmaster(data, 'query') if query['status'] == 'query_success': #TODO: selected random ip address from the pool. selected_ip = '87.120.110.41' #machine will be installed where the switch physically is region_name = query['region'] slave_name = query['slave'] bridge_phy_id = query['phy_id'] data = { 'clientid': str(owner.pid), 'clientemail': str(owner.email), 'hostname': 'c' + str(owner.pid) + '-r' + selected_ip, 'region': str(region_name), 'slave': str(slave_name), 'type': 'lxc', 'cpu': '1', 'mem': '256', 'hdd': '1', 'net0if': 'vmbr' + str(bridge_phy_id), 'net0ip': '192.168.9.1', 'net0mask': '24', 'net1if': 'vmbr0', 'net1ip': selected_ip, 'net1mask': '24', 'net1gw': '87.120.110.1' #should be queried from the current region } query = contact_proxmaster(data, 'create') if query['status'] == 'lxc_created': router = Router(user_id=int(owner.pid), machine_id=query['unit_id']) db.session.add(router) db.session.commit() else: flash('Router cannot be created.') return redirect(url_for('main.dashboard')) 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('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(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)