dash-example.html 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <meta charset="utf-8">
  6. <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  7. <meta name="viewport" content="width=device-width, initial-scale=1">
  8. <meta name="apple-mobile-web-app-capable" content="yes">
  9. <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  10. </head>
  11. <body>
  12. <div id="alarms" class="collapsed">
  13. <div class="alarm-collapse-button" onclick="dash.toggle_alarm_collapse()">&rsaquo;</div>
  14. <span class="alarm-count"></span>
  15. <h1>Alarms</h1>
  16. <div class="alarm-host-list"></div>
  17. <div class="settings-button" onclick="dash.reorder_hosts()">&#9881;&#xFE0E;</div>
  18. </div>
  19. <div id="dash">
  20. <div class="netdata-host-stats-container template">
  21. <div class="netdata-host-name">host</div>
  22. <div class="netdata-host-stats">
  23. <div class="dash-graph"
  24. data-dash-netdata="system.cpu"
  25. data-dygraph-valuerange="[0, 100]">
  26. </div>
  27. <div class="dash-graph"
  28. data-dash-netdata="system.load">
  29. </div>
  30. <div class="dash-graph"
  31. data-dash-netdata="system.ram">
  32. </div>
  33. <div class="dash-graph"
  34. data-dash-netdata="disk_space._">
  35. </div>
  36. <div class="dash-graph"
  37. data-dash-netdata="system.net">
  38. </div>
  39. <div class="dash-graph"
  40. data-dash-netdata="system.processes">
  41. </div>
  42. <div class="dash-graph"
  43. data-dash-netdata="apps.cpu">
  44. </div>
  45. <div class="dash-graph"
  46. data-dash-netdata="apps.mem">
  47. </div>
  48. <div class="dash-charts">
  49. <div class="dash-chart"
  50. data-dash-netdata="system.io"
  51. data-dimensions="in"
  52. data-title="Disk Read"
  53. data-common-units="dash.io">
  54. </div>
  55. <div class="dash-chart"
  56. data-dash-netdata="system.io"
  57. data-dimensions="out"
  58. data-title="Disk Write"
  59. data-common-units="dash.io">
  60. </div>
  61. <div class="dash-chart"
  62. data-dash-netdata="system.cpu"
  63. data-chart-library="gauge"
  64. data-title="CPU"
  65. data-units="%"
  66. data-gauge-max-value="100"
  67. data-colors="#22AA99"
  68. data-gauge-stroke-color="#373B40">
  69. </div>
  70. <div class="dash-chart"
  71. data-dash-netdata="system.net"
  72. data-dimensions="received"
  73. data-title="Net Inbound"
  74. data-common-units="dash.net">
  75. </div>
  76. <div class="dash-chart"
  77. data-dash-netdata="system.net"
  78. data-dimensions="sent"
  79. data-title="Net Outbound"
  80. data-common-units="dash.net">
  81. </div>
  82. <div class="dash-chart"
  83. data-dash-netdata="system.ram"
  84. data-dimensions="used|buffers|active|wired"
  85. data-append-options="percentage"
  86. data-title="Used RAM"
  87. data-units="%"
  88. data-easypiechart-max-value="100"
  89. data-colors="#EE9911">
  90. </div>
  91. </div>
  92. </div>
  93. </div>
  94. </div>
  95. </body>
  96. <script
  97. src="https://code.jquery.com/jquery-3.4.1.min.js"
  98. integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
  99. crossorigin="anonymous">
  100. </script>
  101. <script type="text/javascript">
  102. class PickNSort {
  103. // PickNSort.js
  104. constructor (dark) {
  105. this.items = [];
  106. this.callback = function (output, disabled) { console.log(output, disabled) };
  107. this.reset = function () { this.items = [] };
  108. this.last_output = {
  109. enabled: [],
  110. disabled: []
  111. }
  112. this.add_css_to_page(dark);
  113. }
  114. create_modal () {
  115. $("<div></div>", {
  116. id: "picknsort-container"
  117. }).appendTo('body');
  118. $("<div></div>", {
  119. id: 'picknsort-window'
  120. }).appendTo('#picknsort-container');
  121. $("<div></div>", {
  122. id: "picknsort-close-button",
  123. text: '\u2573'
  124. }).appendTo('#picknsort-window');
  125. $("<div></div>", {
  126. id: "picknsort-item-list"
  127. }).appendTo('#picknsort-window')
  128. $("<div></div>", {
  129. text: "Apply",
  130. id: "picknsort-apply-button"
  131. }).appendTo('#picknsort-window');
  132. $("<div></div>", {
  133. text: "Reset",
  134. id: "picknsort-reset-button"
  135. }).appendTo('#picknsort-window');
  136. $('#picknsort-close-button').click(function () {
  137. picknsort.destroy_modal();
  138. picknsort.callback(null, null);
  139. });
  140. $('#picknsort-apply-button').click(function () {
  141. picknsort.apply();
  142. });
  143. $('#picknsort-reset-button').click(function () {
  144. picknsort.reset();
  145. });
  146. }
  147. destroy_modal () {
  148. $('#picknsort-container').remove();
  149. }
  150. populate_list () {
  151. this.clear_list();
  152. for (var i=0, len=this.items.length; i<len; i++) {
  153. this.draw_item(i);
  154. }
  155. }
  156. draw_item (index) {
  157. var item = this.items[index];
  158. var $item = $("<div></div>").addClass("picknsort-item");
  159. var $checkbox= $("<div></div>").addClass('picknsort-item-checkbox-wrapper');
  160. $("<input>", {
  161. type: "checkbox",
  162. checked: item.enabled
  163. }).addClass('picknsort-item-checkbox')
  164. .appendTo($checkbox);
  165. var $value = $("<div></div>", {
  166. text: item.value
  167. }).addClass("picknsort-item-value");
  168. var $nav = $("<div></div>").addClass("picknsort-item-nav")
  169. .append("<div class='picknsort-item-down' data-index='" + index + "' onclick='picknsort.shift_down(this)'>&#8595;</div>")
  170. .append("<div class='picknsort-item-up' data-index='" + index + "' onclick='picknsort.shift_up(this)'>&#8593;</div>");
  171. $item.append($checkbox).append($value).append($nav).appendTo('#picknsort-item-list');
  172. }
  173. clear_list () {
  174. $('#picknsort-item-list').html('');
  175. }
  176. popup (callback, reset, options={}) {
  177. this.callback = callback || this.callback;
  178. this.reset = reset || this.reset;
  179. if (!options.enabled) {
  180. options.enabled = this.last_output.enabled;
  181. }
  182. if (!options.disabled) {
  183. options.disabled = this.last_output.disabled;
  184. }
  185. this.parse_items(options.enabled, options.disabled);
  186. if (this.items.length) {
  187. this.create_modal();
  188. this.populate_list();
  189. }
  190. }
  191. parse_items (enabled, disabled) {
  192. this.items = [];
  193. for (var i=0, len=enabled.length; i<len; i++) {
  194. this.items.push({
  195. value: enabled[i],
  196. enabled: true
  197. });
  198. }
  199. for (var i=0, len=disabled.length; i<len; i++) {
  200. this.items.push({
  201. value: disabled[i],
  202. enabled: false
  203. });
  204. }
  205. }
  206. shift_down (el) {
  207. var index = $(el).data('index');
  208. if (index === this.items.length - 1) {
  209. return;
  210. }
  211. var temp = this.items[index];
  212. this.items[index] = this.items[index + 1];
  213. this.items[index + 1] = temp;
  214. this.move_element_down(index);
  215. }
  216. shift_up (el) {
  217. var index = $(el).data('index');
  218. if (index === 0) {
  219. return;
  220. }
  221. var temp = this.items[index];
  222. this.items[index] = this.items[index - 1];
  223. this.items[index - 1] = temp;
  224. this.move_element_up(index);
  225. }
  226. move_element_up (index) {
  227. var $src = $('.picknsort-item:eq(' + index + ')');
  228. var $dest = $('.picknsort-item:eq(' + (index-1) + ')');
  229. $src.insertBefore($dest);
  230. // Update data-index
  231. $src.find('.picknsort-item-down, .picknsort-item-up').data("index", index-1);
  232. $dest.find('.picknsort-item-down, .picknsort-item-up').data("index", index);
  233. }
  234. move_element_down (index) {
  235. var $src = $('.picknsort-item:eq(' + index + ')');
  236. var $dest = $('.picknsort-item:eq(' + (index+1) + ')');
  237. $src.insertAfter($dest);
  238. // Update data-index
  239. $src.find('.picknsort-item-down, .picknsort-item-up').data("index", index+1);
  240. $dest.find('.picknsort-item-down, .picknsort-item-up').data("index", index);
  241. }
  242. apply () {
  243. var out = [];
  244. for (var i=0, len=this.items.length; i<len; i++) {
  245. out.push(this.items[i].value);
  246. }
  247. var disabled = [];
  248. $('input.picknsort-item-checkbox:not(:checked)').each(function (elindex) {
  249. var itemindex = $('input.picknsort-item-checkbox').index($(this));
  250. // Adjust for deleted elements
  251. var spliceindex = itemindex - (1 * elindex);
  252. var del = out.splice(spliceindex, 1);
  253. disabled.push(del[0]);
  254. });
  255. this.callback(out, disabled);
  256. this.last_output = {
  257. enabled: out,
  258. disabled: disabled
  259. }
  260. this.destroy_modal();
  261. }
  262. add_css_to_page (dark) {
  263. /*
  264. * TUTORIAL: Change your theme colors here. Not sure how well it will work...
  265. */
  266. var light_theme = {
  267. windowbg: "#FFF",
  268. itembg: "#F4F4F4",
  269. itemcolor: "#535353"
  270. }
  271. var dark_theme = {
  272. windowbg: "#444",
  273. itembg: "#333",
  274. itemcolor: "#DBDBDB"
  275. }
  276. var current_theme = (dark) ? dark_theme : light_theme;
  277. var css_string = `
  278. #picknsort-container {
  279. position: fixed;
  280. z-index: 9999 !important;
  281. top: 0;
  282. bottom: 0;
  283. left: 0;
  284. right: 0;
  285. background: rgba(0,0,0,0.5);
  286. }
  287. #picknsort-window {
  288. max-height: 90vh;
  289. width: 40em;
  290. background: ${current_theme.windowbg};
  291. position: absolute;
  292. top: 5em;
  293. left: calc(50% - 20em);
  294. padding: 2em;
  295. padding-top: 4em;
  296. }
  297. #picknsort-close-button {
  298. position: absolute;
  299. top: 1em;
  300. font-size: 1.5em;
  301. color: #666;
  302. right: 1em;
  303. cursor: pointer;
  304. }
  305. #picknsort-item-list {
  306. max-height: 60vh;
  307. overflow-y: scroll;
  308. margin-bottom: 1em;
  309. }
  310. #picknsort-apply-button, #picknsort-reset-button {
  311. text-align: center;
  312. font-size: 1.5em;
  313. padding: 1em;
  314. background: rgb(106, 232, 165);
  315. font-weight: bold;
  316. color: #FFF;
  317. cursor: pointer;
  318. margin-top: 1em;
  319. }
  320. #picknsort-reset-button {
  321. background: #CCC;
  322. }
  323. .picknsort-item {
  324. padding: 1em;
  325. border-top: solid 1px #CCC;
  326. margin-bottom: 1em;
  327. position: relative;
  328. background: ${current_theme.itembg};
  329. color: ${current_theme.itemcolor}
  330. }
  331. .picknsort-item-checkbox-wrapper {
  332. position: absolute;
  333. top: 0;
  334. left: 0;
  335. bottom: 0;
  336. width: 3em;
  337. text-align: center;
  338. }
  339. .picknsort-item-checkbox {
  340. transform: scale(1.5);
  341. margin-top: 1.3em !important;
  342. }
  343. .picknsort-item-value {
  344. margin-left: 3em;
  345. }
  346. .picknsort-item-nav {
  347. font-size: 2em;
  348. position: absolute;
  349. right: 0;
  350. top: 0;
  351. bottom: 0;
  352. color: #CCC;
  353. cursor: pointer;
  354. }
  355. .picknsort-item-down, .picknsort-item-up {
  356. display: inline-block;
  357. padding: 0em 0.5em;
  358. height: calc(100% - 0.2em);
  359. }
  360. ` // End multiline string
  361. $("<style>")
  362. .prop("type", "text/css")
  363. .html(css_string).appendTo("head");
  364. }
  365. }
  366. </script>
  367. <script type="text/javascript">
  368. // Dash JS
  369. function picknsort_setup () {
  370. if (localStorage.getItem('netdata_ordered_hosts')) {
  371. picknsort.last_output = JSON.parse(localStorage.getItem('netdata_ordered_hosts'));
  372. } else {
  373. picknsort.last_output = {
  374. enabled: dash.netdata_info.mirrored_hosts,
  375. disabled: []
  376. }
  377. }
  378. }
  379. class Dash {
  380. constructor (base_url, link_base_url) {
  381. this.base_url = base_url; // URL of netdata host, with port
  382. this.link_base_url = link_base_url || base_url; // Reverse proxy URL (Optional)
  383. this.current_alarms = {};
  384. this.new_alarms = {};
  385. this.first_build = true;
  386. /*
  387. * TUTORIAL: Change your graph/chart dimensions here. Host columns will automatically adjust.
  388. * Charts are square! Their width is the same as their height.
  389. */
  390. this.options = {
  391. graph_width: '40em',
  392. graph_height: '20em',
  393. chart_width: '10em' // Charts are square
  394. };
  395. this.init();
  396. }
  397. reorder_hosts () {
  398. picknsort.popup(dash.update_ordered_hosts, dash.find_new_hosts);
  399. }
  400. get_enabled_hosts () {
  401. try {
  402. return JSON.parse(localStorage.getItem('netdata_ordered_hosts')).enabled
  403. } catch (e) {
  404. return this.netdata_info.mirrored_hosts;
  405. }
  406. }
  407. get_ordered_hosts () {
  408. try {
  409. return JSON.parse(localStorage.getItem('netdata_ordered_hosts'));
  410. } catch (e) {
  411. return null;
  412. }
  413. }
  414. set_ordered_hosts (hosts) {
  415. localStorage.setItem('netdata_ordered_hosts', JSON.stringify(hosts));
  416. location.reload();
  417. }
  418. update_ordered_hosts (enabled, disabled) {
  419. if (enabled === null && disabled === null) {
  420. return;
  421. }
  422. dash.set_ordered_hosts({
  423. enabled: enabled,
  424. disabled: disabled
  425. });
  426. }
  427. find_new_hosts () {
  428. var newhosts = dash.netdata_info.mirrored_hosts.slice();
  429. var currenthosts = dash.get_ordered_hosts();
  430. for (var i=0,len=currenthosts.enabled.length; i<len; i++) {
  431. var found = newhosts.indexOf(currenthosts.enabled[i]);
  432. if (found > -1) {
  433. newhosts.splice(found, 1);
  434. } else {
  435. currenthosts.enabled.splice(i, 1);
  436. }
  437. }
  438. for (var i=0,len=currenthosts.disabled.length; i<len; i++) {
  439. var found = newhosts.indexOf(currenthosts.disabled[i]);
  440. if (found > -1) {
  441. newhosts.splice(found, 1);
  442. } else {
  443. currenthosts.enabled.splice(i, 1);
  444. }
  445. }
  446. for (var i=0,len=newhosts.length; i<len; i++) {
  447. currenthosts.enabled.push(newhosts[i]);
  448. }
  449. dash.set_ordered_hosts(currenthosts);
  450. }
  451. get_host_url (hostname, link) {
  452. var base = ( link ) ? this.link_base_url : this.base_url;
  453. return (hostname) ? base + '/host/' + hostname : base;
  454. }
  455. get_api_url (hostname) {
  456. return this.get_host_url(hostname) + '/api/v1'
  457. }
  458. init () {
  459. var that = this;
  460. $.get(this.get_api_url() + '/info', function (data) {
  461. that.netdata_info = data;
  462. picknsort_setup()
  463. that.build();
  464. });
  465. }
  466. digest () {
  467. this.fetch_active_alarms();
  468. this.fix_layout_errors();
  469. }
  470. build () {
  471. // Fix vertically misaligned stats on load error
  472. $('.dash-graph').css({
  473. height: this.options.graph_height,
  474. width: this.options.graph_width
  475. });
  476. $('.dash-chart').css({
  477. height: this.options.chart_width, // Charts are square
  478. width: this.options.chart_width
  479. });
  480. // Fix chart alignment
  481. $('.netdata-host-stats-container').css({
  482. width: 'calc(' + this.options.graph_width + ' + (2 * ' + $('.netdata-host-stats-container').css('padding-left') + '))'
  483. });
  484. var $template = $('.netdata-host-stats-container').first();
  485. var hosts = this.get_enabled_hosts();
  486. for (var i=0, len=hosts.length; i<len; i++) {
  487. var hostname = hosts[i];
  488. $('#alarms .alarm-host-list').append('<div class="host-alarms ' + hostname + '"><a target="_blank" href="' + this.get_host_url(hostname, true) + '/"><h2>' + hostname + '</h2></a></div>');
  489. $template.clone().removeClass('template').appendTo('#dash');
  490. var $newest = $('.netdata-host-stats-container').last();
  491. this.build_stats($newest, hostname);
  492. }
  493. if (this.first_build === true) {
  494. this.remove_template_elements();
  495. this.first_build = false;
  496. }
  497. }
  498. build_stats ($hoststats, hostname) {
  499. var that = this;
  500. $hoststats.find('.netdata-host-stats .dash-graph').each(function () {
  501. that.build_graph($(this), hostname);
  502. });
  503. $hoststats.find('.netdata-host-stats .dash-chart').each(function () {
  504. that.build_chart($(this), hostname);
  505. });
  506. $hoststats.find('.netdata-host-name').html('<a target="_blank" href="' + that.get_host_url(hostname, true) + '/">' + hostname + '</a>');
  507. }
  508. build_graph ($wrapper, hostname) {
  509. var that = this;
  510. var $graph = $('<div>', {
  511. // Defaults
  512. 'data-netdata': $wrapper.attr('data-dash-netdata'),
  513. 'data-host': that.get_host_url(hostname),
  514. 'data-before': '0',
  515. 'data-after': '-540',
  516. 'role': 'application',
  517. 'data-width': that.options.graph_width,
  518. 'data-height': that.options.graph_height
  519. });
  520. $.each($wrapper[0].attributes, function (key, node) {
  521. if ( node.name.match(/^data-(?!dash).*/ ) ) {
  522. $graph.attr(node.name, node.value);
  523. }
  524. });
  525. $graph.appendTo($wrapper);
  526. }
  527. build_chart ($wrapper, hostname) {
  528. var that = this;
  529. var $graph = $('<div>', {
  530. // Defaults
  531. 'data-netdata': $wrapper.attr('data-dash-netdata'),
  532. 'data-host': that.get_host_url(hostname),
  533. 'data-before': '0',
  534. 'data-after': '-540',
  535. 'data-points': '540',
  536. 'role': 'application',
  537. 'data-width': that.options.chart_width,
  538. 'data-title': ' ',
  539. 'data-easypiechart-trackcolor': "#373B40",
  540. 'data-chart-library': 'easypiechart'
  541. })
  542. $.each($wrapper[0].attributes, function (key, node) {
  543. if ( node.name.match(/^data-(?!dash).*/ ) ) {
  544. $graph.attr(node.name, node.value);
  545. }
  546. });
  547. $graph.appendTo($wrapper);
  548. }
  549. all_alarms_are_warnings () {
  550. var out = true;
  551. $.each(this.current_alarms, function (host, alarms) {
  552. if (out === false) {
  553. return false; // Maximum efficiency
  554. }
  555. $.each(alarms, function (name, data) {
  556. if (data.status != "WARNING") {
  557. out = false;
  558. return false;
  559. }
  560. });
  561. });
  562. return out;
  563. }
  564. update_alarm_count () {
  565. console.log(this.all_alarms_are_warnings());
  566. var count = $('img.alarm-badge').length;
  567. $('.alarm-count').text($('img.alarm-badge').length);
  568. $('.alarm-count').removeClass('no-alarms warnings-only');
  569. if ( count === 0 ) {
  570. $('.alarm-count').addClass('no-alarms');
  571. return
  572. }
  573. if (this.all_alarms_are_warnings()) {
  574. $('.alarm-count').addClass('warnings-only');
  575. }
  576. }
  577. fetch_active_alarms () {
  578. var that = this;
  579. console.log("Getting alarms...");
  580. var hosts = this.get_enabled_hosts();
  581. for (var i=0, len=hosts.length; i<len; i++) {
  582. var hostname = hosts[i];
  583. $.get(this.get_api_url(hostname) + '/alarms', function (data) {
  584. that.new_alarms[ data.hostname ] = data.alarms;
  585. if ( Object.keys(that.new_alarms).length === len ) {
  586. // All responses received
  587. if ( that.check_new_current_alarms() ) {
  588. console.log("No alarm changes detected... Refreshing images.");
  589. that.new_alarms = {};
  590. that.refresh_alarm_images();
  591. return;
  592. }
  593. that.current_alarms = that.new_alarms;
  594. that.new_alarms = {};
  595. that.draw_alarms();
  596. that.update_alarm_count();
  597. }
  598. });
  599. }
  600. }
  601. toggle_alarm_collapse () {
  602. $('#alarms').toggleClass('collapsed');
  603. }
  604. check_new_current_alarms () {
  605. // Create arrays of property names
  606. var a = this.current_alarms;
  607. var b = this.new_alarms;
  608. var aProps = Object.keys(a);
  609. var bProps = Object.keys(b);
  610. // If number of properties is different,
  611. // objects are not equivalent
  612. if (aProps.length != bProps.length) {
  613. return false;
  614. }
  615. for (var i = 0; i < aProps.length; i++) {
  616. var propName = aProps[i];
  617. var aa = a[propName];
  618. var aaProps = Object.keys(aa);
  619. var bb = b[propName];
  620. var bbProps = Object.keys(bb);
  621. if (aaProps.length != bbProps.length) {
  622. return false;
  623. }
  624. for (var n = 0, len=aaProps.length; i < len; i++) {
  625. if ( bb[aaProps[n]] === undefined ) {
  626. return false;
  627. }
  628. }
  629. }
  630. // If we made it this far, objects
  631. // are considered equivalent
  632. return true;
  633. }
  634. refresh_alarm_images () {
  635. // Use Math.random() to "reload" the image
  636. $('.alarm-badge').each(function () {
  637. var old_src = $(this).attr('src').replace(/&rand=.*/g, "");
  638. $(this).attr('src', old_src + "&rand=" + Math.random());
  639. });
  640. }
  641. draw_alarms () {
  642. var that = this;
  643. console.log("Drawing...", that.current_alarms);
  644. $.each(that.current_alarms, function(hostname, alarms) {
  645. $('#alarms .host-alarms.' + hostname + ' .alarm-badge').remove();
  646. $.each(alarms, function (index, alarm) {
  647. that.draw_alarm(hostname, alarm);
  648. })
  649. });
  650. }
  651. draw_alarm (hostname, alarm) {
  652. var queryStr = '/badge.svg?alarm=' + alarm.name + '&chart=' + alarm.chart
  653. $('<img />', {
  654. src: this.get_api_url(hostname) + queryStr,
  655. class: 'alarm-badge'
  656. }).appendTo($('#alarms .host-alarms.' + hostname));
  657. }
  658. remove_template_elements () {
  659. $('.netdata-host-stats-container').first().remove();
  660. }
  661. fix_layout_errors () {
  662. $('.dash-graph:contains("chart not found")').html('<div class="loading-error">Not found</div>');
  663. $('.dash-chart:contains("chart not found")').html('<div class="loading-error">Not found</div>');
  664. }
  665. }
  666. /*
  667. * TUTORIAL: Change this to the URL of your netdata host
  668. * If you use netdata behind a reverse proxy, add a second parameter for the reverse proxy url like so:
  669. * new Dash('http://localhost:19999', 'https://my-domain.com/stats');
  670. */
  671. var dash = new Dash('http://localhost:19999');
  672. var picknsort = new PickNSort(true);
  673. // Import dashboard.js
  674. $.getScript(dash.base_url + '/dashboard.js', function() {
  675. console.log("Loaded dashboard.js");
  676. setTimeout(function () {
  677. $('#alarms').css("visibility", "visible");
  678. }, 400);
  679. });
  680. setInterval(function () {
  681. dash.digest();
  682. }, 6 * 1000);
  683. </script>
  684. <style type="text/css">
  685. body {
  686. background-color: #272b30;
  687. }
  688. a, a:hover {
  689. color: #AAA !important;
  690. }
  691. #dash {
  692. overflow: scroll;
  693. width: 100vw;
  694. white-space: nowrap;
  695. height: 100vh;
  696. }
  697. #alarms {
  698. display: block;
  699. z-index: 9999;
  700. position: fixed;
  701. right: 0;
  702. top: 0;
  703. bottom: 0;
  704. background-color: #111;
  705. width: 23em;
  706. padding: 1em;
  707. color: #AAA;
  708. box-shadow: 0 0 3em #060606;
  709. border: solid 1px #2d2d2d;
  710. visibility: hidden;
  711. }
  712. #alarms h1 {
  713. text-align: center;
  714. margin: 0;
  715. margin-bottom: 0.5em;
  716. margin-top: -0.1em;
  717. font-size: 2em;
  718. }
  719. #alarms h2 {
  720. font-size: 1.5em;
  721. }
  722. #alarms .alarm-collapse-button {
  723. position: absolute;
  724. top: 0;
  725. left: 0;
  726. width: 0.9em;
  727. height: 0.9em;
  728. font-size: 4em;
  729. line-height: 0.8;
  730. background: #111;
  731. text-align: center;
  732. cursor: default;
  733. }
  734. #alarms .alarm-collapse-button:hover {
  735. background: #000;
  736. }
  737. #alarms.collapsed {
  738. width: 4em;
  739. }
  740. #alarms.collapsed .alarm-collapse-button {
  741. position: fixed;
  742. left: auto;
  743. right: 0;
  744. top: 0;
  745. bottom: 0;
  746. width: 4em;
  747. height: 100vh;
  748. opacity: 0;
  749. z-index: 9999;
  750. }
  751. #alarms.collapsed h1 {
  752. transform: rotate(-90deg);
  753. position: absolute;
  754. left: -9.1em;
  755. top: 4em;
  756. width: 20em;
  757. }
  758. #alarms .alarm-host-list {
  759. margin-top: 1em;
  760. height: 100%;
  761. overflow-y: scroll;
  762. scrollbar-width: thin;
  763. }
  764. #alarms.collapsed .alarm-host-list {
  765. display: none;
  766. }
  767. #alarms .host-alarms {
  768. background: #1d1d1d;
  769. margin-bottom: 1em;
  770. padding: 0.2em 1em;
  771. padding-bottom: 1.5em;
  772. border-top: 1px solid;
  773. position: relative;
  774. }
  775. .host-alarms a:last-child:after {
  776. position: absolute;
  777. display: block;
  778. content: 'no alarms';
  779. width: 6em;
  780. height: 1em;
  781. right: 0;
  782. top: 2em;
  783. color: #646464;
  784. }
  785. #alarms .host-alarms img.alarm-badge {
  786. margin: .25em;
  787. height: 1.2em;
  788. }
  789. #alarms .alarm-count {
  790. width: 1.5em;
  791. height: 1.5em;
  792. text-align: center;
  793. position: absolute;
  794. right: 0.5em;
  795. top: 0.5em;
  796. font-size: 1.5em;
  797. background: #6f1515;
  798. border-radius: 50%;
  799. }
  800. #alarms .alarm-count:empty {
  801. background: #333;
  802. }
  803. #alarms .no-alarms {
  804. background: #156f15;
  805. color: rgba(0,0,0,0);
  806. }
  807. #alarms .warnings-only {
  808. background: #f48041;
  809. color: #EEE;
  810. }
  811. .settings-button {
  812. position: absolute;
  813. bottom: 0;
  814. right: 0;
  815. font-size: 3em;
  816. width: 1.3em;
  817. height: 1.3em;
  818. z-index: 9999;
  819. text-align: center;
  820. line-height: 1.2;
  821. cursor: default;
  822. }
  823. .settings-button:hover {
  824. background: #424242;
  825. }
  826. .netdata-host-name {
  827. font-size: 3em;
  828. color: #FFF;
  829. text-align: center;
  830. white-space: nowrap;
  831. background: inherit;
  832. position: sticky;
  833. top: 0;
  834. z-index: 9998;
  835. box-shadow: 0 0.5em 1em #232323;
  836. margin-bottom: 0.5em;
  837. border-bottom: solid 0.1em #AAA;
  838. }
  839. .netdata-host-stats-container {
  840. position: relative;
  841. margin: 0 1em;
  842. padding: 1em 2em;
  843. display: inline-block;
  844. text-align: center;
  845. color: #CCC;
  846. background: #232323;
  847. }
  848. .netdata-host-stats-container:last-of-type {
  849. margin-right: 27em;
  850. }
  851. .netdata-host-stats {
  852. }
  853. .netdata-message {
  854. }
  855. .dash-graph {
  856. margin-bottom: 1em;
  857. }
  858. .netdata-legend-resize-handler {
  859. display: none
  860. }
  861. .dash-charts {
  862. white-space: normal;
  863. margin: 2em 0;
  864. }
  865. .dash-chart {
  866. display: inline-block;
  867. vertical-align: top;
  868. }
  869. .easyPieChartLabel {
  870. color: #FFF;
  871. }
  872. .loading-error {
  873. padding-top: 2em;
  874. font-size: 2em;
  875. }
  876. .netdata-legend-value, .netdata-legend-toolbox, .netdata-legend-toolbox-button, .netdata-legend-resize-handler {
  877. background: initial;
  878. }
  879. </style>
  880. </html>