simplify admin page

This commit is contained in:
deflax 2017-06-07 18:04:27 +03:00
parent a29ffc0133
commit ca09e02f70
6 changed files with 53 additions and 278 deletions

View file

@ -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)

View file

@ -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'

View file

@ -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 %}

View file

@ -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>

View file

@ -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 = {}

View file

@ -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():