dashboard.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. var MONTHS = { 1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec' }
  2. var monitor = {}
  3. monitor.processes = {}
  4. monitor.processes.fetch = function(){
  5. var httpclient = HttpClient.instance()
  6. httpclient.get(HTTP_CONTEXT+'/get/processes',monitor.processes.init);
  7. }
  8. monitor.processes.init = function (x) {
  9. var r = JSON.parse(x.responseText)
  10. monitor.processes.summary.init(r)
  11. var keys = jx.utils.keys(r)
  12. jx.dom.set.value('menu','')
  13. jx.utils.patterns.visitor(keys,function(label){
  14. var div = jx.dom.get.instance('DIV')
  15. var frame= jx.dom.get.instance('DIV')
  16. var i = jx.dom.get.instance('I')
  17. i.className = 'fa fa-chevron-right left'
  18. div.innerHTML = label
  19. frame.data = r[label]
  20. frame.label = label
  21. frame.appendChild(i)
  22. frame.appendChild(div)
  23. frame.className = 'menu-item'
  24. frame.onclick = function () {
  25. monitor.processes.render(this.label, this.data);
  26. jx.dom.set.value('trends_chart','')
  27. //monitor.processes.trend.init(this.label)
  28. }
  29. jx.dom.append('menu',frame)
  30. })
  31. //
  32. // Auto start the first item in the menu
  33. // This is designed not to let the user wander or wonder what is going on
  34. //
  35. var nodes = jx.dom.get.children('menu')
  36. if (nodes.length > 0) {
  37. nodes[0].click()
  38. } else {
  39. //
  40. // We should hide the panes for this
  41. //
  42. jx.dom.hide('apps')
  43. }
  44. monitor.sandbox.init()
  45. // setTimeout(monitor.sandbox.init,1000)
  46. }
  47. /**
  48. * This function renders the grid of processes being monitored,
  49. * @param label label the list of processes belongs to
  50. * @param data dataset of a selected set of processes (works a bit like top)
  51. */
  52. monitor.processes.render = function(label,data) {
  53. data = jx.utils.patterns.visitor(data,function(row){
  54. var status = {"idle":'<i class="fa fa-ellipsis-h" title="IDLE"></i>',"running":'<i class="fa fa-check" title="RUNNING"></i>',"crash":'<i class="fa fa-times" title="CRASHED"></i>'}
  55. if (!row.status.match(/class/)) {
  56. row.status_id = row.status
  57. row.status = status[row.status]
  58. }
  59. return row
  60. })
  61. jx.dom.set.value('latest_processes','') ;
  62. jx.dom.set.value('latest_processes_label',label)
  63. var options = {
  64. width: $('#latest_processes').width(), height:'auto'
  65. }
  66. options.paging = true
  67. options.pageSize = 4
  68. options.pageIndex = 1
  69. options.pageButtonCount = 4
  70. options.pagerContainer = '#latest_process_pager'
  71. options.pagerFormat= "{prev} Page {pageIndex} of {pageCount} {next}"
  72. options.pagePrevText= '<i class="fa fa-chevron-left"></i>'
  73. options.pageNextText= "<i class='fa fa-chevron-right small' title='Next'> </i>"
  74. options.data = data
  75. options.rowClass = function (item, index,evt) {
  76. return 'small'
  77. }
  78. options.rowClick = function(args){
  79. var item = args.item
  80. var id = jx.dom.get.value('latest_processes_label')
  81. var app = item.label
  82. monitor.processes.trend.init(id, app)
  83. if (item.anomaly == true) {
  84. jx.dom.show('has_anomaly')
  85. } else {
  86. jx.dom.hide('has_anomaly')
  87. }
  88. }
  89. options.autoload = true
  90. options.fields = [
  91. { name: 'label', type: 'text', title: "Process", headercss: "small bold", css: "small"},
  92. { name: "cpu_usage", type: "number", title: "CPU", headercss: "small bold" , width:'64px'},
  93. { name: "memory_usage", type: "text", title: "Mem. Used", type: "number", headercss: "small bold" },
  94. { name: "proc_count", type: "number", title: "Proc Count", headercss: "small bold" },
  95. {name:"status",type:"text",title:"Status",headercss:"small bold",align:"center", width:'64px'}
  96. ]
  97. var grid = $('#latest_processes').jsGrid(options) ;
  98. //
  99. // We need to auto click the first row
  100. $('#latest_processes').find('.jsgrid-row')[0].click()
  101. }
  102. monitor.processes.trend = {}
  103. monitor.processes.trend.init = function (label,app) {
  104. var httpclient = HttpClient.instance()
  105. var uri = HTTP_CONTEXT+'/trends?id='+label+'&app='+encodeURIComponent(app)
  106. httpclient.get(uri, function (x) {
  107. var logs = JSON.parse(x.responseText)
  108. var dom = jx.dom.get.instance('trend_info');
  109. dom.logs = logs
  110. jx.dom.set.value('trend_info',app.trim())
  111. // jx.dom.set.attribute(label,'logs',logs)
  112. monitor.processes.trend.render(logs,null,app)
  113. })
  114. }
  115. monitor.processes.trend.render = function (logs, key,label) {
  116. // if (key == null) {
  117. // key = 'memory_usage'
  118. // }
  119. // if (logs == null || label == null){
  120. // logs = jx.dom.get.instance('trend_info').logs
  121. // label= jx.dom.get.value('trend_info') ;
  122. // }
  123. var frame = $('#trends_chart')
  124. jx.dom.set.value('trends_chart','')
  125. var context = jx.dom.get.instance('CANVAS')
  126. context.width = $(frame).width()
  127. context.height= $(frame).height()
  128. var conf = { type: 'line',responsive:true }
  129. conf.data = {}
  130. conf.options = { legend: { position: 'bottom' } }
  131. conf.options.scales = {}
  132. conf.options.scales.yAxes = [
  133. {id:'0',scaleLabel:{display:true,labelString:' % CPU & MEMORY USAGE'},ticks:{min:0,max:100,beginAtZero:true},gridLines: {display:false}}
  134. // {id:'1',position:'right',scaleLabel:{display:true,labelString:'PROCESS COUNT'},ticks:{min:0,stepSize:1,beginAtZero:true},gridLines: {display:false}}
  135. ]
  136. conf.options.scales.xAxes = [
  137. {
  138. gridLines: {display:false},
  139. time: {
  140. format:'DD-MMM HH:mm'
  141. }
  142. }
  143. ]
  144. conf.data.datasets = [ ]
  145. var x_axis = []
  146. var _x = {}
  147. // var _y = {}
  148. var cpu = {label: 'CPU Usage (%)', data: [] ,backgroundColor:'transparent',borderColor:COLORS[187],fill:false,borderWidth:1}
  149. var mem = {label : 'Memory Usage(%)',data:[],backgroundColor:'transparent',borderColor:COLORS[32],fill:false,borderWidth:1}
  150. // var proc= {yAxisID:'1',label : 'Proc Count',data:[],backgroundColor:'transparent',borderColor:COLORS[542],fill:false,borderWidth:1}
  151. var months={1:"Jan",2:"Feb",3:"Mar",4:"Apr",5:"May",6:"Jun",7:"Jul",8:"Aug",9:"Sep",10:"Oct",11:"Nov",12:"Dec"}
  152. jx.utils.patterns.visitor(logs,function(item){
  153. //x = new Date(item.year,item.month-1,item.day,item.hour,item.minute)
  154. day = item.day.length > 1? (['0',item.day]).join(''): item.day
  155. month = months[item.month]
  156. x = ([month,day,item.hour+':'+item.minute]).join(' ')
  157. y = item[key]
  158. if (_x[x] == null ){//||(_x[x] == null && _y[y] == null)) {
  159. _x[x] = 1
  160. // _y[y] = 1
  161. x_axis.push(x)
  162. cpu.data.push({ x: x, y: item.cpu_usage })
  163. mem.data.push({x:x,y:item.memory_usage})
  164. // proc.data.push({x:x,y:item.proc_count})
  165. // return {x:x,y:y}
  166. }
  167. })
  168. conf.data.datasets = [cpu,mem]
  169. x_axis = jx.utils.unique(x_axis)
  170. conf.data.labels = x_axis
  171. // console.log(conf)
  172. jx.dom.append('trends_chart',context)
  173. var chart = new Chart(context,conf)
  174. }
  175. monitor.processes.summary = {}
  176. monitor.processes.summary.init = function(logs){
  177. var xr = 0, xc = 0, xi = 0
  178. var series = {}
  179. //var colors = [COLORS[11], COLORS[1], COLORS[2]]
  180. colors = [COLORS[11], COLORS[2], COLORS[100]]
  181. RUNNING_COLOR = COLORS[26]
  182. IDLE_COLOR = COLORS[100]
  183. CRASH_COLOR=COLORS[2]
  184. var i = 0;
  185. var date = null;
  186. for( label in logs ){
  187. var rows = logs[label]
  188. series[label] = {data:[0,0,0],label:label}
  189. jx.utils.patterns.visitor(rows,function(item){
  190. if (date == null) {
  191. // date = new Date(item.year,item.month-1,item.day,item.hour,item.minute)
  192. date = ([item.day+'-'+MONTHS[item.month]+'-'+item.year,item.hour+':'+item.minute]).join(' ')
  193. }
  194. if (item.status == 'running'){
  195. xr += 1
  196. }else if(item.status == 'idle'){
  197. xi += 1
  198. }else{
  199. xc += 1
  200. }
  201. })
  202. }
  203. var data = {labels:['Running','Crash','Idle'],datasets:[{data:[xr,xc,xi],backgroundColor:[RUNNING_COLOR,CRASH_COLOR,IDLE_COLOR/**COLORS[11],COLORS[2],COLORS[100]*/]}]}
  204. var context = jx.dom.get.instance('CANVAS')
  205. context.id = 'doughnut'
  206. jx.dom.set.value('total-running', xr)
  207. jx.dom.set.value('total-crash', xc)
  208. jx.dom.set.value('total-idle', xi)
  209. // jx.dom.set.value('total-apps', xr + xi + xc)
  210. jx.dom.set.value('app-summary-date', date)
  211. jx.dom.set.value('summary_chart','')
  212. jx.dom.append('summary_chart', context)
  213. $("#doughnut").attr('width', 50)
  214. $("#doughnut").attr('height', 50)
  215. var conf = {}//width:100,height:100}//width:'auto',height:$('#process_summary').height}
  216. conf.type = 'doughnut'
  217. conf.responsive = true
  218. conf.data = data
  219. conf.options = { legend: { position: 'right' }, repsonsive: true }
  220. var _chart = new Chart(context,conf)
  221. $('#summary_chart').click(function (evt) {
  222. console.log(_chart)
  223. console.log($(_chart))
  224. var activePoints = $(_chart).getSegmentsAtEvent(evt);
  225. console.log(activePoints)
  226. })
  227. jx.dom.set.value('summary_ranking','')
  228. context = jx.dom.get.instance('CANVAS')
  229. jx.dom.append('summary_ranking',context)
  230. conf = { type: 'bar', responsive: true }
  231. conf.options={scales:{xAxes:[{gridLines: {display:false}}],yAxes:[{gridLines: {display:false},scaleLabel:{display:true,labelString:'PROCESS COUNTS'} }] }}
  232. conf.options.legend ={position:'right'}
  233. /*
  234. conf.data = {labels:['Running','Idle','Crash']}
  235. var labels = jx.utils.keys(series)
  236. var i = 0
  237. conf.data.datasets = jx.utils.patterns.visitor(labels,function(id){
  238. series[id].backgroundColor = COLORS[i++]
  239. return series[id]})
  240. chart = new Chart(context,conf);
  241. */
  242. var labels = jx.utils.keys(logs)
  243. conf.data = { labels: labels, backgroundColor:colors }
  244. var xr = [], xi = [], xc = [],xr_bg = [],xc_bg = [],xi_bg = []
  245. jx.utils.patterns.visitor(labels, function (id) {
  246. var rows = logs[id]
  247. var index = xr.length
  248. xr_bg[index] = RUNNING_COLOR
  249. xi_bg[index] = IDLE_COLOR
  250. xc_bg[index] = CRASH_COLOR
  251. if (xr[index] == null) {
  252. xr[index] = 0
  253. xc[index] = 0
  254. xi[index] = 0
  255. }
  256. jx.utils.patterns.visitor(logs[id], function (row) {
  257. if (row.status.match(/running/i)) {
  258. xr[index] += 1
  259. } else if (row.status.match(/idle/i)) {
  260. xi[index] += 1
  261. } else {
  262. xc[index] += 1
  263. }
  264. })
  265. })
  266. conf.data.datasets = [{ label: 'Running', data:xr,backgroundColor:xr_bg},{label:'Crash',data:xc,backgroundColor:xc_bg},{label:'Idle',data:xi,backgroundColor:xi_bg} ]
  267. chart = new Chart(context, conf)
  268. }
  269. monitor.sandbox = {}
  270. monitor.sandbox.init = function () {
  271. jx.dom.hide('inspect_sandbox')
  272. var httpclient = HttpClient.instance()
  273. httpclient.get(HTTP_CONTEXT+'/sandbox', function (x) {
  274. var r = JSON.parse(x.responseText)
  275. if (r.length > 0) {
  276. jx.dom.show('sandbox')
  277. monitor.sandbox.render(r);
  278. } else {
  279. jx.dom.hide('sandbox')
  280. }
  281. })
  282. }
  283. monitor.sandbox.render = function (logs) {
  284. months = { 1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun', 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov', 12: 'Dec' }
  285. var d = ([logs[0].day, '-', MONTHS[logs[0].month], '-', logs[0].year, ' ', logs[0].hour, ':', logs[0].minute]).join('')
  286. jx.dom.set.value('sandbox_date', d)
  287. var options = { width: $('#sandbox_status').width()-8, height: 'auto' }
  288. options.data = jx.utils.patterns.visitor(logs, function (item) {
  289. if (item.value == 100) {
  290. item.status = '<i class="fa fa-check" style="color:green"></i>'
  291. } else {
  292. item.status = '<i class="fa fa-download" style="color:black"></i>'
  293. }
  294. return item
  295. })
  296. options.paging = true
  297. options.pageSize = 4
  298. options.pageIndex = 1
  299. options.pageButtonCount = 4
  300. options.pagerContainer = '#folders_pager'
  301. options.pagerFormat = "{prev} Page {pageIndex} of {pageCount} {next}"
  302. options.pagePrevText = '<i class="fa fa-chevron-left"></i>'
  303. options.pageNextText = "<i class='fa fa-chevron-right small' title='Next'> </i>";
  304. options.rowClass = function (item) {
  305. if (item.value < 70) {
  306. return 'bad'
  307. } else if (item.value < 100) {
  308. return 'warning'
  309. } else {
  310. return 'good'
  311. }
  312. }
  313. options.rowClick = function (args) {
  314. var item = args.item;
  315. if (item.missing.length > 0) {
  316. var form = jx.dom.get.instance('FORM')
  317. var dom = jx.dom.get.instance('INPUT')
  318. dom.type = 'hidden'
  319. dom.name = 'missing'
  320. dom.value = JSON.stringify(item.missing)
  321. form.action = HTTP_CONTEXT+'/download'
  322. form.method = 'POST'
  323. form.appendChild(dom)
  324. form.submit()
  325. }
  326. }
  327. options.fields = [
  328. {name:"status",title:"",width:20},
  329. { name: 'label',title:'Virtual Environment Label',type:'text',css:'small',headercss:'small bold' },
  330. { name: 'value', title:'Completeness %',type: 'number', css: 'small', headercss: 'small bold' }
  331. ]
  332. var grid = $('#sandbox_status').jsGrid(options)
  333. jx.dom.show('inspect_sandbox')
  334. }
  335. monitor.folders = {}
  336. monitor.folders.init = function () {
  337. var httpclient = HttpClient.instance()
  338. httpclient.get(HTTP_CONTEXT+'/folders', function (x) {
  339. var r = JSON.parse(x.responseText)
  340. var data = []
  341. for (var id in r) {
  342. var item = r[id]
  343. // item.id = id
  344. data = data.concat(item)
  345. }
  346. monitor.folders.render.init(data)
  347. })
  348. }
  349. monitor.folders.search = {}
  350. monitor.folders.search.reset = function () {
  351. jx.dom.set.value('folder_search', '')
  352. var data = jx.dom.get.attribute('folder_search', 'data')
  353. monitor.folders.render.summary(data)
  354. }
  355. monitor.folders.search.init = function(){
  356. var term = jx.dom.get.value('folder_search')
  357. var data = jx.dom.get.attribute('folder_search', 'data')
  358. term = term.replace(/ /g,'')
  359. if (term.length == 0) {
  360. monitor.folders.render.summary(data)
  361. } else if (term.length > 0) {
  362. data = jx.utils.patterns.visitor(data, function (row) {
  363. pattern = "(.*" + term + ".*)"
  364. if (row.id.match(pattern)) {
  365. return row
  366. }
  367. })
  368. monitor.folders.render.summary(data)
  369. }
  370. }
  371. monitor.folders.render = {}
  372. monitor.folders.render.init = function (data) {
  373. jx.dom.set.attribute('folder_search','data',data)
  374. monitor.folders.render.summary(data)
  375. }
  376. monitor.folders.show = {}
  377. monitor.folders.show.plan = function () {
  378. $('#folder_summary').slideUp(function () {
  379. $('#folder_plan').slideDown()
  380. })
  381. }
  382. monitor.folders.show.grid = function () {
  383. $('#folder_plan').slideUp(function () {
  384. $('#folder_summary').slideDown()
  385. })
  386. }
  387. /***
  388. * This function is designed to establish a folder clean up strategy i.e :
  389. * - We will look for anomalies given age,file size
  390. * - We will also look for where most of the data is distributed (mode)
  391. */
  392. monitor.folders.render.details = function (folder,data) {
  393. //
  394. // We need to normalize the data at this point so as to be able to show it all in the same chart
  395. // jx.math.scale x: counts, y: measure ment
  396. //
  397. var r = [data.age, data.size]
  398. var plans = []
  399. for (var i in r) {
  400. var xy = r[i]
  401. var mode = jx.math.mode(jx.utils.vector('x', xy))
  402. var yvalues = jx.utils.patterns.visitor(xy, function (row) {
  403. if (row.x == mode) {
  404. return row.y
  405. }
  406. })
  407. var sd = jx.math.sd(yvalues)
  408. if (i == 0) {
  409. prefix = 'age'
  410. var mean = jx.math.mean(yvalues)
  411. var max = (mean + (1.5 * sd))
  412. if (mean > 30 && mean < 365) {
  413. divide_by = 30
  414. units = 'MONTHS'
  415. } else if (mean > 365) {
  416. divide_by=365
  417. units = 'YEARS'
  418. } else {
  419. divide_by = 1
  420. units = 'DAYS'
  421. }
  422. } else {
  423. prefix = 'size'
  424. var mean = jx.math.sum(yvalues)
  425. var max = 0// (mean + (1.5 * sd))
  426. if (mean > 1000) {
  427. divide_by = 1000
  428. units = 'GB'
  429. } else {
  430. divide_by = 1
  431. units = 'MB'
  432. }
  433. }
  434. if (isNaN(mean)) {
  435. mean = 0
  436. }
  437. //
  438. // We need to assess the outliars i.e too old, too large
  439. //
  440. y = jx.utils.vector('y', xy)
  441. var _mean = jx.math.mean(y)
  442. var _sd = jx.math.sd(y)
  443. var outlier = _mean < mean || max > (_mean + (1.5 * _sd))
  444. plans.push({ 'label': prefix, 'max': max, 'sd': sd, 'mean': mean, 'count': yvalues.length, 'outlier': outlier })
  445. jx.dom.set.value(prefix + '_count', yvalues.length)
  446. jx.dom.set.value(prefix + '_value', (mean/divide_by).toFixed(2))
  447. jx.dom.set.value(prefix+'_units',units)
  448. monitor.folders.show.plan()
  449. }
  450. jx.dom.set.value('folder_name', folder)
  451. }
  452. monitor.folders.render.summary = function (data) {
  453. jx.dom.set.value('gridfolders', '')
  454. var options = {
  455. width: $('#gfolderframe').width()-8, height:'auto'
  456. }
  457. options.paging = true
  458. options.pageSize = 4
  459. options.pageIndex = 1
  460. options.pageButtonCount = 4
  461. options.pagerContainer = '#folderspager'
  462. options.pagerFormat= "{prev} Page {pageIndex} of {pageCount} {next}"
  463. options.pagePrevText= '<i class="fa fa-chevron-left"></i>'
  464. options.pageNextText= "<i class='fa fa-chevron-right small' title='Next'> </i>"
  465. options.data = data
  466. options.rowClass = function (item, index,evt) {
  467. return 'small'
  468. }
  469. options.rowClick = function(args){
  470. // var item = args.item
  471. // age = jx.utils.patterns.visitor(item.details.age, function (row) {
  472. // return {y:row[0],x:row[1]}
  473. // })
  474. // size = jx.utils.patterns.visitor(item.details.size, function (row) {
  475. // return {y:row[0],x:row[1]}
  476. // })
  477. // monitor.folders.render.details(item.name,{age:age,size:size})
  478. }
  479. //
  480. // @TODO Add the units in days just in case
  481. options.autoload = true
  482. options.fields = [
  483. { name: 'id', type: 'text', title: "Host", headercss: "small bold", css: "small"},
  484. { name: 'name', type: 'text', title: "Folder Name", headercss: "small bold", css: "small"},
  485. { name: "size", type: "number", title: "Folder Size", type: "number", headercss: "small bold" },
  486. { name: "count", type: "number", title: "File Count", type: "number", headercss: "small bold" }
  487. ]
  488. var grid = $('#gridfolders').jsGrid(options) ;
  489. }
  490. monitor.menu = {}
  491. monitor.menu.event = {}
  492. monitor.menu.event.toggle = function () {
  493. var dom = jx.dom.get.instance('menuframe')
  494. var value = dom.style.marginLeft.trim()
  495. if (value==0 || value == "0px" || value == "") {
  496. var width = -$(dom).width() - 10
  497. $('#menuframe').animate({marginLeft:"-12%"})
  498. } else {
  499. $('#menuframe').animate({marginLeft:"0"})
  500. }
  501. }
  502. /**
  503. * Socket handler, check for learning status
  504. */