new changes, with key generation and configuration handling via web

community
Steve L. Nyemba 6 years ago
parent d4a3c36b60
commit f48d4decfd

@ -0,0 +1,81 @@
<div id="dialog" class="input-dialog border-round">
<input id="server_name" type="text" placeholder="Node/Computer name"/>
<div class="cols">
<div class="menu border-right" style="margin-right:4px">
<div class="border-round"><i class="fa fa-bars"></i> Setup</div>
<div id="menu-items" class="items" >
<div class="item active" onclick="jx.dom.hide('folder_input'); jx.dom.show('app_input'); jx.dom.set.focus('apps') ">
<i class="fa fa-cog"></i>
Apps</div>
<div class="item active" onclick="jx.dom.hide('app_input'); jx.dom.show('folder_input'); jx.dom.set.focus('folders') ">
<i class="fa fa-folder-open"></i> Folders
</div>
</div>
</div>
<div>
<div id="app_input">
<div class="caption" style="display:grid; grid-template-columns: 32px auto">
<i class="fa fa-cog"></i>
Apps to Monitor</div>
<input id="apps" type="text" placeholder="app_1, app_2"/>
<div class="option">
<input type="checkbox" id="reboot" style="display:none"/>
<label for="reboot" class="active">
<div class="" align="center">
<i id="check_reboot" class="fa fa-times" onclick="$('#reboot').prop('checked',false)"></i>
<i class="fa fa-check" onclick="$('#reboot').prop('checked',true)"></i>
</div>
<div>Reboot Apps on this node/computer</div>
</label>
</div>
</div>
<div id="folder_input" style="display:grid; grid-template-columns: 32px auto; display:none;">
<div class="caption" >
<i class="fa fa-folder-open"></i> Folders to Monitor</div>
<input id="folders" type="text" placeholder="path_1, path_2"/>
<div class="option">
<input type="checkbox" id="archive" style="display:none"/>
<label for="archive" class="active">
<div class="" align="center">
<i id="check_archive" class="fa fa-times" onclick="$('#archive').prop('checked',false)"></i>
<i class="fa fa-check" onclick="$('#archive').prop('checked',true)"></i>
</div>
<div>Archive to the cloud</div>
</label>
</div>
</div>
</div>
</div>
<p>
<div style="display:grid; grid-template-columns: auto 105px 105px 105px; grid-gap:2px;">
<div></div>
<div >
<div id="download_config" class="button border-round border" onclick="downloadConfig()">
<div><i class="fa fa-download"></i></div>
<div>Config</div>
</div>
</div>
<div class="button border-round border" onclick="jx.modal.close('dialog')">
<div><i class="fa fa-times"></i></div>
<div>Cancel</div>
</div>
<div class="border-round border button" onclick="save()">
<div><i class="fa fa-check"></i></div>
<div align="center">Save</div>
</div>
</div>
</p>
</div>

@ -0,0 +1,69 @@
body {
/* margin-left:1%; */
/* margin-right:1%; */
font-family:sans-serif;
font-weight: lighter;
font-size:14px;
}
.small {font-size:12px;}
.bold {font-weight:bold}
.no-border{border:1px solid transparent}
.border {border: 1px solid #CAD5E0}
.border-top {border-top: 1px solid #CAD5E0}
.border-right {border-right: 1px solid #CAD5E0}
.border-left {border-left: 1px solid #CAD5E0}
.border-bottom {border-bottom: 1px solid #CAD5E0}
.left {float:left}
.right{float:right}
.no-float{float:none}
.title-bar {padding:4px; margin:4px}
.title-bar .title {font-size:18px;}
.jsgrid-edit-row > .jsgrid-cell {
background: #ffffff;
}
.active { border:2px solid transparent; cursor:pointer; margin:4px;}
.active:hover {border-bottom:2px solid #4682b4}
.edit-grid input[type=text]{
font-family:sans-serif;
font-weight:lighter;
border:0px;
border-left:2px solid transparent;
background-color:#f3f3f3;
font-size:14px;
margin:0px;
padding:4px;
outline:0px;
}
.edit-grid input[type=text]:focus {
outline:0px;
border-left:2px solid #4682b4;
}
.fa-times {color:maroon}
.fa-check {color:green}
.search-box{}
.search-box input[type=text] {font-size:13px;
padding-left:4px;
font-weight: lighter;
background-color:#f3f3f3;
border:4px solid transparent;
outline:none}
.search-box input[type=text]:focus{
outline:none;
border-left:4px solid #4682b4;
}
.button {
padding:4px;
cursor:pointer;
font-family:arial;
}
.button:hover{
background-color:#4682B4 ;
color:#ffffff;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1 @@
!function(r){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=r();else if("function"==typeof define&&define.amd)define([],r);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.uuidv5=r()}}(function(){return function r(e,n,t){function o(f,i){if(!n[f]){if(!e[f]){var u="function"==typeof require&&require;if(!i&&u)return u(f,!0);if(a)return a(f,!0);var c=new Error("Cannot find module '"+f+"'");throw c.code="MODULE_NOT_FOUND",c}var s=n[f]={exports:{}};e[f][0].call(s.exports,function(r){var n=e[f][1][r];return o(n?n:r)},s,s.exports,r,e,n,t)}return n[f].exports}for(var a="function"==typeof require&&require,f=0;f<t.length;f++)o(t[f]);return o}({1:[function(r,e,n){function t(r,e){var n=e||0,t=o;return[t[r[n++]],t[r[n++]],t[r[n++]],t[r[n++]],"-",t[r[n++]],t[r[n++]],"-",t[r[n++]],t[r[n++]],"-",t[r[n++]],t[r[n++]],"-",t[r[n++]],t[r[n++]],t[r[n++]],t[r[n++]],t[r[n++]],t[r[n++]]].join("")}for(var o=[],a=0;a<256;++a)o[a]=(a+256).toString(16).substr(1);e.exports=t},{}],2:[function(r,e,n){"use strict";function t(r,e,n,t){switch(r){case 0:return e&n^~e&t;case 1:return e^n^t;case 2:return e&n^e&t^n&t;case 3:return e^n^t}}function o(r,e){return r<<e|r>>>32-e}function a(r){var e=[1518500249,1859775393,2400959708,3395469782],n=[1732584193,4023233417,2562383102,271733878,3285377520];if("string"==typeof r){var a=unescape(encodeURIComponent(r));r=new Array(a.length);for(var f=0;f<a.length;f++)r[f]=a.charCodeAt(f)}r.push(128);for(var i=r.length/4+2,u=Math.ceil(i/16),c=new Array(u),f=0;f<u;f++){c[f]=new Array(16);for(var s=0;s<16;s++)c[f][s]=r[64*f+4*s]<<24|r[64*f+4*s+1]<<16|r[64*f+4*s+2]<<8|r[64*f+4*s+3]}c[u-1][14]=8*(r.length-1)/Math.pow(2,32),c[u-1][14]=Math.floor(c[u-1][14]),c[u-1][15]=8*(r.length-1)&4294967295;for(var f=0;f<u;f++){for(var d=new Array(80),p=0;p<16;p++)d[p]=c[f][p];for(var p=16;p<80;p++)d[p]=o(d[p-3]^d[p-8]^d[p-14]^d[p-16],1);for(var l=n[0],v=n[1],y=n[2],h=n[3],b=n[4],p=0;p<80;p++){var g=Math.floor(p/20),w=o(l,5)+t(g,v,y,h)+b+e[g]+d[p]>>>0;b=h,h=y,y=o(v,30)>>>0,v=l,l=w}n[0]=n[0]+l>>>0,n[1]=n[1]+v>>>0,n[2]=n[2]+y>>>0,n[3]=n[3]+h>>>0,n[4]=n[4]+b>>>0}return[n[0]>>24&255,n[0]>>16&255,n[0]>>8&255,255&n[0],n[1]>>24&255,n[1]>>16&255,n[1]>>8&255,255&n[1],n[2]>>24&255,n[2]>>16&255,n[2]>>8&255,255&n[2],n[3]>>24&255,n[3]>>16&255,n[3]>>8&255,255&n[3],n[4]>>24&255,n[4]>>16&255,n[4]>>8&255,255&n[4]]}e.exports=a},{}],3:[function(r,e,n){function t(r){var e=[];return r.replace(/[a-fA-F0-9]{2}/g,function(r){e.push(parseInt(r,16))}),e}function o(r){r=unescape(encodeURIComponent(r));for(var e=new Array(r.length),n=0;n<r.length;n++)e[n]=r.charCodeAt(n);return e}var a=r("./bytesToUuid");e.exports=function(r,e,n){var f=function(r,f,i,u){var c=i&&u||0;if("string"==typeof r&&(r=o(r)),"string"==typeof f&&(f=t(f)),!Array.isArray(r))throw TypeError("value must be an array of bytes");if(!Array.isArray(f)||16!==f.length)throw TypeError("namespace must be uuid string or an Array of 16 byte values");var s=n(f.concat(r));if(s[6]=15&s[6]|e,s[8]=63&s[8]|128,i)for(var d=0;d<16;++d)i[c+d]=s[d];return i||a(s)};try{f.name=r}catch(r){}return f.DNS="6ba7b810-9dad-11d1-80b4-00c04fd430c8",f.URL="6ba7b811-9dad-11d1-80b4-00c04fd430c8",f}},{"./bytesToUuid":1}],4:[function(r,e,n){var t=r("./lib/v35.js"),o=r("./lib/sha1");e.exports=t("v5",80,o)},{"./lib/sha1":2,"./lib/v35.js":3}]},{},[4])(4)});

@ -0,0 +1,44 @@
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<link href="{{context}}static/css/fa/css/font-awesome.css" type="text/css" rel="stylesheet">
<link href="{{context}}static/css/fa/animation.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jsgrid/jsgrid.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<script src="{{context}}/static/js/jx/rpc.js"></script>
<link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css">
<script>
var DATA_GRID={{grid|tojson}}
var STATUS_ICONS = {
"S":"fa ellipsis-h",
"T":"fa fa-times",
"Z":"fa fa-exclamation-triangle",
"R":"fa fa-check"
}
DATA_GRID.width = '100%'
DATA_GRID.rowClass = function(item,index){
return "small"
}
$(document).ready(function(){
$("#grid").jsGrid(DATA_GRID)
})
</script>
<body>
<div class="title-bar">
<span class="title">Application/Processes</span>
</div>
<div class="search-box border" style="padding:2px; margin-bottom:4px; display:grid; grid-template-columns: auto 64px">
<input type="text" id="search" placeholder="[Enter Application Name]" onkeyup="on_keyup(event)"/>
<div class="default" style="display:grid; grid-template-columns: 50% 50%; grid-gap:2px">
<div class="active" align="center" onclick="save_emails()"><i class="fa fa-check"></i></div>
<div class="active" align="center" onclick="clear_emails()"><i class="fa fa-times"></i></div>
</div>
</div>
<div id="grid"></div>
</body>

@ -0,0 +1,231 @@
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
<!--
This file is designed to provide a summary view of a given node
-->
<link href="{{context}}/static/css/default.css" type="text/css" rel="stylesheet">
<link href="{{context}}/static/css/fa/css/font-awesome.css" type="text/css" rel="stylesheet">
<link href="{{context}}/static/css/fa/animation.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jsgrid/jsgrid.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<script src="{{context}}/static/js/jx/rpc.js"></script>
<script src="{{context}}/static/js/jx/utils.js"></script>
<script src="{{context}}/static/js/jx/ext/math.js"></script>
<link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css">
<style>
.board {
display:grid ;
grid-template-rows: 32px auto;
grid-gap:10px;
}
.border-round {
border-radius:6px;
-webkit-border-radius:6px;
-moz-border-radius:6px;
padding:6px;
}
.widget {
width:100%;
}
.widget .title {
font-size:22px;
align-items: center;
display:grid;
grid-template-columns: auto 95px;
grid-gap:2px;
}
.widget .chart {
display:grid;
grid-template-columns: 35% 35% 30%;
grid-gap:1px;
width:100%;
}
.widget .chart iframe {width:100%; height:200px; overflow:hidden}
input[type=text] {
border:0px;
background:#f3f3f3;
padding:8px;
border-radius:6px;
margin:2px;
width:100%;
font-size:13px;
}
.search {display:grid;
grid-template-columns:auto 32px;
}
.search i {font-size:22px; margin:4px;}
/* .search div {align-items: center;} */
/* .search div span {font-size:18px; padding:4px; margin:4px;} */
.stat-board {
display:grid;
grid-template-columns: repeat(2, [col] 1fr);
grid-gap:1px;
margin:4px;
}
.stat-board .number {
margin:1px;
background-color:#f3f3f3;
padding:4px;
}
.stat-board .number .value {
font-size:22px;
font-family:verdana;
text-align:right;
margin:4px;
}
.stat-board .number .unit {
text-align:right;
padding:4px;
border-top:1px solid #FFFFFF;
}
.fa-check {color:green}
.fa-ellipsis-h {color:orange}
.fa-times {color:maroon}
.fa-folder-open {color:#FF7F24;}
.fa-cog {color:#d3d3d3}
</style>
<script>
// var nodes = jx.utils.keys(LOGS.apps)
// nodes = jx.math.sets.union(nodes,jx.utils.keys(LOGS.folders))
// nodes = jx.math.sets.unique(nodes)
// console.log(nodes)
// var config = {width:'100%',fields:[{'name':'name'},{'name':'mem'},{'name':'cpu'},{'name':'args'}]}
// config.rowClass = 'small'
// $(document).ready(function(){
// config.data = apps
// $('#grid').jsGrid(config)
// })
var render_grid = function(name){
var logs = {{app_logs|tojson}}
var config = {{ app_grid|tojson}}
config.data = logs[name]
config.fields[config.fields.length-1].itemTemplate = function(value,item){
var p = {"X":"fa fa-times","S":"fa fa-ellipsis-h","S+":"fa fa-ellipsis-h","R":"fa fa-check"}
var i = jx.dom.get.instance('I')
i.className = p[value]
return i
}
var id = '#app_grid_'+name
$(id).jsGrid(config)
}
/**
* @param id identifier to be hidden
* @param aid idetifiers to be shown
*/
var show_hide = function(xo,xi){
jx.utils.patterns.visitor(xi,jx.dom.hide)
jx.utils.patterns.visitor(xo,jx.dom.show)
}
</script>
<div class="board">
<div class="search">
<input type="text" placeholder="[Node/Data Collector]">
<div align="left" style="align-items: center">
<span class="active"><i class="fa fa-times"></i></span>
</div>
</div>
<div class=" border-round">
{% for name in nodes %}
<div class="widget">
<div class="title border-bottom" align='center'>
<h3>{{name}}</h3>
<div>
<span class="active"><i class="fa fa-file-pdf-o"></i></span>
<span id="{{name}}_o" class="active" onclick="show_hide(['{{name}}_data','{{name}}_i'],['{{name}}','{{name}}_o'])" ><i class="fa fa-th" style="color:darkgray"></i></span>
<span id="{{name}}_i" class="active" onclick="show_hide(['{{name}}','{{name}}_o'],['{{name}}_i','{{name}}_data'])" style="display:none"><i class="fa fa-pie-chart" style=" color:orangered"></i></span>
</div>
</div>
<div id="{{name}}" class="chart">
<div class="no-border border-right">
<div align="center">
<h3>Crash Analysis</h3>
</div>
<iframe class="no-border" src="{{context}}/1/plot/html/pie/apps.status?index={{loop.index -1}}&no=title" style=""></iframe>
<div align="center" class="bold">
Status Counts
</div>
<div class="stat-board">
<div class="number border">
<div class="value">{{ app_summary[loop.index-1].crash}}</div>
<div class="small unit"> Crash</div>
</div>
<div class="number border">
<div class="value">{{ app_summary[loop.index-1].crash+app_summary[loop.index-1].idle+app_summary[loop.index-1].running}}</div>
<div class="small unit"> Total</div>
</div>
</div>
</div>
<div>
<div align="center">
<h3>Resource Usage</h3>
</div>
<iframe class="no-border" src="{{context}}/1/plot/html/bar/apps.resource?index={{loop.index -1}}" style=""></iframe>
</div>
<div class="border-left">
<div align="center">
<h3>Folder Analysis</h3>
</div>
{%for row in folders_summary %}
{%if row.node == name %}
<div class="stat-board ">
<div class="number border">
<div class="value">{{ row.folders}}</div>
<div class="unit small">Folders</div>
</div>
<div class="number border">
<div class="value">{{ row.age}}</div>
<div class="unit small">Days</div>
</div>
<div class="number border">
<div class="value">{{ row.size}}</div>
<div class="unit small">MB Used</div>
</div>
<div class="number border">
<div class="value">{{ row.max_size}}</div>
<div class="small unit">MB Threshold</div>
</div>
</div>
{% endif %}
{% endfor %}
<div>
</div>
</div>
</div>
<div id="{{name}}_data"class="grid no-border" style="display:none;">
<div id="app_grid_{{name}}" style="margin-top:10px;"></div>
<script>
render_grid('{{name}}')
</script>
</div>
</div>
{% endfor %}
</div>
</div>

@ -0,0 +1,334 @@
<!--
generating client keys
-->
<script src="{{context}}/static/js/uuid5.js"></script>
<script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<link href="{{context}}/static/css/fa/css/font-awesome.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/css/fa/font-awesome-animation.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jsgrid/jsgrid.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<script src="{{context}}/static/js/jx/rpc.js"></script>
<script src="{{context}}/static/js/jx/utils.js"></script>
<script src="{{context}}/static/js/jx/ext/modal.js"></script>
<link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css">
<script>
var GRID={{grid|tojson}}
GRID.width = '90%'
GRID.editing = false
GRID.fields.push(
{"name":"edit","title":"","align":"center","width":"28px",
itemTemplate:function(value,item){
var size = $('#grid').jsGrid("option","data").length
if (value == null){
var i = jx.dom.get.instance('I')
i.className = 'fa fa-pencil active'
i.index = size -1
i.onclick = function(){
show_dialog(item)
setTimeout(function(){
jx.dom.set.value('server_name',item.node)
if(! item.apps){
jx.dom.set.value('apps','')
}else{
jx.dom.set.value('apps',item.apps.join(','))
}
if (! item.folders){
jx.dom.set.value('folders','')
}else{
jx.dom.set.value('folders',item.folders.join(','))
}
jx.dom.set.attribute('server_name','disabled',true)
jx.dom.set.attribute('server_name','item',item)
jx.dom.set.attribute('server_name','index',(size-1))
if (item.archive){
if (item.archive == true){
$('#check_archive').click()
}
}
if (item.reboot){
if(item.reboot == true){
$('#check_reboot').click()
}
}
},200)
}
return i
}
}
}
);
GRID.fields.push({
"name":"delete","title":"","align":"center","width":"28px",
itemTemplate:function(value,item){
var i = jx.dom.get.instance('I')
i.className='fa fa-times active'
i.onclick = function(){
$('#grid').jsGrid('deleteItem',item)
$('#grid').jsGrid('option','refresh')
var data = $('#grid').jsGrid('option','data')
post(data)
}
return i
}
});
GRID.fields.push({
"name":"download","title":"","align":"center","width":"28px",
itemTemplate:function(value,item){
if (item.key != null){
var i = jx.dom.get.instance('I')
i.className = 'fa fa-download active'
i.title = 'Download Configuration for '+item.node
i.data_value = item
i.onclick = function(){
$('#grid').jsGrid('cancelEdit')
$('#grid').jsGrid('option','refresh')
var item = this.data_value
downloadConfig(item)
}
return i
//return '<i class="fa fa-download active" title="Download Configuration" onclick="getConfig()"></i>'
}
}
})
FEATURES = {{features|tojson}}
var add = function(){
var data = $('#grid').jsGrid('option','data')
if (data.length < FEATURES.clients){
show_dialog({node:'server_name'})
setTimeout(function(){
jx.dom.set.attribute('server_name','item',{})
jx.dom.set.attribute('server_name','index',-1)
jx.dom.hide('download_config')
jx.dom.set.focus('server_name')
},200)
}
}
var save = function(){
var _item = jx.dom.get.attribute('server_name','item')
var index = jx.dom.get.attribute('server_name','index')
archive = jx.dom.get.attribute('archive','checked')
reboot = jx.dom.get.attribute('reboot','checked')
var apps = jx.utils.patterns.visitor(jx.dom.get.value('apps').split(','),function(name){
if(name.trim()!="")
return name.trim()
})
var folders = jx.utils.patterns.visitor(jx.dom.get.value('folders').split(','),function(name){
if(name.trim() != "")
return name.trim()
})
var item = {node:jx.dom.get.value('server_name'),key:_item.key,apps:apps,folders:folders}
item.archive = archive
item.reboot = reboot
// var index = $('#grid').jsGrid('rowByItem',_item)
data = $('#grid').jsGrid("option","data")
data[index] = item
// $('#grid').jsGrid("option","data",data)
$('#grid').jsGrid("updateItem",_item,item)
$('#grid').jsGrid("option","refresh")
post(data)
jx.modal.close()
}
var show_dialog = function(item){
var httpclient = HttpClient.instance()
var url = "{{context|safe}}/static/client-dialog.html"
httpclient.get(url,function(x){
var html = x.responseText
jx.modal.show({html:html,id:'dialog'})
})
}
var onUpdated = function(args){
var hash = "{{hash|safe}}"
var data = $('#grid').jsGrid('option','data')
var key = uuidv5(args.item.node,"{{hash|safe}}")
if (args.itemIndex < FEATURES.clients ){
// console.log(args.itemIndex)
args.item.key = key
if (args.itemIndex < 0){
//
// let's make sure the key doesn't exist
var found = false
jx.utils.patterns.visitor(data,function(row){
if (row.key == args.item.key){
found = true
}
})
if (found == false){
data.push(args.item)
}
}else{
data[args.index] = args.item
}
//
// post the data
post(data)
}
}
var post = function(data){
// $('#grid').jsGrid("option","refresh")
$('#grid').jsGrid('option','data',data)
$('#grid').jsGrid("option","refresh")
var url = "{{context}}/1/clients"
http = HttpClient.instance()
http.setData(JSON.stringify(data))
http.setHeader("Content-Type","application/json")
http.post(url,function(x){
})
}
var downloadConfig = function(item){
item = (item == null)? {id:jx.dom.get.value('server_name'),key:jx.dom.get.attribute('server_name','key')}: item
var p = {id:item.node,key:item.key}
var a = jx.dom.get.instance('a')
a.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(p) ))
a.setAttribute('download','config.json');
a.style.display = 'none'
document.body.appendChild(a)
a.click()
document.body.removeChild(a);
}
GRID.onItemUpdated = onUpdated
$(document).ready(function(){
$("#grid").jsGrid(GRID)
})
</script>
<style>
.caption {font-size:20px; margin:4px; font-family:arial; font-weight:lighter;}
.data-frame {
display:grid;
margin:4px;
padding:4px;
grid-template-rows: 80% 20%;
grid-gap:2px;
height:85%;
overflow:hidden;
}
.data-frame .comment {
grid-row: 2;
padding:4px;
font-size:12px;
color:gray;
font-weight: lighter;
font-family: verdana;
}
.fa-pencil {color:#000000;}
.data-frame .grid {
grid-row:1;
width:100%;
padding:4px;
}
.footer {
padding:4px;
margin:4px;
font-size:11px;
border-top:1px solid #CAD5E0;
display:none;
}
.border-round { padding:6px; border-radius:8px;}
.input-dialog {width:500px}
.input-dialog .cols {
display:grid;
grid-template-columns: 105px auto;
}
.input-dialog .cols .menu {
cursor:pointer;
}
.input-dialog input[type=text]{
padding:6px;
width:99%;
font-size:14px;
border:1px solid #cad5e0;
margin:4px;
}
.input-dialog input[type=text]:focus {
outline:none
}
.input-dialog .option i { font-size:22px; cursor:pointer}
.input-dialog .option label {
display:grid;
grid-template-columns: 28px auto;
grid-gap:1px;
}
.input-dialog .option label div { display:flex; align-items: center;
font-size: 12px;
padding:4px;
color:gray;
}
.input-dialog .fa-folder-open {color:orange}
.input-dialog .fa-cog {color:gray}
.input-dialog input[type=checkbox] +label .fa-check {display:none}
.input-dialog input[type=checkbox]:checked +label .fa-check {display:block;}
.input-dialog input[type=checkbox]:checked +label .fa-times {display:none;}
.button {
display:grid;
grid-template-columns: 22px auto;
}
.button i {font-size:20px;}
</style>
<div class="title-bar border-bottom">
<span class="title">Register Clients</span>
<div class="right">
<span class="active"><i class="fa fa-refresh title"></i></span>
<span class="active"><i class="fa fa-plus title" onclick="add()"></i></span>
</div>
</div>
<div class="data-frame">
<div class="comment border-top">
<div style="width:100%">
Add clients and download the configuration template that will be used on the data-collector
<ul>
<li type="square">This account is entitled to <b>{{features.clients|tojson}}</b> data-collect(s)</li>
<li type="square">Keys are unique to data-collectors installed on systems (servers)</li>
</ul>
</div>
</div>
<div id="grid" class="grid " ></div>
</div>
<div class="footer small" align="center">{{uid}}</div>

@ -0,0 +1,9 @@
<!-- <link rel="shortcut icon" href="{{context}}/static/img/logo-small.png" type="image/x-icon"> -->
<link rel="icon" href="{{context}}/static/img/logo-small.png" type="image/x-icon">
<link href="{{context}}/static/chartist/chartist.css" type="text/css" rel="stylesheet">
<script src="{{context}}/static/js/chart.js/chart.bundle.js"></script>
<script src="{{context}}/static/js/jquery/jquery.min.js"></script>
{% block content %} {% endblock%}

@ -0,0 +1,53 @@
{% extends 'dashboard/graphs/base.html' %}
{% block content %}
<script>
$(document).ready(function(){
// Chart.plugins.register({
// afterDatasetsDraw: function(chart) {
// var ctx = chart.chart.ctx;
// chart.data.datasets.forEach(function(dataset, i) {
// var meta = chart.getDatasetMeta(i);
// if (!meta.hidden) {
// meta.data.forEach(function(element, index) {
// // Draw the text in black, with the specified font
// ctx.fillStyle = 'rgb(0, 0, 0)';
// var fontSize = 16;
// var fontStyle = 'normal';
// var fontFamily = 'Helvetica Neue';
// ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
// // Just naively convert to string for now
// var dataString = dataset.datalabels[index].toString();
// // Make sure alignment settings are correct
// ctx.textAlign = 'center';
// ctx.textBaseline = 'middle';
// ctx.fillStyle = 'white'
// var padding = 5;
// var position = element.tooltipPosition();
// ctx.fillText(dataString, position.x -40 , position.y - (fontSize / 2) - padding);
// });
// }
// });
// }
// });
var context = document.getElementById('{{chart_id|safe}}').getContext('2d')
var args = {{config|tojson}}
// args.options.plugins.datalabels = {backgroundColor:'rgba(242,242,242,0.7)',
// display:function(context){
// var labels = context.dataset.datalabels
// var index = context.dataIndex
// return labels[index]
// }
// }
new Chart(context, args)
})
</script>
<div>
<canvas id="{{chart_id|safe}}"></canvas>
</div>
{% endblock %}

@ -0,0 +1,133 @@
<!--
Managing user emails that will be notified or receive emails from the user
-->
<script src="{{context}}/static/js/jquery/jquery.min.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<link href="{{context}}/static/css/fa/css/font-awesome.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/css/fa/font-awesome-animation.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid.css" rel="stylesheet" type="text/css">
<link href="{{context}}/static/js/jsgrid/jsgrid-theme.css" rel="stylesheet" type="text/css">
<script src="{{context}}/static/js/jsgrid/jsgrid.js"></script>
<script src="{{context}}/static/js/jx/dom.js"></script>
<script src="{{context}}/static/js/jx/utils.js"></script>
<script src="{{context}}/static/js/jx/rpc.js"></script>
<link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css">
<script>
var on_keyup=function(event){
event.preventDefault();
if (event.keyCode == 13){
save_emails()
}
}
var save_emails = function(){
uid = jx.dom.get.value('search')
if(uid.match(/^.+@.+..[a-z]{2,3}$/i)){
// var data = jx.dom.get.attribute('data','data')
var emails = $('#emails').jsGrid('option','data')
emails.push({"email":uid})
$('#emails').jsGrid("option","data",emails)
$("#emails").jsGrid("refresh")
clear_emails()
post_changes(emails)
}else{
}
}
var post_changes = function(emails){
var http = HttpClient.instance()
var url = "{{context|safe}}/1/emails"
var data = jx.utils.patterns.visitor(emails,function(item){ return item.email})
http.setData(JSON.stringify(data))
http.setHeader("Content-Type","application/json")
http.post(url,function(x){
console.log([x.responseText,x.status])
})
}
var clear_emails = function(){
jx.dom.set.value('search','')
jx.dom.set.focus('search')
}
var render_emails = function(emails){
var options = {width:'100%',editing:false,paging:true,
pageSize:4,
pageIndex:1,
pagerContainer: $('#email_pager'),
pagerFormat: "{prev} Page {pageIndex} of {pageCount} {next}",
pagePrevText: "<i class='fa fa-chevron-left left'> </i>",
pageNextText: "<i class='fa fa-chevron-right right' > </i>"
}
options.fields = [{name:'email',css:'small',cssheader:'small bold',title:'Email',width:'90%'},
{type:'action',align:'center',cssheader:'small bold',width:'10%',
itemTemplate:function(value,item){
// if(value != null){
var uid = jx.dom.get.value('uid').trim()
if(uid != item.email.trim()){
var i = jx.dom.get.instance('I')
i.className = 'fa fa-times'
i.onclick = function(){
var emails = $('#emails').jsGrid('option','data')
var mails = jx.utils.patterns.visitor(emails,function(node){
if(node.email.trim() != item.email.trim()){
return node
}
})
emails = mails
// console.log(jx.utils.patterns.visitor(emails,function(item){return item.email}))
$('#emails').jsGrid('option','data',emails)
$('emails').jsGrid('refresh')
post_changes(emails)
clear_emails()
// jx.dom.set.attribute('data','data',data)
// render_emails(mails)
//
// We can now save the data
// monitor.data.submit.emails()
}
return i
}
}
// }
}]
options.data = emails
jx.dom.set.value('emails','')
$("#emails").jsGrid(options)
}
$(document).ready(function(){
var EMAILS = {{emails|tojson}}
render_emails(EMAILS)
}) ;
</script>
<div id="uid" style="display:none">{{uid|safe}}</div>
<div class="search-box border" style="padding:2px; margin-bottom:4px; display:grid; grid-template-columns: auto 64px">
<input type="text" id="search" placeholder="[Enter Email Address]" onkeyup="on_keyup(event)"/>
<div class="default" style="display:grid; grid-template-columns: 50% 50%; grid-gap:2px">
<div class="active" align="center" onclick="save_emails()"><i class="fa fa-check"></i></div>
<div class="active" align="center" onclick="clear_emails()"><i class="fa fa-times"></i></div>
</div>
</div>
<div>
<div id="emails"></div>
<div id="email_pager"></div>
</div>

File diff suppressed because one or more lines are too long

@ -0,0 +1,5 @@
"""
Machine learning and analytics module. This module will have :
- Basic descriptive statistics
- Machine learning models
"""

@ -0,0 +1,181 @@
"""
Smart Top Analytics
Steve L. Nyemba<steve@the-phi.com>
"""
import pandas as pd
import numpy as np
from utils.transport import DataSourceFactory as Factory
import json
class analytics :
"""
This class generates basic statistics and some generic navigation of analytics
"""
def __init__(self,**args) :
factory = Factory()
self.handler = factory.instance(**args['config'])
self.cache = {}
self.cache['key'] = args['key']
self.cache['name'] = self.__class__.__name__
self.init()
def init(self):
"""
Store logs as is per node i.e this enables to see the latest pull
The function will summarize
"""
key = self.cache['key']
r = self.handler.view('clients/latest_logs',key=key)
id = self.get('name')
if id in r :
nodes = r[id].keys()
self.set('nodes',nodes)
logs = {}
for name in nodes :
logs[name] = r[id][name]['log']
self.set('logs',logs)
self.set('summary',self.summary(r[id]))
self.format(self.summary(r[id]))
def summary(self,logs) :
raise Exception("needs to be implemented")
def get(self,key):
"""
This function will return a particular attribute from the cache
"""
id = self.cache['name']+'.'+key
if key not in self.cache :
return self.cache[id] if id in self.cache else None
else:
return self.cache[key] if key in self.cache else None
def set(self,key,value):
id = self.cache['name']+'.'+key
self.cache[id] = value
def from_json(self,stream) :
self.cache = json.loads(stream)
def to_json(self) :
return self.cache
class apps(analytics) :
"""
This class will handle analytics associated with applications
- latest logs
- nodes
- other summaries
"""
def __init__(self,**args):
analytics.__init__(self,**args)
logs = self.get('logs')
def init(self):
"""
This function will handle initialization of critical variables
In addition to basic known variables!
"""
analytics.init(self)
grid = {"width":"100%","editing":False,"rowClass":"small"}
grid['fields'] = [{"name":"name","title":"Process","headercss":"small"},{"name":"cpu","title":"CPU Usage","headercss":"small","type":"number"},{"name":"mem","title":"RAM Usage","headercss":"small","type":"number"},{"name":"status","title":"Status","headercss":"small","align":"center"}]
self.set('grid',grid)
def summary(self,logs):
"""
The
Status count and load assessment
"""
# logs = pd.DataFrame(self.get('logs'))
r = []
for id in logs :
row = pd.DataFrame(logs[id]['log'])
crash = np.sum(row.status == 'X')
idle = np.sum(row.status == 'S') + np.sum(row.status == 'S+')
cpu = row.cpu.sum()
mem = row.mem.sum() #{"sum":row.mem.sum(),"mean":row.mem.mean(),"sd":np.sqrt(row.mem.var())}
running = row.shape[0] - crash - idle
r.append({"node":id,"date":logs[id]['date']['long'],"running":running,"idle":idle,"crash":crash,"mem":mem,"cpu":cpu})
return r
# self.set("summary",)
def format(self,slogs) :
r = []
q = []
for row in slogs :
r.append( {"x":[row['running'],row['idle'],row['crash']],"labels":['Crash','Idle','Running'],"title":row['node'],"date":row['date']})
# q.append({"x":[[row['cpu'],row['mem']]],"labels":["CPU","RAM"],"title":"Resources","date":row['date'],"title":row['node'],"series":[ 'CPU','RAM' ]})
q.append({"x":[[row['cpu'],0],[0,row['mem']]],"labels":["",""],"title":"Resources","date":row['date'],"title":row['node'],"series":[ 'CPU','RAM' ],"ylabel":"% Resource Used"})
self.set('status',r)
self.set('resource',q)
class folders(analytics):
def __init__(self,**args):
analytics.__init__(self,**args)
def format(self,slogs):
pass
def init(self):
"""
This function will initialize the folder analysis
"""
analytics.init(self)
grid = {"width":"100%","editing":False}
grid['fields'] = [{"name":"name","name":"Process"},{"name":"size","title":"Size (MB)"},{"name":"mem","title":"RAM Usage"}]
self.set('grid',grid)
def summary(self,logs):
r = []
features = self.handler.view('clients/features',key=self.get('key'))
if features :
max_size = features['features']['folder_size']
if max_size.endswith('MB') :
max_size = int(max_size.replace('MB','').strip())
else :
max_size = int(max_size.replace('GB','').strip()) * 1000
else:
max_size = 0
for id in logs :
row = pd.DataFrame(logs[id]['log'])
size = row.size_in_kb.mean() * .001
N = row.shape[0]
age = np.round(row.age_in_days.mean(),2)
files=row.files.mean()
r.append({"node":id,"size":size,"max_size":max_size,"age":age,"folders":N,"files":files})
return r
class protocol(analytics):
pass
class didact():
"""
This class is designed to handle data and the front-end like a didact (Halo reference)
In order to discharge its role the class serves as a wrapper around apps,folders, protocols
"""
def __init__(self,**args) :
"""
Initializing all the data classes (apps,folders,protocols) into a collection and will be
@param config data-store configuration
@param key customer key provided by calling code
"""
config = args['config']
key = args['key']
collection = [apps(config=config,key=key),folders(config=config,key=key)]
self.cache = {}
for item in collection :
self.cache = dict(self.cache,**item.cache)
r = []
for item in collection:
id = item.get('name')+'.nodes'
r += item.get(id)
self.cache['nodes'] = list(set(r) - set([None]))
def get(self,id):
return self.cache[id] if id in self.cache else None
config = {"args":{"uid":"cus_D2x3ItYNfWjSY3", "dbname":"smart-top","uri":"http://admin:@dm1n@dev.the-phi.com:5985"},"type":"CouchdbReader"}
p = folders(config = config,key="cus_D2x3ItYNfWjSY3")
Loading…
Cancel
Save