simplify admin page
This commit is contained in:
parent
a29ffc0133
commit
ca09e02f70
6 changed files with 53 additions and 278 deletions
|
@ -32,10 +32,10 @@ def after_request(response):
|
|||
@login_required
|
||||
@admin_required
|
||||
def index():
|
||||
alldeployments = Deployment.query.order_by(Deployment.date_created.desc()).all()
|
||||
alldomains = Domain.query.order_by(Domain.date_created.desc()).all()
|
||||
allservices = Service.query.order_by(Service.date_created.desc()).all()
|
||||
alladdresses = Address.query.order_by(Address.date_assigned.desc()).all()
|
||||
alldeployments = Deployment.query.order_by(Deployment.user_id.desc()).all()
|
||||
alldomains = Domain.query.order_by(Domain.user_id.desc()).all()
|
||||
allservices = Service.query.order_by(Service.user_id.desc()).all()
|
||||
alladdresses = Address.query.order_by(Address.user_id.asc()).all()
|
||||
return render_template('admin/index.html', deployments=alldeployments, domains=alldomains, services=allservices, addresses=alladdresses)
|
||||
|
||||
@admin.route("/users", methods=['GET'])
|
||||
|
@ -84,7 +84,7 @@ def dashboard(user_pid=0):
|
|||
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()
|
||||
addresses = cuser.inv_addresses.order_by(Address.ip.asc()).all()
|
||||
inv_addresses = []
|
||||
for invcls in addresses:
|
||||
if invcls.user_id == cuser.pid and invcls.enabled == True:
|
||||
|
@ -114,6 +114,6 @@ def dashboard(user_pid=0):
|
|||
'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)
|
||||
return render_template('vmanager/dashboard.html', rrd=rrd, status=statuses, inv_deployments=deployments, inv_services=services, inv_domains=domains, inv_addresses=addresses)
|
||||
|
||||
|
||||
|
|
|
@ -7,11 +7,12 @@ from app.exceptions import ValidationError
|
|||
from . import db, lm
|
||||
|
||||
import os
|
||||
import random
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
from decimal import Decimal
|
||||
from datetime import date, time, datetime, timedelta
|
||||
import json
|
||||
from sortedcontainers import SortedDict
|
||||
import requests
|
||||
import onetimepass
|
||||
|
@ -249,6 +250,7 @@ class Deployment(db.Model):
|
|||
machine_cpu = db.Column(db.Integer)
|
||||
machine_mem = db.Column(db.Integer)
|
||||
machine_hdd = db.Column(db.Integer)
|
||||
machine_addresses = db.relationship('Address', backref='deployments', lazy='dynamic')
|
||||
|
||||
def charge():
|
||||
result = Deployment.query.all()
|
||||
|
@ -288,10 +290,24 @@ class Address(db.Model):
|
|||
date_assigned = db.Column(db.DateTime, index=True, default=datetime.utcnow)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.pid')) #FK
|
||||
region_id = db.Column(db.Integer, db.ForeignKey('regions.pid')) #FK
|
||||
deploy_id = db.Column(db.Integer, db.ForeignKey('deployments.pid')) #FK
|
||||
|
||||
ip = db.Column(db.String(64))
|
||||
rdns = db.Column(db.String(256))
|
||||
macaddr = db.Column(db.String(128))
|
||||
reserved = db.Column(db.Boolean, default=False)
|
||||
reserved = db.Column(db.Boolean, default=False) #this ip SHOULD NOT be listed as available to assign even if its not currently owned by anyone
|
||||
|
||||
def __init__(self):
|
||||
if self.macaddr is None:
|
||||
self.macaddr = self.genmac()
|
||||
|
||||
def genmac(self):
|
||||
alladdr = Address.query.all()
|
||||
|
||||
mac = [ random.randint(0, 255) for x in range(0, 6) ]
|
||||
mac[0] = (mac[0] & 0xfc) | 0x02
|
||||
mac = ''.join([ '{0:02x}'.format(x) for x in mac ])
|
||||
return mac
|
||||
|
||||
class Domain(db.Model):
|
||||
__tablename__ = 'domains'
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
{% 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">
|
||||
{% include "admin/admin_tasks.html" %}
|
||||
|
||||
<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 %}
|
||||
|
|
@ -7,10 +7,8 @@
|
|||
.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) {
|
||||
@media only screen and (max-width: 800px) {
|
||||
|
||||
/* Force table to not be like tables anymore */
|
||||
#no-more-tables table,
|
||||
|
@ -64,6 +62,24 @@
|
|||
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$(".nmt").each(function() {
|
||||
var nmtTable = $(this);
|
||||
var nmtHeadRow = nmtTable.find("thead tr");
|
||||
nmtTable.find("tbody tr").each(function () {
|
||||
var curRow = $(this);
|
||||
for (var i = 0; i < curRow.find("td").length; i++) {
|
||||
var rowSelector = "td:eq(" + i + ")";
|
||||
var headSelector = "th:eq(" + i + ")";
|
||||
curRow.find(rowSelector).attr('data-title', nmtHeadRow.find(headSelector).html());
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('a[data-toggle="tooltip"]').tooltip({
|
||||
animated: 'fade',
|
||||
|
@ -71,6 +87,7 @@ $('a[data-toggle="tooltip"]').tooltip({
|
|||
html: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Only run what comes next *after* the page has loaded
|
||||
addEventListener("DOMContentLoaded", function() {
|
||||
|
@ -142,7 +159,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]['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>{% for addr in deploy.machine_addresses %}<a data-toggle="tooltip" title="<img src='data:image/png;base64,{{ rrd[deploy.machine_id]['net'] }}' />">{{ addr.ip }}</a><br />{% endfor %}</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>
|
||||
|
@ -195,22 +212,22 @@ addEventListener("DOMContentLoaded", function() {
|
|||
<thead>
|
||||
<tr>
|
||||
<th>IP</th>
|
||||
<th>Reverse DNS</th>
|
||||
<th>MAC Addr.</th>
|
||||
<th>Reverse DNS</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>{{ address.rdns }}</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>
|
||||
<!--<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>
|
||||
|
|
|
@ -143,11 +143,11 @@ def dashboard():
|
|||
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])
|
||||
addresses = cuser.inv_addresses.order_by(Address.ip.asc()).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 = {}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import os
|
||||
import subprocess, shlex
|
||||
from app import app, db
|
||||
from flask_alchemydumps import AlchemyDumps, AlchemyDumpsCommand
|
||||
from flask_script import Manager, Shell, Command
|
||||
from flask_migrate import Migrate, MigrateCommand
|
||||
|
||||
|
@ -16,12 +15,10 @@ def make_shell_context():
|
|||
Deployment=Deployment)
|
||||
|
||||
migrate = Migrate(app, db)
|
||||
dump = AlchemyDumps(app, db)
|
||||
|
||||
manager = Manager(app)
|
||||
manager.add_command('shell', Shell(make_context=make_shell_context))
|
||||
manager.add_command('db', MigrateCommand)
|
||||
manager.add_command('dump', AlchemyDumpsCommand)
|
||||
|
||||
@manager.command
|
||||
def deploy():
|
||||
|
|
Loading…
Reference in a new issue