X-Git-Url: https://arthur.barton.de/gitweb/?p=netdata.git;a=blobdiff_plain;f=web%2Findex.html;h=b62306a2655ae56069a26e1bb909d0a1b94ee4e1;hp=e95e31513aabd23fd494642c238d56b5b107bc27;hb=8679670bdbe3c5928ec2e266d9c72e1a758fdf37;hpb=6a0aee81aba1b0dabdb6dddd0ef37056a65855a1 diff --git a/web/index.html b/web/index.html index e95e3151..b62306a2 100644 --- a/web/index.html +++ b/web/index.html @@ -35,7 +35,7 @@ - + @@ -508,16 +508,36 @@ // -------------------------------------------------------------------- // check options that should be processed before loading netdata.js + var localStorageTested = -1; + function localStorageTest() { + if(localStorageTested !== -1) + return localStorageTested; + + if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + var test = 'test'; + try { + localStorage.setItem(test, test); + localStorage.removeItem(test); + localStorageTested = true; + } + catch (e) { + localStorageTested = false; + } + } + else + localStorageTested = false; + + return localStorageTested; + } + function loadLocalStorage(name) { var ret = null; try { - if(typeof Storage !== "undefined" && typeof localStorage === 'object') + if(localStorageTest() === true) ret = localStorage.getItem(name); } - catch(error) { - ; - } + catch(error) {} if(typeof ret === 'undefined' || ret === null) return null; @@ -530,14 +550,12 @@ function saveLocalStorage(name, value) { // console.log('saving: ' + name.toString() + ' = ' + value.toString()); try { - if(typeof Storage !== "undefined" && typeof localStorage === 'object') { + if(localStorageTest() === true) { localStorage.setItem(name, value.toString()); return true; } } - catch(error) { - ; - } + catch(error) {} return false; } @@ -579,7 +597,52 @@ var netdataRegistryCallback = function(machines_array) { var el = ''; var a1 = ''; - var found = 0; + var found = 0, hosted = 0; + var len, i, url, hostname, icon; + + if(options.hosts.length > 1) { + // there are mirrored hosts here + + el += '
  • databases available on this host
  • '; + a1 += '
  • '; + + var base = document.location.origin.toString() + document.location.pathname.toString(); + if(base.endsWith("/host/" + options.hostname + "/")) + base = base.substring(0, base.length - ("/host/" + options.hostname + "/").toString().length); + + if(base.endsWith("/")) + base = base.substring(0, base.length - 1); + + var master = options.hosts[0].hostname; + var sorted = options.hosts.sort(function(a, b) { + if(a.hostname === master) return -1; + if(a.hostname === b.hostname) return 0; + else if(a.hostname > b.hostname) return 1; + return -1; + }); + + i = 0; + len = sorted.length; + while(len--) { + hostname = sorted[i].hostname; + if(hostname == master) { + url = base + "/"; + icon = "home"; + } + else { + url = base + "/host/" + hostname + "/"; + icon = "window-restore"; + } + + el += '
  • ' + hostname + '
  • '; + a1 += '
  • '; + hosted++; + i++; + } + + el += ''; + a1 += ''; + } if(machines_array === null) { var ret = loadLocalStorage("registryCallback"); @@ -598,7 +661,7 @@ return 0; }); - var len = machines.length; + len = machines.length; while(len--) { var u = machines[len]; found++; @@ -663,10 +726,7 @@ this_is_demo = true; } } - catch(error) { - ; - } - + catch(error) {} return this_is_demo; } @@ -685,15 +745,18 @@ } function netdataReload(url) { - var t = netdataURL(url); - // console.log('netdataReload: ' + t); - document.location = t; + document.location = netdataURL(url); // since we play with hash // this is needed to reload the page location.reload(); } + function gotoHostedModalHandler(url) { + document.location = url + urlOptions.genHash(); + return false; + } + var gotoServerValidateRemaining = 0; var gotoServerMiddleClick = false; var gotoServerStop = false; @@ -830,6 +893,8 @@ var deleteRegistryUrl = null; function deleteRegistryModalHandler(guid, name, url) { + void(guid); + deleteRegistryUrl = url; document.getElementById('deleteRegistryServerName').innerHTML = name; document.getElementById('deleteRegistryServerName2').innerHTML = name; @@ -854,47 +919,28 @@ } var options = { - sparklines_registry: {}, menus: {}, submenu_names: {}, data: null, hostname: 'netdata_server', // will be overwritten by the netdata server - categories: new Array(), + version: 'unknown', + categories: [], categories_idx: {}, - families: new Array(), + families: [], families_idx: {}, + hosts: [], chartsPerRow: 0, - chartsMinWidth: 1450, - chartsHeight: 180, - sparklinesHeight: 60, + // chartsMinWidth: 1450, + chartsHeight: 180 }; - // generate a sparkline - // used in the documentation - function sparkline(chart, dimension, units) { - var key = chart + '.' + dimension; - - if(typeof units === 'undefined') - units = ''; - - if(typeof options.sparklines_registry[key] === 'undefined') - options.sparklines_registry[key] = { count: 1 }; - else - options.sparklines_registry[key].count++; - - key = key + '.' + options.sparklines_registry[key].count; - - var h = '
    (X' + units + ')'; - - return h; - } - function chartsPerRow(total) { if(options.chartsPerRow === 0) { - width = Math.floor(total / options.chartsMinWidth); - if(width === 0) width = 1; - return width; + return 1; + //var width = Math.floor(total / options.chartsMinWidth); + //if(width === 0) width = 1; + //return width; } else return options.chartsPerRow; } @@ -908,9 +954,11 @@ function sortObjectByPriority(object) { var idx = {}; - var sorted = new Array(); + var sorted = []; for(var i in object) { + if(!object.hasOwnProperty(i)) continue; + if(typeof idx[i] === 'undefined') { idx[i] = object[i]; sorted.push(i); @@ -944,11 +992,52 @@ // ---------------------------------------------------------------------------- + // user editable information + var customDashboard = { + menu: {}, + submenu: {}, + context: {} + }; + + // netdata standard information var netdataDashboard = { + sparklines_registry: {}, + os: 'unknown', + menu: {}, submenu: {}, context: {}, + // generate a sparkline + // used in the documentation + sparkline: function (prefix, chart, dimension, units, suffix) { + if(options.data === null || typeof options.data.charts === 'undefined') + return ''; + + if(typeof options.data.charts[chart] === 'undefined') + return ''; + + if(typeof options.data.charts[chart].dimensions === 'undefined') + return ''; + + if(typeof options.data.charts[chart].dimensions[dimension] === 'undefined') + return ''; + + var key = chart + '.' + dimension; + + if(typeof units === 'undefined') + units = ''; + + if(typeof this.sparklines_registry[key] === 'undefined') + this.sparklines_registry[key] = { count: 1 }; + else + this.sparklines_registry[key].count++; + + key = key + '.' + this.sparklines_registry[key].count; + + return prefix + '
    (X' + units + ')' + suffix; + }, + gaugeChart: function(title, width, dimensions, colors) { if(typeof colors === 'undefined') colors = ''; @@ -957,53 +1046,69 @@ dimensions = ''; return '
    '; + + ' data-dimensions="' + dimensions + '"' + + ' data-chart-library="gauge"' + + ' data-gauge-adjust="width"' + + ' data-title="' + title + '"' + + ' data-width="' + width + '"' + + ' data-before="0"' + + ' data-after="-CHART_DURATION"' + + ' data-points="CHART_DURATION"' + + ' data-colors="' + colors + '"' + + ' role="application">'; }, anyAttribute: function(obj, attr, key, def) { - if(typeof obj[key] !== 'undefined') { - if(typeof obj[key][attr] !== 'undefined') - return obj[key][attr]; + if(typeof(obj[key]) !== 'undefined') { + var x = obj[key][attr]; + + if(typeof(x) === 'undefined') + return def; + + if(typeof(x) === 'function') { + return x(netdataDashboard.os); + } + + return x; } + return def; }, menuTitle: function(chart) { if(typeof chart.menu_pattern !== 'undefined') { - return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() + return (this.anyAttribute(this.menu, 'title', chart.menu_pattern, chart.menu_pattern).toString() + ' ' + chart.type.slice(-(chart.type.length - chart.menu_pattern.length - 1)).toString()).replace(/_/g, ' '); } - return (netdataDashboard.anyAttribute(netdataDashboard.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); + return (this.anyAttribute(this.menu, 'title', chart.menu, chart.menu)).toString().replace(/_/g, ' '); }, menuIcon: function(chart) { if(typeof chart.menu_pattern !== 'undefined') - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu_pattern, '').toString(); + return this.anyAttribute(this.menu, 'icon', chart.menu_pattern, '').toString(); - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'icon', chart.menu, ''); + return this.anyAttribute(this.menu, 'icon', chart.menu, ''); }, - menuInfo: function(menu) { - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'info', menu, null); + menuInfo: function(chart) { + if(typeof chart.menu_pattern !== 'undefined') + return this.anyAttribute(this.menu, 'info', chart.menu_pattern, null); + + return this.anyAttribute(this.menu, 'info', chart.menu, null); }, - menuHeight: function(menu, relative) { - return netdataDashboard.anyAttribute(netdataDashboard.menu, 'height', menu, 1.0) * relative; + menuHeight: function(chart) { + if(typeof chart.menu_pattern !== 'undefined') + return this.anyAttribute(this.menu, 'height', chart.menu_pattern, 1.0); + + return this.anyAttribute(this.menu, 'height', chart.menu, 1.0); }, submenuTitle: function(menu, submenu) { var key = menu + '.' + submenu; - var title = netdataDashboard.anyAttribute(netdataDashboard.submenu, 'title', key, submenu).toString().replace(/_/g, ' ');; + // console.log(key); + var title = this.anyAttribute(this.submenu, 'title', key, submenu).toString().replace(/_/g, ' '); if(title.length > 28) { var a = title.substring(0, 13); var b = title.substring(title.length - 12, title.length); @@ -1014,31 +1119,33 @@ submenuInfo: function(menu, submenu) { var key = menu + '.' + submenu; - return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'info', key, null); + return this.anyAttribute(this.submenu, 'info', key, null); }, submenuHeight: function(menu, submenu, relative) { var key = menu + '.' + submenu; - return netdataDashboard.anyAttribute(netdataDashboard.submenu, 'height', key, 1.0) * relative; + return this.anyAttribute(this.submenu, 'height', key, 1.0) * relative; }, contextInfo: function(id) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].info !== 'undefined') - return '
    ' + netdataDashboard.context[id].info + '
    '; + var x = this.anyAttribute(this.context, 'info', id, null); + + if(x !== null) + return '
    ' + x + '
    '; else return ''; }, contextValueRange: function(id) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].valueRange !== 'undefined') - return netdataDashboard.context[id].valueRange; + if(typeof this.context[id] !== 'undefined' && typeof this.context[id].valueRange !== 'undefined') + return this.context[id].valueRange; else return '[null, null]'; }, contextHeight: function(id, def) { - if(typeof netdataDashboard.context[id] !== 'undefined' && typeof netdataDashboard.context[id].height !== 'undefined') - return def * netdataDashboard.context[id].height; + if(typeof this.context[id] !== 'undefined' && typeof this.context[id].height !== 'undefined') + return def * this.context[id].height; else return def; } @@ -1048,8 +1155,10 @@ // enrich the data structure returned by netdata // to reflect our menu system and content + // FIXME: this is a shame - we should fix charts naming (issue #807) function enrichChartData(chart) { - var tmp = chart.type.split('_')[0]; + var parts = chart.type.split('_'); + var tmp = parts[0]; switch(tmp) { case 'ap': @@ -1058,6 +1167,22 @@ chart.menu = tmp; break; + case 'apache': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'cache') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + + case 'bind': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'rndc') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + case 'cgroup': chart.menu = chart.type; if(chart.id.match(/.*[\._\/-:]qemu[\._\/-:]*/) || chart.id.match(/.*[\._\/-:]kvm[\._\/-:]*/)) @@ -1066,27 +1191,29 @@ chart.menu_pattern = 'cgroup'; break; - case 'apache': - case 'exim': - case 'dovecot': - case 'hddtemp': - case 'ipfs': - case 'memcached': - case 'mysql': - case 'named': - case 'nginx': - case 'nut': - case 'phpfpm': - case 'postfix': - case 'postgres': - case 'redis': - case 'retroshare': - case 'smawebbox': - case 'snmp': - case 'squid': - case 'tomcat': + case 'isc': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'dhcpd') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + + case 'ovpn': chart.menu = chart.type; - chart.menu_pattern = tmp; + if(parts.length > 3 && parts[1] === 'status' && parts[2] === 'log') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; + break; + + case 'smartd': + case 'web': + chart.menu = chart.type; + if(parts.length > 2 && parts[1] === 'log') + chart.menu_pattern = tmp + '_' + parts[1]; + else if(parts.length > 1) + chart.menu_pattern = tmp; break; case 'tc': @@ -1094,7 +1221,7 @@ // find a name for this device from fireqos info // we strip '_(in|out)' or '(in|out)_' - if(typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family) { + if(chart.context === 'tc.qos' && (typeof options.submenu_names[chart.family] === 'undefined' || options.submenu_names[chart.family] === chart.family)) { var n = chart.name.split('.')[1]; if(n.endsWith('_in')) options.submenu_names[chart.family] = n.slice(0, n.lastIndexOf('_in')); @@ -1104,6 +1231,8 @@ options.submenu_names[chart.family] = n.slice(3, n.length); else if(n.startsWith('out_')) options.submenu_names[chart.family] = n.slice(4, n.length); + else + options.submenu_names[chart.family] = n; } // increase the priority of IFB devices @@ -1115,6 +1244,8 @@ default: chart.menu = chart.type; + if(parts.length > 1) + chart.menu_pattern = tmp; break; } @@ -1123,7 +1254,9 @@ // ---------------------------------------------------------------------------- - function headMain(charts, duration) { + function headMain(os, charts, duration) { + void(os); + var head = ''; if(typeof charts['system.swap'] !== 'undefined') @@ -1244,7 +1377,7 @@ var hi = 0, hlen = hcharts.length; while(hi < hlen) { if(typeof hcharts[hi] === 'function') - head += hcharts[hi](chart.id).replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); + head += hcharts[hi](netdataDashboard.os, chart.id).replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); else head += hcharts[hi].replace('CHART_DURATION', duration.toString()).replace('CHART_UNIQUE_ID', chart.id); hi++; @@ -1261,7 +1394,7 @@ var duration = Math.round(($(div).width() * pcent_width / 100 * data.update_every / 3) / 60) * 60; var html = ''; var sidebar = ''; div.innerHTML = html; document.getElementById('sidebar').innerHTML = sidebar; @@ -1348,53 +1481,71 @@ function renderChartsAndMenu(data) { var menus = options.menus; var charts = data.charts; + var m, menu_key; for(var c in charts) { - enrichChartData(charts[c]); + if(!charts.hasOwnProperty(c)) continue; + + var chart = charts[c]; + enrichChartData(chart); + m = chart.menu; // create the menu - if(typeof menus[charts[c].menu] === 'undefined') { - menus[charts[c].menu] = { - priority: charts[c].priority, + if(typeof menus[m] === 'undefined') { + menus[m] = { + menu_pattern: chart.menu_pattern, + priority: chart.priority, submenus: {}, - title: netdataDashboard.menuTitle(charts[c]), - icon: netdataDashboard.menuIcon(charts[c]), - info: netdataDashboard.menuInfo(charts[c].menu), - height: netdataDashboard.menuHeight(charts[c].menu, options.chartsHeight) + title: netdataDashboard.menuTitle(chart), + icon: netdataDashboard.menuIcon(chart), + info: netdataDashboard.menuInfo(chart), + height: netdataDashboard.menuHeight(chart) * options.chartsHeight }; } + else { + if(typeof(menus[m].menu_pattern) === 'undefined') + menus[m].menu_pattern = chart.menu_pattern; + + if(chart.priority < menus[m].priority) + menus[m].priority = chart.priority; + } - if(charts[c].priority < menus[charts[c].menu].priority) - menus[charts[c].menu].priority = charts[c].priority; + menu_key = (typeof(menus[m].menu_pattern) !== 'undefined')?menus[m].menu_pattern:m; // create the submenu - if(typeof menus[charts[c].menu].submenus[charts[c].submenu] === 'undefined') { - menus[charts[c].menu].submenus[charts[c].submenu] = { - priority: charts[c].priority, - charts: new Array(), + if(typeof menus[m].submenus[chart.submenu] === 'undefined') { + menus[m].submenus[chart.submenu] = { + priority: chart.priority, + charts: [], title: null, - info: netdataDashboard.submenuInfo(charts[c].menu, charts[c].submenu), - height: netdataDashboard.submenuHeight(charts[c].menu, charts[c].submenu, menus[charts[c].menu].height) + info: netdataDashboard.submenuInfo(menu_key, chart.submenu), + height: netdataDashboard.submenuHeight(menu_key, chart.submenu, menus[m].height) }; } - - if(charts[c].priority < menus[charts[c].menu].submenus[charts[c].submenu].priority) - menus[charts[c].menu].submenus[charts[c].submenu].priority = charts[c].priority; + else { + if (chart.priority < menus[m].submenus[chart.submenu].priority) + menus[m].submenus[chart.submenu].priority = chart.priority; + } // index the chart in the menu/submenu - menus[charts[c].menu].submenus[charts[c].submenu].charts.push(charts[c]); + menus[m].submenus[chart.submenu].charts.push(chart); } // propagate the descriptive subname given to QoS // to all the other submenus with the same name - for(var m in menus) { + for(m in menus) { + if(!menus.hasOwnProperty(m)) continue; + for(var s in menus[m].submenus) { + if(!menus[m].submenus.hasOwnProperty(s)) continue; + // set the family using a name if(typeof options.submenu_names[s] !== 'undefined') { menus[m].submenus[s].title = s + ' (' + options.submenu_names[s] + ')'; } else { - menus[m].submenus[s].title = netdataDashboard.submenuTitle(m, s); + menu_key = (typeof(menus[m].menu_pattern) !== 'undefined')?menus[m].menu_pattern:m; + menus[m].submenus[s].title = netdataDashboard.submenuTitle(menu_key, s); } } } @@ -1423,7 +1574,7 @@ var bootstrapTableLoaded = false; function loadBootstrapTable(callback) { if(bootstrapTableLoaded === false) { - bootstrapTableLoaded === true; + bootstrapTableLoaded = true; loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-1.11.0.min.js', function() { loadJs(NETDATA.serverDefault + 'lib/bootstrap-table-export-1.11.0.min.js', function() { loadJs(NETDATA.serverDefault + 'lib/tableExport-1.6.0.min.js', callback); @@ -1439,7 +1590,7 @@ var footer = '
    netdata badges refresh automatically. Their color indicates the state of the alarm:  red  is critical,  orange  is warning,  bright green  is ok,  light grey  is undefined (i.e. no data or no status),  black  is not initialized. You can copy and paste their URLs to embed them in any web page.
    netdata can send notifications for these alarms. Check this configuration file for more information.'; NETDATA.alarms.get('all', function(data) { - options.alarm_families = new Array(); + options.alarm_families = []; alarmsCallback(data); @@ -1474,70 +1625,6 @@ return t.toLocaleDateString() + space + t.toLocaleTimeString(); } - function seconds4human(seconds, options) { - var default_options = { - now: 'now', - space: ' ', - negative_suffix: 'ago', - hour: 'hour', - hours: 'hours', - minute: 'minute', - minutes: 'minutes', - second: 'second', - seconds: 'seconds', - and: 'and' - }; - - if(typeof options !== 'object') - options = default_options; - else { - var x; - for(x in default_options) { - if(typeof options[x] !== 'string') - options[x] = default_options[x]; - } - } - - if(typeof seconds === 'string') - seconds = parseInt(seconds); - - if(seconds === 0) - return options.now; - - var suffix = ''; - if(seconds < 0) { - seconds = -seconds; - if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; - } - - var hours = Math.floor(seconds / 3600); - seconds -= (hours * 3600); - - var minutes = Math.floor(seconds / 60); - seconds -= (minutes * 60); - - var txt = ''; - - if(hours > 1) txt += hours.toString() + options.space + options.hours; - else if(hours === 1) txt += hours.toString() + options.space + options.hour; - - if(hours > 0 && minutes > 0 && seconds == 0) - txt += options.space + options.and + options.space; - else if(hours > 0 && minutes > 0 && seconds > 0) - txt += ',' + options.space; - - if(minutes > 1) txt += minutes.toString() + options.space + options.minutes; - else if(minutes === 1) txt += minutes.toString() + options.space + options.minute; - - if((minutes > 0 || minutes > 0) && seconds > 0) - txt += options.space + options.and + options.space; - - if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds; - else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second; - - return txt + suffix; - } - function alarm_lookup_explain(alarm, chart) { var dimensions = ' of all values '; @@ -1562,7 +1649,14 @@ function alarm_to_html(alarm, full) { var chart = options.data.charts[alarm.chart]; - var has_alarm = ((typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined')?true:false); + if(typeof(chart) === 'undefined') { + // this means the charts loaded are incomplete + // probably netdata was restarted and more charts + // are now available. + return ''; + } + + var has_alarm = (typeof alarm.warn !== 'undefined' || typeof alarm.crit !== 'undefined'); var role_href = ((has_alarm === true)?('
     
    role: ' + alarm.recipient + '
     
      jump to chart'):(' ')); @@ -1625,14 +1719,16 @@ // find the proper family of each alarm var now = Date.now(); - var x; + var x, family, alarm; var count_active = 0; var count_all = 0; var families = {}; - var families_sort = new Array(); + var families_sort = []; for(x in data.alarms) { - var alarm = data.alarms[x]; - var family = alarm.family; + if(!data.alarms.hasOwnProperty(x)) continue; + + alarm = data.alarms[x]; + family = alarm.family; // find the chart var chart = options.data.charts[alarm.chart]; @@ -1651,7 +1747,7 @@ if(typeof families[family] === 'undefined') { families[family] = { name: family, - arr: new Array(), + arr: [], priority: chart.priority }; @@ -1674,7 +1770,7 @@ var fc = 0; var len = families_sorted.length; while(len--) { - var family = families_sorted[len].name; + family = families_sorted[len].name; var active_family_added = false; var expanded = 'true'; var collapsed = ''; @@ -1683,7 +1779,7 @@ if(fc !== 0) { all += ""; expanded = 'false'; - collapsed = 'class="collapsed"' + collapsed = 'class="collapsed"'; cin = ''; } @@ -1696,7 +1792,7 @@ var arr = families[family].arr; var c = arr.length; while(c--) { - var alarm = arr[c]; + alarm = arr[c]; if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') { if(!active_family_added) { active_family_added = true; @@ -1729,12 +1825,13 @@ if(families_sorted.length > 0) alarm_family_show(0); // register bootstrap events - $('#alarms_all_accordion').on('show.bs.collapse', function (d) { + var $accordion = $('#alarms_all_accordion'); + $accordion.on('show.bs.collapse', function (d) { var target = $(d.target); var id = $(target).data('alarm-id'); alarm_family_show(id); }); - $('#alarms_all_accordion').on('hidden.bs.collapse', function (d) { + $accordion.on('hidden.bs.collapse', function (d) { var target = $(d.target); var id = $(target).data('alarm-id'); $('#alarm_all_' + id.toString()).html(''); @@ -1759,6 +1856,8 @@ fileName: 'netdata_alarm_log' }, rowStyle: function(row, index) { + void(index); + switch(row.status) { case 'CRITICAL' : return { classes: 'danger' }; break; case 'WARNING' : return { classes: 'warning' }; break; @@ -1779,9 +1878,8 @@ title: 'Event Date', valign: 'middle', titleTooltip: 'The date and time the even took place', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); }, align: 'center', - valign: 'middle', switchable: false, sortable: true }, @@ -1791,7 +1889,6 @@ valign: 'middle', titleTooltip: 'The host that generated this event', align: 'center', - valign: 'middle', visible: false, sortable: true }, @@ -1799,7 +1896,7 @@ field: 'unique_id', title: 'Unique ID', titleTooltip: 'The host unique ID for this event', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1809,7 +1906,7 @@ field: 'alarm_id', title: 'Alarm ID', titleTooltip: 'The ID of the alarm that generated this event', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1819,7 +1916,7 @@ field: 'alarm_event_id', title: 'Alarm Event ID', titleTooltip: 'The incremental ID of this event for the given alarm', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1848,6 +1945,8 @@ title: 'Alarm', titleTooltip: 'The alarm name that generated this event', formatter: function(value, row, index) { + void(row); + void(index); return value.toString().replace(/_/g, ' '); }, align: 'center', @@ -1855,11 +1954,30 @@ switchable: false, sortable: true }, + { + field: 'value_string', + title: 'Friendly Value', + titleTooltip: 'The value of the alarm, that triggered this event', + align: 'right', + valign: 'middle', + sortable: true + }, + { + field: 'old_value_string', + title: 'Friendly Old Value', + titleTooltip: 'The value of the alarm, just before this event', + align: 'right', + valign: 'middle', + visible: false, + sortable: true + }, { field: 'old_value', title: 'Old Value', titleTooltip: 'The value of the alarm, just before this event', formatter: function(value, row, index) { + void(row); + void(index); return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString(); }, align: 'center', @@ -1872,10 +1990,13 @@ title: 'Value', titleTooltip: 'The value of the alarm, that triggered this event', formatter: function(value, row, index) { + void(row); + void(index); return ((value !== null)?Math.round(value * 100) / 100:'NaN').toString(); }, align: 'right', valign: 'middle', + visible: false, sortable: true }, { @@ -1884,6 +2005,7 @@ titleTooltip: 'The units of the value of the alarm', align: 'left', valign: 'middle', + visible: false, sortable: true }, { @@ -1908,7 +2030,11 @@ field: 'duration', title: 'Last Duration', titleTooltip: 'The duration the alarm was at its previous state, just before this event', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + formatter: function(value, row, index) { + void(row); + void(index); + return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); + }, align: 'center', valign: 'middle', visible: false, @@ -1918,7 +2044,11 @@ field: 'non_clear_duration', title: 'Raised Duration', titleTooltip: 'The duration the alarm was raised, just before this event', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + formatter: function(value, row, index) { + void(row); + void(index); + return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); + }, align: 'center', valign: 'middle', visible: false, @@ -1938,6 +2068,9 @@ title: 'Processed Status', titleTooltip: 'True when this event is processed', formatter: function(value, row, index) { + void(row); + void(index); + if(value === true) return 'DONE'; else @@ -1953,6 +2086,9 @@ title: 'Updated Status', titleTooltip: 'True when this event has been updated by another event', formatter: function(value, row, index) { + void(row); + void(index); + if(value === true) return 'UPDATED'; else @@ -1967,7 +2103,7 @@ field: 'updated_by_id', title: 'Updated By ID', titleTooltip: 'The unique ID of the event that obsoleted this one', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1977,7 +2113,7 @@ field: 'updates_id', title: 'Updates ID', titleTooltip: 'The unique ID of the event obsoleted because of this event', - formatter: function(value, row, index) { return alarmid4human(value); }, + formatter: function(value, row, index) { void(row); void(index); return alarmid4human(value); }, align: 'center', valign: 'middle', visible: false, @@ -1996,7 +2132,7 @@ field: 'exec_run', title: 'Script Run At', titleTooltip: 'The date and time the script has been ran', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); }, align: 'center', valign: 'middle', visible: false, @@ -2007,6 +2143,9 @@ title: 'Script Return Value', titleTooltip: 'The return code of the script', formatter: function(value, row, index) { + void(row); + void(index); + if(value === 0) return 'OK (returned 0)'; else @@ -2021,7 +2160,12 @@ field: 'delay', title: 'Script Delay', titleTooltip: 'The hysteresis of the notification', - formatter: function(value, row, index) { return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); }, + formatter: function(value, row, index) { + void(row); + void(index); + + return seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' }); + }, align: 'center', valign: 'middle', visible: false, @@ -2031,7 +2175,7 @@ field: 'delay_up_to_timestamp', title: 'Script Delay Run At', titleTooltip: 'The date and time the script should be run, after hysteresis', - formatter: function(value, row, index) { return timestamp4human(value, ' '); }, + formatter: function(value, row, index) { void(row); void(index); return timestamp4human(value, ' '); }, align: 'center', valign: 'middle', visible: false, @@ -2062,9 +2206,75 @@ }); } + function seconds4human(seconds, options) { + var default_options = { + now: 'now', + space: ' ', + negative_suffix: 'ago', + hour: 'hour', + hours: 'hours', + minute: 'minute', + minutes: 'minutes', + second: 'second', + seconds: 'seconds', + and: 'and' + }; + + if(typeof options !== 'object') + options = default_options; + else { + var x; + for(x in default_options) { + if(typeof options[x] !== 'string') + options[x] = default_options[x]; + } + } + + if(typeof seconds === 'string') + seconds = parseInt(seconds); + + if(seconds === 0) + return options.now; + + var suffix = ''; + if(seconds < 0) { + seconds = -seconds; + if(options.negative_suffix !== '') suffix = options.space + options.negative_suffix; + } + + var hours = Math.floor(seconds / 3600); + seconds -= (hours * 3600); + + var minutes = Math.floor(seconds / 60); + seconds -= (minutes * 60); + + var txt = ''; + + if(hours > 1) txt += hours.toString() + options.space + options.hours; + else if(hours === 1) txt += hours.toString() + options.space + options.hour; + + if(hours > 0 && minutes > 0 && seconds == 0) + txt += options.space + options.and + options.space; + else if(hours > 0 && minutes > 0 && seconds > 0) + txt += ',' + options.space; + + if(minutes > 1) txt += minutes.toString() + options.space + options.minutes; + else if(minutes === 1) txt += minutes.toString() + options.space + options.minute; + + if((minutes > 0 || minutes > 0) && seconds > 0) + txt += options.space + options.and + options.space; + + if(seconds > 1) txt += Math.floor(seconds).toString() + options.space + options.seconds; + else if(seconds === 1) txt += Math.floor(seconds).toString() + options.space + options.second; + + return txt + suffix; + } + function alarmsCallback(data) { var count = 0; for(x in data.alarms) { + if(!data.alarms.hasOwnProperty(x)) continue; + var alarm = data.alarms[x]; if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') count++; @@ -2076,6 +2286,43 @@ document.getElementById('alarms_count_badge').innerHTML = ''; } + function initializeDynamicDashboardWithData(data) { + if(data !== null) { + options.hostname = data.hostname; + options.data = data; + options.version = data.version; + netdataDashboard.os = data.os; + + if(typeof data.hosts != 'undefined') + options.hosts = data.hosts; + + // update the dashboard hostname + document.getElementById('hostname').innerHTML = options.hostname; + document.getElementById('hostname').href = NETDATA.serverDefault; + document.getElementById('netdataVersion').innerHTML = options.version; + + // update the dashboard title + document.title = options.hostname + ' netdata dashboard'; + + // close the splash screen + $("#loadOverlay").css("display","none"); + + // create a chart_by_name index + data.charts_by_name = {}; + var charts = data.charts; + var x; + for(x in charts) { + if(!charts.hasOwnProperty(x)) continue; + + var chart = charts[x]; + data.charts_by_name[chart.name] = chart; + } + + // render all charts + renderChartsAndMenu(data); + } + } + function initializeDynamicDashboard(netdata_url) { if(typeof netdata_url === 'undefined' || netdata_url === null) netdata_url = NETDATA.serverDefault; @@ -2090,31 +2337,16 @@ // download all the charts the server knows NETDATA.chartRegistry.downloadAll(netdata_url, function(data) { - if(data !== null) { - options.hostname = data.hostname; - options.data = data; - - // update the dashboard hostname - document.getElementById('hostname').innerHTML = options.hostname; - document.getElementById('hostname').href = NETDATA.serverDefault; - - // update the dashboard title - document.title = options.hostname + ' netdata dashboard'; - - // close the splash screen - $("#loadOverlay").css("display","none"); - - // create a chart_by_name index - data.charts_by_name = {}; - var charts = data.charts; - var x; - for(x in charts) { - var chart = charts[x]; - data.charts_by_name[chart.name] = chart; + if(data != null) { + if(typeof data.custom_info !== 'undefined' && data.custom_info !== "") { + loadJs(data.custom_info, function () { + $.extend(true, netdataDashboard, customDashboard); + initializeDynamicDashboardWithData(data); + }); + } + else { + initializeDynamicDashboardWithData(data); } - - // render all charts - renderChartsAndMenu(data); } }); }); @@ -2126,8 +2358,23 @@ document.getElementById('versionCheckLog').innerHTML = msg; } - function getNetdataVersion(callback) { - versionLog('Downloading installed version info from netdata...'); + function getNetdataCommitIdFromVersion() { + var s = options.version.split('-'); + + if(s.length !== 3) return null; + if(s[2][0] == 'g') { + var v = s[2].split('_')[0].substring(1, 8); + if(v.length === 7) { + versionLog('Installed git commit id of netdata is ' + v); + document.getElementById('netdataCommitId').innerHTML = v; + return v; + } + } + return null; + } + + function getNetdataCommitId(force, callback) { + versionLog('Downloading installed git commit id from netdata...'); $.ajax({ url: 'version.txt', @@ -2138,23 +2385,31 @@ .done(function(data) { data = data.replace(/(\r\n|\n|\r| |\t)/gm,""); if(data.length !== 40) { - versionLog('Received version string is invalid.'); - callback(null); + var c = getNetdataCommitIdFromVersion(); + if(c === null) versionLog('Cannot find the git commit id of netdata.'); + callback(c); } else { - versionLog('Installed version of netdata is ' + data); - document.getElementById('netdataVersion').innerHTML = data; + versionLog('Installed git commit id of netdata is ' + data); + document.getElementById('netdataCommitId').innerHTML = data.substring(0, 7); callback(data); } }) .fail(function() { - versionLog('Failed to download installed version info from netdata!'); - callback(null); + versionLog('Failed to download installed git commit id from netdata!'); + + if(force === true) { + var c = getNetdataCommitIdFromVersion(); + if(c === null) versionLog('Cannot find the git commit id of netdata.'); + callback(c); + } + else + callback(null); }); } function getGithubLatestCommit(callback) { - versionLog('Downloading latest version info from github...'); + versionLog('Downloading latest git commit id info from github...'); $.ajax({ url: 'https://api.github.com/repos/firehol/netdata/commits', @@ -2162,17 +2417,17 @@ cache: false }) .done(function(data) { - versionLog('Latest version info from github is ' + data[0].sha); + versionLog('Latest git commit id from github is ' + data[0].sha); callback(data[0].sha); }) .fail(function() { - versionLog('Failed to download installed version info from github!'); + versionLog('Failed to download installed git commit id from github!'); callback(null); }); } - function checkForUpdate(callback) { - getNetdataVersion(function(sha1) { + function checkForUpdate(force, callback) { + getNetdataCommitId(force, function(sha1) { if(sha1 === null) callback(null, null); getGithubLatestCommit(function(sha2) { @@ -2202,26 +2457,26 @@ } } - checkForUpdate(function(sha1, sha2) { + checkForUpdate(force, function(sha1, sha2) { var save = false; if(sha1 === null) { save = false; - versionLog('

    Failed to get your netdata version!

    You can always get the latest version of netdata from its github page.

    '); + versionLog('

    Failed to get your netdata git commit id!

    You can always get the latest netdata from its github page.

    '); } else if(sha2 === null) { save = false; - versionLog('

    Failed to get the latest version from github.

    You can always get the latest version of netdata from its github page.

    '); + versionLog('

    Failed to get the latest git commit id from github.

    You can always get the latest netdata from its github page.

    '); } else if(sha1 === sha2) { save = true; - versionLog('

    You already have the latest version of netdata!

    No update yet?
    Probably, we need some motivation to keep going on!

    If you haven\'t already, give netdata a Star at its github page.

    '); + versionLog('

    You already have the latest netdata!

    No update yet?
    Probably, we need some motivation to keep going on!

    If you haven\'t already, give netdata a Star at its github page.

    '); } else { save = true; var compare = 'https://github.com/firehol/netdata/compare/' + sha1.toString() + '...' + sha2.toString(); - versionLog('

    New version of netdata available!

    Latest version: ' + sha2.toString() + '

    Click here for the changes log since your installed version, and
    click here for directions on updating your netdata installation.

    We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
    Keeping your netdata updated, is generally a good idea.

    '); + versionLog('

    New version of netdata available!

    Latest commit: ' + sha2.substring(0, 7).toString() + '

    Click here for the changes log since your installed version, and
    click here for directions on updating your netdata installation.

    We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
    Keeping your netdata updated, is generally a good idea.

    '); document.getElementById('update_badge').innerHTML = '!'; } @@ -2348,7 +2603,7 @@ { //console.log('They were open tags'); //console.log(openTags); - for (j = 0; j < openTags.length; j++) { + for (var j = 0; j < openTags.length; j++) { //console.log('Cierro tag ' + openTags[j]); bag += ''; // Close all tags that were opened @@ -2394,8 +2649,9 @@ //console.log('hash = ' + urlOptions.hash); } + var $sidebar = $('#sidebar'); /* activate bootstrap sidebar (affix) */ - $('#sidebar').affix({ + $sidebar.affix({ offset: { top: (isdemo())?150:0, bottom: 0 @@ -2405,7 +2661,7 @@ /* fix scrolling of very long affix lists http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long */ - $('#sidebar').on('affixed.bs.affix', function() { + $sidebar.on('affixed.bs.affix', function() { $(this).removeAttr('style'); }); @@ -2416,7 +2672,7 @@ }); // change the URL based on the current position of the screen - $('#sidebar').on('activate.bs.scrollspy', function (e) { + $sidebar.on('activate.bs.scrollspy', function (e) { // console.log(e); var el = $(e.target); //if(el.find('ul').size() == 0) { @@ -2443,13 +2699,13 @@ // console.log('switching ' + option.toString()); self.bootstrapToggle(NETDATA.getOption(option)?'on':'off'); } - } + }; var theme_sync_option = function(option) { var self = $('#' + option); self.bootstrapToggle(netdataTheme === 'slate'?'on':'off'); - } + }; sync_option('eliminate_zero_dimensions'); sync_option('destroy_on_hide'); @@ -2507,19 +2763,21 @@ netdataReload(); }); - $('#updateModal').on('show.bs.modal', function() { + var $updateModal = $('#updateModal'); + $updateModal.on('show.bs.modal', function() { versionLog('checking, please wait...'); }); - $('#updateModal').on('shown.bs.modal', function() { + $updateModal.on('shown.bs.modal', function() { notifyForUpdate(true); }); - $('#alarmsModal').on('shown.bs.modal', function() { + var $alarmsModal = $('#alarmsModal'); + $alarmsModal.on('shown.bs.modal', function() { NETDATA.pause(alarmsUpdateModal); }); - $('#alarmsModal').on('hidden.bs.modal', function() { + $alarmsModal.on('hidden.bs.modal', function() { NETDATA.unpause(); document.getElementById('alarms_active').innerHTML = document.getElementById('alarms_all').innerHTML = @@ -2635,7 +2893,7 @@ }); NETDATA.requiredJs.push({ - url: NETDATA.serverDefault + 'dashboard_info.js?v20170115-1', + url: NETDATA.serverDefault + 'dashboard_info.js?v20170308-1', async: false, isAlreadyLoaded: function() { return false; } }); @@ -3147,7 +3405,8 @@ - +