adding admin page
This commit is contained in:
parent
31ab20e837
commit
bf775e658e
10 changed files with 475 additions and 53 deletions
|
@ -68,3 +68,6 @@ server {
|
||||||
1. crontab -e
|
1. crontab -e
|
||||||
2. @reboot /usr/bin/screen -dmS proxadmin /home/proxadmin/appserver/proxmaster-panel/start.sh
|
2. @reboot /usr/bin/screen -dmS proxadmin /home/proxadmin/appserver/proxmaster-panel/start.sh
|
||||||
|
|
||||||
|
|
||||||
|
### backup the database:
|
||||||
|
1. python3 manage.py dump create
|
||||||
|
|
|
@ -44,6 +44,9 @@ app.register_blueprint(news_blueprint, url_prefix='/news')
|
||||||
from .auth import auth as auth_blueprint
|
from .auth import auth as auth_blueprint
|
||||||
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
||||||
|
|
||||||
|
from .admin import admin as admin_blueprint
|
||||||
|
app.register_blueprint(admin_blueprint, url_prefix='/' + app.config['ADMIN_PREFIX'])
|
||||||
|
|
||||||
from .settings import settings as settings_blueprint
|
from .settings import settings as settings_blueprint
|
||||||
app.register_blueprint(settings_blueprint, url_prefix='/settings')
|
app.register_blueprint(settings_blueprint, url_prefix='/settings')
|
||||||
|
|
||||||
|
@ -85,7 +88,7 @@ def forbidden(e):
|
||||||
response = jsonify({'error': 'forbidden'})
|
response = jsonify({'error': 'forbidden'})
|
||||||
response.status_code = 403
|
response.status_code = 403
|
||||||
return response
|
return response
|
||||||
return render_template('errors/403.html'), 403
|
return render_template('errors/403.html'), 403
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
|
|
3
app/admin/__init__.py
Normal file
3
app/admin/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
admin = Blueprint('admin', __name__)
|
||||||
|
from . import routes
|
94
app/admin/routes.py
Normal file
94
app/admin/routes.py
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
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
|
||||||
|
from flask_sqlalchemy import get_debug_queries
|
||||||
|
|
||||||
|
from . import admin
|
||||||
|
from .. import db
|
||||||
|
from ..email import send_email
|
||||||
|
from ..models import User, Role, 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
|
||||||
|
|
||||||
|
#@admin.before_app_request
|
||||||
|
#def before_request():
|
||||||
|
# g.user = current_user
|
||||||
|
# print('current_user: %s, g.user: %s, leaving bef_req' % (current_user, g.user))
|
||||||
|
|
||||||
|
@admin.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
|
||||||
|
|
||||||
|
@admin.route("/", methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
def index():
|
||||||
|
allusers = User.query.all()
|
||||||
|
return render_template('admin/users.html', users=allusers)
|
||||||
|
|
||||||
|
@admin.route("/dashboard/<int:user_pid>", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
def dashboard(user_pid=0):
|
||||||
|
cuser = User.query.filter_by(pid=user_pid).first()
|
||||||
|
|
||||||
|
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_created.desc()).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.date_assigned.desc()).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.'.format(str(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:
|
||||||
|
print(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=cuser, cubeid=cubeid )
|
||||||
|
|
||||||
|
current_app.logger.info('[{}] deployments: {}, domains: {}, services: {}'.format(cuser.email, inv_deploynames, inv_services, inv_domains, inv_addresses ))
|
||||||
|
return render_template('admin/dashboard.html', rrd=rrd, status=statuses, inv_deployments=deployments, inv_services=services, inv_domains=domains, inv_addresses=addresses)
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class User(db.Model, UserMixin):
|
||||||
inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic')
|
inv_deployments = db.relationship('Deployment', backref='owner', lazy='dynamic')
|
||||||
inv_services = db.relationship('Service', backref='owner', lazy='dynamic')
|
inv_services = db.relationship('Service', backref='owner', lazy='dynamic')
|
||||||
inv_domains = db.relationship('Domain', backref='owner', lazy='dynamic')
|
inv_domains = db.relationship('Domain', backref='owner', lazy='dynamic')
|
||||||
inv_address = db.relationship('Address', backref='owner', lazy='dynamic')
|
inv_addresses = db.relationship('Address', backref='owner', lazy='dynamic')
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(User, self).__init__(**kwargs)
|
super(User, self).__init__(**kwargs)
|
||||||
|
@ -223,12 +223,15 @@ class Service(db.Model):
|
||||||
__tablename__ = 'services'
|
__tablename__ = 'services'
|
||||||
pid = db.Column(db.Integer, primary_key=True) #PK
|
pid = db.Column(db.Integer, primary_key=True) #PK
|
||||||
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
||||||
|
date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
||||||
|
date_expire = db.Column(db.DateTime)
|
||||||
|
enabled = db.Column(db.Boolean)
|
||||||
|
|
||||||
name = db.Column(db.String(64))
|
name = db.Column(db.String(64))
|
||||||
image = db.Column(db.String(128))
|
image = db.Column(db.String(128))
|
||||||
description = db.Column(db.Unicode(128))
|
description = db.Column(db.Unicode(128))
|
||||||
unit = db.Column(db.Integer)
|
unit = db.Column(db.Integer)
|
||||||
unitprice = db.Column(db.Float)
|
unitprice = db.Column(db.Float)
|
||||||
enabled = db.Column(db.Boolean)
|
|
||||||
|
|
||||||
class Deployment(db.Model):
|
class Deployment(db.Model):
|
||||||
__tablename__ = 'deployments'
|
__tablename__ = 'deployments'
|
||||||
|
@ -243,7 +246,6 @@ class Deployment(db.Model):
|
||||||
machine_cpu = db.Column(db.Integer)
|
machine_cpu = db.Column(db.Integer)
|
||||||
machine_mem = db.Column(db.Integer)
|
machine_mem = db.Column(db.Integer)
|
||||||
machine_hdd = db.Column(db.Integer)
|
machine_hdd = db.Column(db.Integer)
|
||||||
machine_ipv4list = db.Column(db.String)
|
|
||||||
|
|
||||||
def charge():
|
def charge():
|
||||||
result = Deployment.query.all()
|
result = Deployment.query.all()
|
||||||
|
@ -269,6 +271,8 @@ class Deployment(db.Model):
|
||||||
class Region(db.Model):
|
class Region(db.Model):
|
||||||
__tablename__ = 'regions'
|
__tablename__ = 'regions'
|
||||||
pid = db.Column(db.Integer, primary_key=True)
|
pid = db.Column(db.Integer, primary_key=True)
|
||||||
|
enabled = db.Column(db.Boolean)
|
||||||
|
|
||||||
name = db.Column(db.String(64))
|
name = db.Column(db.String(64))
|
||||||
description = db.Column(db.String(128))
|
description = db.Column(db.String(128))
|
||||||
extraprice = db.Column(db.Float)
|
extraprice = db.Column(db.Float)
|
||||||
|
@ -276,17 +280,20 @@ class Region(db.Model):
|
||||||
class Address(db.Model):
|
class Address(db.Model):
|
||||||
__tablename__ = 'address'
|
__tablename__ = 'address'
|
||||||
pid = db.Column(db.Integer, primary_key=True)
|
pid = db.Column(db.Integer, primary_key=True)
|
||||||
|
enabled = db.Column(db.Boolean)
|
||||||
|
|
||||||
date_assigned = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
date_assigned = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
||||||
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
||||||
region_id = db.Column(db.Integer, db.ForeignKey('regions.pid')) #FK
|
region_id = db.Column(db.Integer, db.ForeignKey('regions.pid')) #FK
|
||||||
ipv4 = db.Column(db.String(64))
|
ip = db.Column(db.String(64))
|
||||||
ipv6 = db.Column(db.String(256))
|
|
||||||
rdns = db.Column(db.String(256))
|
rdns = db.Column(db.String(256))
|
||||||
macaddr = db.Column(db.String(128))
|
macaddr = db.Column(db.String(128))
|
||||||
|
|
||||||
class Domain(db.Model):
|
class Domain(db.Model):
|
||||||
__tablename__ = 'domains'
|
__tablename__ = 'domains'
|
||||||
pid = db.Column(db.Integer, primary_key=True)
|
pid = db.Column(db.Integer, primary_key=True)
|
||||||
|
enabled = db.Column(db.Boolean)
|
||||||
|
|
||||||
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
||||||
date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
date_created = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
||||||
date_expire = db.Column(db.DateTime)
|
date_expire = db.Column(db.DateTime)
|
||||||
|
|
255
app/templates/admin/dashboard.html
Normal file
255
app/templates/admin/dashboard.html
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{ super() }}
|
||||||
|
<style type="text/css">
|
||||||
|
.tg {border-collapse:collapse;border-spacing:0;}
|
||||||
|
.tg td{font-family:Arial, sans-serif;font-size:14px;padding:1px 1px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal;}
|
||||||
|
.tg th{font-family:Arial, sans-serif;font-size:14px;padding:1px 1px;font-weight:normal;padding:border-style:solid;border-width:1px;overflow:hidden;word-break:normal;}
|
||||||
|
.tg .tg-yw4l{vertical-align:top}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
|
||||||
|
/* Force table to not be like tables anymore */
|
||||||
|
#no-more-tables table,
|
||||||
|
#no-more-tables thead,
|
||||||
|
#no-more-tables tbody,
|
||||||
|
#no-more-tables th,
|
||||||
|
#no-more-tables td,
|
||||||
|
#no-more-tables tr {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide table headers (but not display: none;, for accessibility) */
|
||||||
|
#no-more-tables thead tr {
|
||||||
|
position: absolute;
|
||||||
|
top: -9999px;
|
||||||
|
left: -9999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#no-more-tables tr { border: 1px solid #ccc; }
|
||||||
|
|
||||||
|
#no-more-tables td {
|
||||||
|
/* Behave like a "row" */
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 50%;
|
||||||
|
white-space: normal;
|
||||||
|
text-align:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#no-more-tables td:before {
|
||||||
|
/* Now like a table header */
|
||||||
|
position: absolute;
|
||||||
|
/* Top/left values mimic padding */
|
||||||
|
top: 6px;
|
||||||
|
left: 6px;
|
||||||
|
width: 45%;
|
||||||
|
padding-right: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align:left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Label the data
|
||||||
|
*/
|
||||||
|
#no-more-tables td:before { content: attr(data-title); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{{ super() }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
$('a[data-toggle="tooltip"]').tooltip({
|
||||||
|
animated: 'fade',
|
||||||
|
placement: 'bottom',
|
||||||
|
html: true
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
// Only run what comes next *after* the page has loaded
|
||||||
|
addEventListener("DOMContentLoaded", function() {
|
||||||
|
// Grab all of the elements with a class of command
|
||||||
|
// (which all of the buttons we just created have)
|
||||||
|
var commandButtons = document.querySelectorAll(".command");
|
||||||
|
for (var i=0, l=commandButtons.length; i<l; i++) {
|
||||||
|
var button = commandButtons[i];
|
||||||
|
// For each button, listen for the "click" event
|
||||||
|
button.addEventListener("click", function(e) {
|
||||||
|
// When a click happens, stop the button
|
||||||
|
// from submitting our form (if we have one)
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (window.confirm("Are you sure?")) {
|
||||||
|
var clickedButton = e.target;
|
||||||
|
var command = clickedButton.value;
|
||||||
|
var vmid = clickedButton.getAttribute('vmid');
|
||||||
|
|
||||||
|
// Now we need to send the data to our server
|
||||||
|
// without reloading the page - this is the domain of
|
||||||
|
// AJAX (Asynchronous JavaScript And XML)
|
||||||
|
// We will create a new request object
|
||||||
|
// and set up a handler for the response
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.onload = function() {
|
||||||
|
// We could do more interesting things with the response
|
||||||
|
// or, we could ignore it entirely
|
||||||
|
//alert(request.responseText);
|
||||||
|
};
|
||||||
|
// We point the request at the appropriate command
|
||||||
|
request.open("GET", "/vmanager/" + command + "/" + vmid, true);
|
||||||
|
// and then we send it off
|
||||||
|
request.send();
|
||||||
|
//alert("command " + command + " executed.");
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block page_content %}
|
||||||
|
<div class="container-fluid">
|
||||||
|
<br />
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-danger" id="deployments">
|
||||||
|
<div class="panel-heading">Deployments</div>
|
||||||
|
<div class="panel-body"><p>
|
||||||
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>CPU</th>
|
||||||
|
<th>Mem</th>
|
||||||
|
<th>HDD</th>
|
||||||
|
<th>IPv4</th>
|
||||||
|
<th>Control</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for deploy in inv_deployments %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a data-toggle="tooltip" title="ID# {{ deploy.machine_id }}<br />Deployment state: {{ status[deploy.machine_id] }}"><b>{% if status[deploy.machine_id] == 'running' %}<font color="green">{% else %}<font color="red">{% endif %}{{ deploy.machine_alias }}</font></b></a></td>
|
||||||
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['cpu'] }}' />">{{ deploy.machine_cpu }} Cores</a></td>
|
||||||
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['mem'] }}' />">{{ deploy.machine_mem }} MB</a></td>
|
||||||
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['hdd'] }}' />">{{ deploy.machine_hdd }} GB</a></td>
|
||||||
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['net'] }}' />">0.0.0.0</a></td>
|
||||||
|
<td>{% if status[deploy.machine_id] == 'running' %}
|
||||||
|
<button class="confirm command command-vmshutdown btn btn-default btn-warning" value="vmshutdown" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-off" aria-hidden="true"></span> Shutdown</button>
|
||||||
|
<button class="confirm command command-vmstop btn btn-default btn-danger" value="vmstop" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> Force Stop</button>
|
||||||
|
{% else %}
|
||||||
|
<button class="command command-vmstart btn btn-default btn-success" value="vmstart" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-play" aria-hidden="true"></span> Start</button>
|
||||||
|
{% endif %}</td>
|
||||||
|
<td>{% if status[deploy.machine_id] == 'running' %}
|
||||||
|
<button class="btn btn-default btn-info" onclick="window.open('/vmanager/vmvnc/{{ deploy.machine_id }}', '_blank');"><span class="glyphicon glyphicon-console" aria-hidden="true"></span> Console</button>
|
||||||
|
{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Create</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-danger" id="domains">
|
||||||
|
<div class="panel-heading">Domains</div>
|
||||||
|
<div class="panel-body"><p>
|
||||||
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Expiry Date</th>
|
||||||
|
<th>Control</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for domain in inv_domains %}
|
||||||
|
<tr>
|
||||||
|
<td><b><a href="http://{{ domain.fqdn }}">{{ domain.fqdn }}</a></b></td>
|
||||||
|
<td>{{ domain.date_expire }}</td>
|
||||||
|
<td>soon...</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Order</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-danger" id="addresses">
|
||||||
|
<div class="panel-heading">Addresses</div>
|
||||||
|
<div class="panel-body"><p>
|
||||||
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>Reverse DNS</th>
|
||||||
|
<th>MAC Addr.</th>
|
||||||
|
<th>Control</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for address in inv_addresses %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ address.ip }}</td>
|
||||||
|
<td>{{ address.rdns }}</td>
|
||||||
|
<td>{{ address.macaddr }}</td>
|
||||||
|
<td>soon...</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Assign</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="panel panel-danger" id="services">
|
||||||
|
<div class="panel-heading">Services</div>
|
||||||
|
<div class="panel-body"><p>
|
||||||
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Unit</th>
|
||||||
|
<th>Price</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for service in inv_services %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ service.name }}</td>
|
||||||
|
<td>{{ service.description }}</td>
|
||||||
|
<td>{{ service.unit }}</td>
|
||||||
|
<td>{{ service.unitprice }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Order</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
8
app/templates/admin/users.html
Normal file
8
app/templates/admin/users.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block page_content %}
|
||||||
|
<center>
|
||||||
|
<div class="row">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -14,14 +14,7 @@
|
||||||
</div><!--/one four-->
|
</div><!--/one four-->
|
||||||
|
|
||||||
<div class="one_four">
|
<div class="one_four">
|
||||||
<h4>Newsletter Signup</h4>
|
<h4></h4>
|
||||||
<p>Join our newsletter to stay informed on our latest updates and sales.</p>
|
|
||||||
<form action="#" method="post">
|
|
||||||
<div class="form_field">
|
|
||||||
<input class="newsletter_input" title="Your Email" type="text" placeholder="Enter Your Email" />
|
|
||||||
<input class="newsletter_submit" type="submit" />
|
|
||||||
</div><!--/form field-->
|
|
||||||
</form>
|
|
||||||
</div><!--/one four-->
|
</div><!--/one four-->
|
||||||
|
|
||||||
<div class="one_four last_col" id="newsmenu">
|
<div class="one_four last_col" id="newsmenu">
|
||||||
|
|
|
@ -106,6 +106,7 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
// and then we send it off
|
// and then we send it off
|
||||||
request.send();
|
request.send();
|
||||||
//alert("command " + command + " executed.");
|
//alert("command " + command + " executed.");
|
||||||
|
window.location.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -119,10 +120,9 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="panel panel-info" id="dashboard">
|
<div class="panel panel-info" id="deployments">
|
||||||
<div class="panel-heading">Deployments</div>
|
<div class="panel-heading">Deployments</div>
|
||||||
<div class="panel-body"><p>
|
<div class="panel-body"><p>
|
||||||
{% if inv_deploymens == None %}
|
|
||||||
<table class="table table-hover table-striped table-condensed cf">
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -142,7 +142,7 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['cpu'] }}' />">{{ deploy.machine_cpu }} Cores</a></td>
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['cpu'] }}' />">{{ deploy.machine_cpu }} Cores</a></td>
|
||||||
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['mem'] }}' />">{{ deploy.machine_mem }} MB</a></td>
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['mem'] }}' />">{{ deploy.machine_mem }} MB</a></td>
|
||||||
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['hdd'] }}' />">{{ deploy.machine_hdd }} GB</a></td>
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['hdd'] }}' />">{{ deploy.machine_hdd }} GB</a></td>
|
||||||
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['net'] }}' />">{% for ip in deploy.machine_ipv4list %}{{ ip }}{% endfor %}</a></td>
|
<td><a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['net'] }}' />">0.0.0.0</a></td>
|
||||||
<td>{% if status[deploy.machine_id] == 'running' %}
|
<td>{% if status[deploy.machine_id] == 'running' %}
|
||||||
<button class="confirm command command-vmshutdown btn btn-default btn-warning" value="vmshutdown" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-off" aria-hidden="true"></span> Shutdown</button>
|
<button class="confirm command command-vmshutdown btn btn-default btn-warning" value="vmshutdown" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-off" aria-hidden="true"></span> Shutdown</button>
|
||||||
<button class="confirm command command-vmstop btn btn-default btn-danger" value="vmstop" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> Force Stop</button>
|
<button class="confirm command command-vmstop btn btn-default btn-danger" value="vmstop" vmid="{{ deploy.machine_id }}"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> Force Stop</button>
|
||||||
|
@ -156,38 +156,89 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
|
||||||
<button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Create</button>
|
<button class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Create</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-6">
|
||||||
<div class="panel panel-info" id="dashboard">
|
<div class="panel panel-info" id="domains">
|
||||||
<div class="panel-heading">Domains</div>
|
<div class="panel-heading">Domains</div>
|
||||||
<div class="panel-body"><p>
|
<div class="panel-body"><p>
|
||||||
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Expiry Date</th>
|
||||||
|
<th>Control</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
{% for domain in inv_domains %}
|
{% for domain in inv_domains %}
|
||||||
{{ fqdn }}
|
<tr>
|
||||||
|
<td><b><a href="http://{{ domain.fqdn }}">{{ domain.fqdn }}</a></b></td>
|
||||||
|
<td>{{ domain.date_expire }}</td>
|
||||||
|
<td>soon...</td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Create</button>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Order</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="panel panel-info" id="addresses">
|
||||||
|
<div class="panel-heading">Addresses</div>
|
||||||
|
<div class="panel-body"><p>
|
||||||
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>Reverse DNS</th>
|
||||||
|
<th>MAC Addr.</th>
|
||||||
|
<th>Control</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for address in inv_addresses %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ address.ip }}</td>
|
||||||
|
<td>{{ address.rdns }}</td>
|
||||||
|
<td>{{ address.macaddr }}</td>
|
||||||
|
<td>soon...</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Assign</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="panel panel-info" id="dashboard">
|
<div class="panel panel-info" id="services">
|
||||||
<div class="panel-heading">Services</div>
|
<div class="panel-heading">Services</div>
|
||||||
<div class="panel-body"><p>
|
<div class="panel-body"><p>
|
||||||
{% for contract in inv_contracts %}
|
<table class="table table-hover table-striped table-condensed cf">
|
||||||
{{ contract.description }}<br />
|
<thead>
|
||||||
{{ contract.units }}<br />
|
<tr>
|
||||||
{% if not contract.discount == 0 %}
|
<th>Name</th>
|
||||||
Discount %{{ contract.discount }}<br />
|
<th>Description</th>
|
||||||
Credit: {{ contract.credit }}<br />
|
<th>Unit</th>
|
||||||
{% endif %}
|
<th>Price</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for service in inv_services %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ service.name }}</td>
|
||||||
|
<td>{{ service.description }}</td>
|
||||||
|
<td>{{ service.unit }}</td>
|
||||||
|
<td>{{ service.unitprice }}</td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Create</button>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button class="btn btn-default" onclick="window.open('{{ url_for('main.index') }}')"><span class="glyphicon glyphicon-plus" aria-hiddent="true"></span> Order</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,4 +253,3 @@ addEventListener("DOMContentLoaded", function() {
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -122,26 +122,32 @@ def deploy(product_id=None):
|
||||||
@vmanager.route("/dashboard", methods=['GET', 'POST'])
|
@vmanager.route("/dashboard", methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
deployments = current_user.inv_deployments.order_by(Deployment.date_created.desc()).all()
|
deployments = cuser.inv_deployments.order_by(Deployment.date_created.desc()).all()
|
||||||
inv_deploycubeids = []
|
inv_deploycubeids = []
|
||||||
inv_deploynames = []
|
inv_deploynames = []
|
||||||
for invcls in deployments:
|
for invcls in deployments:
|
||||||
if invcls.enabled == True:
|
if invcls.user_id == cuser.pid and invcls.enabled == True:
|
||||||
inv_deploycubeids.extend([invcls.machine_id])
|
inv_deploycubeids.extend([invcls.machine_id])
|
||||||
inv_deploynames.extend([invcls.machine_alias])
|
inv_deploynames.extend([invcls.machine_alias])
|
||||||
|
|
||||||
services = current_user.inv_services.order_by(Service.date_created.desc()).all()
|
services = cuser.inv_services.order_by(Service.date_created.desc()).all()
|
||||||
inv_services = []
|
inv_services = []
|
||||||
for invcls in services:
|
for invcls in services:
|
||||||
if invcls.enabled == True:
|
if invcls.user_id == cuser.pid and invcls.enabled == True:
|
||||||
inv_services.extend([invcls.description])
|
inv_services.extend([invcls.description])
|
||||||
|
|
||||||
domains = current_user.inv_domains.order_by(Domain.date_created.desc()).all()
|
domains = cuser.inv_domains.order_by(Domain.date_created.desc()).all()
|
||||||
inv_domains = []
|
inv_domains = []
|
||||||
for invcls in domains:
|
for invcls in domains:
|
||||||
if invcls.enabled == True:
|
if invcls.user_id == cuser.pid and invcls.enabled == True:
|
||||||
inv_domains.extend([invcls.fqdn])
|
inv_domains.extend([invcls.fqdn])
|
||||||
|
|
||||||
|
addresses = cuser.inv_addresses.order_by(Address.date_assigned.desc()).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
|
#extract rrd and status from the deployments
|
||||||
rrd = {}
|
rrd = {}
|
||||||
statuses = {}
|
statuses = {}
|
||||||
|
@ -167,8 +173,8 @@ def dashboard():
|
||||||
send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid),
|
send_email(current_app.config['MAIL_USERNAME'], 'Cube {} is unreachable'.format(cubeid),
|
||||||
'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid )
|
'vmanager/email/adm_unreachable', user=current_user, cubeid=cubeid )
|
||||||
|
|
||||||
current_app.logger.info('[{}] deployments: {}, domains: {}, services: {}'.format(current_user.email, inv_deploynames, inv_contracts, inv_domains ))
|
current_app.logger.info('[{}] deployments: {}, domains: {}, services: {}'.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_contracts=contracts, inv_domains=domains)
|
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/<int:vmid>')
|
@vmanager.route('/vmvnc/<int:vmid>')
|
||||||
@login_required
|
@login_required
|
||||||
|
|
Loading…
Reference in a new issue