dashboard.js 15 KB

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