2021-09-24 23:37:27 +08:00
// Main JavaScript file for the Netdata GUI.
2022-05-23 16:02:11 +08:00
// 翻译 By Jason
2021-09-24 23:37:27 +08:00
// Codacy declarations
/* global NETDATA */
// netdata snapshot data
var netdataSnapshotData = null ;
// enable alarms checking and notifications
var netdataShowAlarms = true ;
// enable registry updates
var netdataRegistry = true ;
// forward definition only - not used here
var netdataServer = undefined ;
var netdataServerStatic = undefined ;
var netdataCheckXSS = undefined ;
// control the welcome modal and analytics
var this _is _demo = null ;
function escapeUserInputHTML ( s ) {
return s . toString ( )
. replace ( /&/g , '&' )
. replace ( /</g , '<' )
. replace ( />/g , '>' )
. replace ( /"/g , '"' )
. replace ( /#/g , '#' )
. replace ( /'/g , ''' )
. replace ( /\(/g , '(' )
. replace ( /\)/g , ')' )
. replace ( /\//g , '/' ) ;
}
function verifyURL ( s ) {
if ( typeof ( s ) === 'string' && ( s . startsWith ( 'http://' ) || s . startsWith ( 'https://' ) ) ) {
return s
. replace ( /'/g , '%22' )
. replace ( /"/g , '%27' )
. replace ( /\)/g , '%28' )
. replace ( /\(/g , '%29' ) ;
}
console . log ( 'invalid URL detected:' ) ;
console . log ( s ) ;
return 'javascript:alert("invalid url");' ;
}
// --------------------------------------------------------------------
// urlOptions
var urlOptions = {
hash : '#' ,
theme : null ,
help : null ,
mode : 'live' , // 'live', 'print'
update _always : false ,
pan _and _zoom : false ,
server : null ,
after : 0 ,
before : 0 ,
highlight : false ,
highlight _after : 0 ,
highlight _before : 0 ,
nowelcome : false ,
show _alarms : false ,
chart : null ,
family : null ,
alarm : null ,
alarm _unique _id : 0 ,
alarm _id : 0 ,
alarm _event _id : 0 ,
alarm _when : 0 ,
hasProperty : function ( property ) {
// console.log('checking property ' + property + ' of type ' + typeof(this[property]));
return typeof this [ property ] !== 'undefined' ;
} ,
genHash : function ( forReload ) {
var hash = urlOptions . hash ;
if ( urlOptions . pan _and _zoom === true ) {
hash += ';after=' + urlOptions . after . toString ( ) +
';before=' + urlOptions . before . toString ( ) ;
}
if ( urlOptions . highlight === true ) {
hash += ';highlight_after=' + urlOptions . highlight _after . toString ( ) +
';highlight_before=' + urlOptions . highlight _before . toString ( ) ;
}
if ( urlOptions . theme !== null ) {
hash += ';theme=' + urlOptions . theme . toString ( ) ;
}
if ( urlOptions . help !== null ) {
hash += ';help=' + urlOptions . help . toString ( ) ;
}
if ( urlOptions . update _always === true ) {
hash += ';update_always=true' ;
}
if ( forReload === true && urlOptions . server !== null ) {
hash += ';server=' + urlOptions . server . toString ( ) ;
}
if ( urlOptions . mode !== 'live' ) {
hash += ';mode=' + urlOptions . mode ;
}
return hash ;
} ,
parseHash : function ( ) {
var variables = document . location . hash . split ( ';' ) ;
var len = variables . length ;
while ( len -- ) {
if ( len !== 0 ) {
var p = variables [ len ] . split ( '=' ) ;
if ( urlOptions . hasProperty ( p [ 0 ] ) && typeof p [ 1 ] !== 'undefined' ) {
urlOptions [ p [ 0 ] ] = decodeURIComponent ( p [ 1 ] ) ;
}
} else {
if ( variables [ len ] . length > 0 ) {
urlOptions . hash = variables [ len ] ;
}
}
}
var booleans = [ 'nowelcome' , 'show_alarms' , 'update_always' ] ;
len = booleans . length ;
while ( len -- ) {
if ( urlOptions [ booleans [ len ] ] === 'true' || urlOptions [ booleans [ len ] ] === true || urlOptions [ booleans [ len ] ] === '1' || urlOptions [ booleans [ len ] ] === 1 ) {
urlOptions [ booleans [ len ] ] = true ;
} else {
urlOptions [ booleans [ len ] ] = false ;
}
}
var numeric = [ 'after' , 'before' , 'highlight_after' , 'highlight_before' , 'alarm_when' ] ;
len = numeric . length ;
while ( len -- ) {
if ( typeof urlOptions [ numeric [ len ] ] === 'string' ) {
try {
urlOptions [ numeric [ len ] ] = parseInt ( urlOptions [ numeric [ len ] ] ) ;
}
catch ( e ) {
console . log ( 'failed to parse URL hash parameter ' + numeric [ len ] ) ;
urlOptions [ numeric [ len ] ] = 0 ;
}
}
}
if ( urlOptions . alarm _when ) {
// if alarm_when exists, create after/before params
// -/+ 2 minutes from the alarm, and reload the page
const alarmTime = new Date ( urlOptions . alarm _when * 1000 ) . valueOf ( ) ;
const timeMarginMs = 120000 ; // 2 mins
const after = alarmTime - timeMarginMs ;
const before = alarmTime + timeMarginMs ;
const newHash = document . location . hash . replace (
/;alarm_when=[0-9]*/i ,
";after=" + after + ";before=" + before ,
) ;
history . replaceState ( null , '' , newHash ) ;
location . reload ( ) ;
}
if ( urlOptions . server !== null && urlOptions . server !== '' ) {
netdataServerStatic = document . location . origin . toString ( ) + document . location . pathname . toString ( ) ;
netdataServer = urlOptions . server ;
netdataCheckXSS = true ;
} else {
urlOptions . server = null ;
}
if ( urlOptions . before > 0 && urlOptions . after > 0 ) {
urlOptions . pan _and _zoom = true ;
urlOptions . nowelcome = true ;
} else {
urlOptions . pan _and _zoom = false ;
}
if ( urlOptions . highlight _before > 0 && urlOptions . highlight _after > 0 ) {
urlOptions . highlight = true ;
} else {
urlOptions . highlight = false ;
}
switch ( urlOptions . mode ) {
case 'print' :
urlOptions . theme = 'white' ;
urlOptions . welcome = false ;
urlOptions . help = false ;
urlOptions . show _alarms = false ;
if ( urlOptions . pan _and _zoom === false ) {
urlOptions . pan _and _zoom = true ;
urlOptions . before = Date . now ( ) ;
urlOptions . after = urlOptions . before - 600000 ;
}
netdataShowAlarms = false ;
netdataRegistry = false ;
this _is _demo = false ;
break ;
case 'live' :
default :
urlOptions . mode = 'live' ;
break ;
}
// console.log(urlOptions);
} ,
hashUpdate : function ( ) {
history . replaceState ( null , '' , urlOptions . genHash ( true ) ) ;
} ,
netdataPanAndZoomCallback : function ( status , after , before ) {
//console.log(1);
//console.log(new Error().stack);
if ( netdataSnapshotData === null ) {
urlOptions . pan _and _zoom = status ;
urlOptions . after = after ;
urlOptions . before = before ;
urlOptions . hashUpdate ( ) ;
}
} ,
netdataHighlightCallback : function ( status , after , before ) {
//console.log(2);
//console.log(new Error().stack);
if ( status === true && ( after === null || before === null || after <= 0 || before <= 0 || after >= before ) ) {
status = false ;
after = 0 ;
before = 0 ;
}
if ( netdataSnapshotData === null ) {
urlOptions . highlight = status ;
} else {
urlOptions . highlight = false ;
}
urlOptions . highlight _after = Math . round ( after ) ;
urlOptions . highlight _before = Math . round ( before ) ;
urlOptions . hashUpdate ( ) ;
var show _eye = NETDATA . globalChartUnderlay . hasViewport ( ) ;
if ( status === true && after > 0 && before > 0 && after < before ) {
var d1 = NETDATA . dateTime . localeDateString ( after ) ;
var d2 = NETDATA . dateTime . localeDateString ( before ) ;
if ( d1 === d2 ) {
d2 = '' ;
}
document . getElementById ( 'navbar-highlight-content' ) . innerHTML =
( ( show _eye === true ) ? '<span class="navbar-highlight-bar highlight-tooltip" onclick="urlOptions.showHighlight();" title="restore the highlighted view" data-toggle="tooltip" data-placement="bottom">' : '<span>' ) . toString ( )
+ 'highlighted time-frame'
+ ' <b>' + d1 + ' <code>' + NETDATA . dateTime . localeTimeString ( after ) + '</code></b> to '
+ ' <b>' + d2 + ' <code>' + NETDATA . dateTime . localeTimeString ( before ) + '</code></b>, '
+ 'duration <b>' + NETDATA . seconds4human ( Math . round ( ( before - after ) / 1000 ) ) + '</b>'
+ '</span>'
+ '<span class="navbar-highlight-button-right highlight-tooltip" onclick="urlOptions.clearHighlight();" title="clear the highlighted time-frame" data-toggle="tooltip" data-placement="bottom"><i class="fas fa-times"></i></span>' ;
$ ( '.navbar-highlight' ) . show ( ) ;
$ ( '.highlight-tooltip' ) . tooltip ( {
html : true ,
2022-05-23 16:02:11 +08:00
delay : { show : 500 , hide : 0 } ,
2021-09-24 23:37:27 +08:00
container : 'body'
} ) ;
} else {
$ ( '.navbar-highlight' ) . hide ( ) ;
}
} ,
clearHighlight : function ( ) {
NETDATA . globalChartUnderlay . clear ( ) ;
if ( NETDATA . globalPanAndZoom . isActive ( ) === true ) {
NETDATA . globalPanAndZoom . clearMaster ( ) ;
}
} ,
showHighlight : function ( ) {
NETDATA . globalChartUnderlay . focus ( ) ;
}
} ;
urlOptions . parseHash ( ) ;
// --------------------------------------------------------------------
// 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 ) {
console . log ( e ) ;
localStorageTested = false ;
}
} else {
localStorageTested = false ;
}
return localStorageTested ;
}
function loadLocalStorage ( name ) {
var ret = null ;
try {
if ( localStorageTest ( ) === true ) {
ret = localStorage . getItem ( name ) ;
} else {
console . log ( 'localStorage is not available' ) ;
}
}
catch ( error ) {
console . log ( error ) ;
return null ;
}
if ( typeof ret === 'undefined' || ret === null ) {
return null ;
}
// console.log('loaded: ' + name.toString() + ' = ' + ret.toString());
return ret ;
}
function saveLocalStorage ( name , value ) {
// console.log('saving: ' + name.toString() + ' = ' + value.toString());
try {
if ( localStorageTest ( ) === true ) {
localStorage . setItem ( name , value . toString ( ) ) ;
return true ;
}
}
catch ( error ) {
console . log ( error ) ;
}
return false ;
}
function getTheme ( def ) {
if ( urlOptions . mode === 'print' ) {
return 'white' ;
}
var ret = loadLocalStorage ( 'netdataTheme' ) ;
if ( typeof ret === 'undefined' || ret === null || ret === 'undefined' ) {
return def ;
} else {
return ret ;
}
}
function setTheme ( theme ) {
if ( urlOptions . mode === 'print' ) {
return false ;
}
if ( theme === netdataTheme ) {
return false ;
}
return saveLocalStorage ( 'netdataTheme' , theme ) ;
}
var netdataTheme = getTheme ( 'slate' ) ;
var netdataShowHelp = true ;
if ( urlOptions . theme !== null ) {
setTheme ( urlOptions . theme ) ;
netdataTheme = urlOptions . theme ;
} else {
urlOptions . theme = netdataTheme ;
}
if ( urlOptions . help !== null ) {
saveLocalStorage ( 'options.show_help' , urlOptions . help ) ;
netdataShowHelp = urlOptions . help ;
} else {
urlOptions . help = loadLocalStorage ( 'options.show_help' ) ;
}
// --------------------------------------------------------------------
// natural sorting
// http://www.davekoelle.com/files/alphanum.js - LGPL
function naturalSortChunkify ( t ) {
var tz = [ ] ;
var x = 0 , y = - 1 , n = 0 , i , j ;
while ( i = ( j = t . charAt ( x ++ ) ) . charCodeAt ( 0 ) ) {
var m = ( i >= 48 && i <= 57 ) ;
if ( m !== n ) {
tz [ ++ y ] = "" ;
n = m ;
}
tz [ y ] += j ;
}
return tz ;
}
function naturalSortCompare ( a , b ) {
var aa = naturalSortChunkify ( a . toLowerCase ( ) ) ;
var bb = naturalSortChunkify ( b . toLowerCase ( ) ) ;
for ( var x = 0 ; aa [ x ] && bb [ x ] ; x ++ ) {
if ( aa [ x ] !== bb [ x ] ) {
var c = Number ( aa [ x ] ) , d = Number ( bb [ x ] ) ;
if ( c . toString ( ) === aa [ x ] && d . toString ( ) === bb [ x ] ) {
return c - d ;
} else {
return ( aa [ x ] > bb [ x ] ) ? 1 : - 1 ;
}
}
}
return aa . length - bb . length ;
}
// --------------------------------------------------------------------
// saving files to client
function saveTextToClient ( data , filename ) {
var blob = new Blob ( [ data ] , {
type : 'application/octet-stream'
} ) ;
var url = URL . createObjectURL ( blob ) ;
var link = document . createElement ( 'a' ) ;
link . setAttribute ( 'href' , url ) ;
link . setAttribute ( 'download' , filename ) ;
var el = document . getElementById ( 'hiddenDownloadLinks' ) ;
el . innerHTML = '' ;
el . appendChild ( link ) ;
setTimeout ( function ( ) {
el . removeChild ( link ) ;
URL . revokeObjectURL ( url ) ;
} , 60 ) ;
link . click ( ) ;
}
function saveObjectToClient ( data , filename ) {
saveTextToClient ( JSON . stringify ( data ) , filename ) ;
}
// -----------------------------------------------------------------------------
// registry call back to render my-netdata menu
function toggleExpandIcon ( svgEl ) {
if ( svgEl . getAttribute ( 'data-icon' ) === 'caret-down' ) {
svgEl . setAttribute ( 'data-icon' , 'caret-up' ) ;
} else {
svgEl . setAttribute ( 'data-icon' , 'caret-down' ) ;
}
}
function toggleAgentItem ( e , guid ) {
e . stopPropagation ( ) ;
e . preventDefault ( ) ;
toggleExpandIcon ( e . currentTarget . children [ 0 ] ) ;
const el = document . querySelector ( ` .agent-alternate-urls.agent- ${ guid } ` ) ;
if ( el ) {
el . classList . toggle ( 'collapsed' ) ;
}
}
2022-05-23 16:02:11 +08:00
// When you stream metrics from netdata to netdata, the receiving netdata now
2021-09-24 23:37:27 +08:00
// has multiple host databases. It's own, and multiple mirrored. Mirrored databases
// can be accessed with <http://localhost:19999/host/NAME/>
const OLD _DASHBOARD _SUFFIX = "old"
let isOldSuffix = true
try {
const currentScriptMainJs = document . currentScript ;
const mainJsSrc = currentScriptMainJs . getAttribute ( "src" )
isOldSuffix = mainJsSrc . startsWith ( "../main.js" )
} catch {
console . warn ( "current script not detecting, assuming the dashboard is running with /old suffix" )
}
function transformWithOldSuffix ( url ) {
return isOldSuffix ? ` ../ ${ url } ` : url
}
function renderStreamedHosts ( options ) {
let html = ` <div class="info-item">Databases streamed to this agent</div> ` ;
var base = document . location . origin . toString ( ) +
document . location . pathname . toString ( )
. replace ( isOldSuffix ? ` / ${ OLD _DASHBOARD _SUFFIX } ` : "" , "" ) ;
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 ;
// We sort a clone of options.hosts, to keep the master as the first element
// for future calls.
var sorted = options . hosts . slice ( 0 ) . sort ( function ( a , b ) {
if ( a . hostname === master ) {
return - 1 ;
}
return naturalSortCompare ( a . hostname , b . hostname ) ;
} ) ;
let displayedDatabases = false ;
for ( var s of sorted ) {
let url , icon ;
const hostname = s . hostname ;
if ( myNetdataMenuFilterValue !== "" ) {
if ( ! hostname . includes ( myNetdataMenuFilterValue ) ) {
continue ;
}
}
displayedDatabases = true ;
if ( hostname === master ) {
url = isOldSuffix ? ` ${ base } / ${ OLD _DASHBOARD _SUFFIX } / ` : ` ${ base } / ` ;
icon = 'home' ;
} else {
url = isOldSuffix ? ` ${ base } /host/ ${ hostname } / ${ OLD _DASHBOARD _SUFFIX } / ` : ` ${ base } /host/ ${ hostname } / ` ;
icon = 'window-restore' ;
}
html += (
` <div class="agent-item">
< a class = "registry_link" href = "${url}#" onClick = "return gotoHostedModalHandler('${url}');" >
< i class = "fas fa-${icon}" style = "color: #999;" > < / i >
< / a >
< span class = "__title" onClick = "return gotoHostedModalHandler('${url}');" >
< a class = "registry_link" href = "${url}#" > $ { hostname } < / a >
< / s p a n >
< div > < / d i v >
< / d i v > `
)
}
if ( ! displayedDatabases ) {
html += (
` <div class="info-item">
< i class = "fas fa-filter" > < / i >
< span style = "margin-left: 8px" > no databases match the filter criteria . < span >
< / d i v > `
)
}
return html ;
}
function renderMachines ( machinesArray ) {
let html = ` <div class="info-item">My nodes</div> ` ;
if ( machinesArray === null ) {
let ret = loadLocalStorage ( "registryCallback" ) ;
if ( ret ) {
machinesArray = JSON . parse ( ret ) ;
console . log ( "failed to contact the registry - loaded registry data from browser local storage" ) ;
}
}
let found = false ;
let displayedAgents = false ;
const maskedURL = NETDATA . registry . MASKED _DATA ;
if ( machinesArray ) {
saveLocalStorage ( "registryCallback" , JSON . stringify ( machinesArray ) ) ;
var machines = machinesArray . sort ( function ( a , b ) {
return naturalSortCompare ( a . name , b . name ) ;
} ) ;
for ( var machine of machines ) {
found = true ;
if ( myNetdataMenuFilterValue !== "" ) {
if ( ! machine . name . includes ( myNetdataMenuFilterValue ) ) {
continue ;
}
}
displayedAgents = true ;
const alternateUrlItems = (
` <div class="agent-alternate-urls agent- ${ machine . guid } collapsed">
$ { machine . alternate _urls . reduce ( ( str , url ) => {
2022-05-23 16:02:11 +08:00
if ( url === maskedURL ) {
return str
}
2021-09-24 23:37:27 +08:00
2022-05-23 16:02:11 +08:00
return str + (
` <div class="agent-item agent-item--alternate">
2021-09-24 23:37:27 +08:00
< div > < / d i v >
< a href = "${url}" title = "${url}" > $ { truncateString ( url , 64 ) } < / a >
< a href = "#" onclick = "deleteRegistryModalHandler('${machine.guid}', '${machine.name}', '${url}'); return false;" >
< i class = "fas fa-trash" style = "color: #777;" > < / i >
< / a >
< / d i v > `
2022-05-23 16:02:11 +08:00
)
} ,
2021-09-24 23:37:27 +08:00
''
) }
< / d i v > `
)
html += (
` <div class="agent-item agent- ${ machine . guid } ">
< i class = "fas fa-chart-bar" color : # fff " > < / i >
< span class = "__title" onClick = "return gotoServerModalHandler('${machine.guid}');" >
< a class = "registry_link" href = "${machine.url}#" > $ { machine . name } < / a >
< / s p a n >
< a href = "#" onClick = "toggleAgentItem(event, '${machine.guid}');" >
< i class = "fas fa-caret-down" style = "color: #999" > < / i >
< / a >
< / d i v >
$ { alternateUrlItems } `
)
}
if ( found && ( ! displayedAgents ) ) {
html += (
` <div class="info-item">
< i class = "fas fa-filter" > < / i >
< span style = "margin-left: 8px" > zero nodes are matching the filter value . < span >
< / d i v > `
)
}
}
if ( ! found ) {
if ( machines ) {
html += (
` <div class="info-item">
< a href = "https://github.com/netdata/netdata/tree/master/registry#registry" target = "_blank" > Your nodes list is empty < / a >
< / d i v > `
)
} else {
html += (
` <div class="info-item">
< a href = "https://github.com/netdata/netdata/tree/master/registry#registry" target = "_blank" > Failed to contact the registry < / a >
< / d i v > `
)
}
html += ` <hr /> ` ;
html += ` <div class="info-item">Demo netdata nodes</div> ` ;
const demoServers = [
2022-05-23 16:02:11 +08:00
{ url : "//london.netdata.rocks/default.html" , title : "UK - London (DigitalOcean.com)" } ,
{ url : "//newyork.netdata.rocks/default.html" , title : "US - New York (DigitalOcean.com)" } ,
{ url : "//sanfrancisco.netdata.rocks/default.html" , title : "US - San Francisco (DigitalOcean.com)" } ,
{ url : "//atlanta.netdata.rocks/default.html" , title : "US - Atlanta (CDN77.com)" } ,
{ url : "//frankfurt.netdata.rocks/default.html" , title : "Germany - Frankfurt (DigitalOcean.com)" } ,
{ url : "//toronto.netdata.rocks/default.html" , title : "Canada - Toronto (DigitalOcean.com)" } ,
{ url : "//singapore.netdata.rocks/default.html" , title : "Japan - Singapore (DigitalOcean.com)" } ,
{ url : "//bangalore.netdata.rocks/default.html" , title : "India - Bangalore (DigitalOcean.com)" } ,
2021-09-24 23:37:27 +08:00
]
for ( var server of demoServers ) {
html += (
` <div class="agent-item">
< i class = "fas fa-chart-bar" style = "color: #fff" > < / i >
< a href = "${server.url}" > $ { server . title } < / a >
< div > < / d i v >
< / d i v >
`
) ;
}
}
return html ;
}
function setMyNetdataMenu ( html ) {
const el = document . getElementById ( 'my-netdata-dropdown-content' )
el . innerHTML = html ;
}
function clearMyNetdataMenu ( ) {
setMyNetdataMenu ( ` <div class="agent-item" style="white-space: nowrap">
< i class = "fas fa-hourglass-half" > < / i >
Loading , please wait ...
< div > < / d i v >
< / d i v > ` ) ;
}
function errorMyNetdataMenu ( ) {
setMyNetdataMenu ( ` <div class="agent-item" style="padding: 0 8px">
< i class = "fas fa-exclamation-triangle" style = "color: red" > < / i >
Cannot load known Netdata agents from Netdata Cloud ! Please make sure you have the latest version of Netdata .
< / d i v > ` ) ;
}
function restrictMyNetdataMenu ( ) {
setMyNetdataMenu ( ` <div class="info-item" style="white-space: nowrap">
< span > Please < a href = "#" onclick = "signInDidClick(event); return false" > sign in to netdata . cloud < / a > t o v i e w y o u r n o d e s ! < / s p a n >
< div > < / d i v >
< / d i v > ` ) ;
}
function openAuthenticatedUrl ( url ) {
if ( isSignedIn ( ) ) {
window . open ( url ) ;
} else {
window . open ( ` ${ NETDATA . registry . cloudBaseURL } /account/sign-in-agent?id= ${ NETDATA . registry . machine _guid } &name= ${ encodeURIComponent ( NETDATA . registry . hostname ) } &origin= ${ encodeURIComponent ( window . location . origin + "/" ) } &redirect_uri= ${ encodeURIComponent ( window . location . origin + "/" + url ) } ` ) ;
}
}
function renderMyNetdataMenu ( machinesArray ) {
const el = document . getElementById ( 'my-netdata-dropdown-content' ) ;
el . classList . add ( ` theme- ${ netdataTheme } ` ) ;
if ( machinesArray == registryAgents ) {
console . log ( "Rendering my-netdata menu from registry" ) ;
} else {
console . log ( "Rendering my-netdata menu from netdata.cloud" , machinesArray ) ;
}
let html = '' ;
if ( ! isSignedIn ( ) ) {
if ( ! NETDATA . registry . isRegistryEnabled ( ) ) {
html += (
` `
) ;
}
}
if ( isSignedIn ( ) ) {
html += (
` <div class="filter-control">
< input
id = "my-netdata-menu-filter-input"
type = "text"
placeholder = "filter nodes..."
autofocus
autocomplete = "off"
value = "${myNetdataMenuFilterValue}"
onkeydown = "myNetdataFilterDidChange(event)"
/ >
< span class = "filter-control__clear" onclick = "myNetdataFilterClearDidClick(event)" > < i class = "fas fa-times" > < / i > < s p a n >
< / d i v >
< hr / > `
) ;
}
// options.hosts = [
// {
// hostname: "streamed1",
// },
// {
// hostname: "streamed2",
// },
// ]
if ( options . hosts . length > 1 ) {
html += ` <div id="my-netdata-menu-streamed"> ${ renderStreamedHosts ( options ) } </div><hr /> ` ;
}
if ( isSignedIn ( ) || NETDATA . registry . isRegistryEnabled ( ) ) {
html += ` <div id="my-netdata-menu-machines"> ${ renderMachines ( machinesArray ) } </div><hr /> ` ;
}
if ( ! isSignedIn ( ) ) {
html += (
` <div class="agent-item">
< i class = "fas fa-cog" " > < / i >
< a href = "#" onclick = "switchRegistryModalHandler(); return false;" > 更换标识 < / a >
< div > < / d i v >
< / d i v >
< div class = "agent-item" >
< i class = "fas fa-question-circle" " > < / i >
< a href = "https://github.com/netdata/netdata/tree/master/registry#registry" target = "_blank" > 这是什么 ? < / a >
< div > < / d i v >
< / d i v > `
)
} else {
html += (
` <div class="agent-item">
< i class = "fas fa-tv" > < / i >
< a onclick = "openAuthenticatedUrl('console.html');" target = "_blank" > Nodes < sup class = "beta" > beta < / s u p > < / a >
< div > < / d i v >
< / d i v >
< div class = "agent-item" >
< i class = "fas fa-sync" > < / i >
< a href = "#" onclick = "showSyncModal(); return false" > Synchronize with netdata . cloud < / a >
< div > < / d i v >
< / d i v >
< div class = "agent-item" >
< i class = "fas fa-question-circle" " > < / i >
< a href = "https://netdata.cloud/about" target = "_blank" > What is this ? < / a >
< div > < / d i v >
< / d i v > `
)
}
el . innerHTML = html ;
gotoServerInit ( ) ;
}
function isdemo ( ) {
if ( this _is _demo !== null ) {
return this _is _demo ;
}
this _is _demo = false ;
try {
if ( typeof document . location . hostname === 'string' ) {
if ( document . location . hostname . endsWith ( '.my-netdata.io' ) ||
document . location . hostname . endsWith ( '.mynetdata.io' ) ||
document . location . hostname . endsWith ( '.netdata.rocks' ) ||
document . location . hostname . endsWith ( '.netdata.ai' ) ||
document . location . hostname . endsWith ( '.netdata.live' ) ||
document . location . hostname . endsWith ( '.firehol.org' ) ||
document . location . hostname . endsWith ( '.netdata.online' ) ||
document . location . hostname . endsWith ( '.netdata.cloud' ) ) {
this _is _demo = true ;
}
}
}
catch ( error ) {
}
return this _is _demo ;
}
function netdataURL ( url , forReload ) {
if ( typeof url === 'undefined' )
// url = document.location.toString();
{
url = '' ;
}
if ( url . indexOf ( '#' ) !== - 1 ) {
url = url . substring ( 0 , url . indexOf ( '#' ) ) ;
}
var hash = urlOptions . genHash ( forReload ) ;
// console.log('netdataURL: ' + url + hash);
return url + hash ;
}
function netdataReload ( url ) {
document . location = verifyURL ( netdataURL ( url , true ) ) ;
// since we play with hash
// this is needed to reload the page
location . reload ( ) ;
}
function gotoHostedModalHandler ( url ) {
document . location = verifyURL ( url + urlOptions . genHash ( ) ) ;
return false ;
}
var gotoServerValidateRemaining = 0 ;
var gotoServerMiddleClick = false ;
var gotoServerStop = false ;
function gotoServerValidateUrl ( id , guid , url ) {
var penalty = 0 ;
var error = 'failed' ;
if ( document . location . toString ( ) . startsWith ( 'http://' ) && url . toString ( ) . startsWith ( 'https://' ) )
// we penalize https only if the current url is http
// to allow the user walk through all its servers.
{
penalty = 500 ;
} else if ( document . location . toString ( ) . startsWith ( 'https://' ) && url . toString ( ) . startsWith ( 'http://' ) ) {
error = 'can\'t check' ;
}
var finalURL = netdataURL ( url ) ;
setTimeout ( function ( ) {
document . getElementById ( 'gotoServerList' ) . innerHTML += '<tr><td style="padding-left: 20px;"><a href="' + verifyURL ( finalURL ) + '" target="_blank">' + escapeUserInputHTML ( url ) + '</a></td><td style="padding-left: 30px;"><code id="' + guid + '-' + id + '-status">checking...</code></td></tr>' ;
NETDATA . registry . hello ( url , function ( data ) {
if ( typeof data !== 'undefined' && data !== null && typeof data . machine _guid === 'string' && data . machine _guid === guid ) {
// console.log('OK ' + id + ' URL: ' + url);
document . getElementById ( guid + '-' + id + '-status' ) . innerHTML = "OK" ;
if ( ! gotoServerStop ) {
gotoServerStop = true ;
if ( gotoServerMiddleClick ) {
window . open ( verifyURL ( finalURL ) , '_blank' ) ;
gotoServerMiddleClick = false ;
document . getElementById ( 'gotoServerResponse' ) . innerHTML = '<b>Opening new window to ' + NETDATA . registry . machines [ guid ] . name + '<br/><a href="' + verifyURL ( finalURL ) + '">' + escapeUserInputHTML ( url ) + '</a></b><br/>(check your pop-up blocker if it fails)' ;
} else {
document . getElementById ( 'gotoServerResponse' ) . innerHTML += 'found it! It is at:<br/><small>' + escapeUserInputHTML ( url ) + '</small>' ;
document . location = verifyURL ( finalURL ) ;
$ ( '#gotoServerModal' ) . modal ( 'hide' ) ;
}
}
} else {
if ( typeof data !== 'undefined' && data !== null && typeof data . machine _guid === 'string' && data . machine _guid !== guid ) {
error = 'wrong machine' ;
}
document . getElementById ( guid + '-' + id + '-status' ) . innerHTML = error ;
gotoServerValidateRemaining -- ;
if ( gotoServerValidateRemaining <= 0 ) {
gotoServerMiddleClick = false ;
document . getElementById ( 'gotoServerResponse' ) . innerHTML = '<b>Sorry! I cannot find any operational URL for this server</b>' ;
}
}
} ) ;
} , ( id * 50 ) + penalty ) ;
}
function gotoServerModalHandler ( guid ) {
// console.log('goto server: ' + guid);
gotoServerStop = false ;
var checked = { } ;
var len = NETDATA . registry . machines [ guid ] . alternate _urls . length ;
var count = 0 ;
document . getElementById ( 'gotoServerResponse' ) . innerHTML = '' ;
document . getElementById ( 'gotoServerList' ) . innerHTML = '' ;
document . getElementById ( 'gotoServerName' ) . innerHTML = NETDATA . registry . machines [ guid ] . name ;
$ ( '#gotoServerModal' ) . modal ( 'show' ) ;
gotoServerValidateRemaining = len ;
while ( len -- ) {
var url = NETDATA . registry . machines [ guid ] . alternate _urls [ len ] ;
checked [ url ] = true ;
gotoServerValidateUrl ( count ++ , guid , url ) ;
}
if ( ! isSignedIn ( ) ) {
// When the registry is enabled, if the user's known URLs are not working
2022-05-23 16:02:11 +08:00
// we consult the registry to get additional URLs.
2021-09-24 23:37:27 +08:00
setTimeout ( function ( ) {
if ( gotoServerStop === false ) {
document . getElementById ( 'gotoServerResponse' ) . innerHTML = '<b>Added all the known URLs for this machine.</b>' ;
NETDATA . registry . search ( guid , function ( data ) {
// console.log(data);
len = data . urls . length ;
while ( len -- ) {
var url = data . urls [ len ] [ 1 ] ;
// console.log(url);
if ( typeof checked [ url ] === 'undefined' ) {
gotoServerValidateRemaining ++ ;
checked [ url ] = true ;
gotoServerValidateUrl ( count ++ , guid , url ) ;
}
}
} ) ;
}
} , 2000 ) ;
}
return false ;
}
function gotoServerInit ( ) {
$ ( ".registry_link" ) . on ( 'click' , function ( e ) {
if ( e . which === 2 ) {
e . preventDefault ( ) ;
gotoServerMiddleClick = true ;
} else {
gotoServerMiddleClick = false ;
}
return true ;
} ) ;
}
function switchRegistryModalHandler ( ) {
document . getElementById ( 'switchRegistryPersonGUID' ) . value = NETDATA . registry . person _guid ;
document . getElementById ( 'switchRegistryURL' ) . innerHTML = NETDATA . registry . server ;
document . getElementById ( 'switchRegistryResponse' ) . innerHTML = '' ;
$ ( '#switchRegistryModal' ) . modal ( 'show' ) ;
}
function notifyForSwitchRegistry ( ) {
var n = document . getElementById ( 'switchRegistryPersonGUID' ) . value ;
if ( n !== '' && n . length === 36 ) {
NETDATA . registry . switch ( n , function ( result ) {
if ( result !== null ) {
$ ( '#switchRegistryModal' ) . modal ( 'hide' ) ;
NETDATA . registry . init ( ) ;
} else {
document . getElementById ( 'switchRegistryResponse' ) . innerHTML = "<b>Sorry! The registry rejected your request.</b>" ;
}
} ) ;
} else {
document . getElementById ( 'switchRegistryResponse' ) . innerHTML = "<b>The ID you have entered is not a GUID.</b>" ;
}
}
2022-05-23 16:02:11 +08:00
var deleteRegistryGuid = null ;
2021-09-24 23:37:27 +08:00
var deleteRegistryUrl = null ;
function deleteRegistryModalHandler ( guid , name , url ) {
// void (guid);
deleteRegistryGuid = guid ;
deleteRegistryUrl = url ;
document . getElementById ( 'deleteRegistryServerName' ) . innerHTML = name ;
document . getElementById ( 'deleteRegistryServerName2' ) . innerHTML = name ;
document . getElementById ( 'deleteRegistryServerURL' ) . innerHTML = url ;
document . getElementById ( 'deleteRegistryResponse' ) . innerHTML = '' ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
$ ( '#deleteRegistryModal' ) . modal ( 'show' ) ;
}
function notifyForDeleteRegistry ( ) {
const responseEl = document . getElementById ( 'deleteRegistryResponse' ) ;
if ( deleteRegistryUrl ) {
if ( isSignedIn ( ) ) {
deleteCloudAgentURL ( deleteRegistryGuid , deleteRegistryUrl )
. then ( ( count ) => {
if ( ! count ) {
responseEl . innerHTML = "<b>Sorry, this command was rejected by netdata.cloud!</b>" ;
return ;
}
NETDATA . registry . delete ( deleteRegistryUrl , function ( result ) {
if ( result === null ) {
console . log ( "Received error from registry" , result ) ;
}
deleteRegistryUrl = null ;
$ ( '#deleteRegistryModal' ) . modal ( 'hide' ) ;
NETDATA . registry . init ( ) ;
2022-05-23 16:02:11 +08:00
} ) ;
2021-09-24 23:37:27 +08:00
} ) ;
} else {
NETDATA . registry . delete ( deleteRegistryUrl , function ( result ) {
if ( result !== null ) {
deleteRegistryUrl = null ;
$ ( '#deleteRegistryModal' ) . modal ( 'hide' ) ;
NETDATA . registry . init ( ) ;
} else {
responseEl . innerHTML = "<b>Sorry, this command was rejected by the registry server!</b>" ;
}
2022-05-23 16:02:11 +08:00
} ) ;
2021-09-24 23:37:27 +08:00
}
}
}
var options = {
menus : { } ,
submenu _names : { } ,
data : null ,
hostname : 'netdata_server' , // will be overwritten by the netdata server
version : 'unknown' ,
release _channel : 'unknown' ,
hosts : [ ] ,
duration : 0 , // the default duration of the charts
update _every : 1 ,
chartsPerRow : 0 ,
// chartsMinWidth: 1450,
chartsHeight : 180 ,
} ;
function chartsPerRow ( total ) {
void ( total ) ;
if ( options . chartsPerRow === 0 ) {
return 1 ;
//var width = Math.floor(total / options.chartsMinWidth);
//if(width === 0) width = 1;
//return width;
} else {
return options . chartsPerRow ;
}
}
function prioritySort ( a , b ) {
if ( a . priority < b . priority ) {
return - 1 ;
}
if ( a . priority > b . priority ) {
return 1 ;
}
return naturalSortCompare ( a . name , b . name ) ;
}
function sortObjectByPriority ( object ) {
var idx = { } ;
var sorted = [ ] ;
for ( var i in object ) {
if ( ! object . hasOwnProperty ( i ) ) {
continue ;
}
if ( typeof idx [ i ] === 'undefined' ) {
idx [ i ] = object [ i ] ;
sorted . push ( i ) ;
}
}
sorted . sort ( function ( a , b ) {
if ( idx [ a ] . priority < idx [ b ] . priority ) {
return - 1 ;
}
if ( idx [ a ] . priority > idx [ b ] . priority ) {
return 1 ;
}
return naturalSortCompare ( a , b ) ;
} ) ;
return sorted ;
}
// ----------------------------------------------------------------------------
// scroll to a section, without changing the browser history
function scrollToId ( hash ) {
if ( hash && hash !== '' && document . getElementById ( hash ) !== null ) {
var offset = $ ( '#' + hash ) . offset ( ) ;
if ( typeof offset !== 'undefined' ) {
//console.log('scrolling to ' + hash + ' at ' + offset.top.toString());
2022-05-23 16:02:11 +08:00
$ ( 'html, body' ) . animate ( { scrollTop : offset . top - 30 } , 0 ) ;
2021-09-24 23:37:27 +08:00
}
}
// we must return false to prevent the default action
return false ;
}
// ----------------------------------------------------------------------------
// 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' ) {
2022-05-23 16:02:11 +08:00
this . sparklines _registry [ key ] = { count : 1 } ;
2021-09-24 23:37:27 +08:00
} else {
this . sparklines _registry [ key ] . count ++ ;
}
key = key + '.' + this . sparklines _registry [ key ] . count ;
return prefix + '<div class="netdata-container" data-netdata="' + chart + '" data-after="-120" data-width="25%" data-height="15px" data-chart-library="dygraph" data-dygraph-theme="sparkline" data-dimensions="' + dimension + '" data-show-value-of-' + dimension + '-at="' + key + '"></div> (<span id="' + key + '" style="display: inline-block; min-width: 50px; text-align: right;">X</span>' + units + ')' + suffix ;
} ,
gaugeChart : function ( title , width , dimensions , colors ) {
if ( typeof colors === 'undefined' ) {
colors = '' ;
}
if ( typeof dimensions === 'undefined' ) {
dimensions = '' ;
}
return '<div class="netdata-container" data-netdata="CHART_UNIQUE_ID"'
+ ' 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"></div>' ;
} ,
anyAttribute : function ( obj , attr , key , def ) {
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 ( 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 ( this . anyAttribute ( this . menu , 'title' , chart . menu , chart . menu ) ) . toString ( ) . replace ( /_/g , ' ' ) ;
} ,
menuIcon : function ( chart ) {
if ( typeof chart . menu _pattern !== 'undefined' ) {
return this . anyAttribute ( this . menu , 'icon' , chart . menu _pattern , '<i class="fas fa-puzzle-piece"></i>' ) . toString ( ) ;
}
return this . anyAttribute ( this . menu , 'icon' , chart . menu , '<i class="fas fa-puzzle-piece"></i>' ) ;
} ,
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 ( 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 ;
// 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 ) ;
return a + '...' + b ;
}
return title ;
} ,
submenuInfo : function ( menu , submenu ) {
var key = menu + '.' + submenu ;
return this . anyAttribute ( this . submenu , 'info' , key , null ) ;
} ,
submenuHeight : function ( menu , submenu , relative ) {
var key = menu + '.' + submenu ;
return this . anyAttribute ( this . submenu , 'height' , key , 1.0 ) * relative ;
} ,
contextInfo : function ( id ) {
var x = this . anyAttribute ( this . context , 'info' , id , null ) ;
if ( x !== null ) {
return '<div class="shorten dashboard-context-info netdata-chart-alignment" role="document">' + x + '</div>' ;
} else {
return '' ;
}
} ,
contextValueRange : function ( id ) {
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 this . context [ id ] !== 'undefined' && typeof this . context [ id ] . height !== 'undefined' ) {
return def * this . context [ id ] . height ;
} else {
return def ;
}
} ,
contextDecimalDigits : function ( id , def ) {
if ( typeof this . context [ id ] !== 'undefined' && typeof this . context [ id ] . decimalDigits !== 'undefined' ) {
return this . context [ id ] . decimalDigits ;
} else {
return def ;
}
}
} ;
// ----------------------------------------------------------------------------
// enrich the data structure returned by netdata
// to reflect our menu system and content
// TODO: this is a shame - we should fix charts naming (issue #807)
function enrichChartData ( chart ) {
var parts = chart . type . split ( '_' ) ;
var tmp = parts [ 0 ] ;
switch ( tmp ) {
case 'ap' :
case 'net' :
case 'disk' :
case 'powersupply' :
case 'statsd' :
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[\._\/-:]*/ ) ) {
chart . menu _pattern = 'cgqemu' ;
} else {
chart . menu _pattern = 'cgroup' ;
}
break ;
case 'go' :
chart . menu = chart . type ;
if ( parts . length > 2 && parts [ 1 ] === 'expvar' ) {
chart . menu _pattern = tmp + '_' + parts [ 1 ] ;
} else if ( parts . length > 1 ) {
chart . menu _pattern = tmp ;
}
break ;
2022-05-23 16:02:11 +08:00
case 'mount' :
if ( parts . length > 2 ) {
chart . menu = tmp + '_' + parts [ 1 ] ;
} else {
chart . menu = tmp ;
}
break ;
2021-09-24 23:37:27 +08:00
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 ;
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' :
chart . menu = tmp ;
// find a name for this device from fireqos info
// we strip '_(in|out)' or '(in|out)_'
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' ) ) ;
} else if ( n . endsWith ( '_out' ) ) {
options . submenu _names [ chart . family ] = n . slice ( 0 , n . lastIndexOf ( '_out' ) ) ;
} else if ( n . startsWith ( 'in_' ) ) {
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
// to have inbound appear before outbound
if ( chart . id . match ( /.*-ifb$/ ) ) {
chart . priority -- ;
}
break ;
default :
chart . menu = chart . type ;
if ( parts . length > 1 ) {
chart . menu _pattern = tmp ;
}
break ;
}
chart . submenu = chart . family ;
}
// ----------------------------------------------------------------------------
function headMain ( os , charts , duration ) {
void ( os ) ;
if ( urlOptions . mode === 'print' ) {
return '' ;
}
var head = '' ;
if ( typeof charts [ 'system.swap' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.swap"'
+ ' data-dimensions="used"'
+ ' data-append-options="percentage"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="Used Swap"'
+ ' data-units="%"'
+ ' data-easypiechart-max-value="100"'
+ ' data-width="9%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-colors="#DD4400"'
+ ' role="application"></div>' ;
}
if ( typeof charts [ 'system.io' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.io"'
+ ' data-dimensions="in"'
2022-05-23 16:02:11 +08:00
+ ' data-chart-library="easypiechart""'
2021-09-24 23:37:27 +08:00
+ ' data-title="磁碟读取"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.io.mainhead"'
+ ' role="application"></div>' ;
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.io"'
+ ' data-dimensions="out"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="磁碟写入"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.io.mainhead"'
+ ' role="application"></div>' ;
}
else if ( typeof charts [ 'system.pgpgio' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.pgpgio"'
+ ' data-dimensions="in"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="磁碟读取"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.pgpgio.mainhead"'
+ ' role="application"></div>' ;
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.pgpgio"'
+ ' data-dimensions="out"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="磁碟写入"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.pgpgio.mainhead"'
+ ' role="application"></div>' ;
}
if ( typeof charts [ 'system.cpu' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.cpu"'
+ ' data-chart-library="gauge"'
+ ' data-title="CPU"'
+ ' data-units="%"'
+ ' data-gauge-max-value="100"'
+ ' data-width="20%"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-colors="' + NETDATA . colors [ 12 ] + '"'
+ ' role="application"></div>' ;
}
if ( typeof charts [ 'system.net' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.net"'
+ ' data-dimensions="received"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="网路流入"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.net.mainhead"'
+ ' role="application"></div>' ;
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.net"'
+ ' data-dimensions="sent"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="网路流出"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.net.mainhead"'
+ ' role="application"></div>' ;
}
else if ( typeof charts [ 'system.ip' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ip"'
+ ' data-dimensions="received"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="IP 流入"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.ip.mainhead"'
+ ' role="application"></div>' ;
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ip"'
+ ' data-dimensions="sent"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="IP 流出"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.ip.mainhead"'
+ ' role="application"></div>' ;
}
else if ( typeof charts [ 'system.ipv4' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv4"'
+ ' data-dimensions="received"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="IPv4 流入"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.ipv4.mainhead"'
+ ' role="application"></div>' ;
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv4"'
+ ' data-dimensions="sent"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="IPv4 流出"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.ipv4.mainhead"'
+ ' role="application"></div>' ;
}
else if ( typeof charts [ 'system.ipv6' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv6"'
+ ' data-dimensions="received"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="IPv6 流入"'
+ ' data-units="kbps"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.ipv6.mainhead"'
+ ' role="application"></div>' ;
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ipv6"'
+ ' data-dimensions="sent"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="IPv6 流出"'
+ ' data-units="kbps"'
+ ' data-width="11%"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-common-units="system.ipv6.mainhead"'
+ ' role="application"></div>' ;
}
if ( typeof charts [ 'system.ram' ] !== 'undefined' ) {
head += '<div class="netdata-container" style="margin-right: 10px;" data-netdata="system.ram"'
+ ' data-dimensions="used|buffers|active|wired"' // active and wired are FreeBSD stats
+ ' data-append-options="percentage"'
+ ' data-chart-library="easypiechart"'
+ ' data-title="已用内存"'
+ ' data-units="%"'
+ ' data-easypiechart-max-value="100"'
+ ' data-width="9%"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-points="' + duration . toString ( ) + '"'
+ ' data-colors="' + NETDATA . colors [ 7 ] + '"'
+ ' role="application"></div>' ;
}
return head ;
}
function generateHeadCharts ( type , chart , duration ) {
if ( urlOptions . mode === 'print' ) {
return '' ;
}
var head = '' ;
var hcharts = netdataDashboard . anyAttribute ( netdataDashboard . context , type , chart . context , [ ] ) ;
if ( hcharts . length > 0 ) {
var hi = 0 , hlen = hcharts . length ;
while ( hi < hlen ) {
if ( typeof hcharts [ hi ] === 'function' ) {
head += hcharts [ hi ] ( netdataDashboard . os , chart . id ) . replace ( /CHART_DURATION/g , duration . toString ( ) ) . replace ( /CHART_UNIQUE_ID/g , chart . id ) ;
} else {
head += hcharts [ hi ] . replace ( /CHART_DURATION/g , duration . toString ( ) ) . replace ( /CHART_UNIQUE_ID/g , chart . id ) ;
}
hi ++ ;
}
}
return head ;
}
function renderPage ( menus , data ) {
var div = document . getElementById ( 'charts_div' ) ;
var pcent _width = Math . floor ( 100 / chartsPerRow ( $ ( div ) . width ( ) ) ) ;
// find the proper duration for per-second updates
var duration = Math . round ( ( $ ( div ) . width ( ) * pcent _width / 100 * data . update _every / 3 ) / 60 ) * 60 ;
options . duration = duration ;
options . update _every = data . update _every ;
var html = '' ;
var sidebar = '<ul class="nav dashboard-sidenav" data-spy="affix" id="sidebar_ul">' ;
var mainhead = headMain ( netdataDashboard . os , data . charts , duration ) ;
// sort the menus
var main = sortObjectByPriority ( menus ) ;
var i = 0 , len = main . length ;
while ( i < len ) {
var menu = main [ i ++ ] ;
// generate an entry at the main menu
var menuid = NETDATA . name2id ( 'menu_' + menu ) ;
sidebar += '<li class=""><a href="#' + menuid + '" onClick="return scrollToId(\'' + menuid + '\');">' + menus [ menu ] . icon + ' ' + menus [ menu ] . title + '</a><ul class="nav">' ;
html += '<div role="section" class="dashboard-section"><div role="sectionhead"><h1 id="' + menuid + '" role="heading">' + menus [ menu ] . icon + ' ' + menus [ menu ] . title + '</h1></div><div role="section" class="dashboard-subsection">' ;
if ( menus [ menu ] . info !== null ) {
html += menus [ menu ] . info ;
}
// console.log(' >> ' + menu + ' (' + menus[menu].priority + '): ' + menus[menu].title);
var shtml = '' ;
var mhead = '<div class="netdata-chart-row">' + mainhead ;
mainhead = '' ;
// sort the submenus of this menu
var sub = sortObjectByPriority ( menus [ menu ] . submenus ) ;
var si = 0 , slen = sub . length ;
while ( si < slen ) {
var submenu = sub [ si ++ ] ;
// generate an entry at the submenu
var submenuid = NETDATA . name2id ( 'menu_' + menu + '_submenu_' + submenu ) ;
sidebar += '<li class><a href="#' + submenuid + '" onClick="return scrollToId(\'' + submenuid + '\');">' + menus [ menu ] . submenus [ submenu ] . title + '</a></li>' ;
shtml += '<div role="section" class="dashboard-section-container" id="' + submenuid + '"><h2 id="' + submenuid + '" class="netdata-chart-alignment" role="heading">' + menus [ menu ] . submenus [ submenu ] . title + '</h2>' ;
if ( menus [ menu ] . submenus [ submenu ] . info !== null ) {
shtml += '<div class="dashboard-submenu-info netdata-chart-alignment" role="document">' + menus [ menu ] . submenus [ submenu ] . info + '</div>' ;
}
var head = '<div class="netdata-chart-row">' ;
var chtml = '' ;
// console.log(' \------- ' + submenu + ' (' + menus[menu].submenus[submenu].priority + '): ' + menus[menu].submenus[submenu].title);
// sort the charts in this submenu of this menu
menus [ menu ] . submenus [ submenu ] . charts . sort ( prioritySort ) ;
var ci = 0 , clen = menus [ menu ] . submenus [ submenu ] . charts . length ;
while ( ci < clen ) {
var chart = menus [ menu ] . submenus [ submenu ] . charts [ ci ++ ] ;
// generate the submenu heading charts
mhead += generateHeadCharts ( 'mainheads' , chart , duration ) ;
head += generateHeadCharts ( 'heads' , chart , duration ) ;
function chartCommonMin ( family , context , units ) {
var x = netdataDashboard . anyAttribute ( netdataDashboard . context , 'commonMin' , context , undefined ) ;
if ( typeof x !== 'undefined' ) {
return ' data-common-min="' + family + '/' + context + '/' + units + '"' ;
} else {
return '' ;
}
}
function chartCommonMax ( family , context , units ) {
var x = netdataDashboard . anyAttribute ( netdataDashboard . context , 'commonMax' , context , undefined ) ;
if ( typeof x !== 'undefined' ) {
return ' data-common-max="' + family + '/' + context + '/' + units + '"' ;
} else {
return '' ;
}
}
// generate the chart
if ( urlOptions . mode === 'print' ) {
chtml += '<div role="row" class="dashboard-print-row">' ;
}
chtml += '<div class="netdata-chartblock-container" style="width: ' + pcent _width . toString ( ) + '%;">' + netdataDashboard . contextInfo ( chart . context ) + '<div class="netdata-container" id="chart_' + NETDATA . name2id ( chart . id ) + '" data-netdata="' + chart . id + '"'
+ ' data-width="100%"'
+ ' data-height="' + netdataDashboard . contextHeight ( chart . context , options . chartsHeight ) . toString ( ) + 'px"'
+ ' data-dygraph-valuerange="' + netdataDashboard . contextValueRange ( chart . context ) + '"'
+ ' data-before="0"'
+ ' data-after="-' + duration . toString ( ) + '"'
+ ' data-id="' + NETDATA . name2id ( options . hostname + '/' + chart . id ) + '"'
+ ' data-colors="' + netdataDashboard . anyAttribute ( netdataDashboard . context , 'colors' , chart . context , '' ) + '"'
+ ' data-decimal-digits="' + netdataDashboard . contextDecimalDigits ( chart . context , - 1 ) + '"'
+ chartCommonMin ( chart . family , chart . context , chart . units )
+ chartCommonMax ( chart . family , chart . context , chart . units )
+ ' role="application"></div></div>' ;
if ( urlOptions . mode === 'print' ) {
chtml += '</div>' ;
}
}
head += '</div>' ;
shtml += head + chtml + '</div>' ;
}
mhead += '</div>' ;
sidebar += '</ul></li>' ;
html += mhead + shtml + '</div></div><hr role="separator"/>' ;
}
const isMemoryModeDbEngine = data . memory _mode === "dbengine" ;
2022-05-23 16:02:11 +08:00
sidebar += '<li class="" style="padding-top:15px;"><a href="https://learn.netdata.cloud/docs/agent/collectors/quickstart/" target="_blank"><i class="fas fa-plus"></i> 加入更多图表</a></li>' ;
sidebar += '<li class=""><a href="https://learn.netdata.cloud/docs/agent/health/quickstart/" target="_blank"><i class="fas fa-plus"></i> 加入更多警报</a></li>' ;
2021-09-24 23:37:27 +08:00
sidebar += '<li class="" style="margin:20px;color:#666;"><small>每 ' +
( ( data . update _every === 1 ) ? '秒' : data . update _every . toString ( ) + ' 秒' ) + ', ' +
'收集<strong>' + data . dimensions _count . toLocaleString ( ) + '</strong> 上的度量 ' +
data . hostname . toString ( ) + ', 把它们呈现在<strong>' +
data . charts _count . toLocaleString ( ) + '</strong> 图表' +
( isMemoryModeDbEngine ? '' : ',' ) + // oxford comma
' 监控<strong>' +
data . alarms _count . toLocaleString ( ) + '</strong> 警报.' ;
if ( ! isMemoryModeDbEngine ) {
sidebar += '<br /> <br />获取更多历史记录 ' +
'<a href="https://learn.netdata.cloud/guides/longer-metrics-storage#using-the-round-robin-database" target=_blank>配置Netdata\'s <strong>历史</strong></a> 或使用 <a href="https://learn.netdata.cloud/docs/agent/database/engine/" target=_blank>DB 引擎.</a>' ;
}
sidebar += '<br/> <br/><strong>netdata</strong><br/>' + data . version . toString ( ) + '</small></li>' ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
sidebar += '</ul>' ;
div . innerHTML = html ;
document . getElementById ( 'sidebar' ) . innerHTML = sidebar ;
if ( urlOptions . highlight === true ) {
NETDATA . globalChartUnderlay . init ( null
, urlOptions . highlight _after
, urlOptions . highlight _before
, ( urlOptions . after > 0 ) ? urlOptions . after : null
, ( urlOptions . before > 0 ) ? urlOptions . before : null
) ;
} else {
NETDATA . globalChartUnderlay . clear ( ) ;
}
if ( urlOptions . mode === 'print' ) {
printPage ( ) ;
} else {
finalizePage ( ) ;
}
}
function renderChartsAndMenu ( data ) {
options . menus = { } ;
options . submenu _names = { } ;
var menus = options . menus ;
var charts = data . charts ;
var m , menu _key ;
for ( var c in charts ) {
if ( ! charts . hasOwnProperty ( c ) ) {
continue ;
}
var chart = charts [ c ] ;
enrichChartData ( chart ) ;
m = chart . menu ;
// create the menu
if ( typeof menus [ m ] === 'undefined' ) {
menus [ m ] = {
menu _pattern : chart . menu _pattern ,
priority : chart . priority ,
submenus : { } ,
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 ;
}
}
menu _key = ( typeof ( menus [ m ] . menu _pattern ) !== 'undefined' ) ? menus [ m ] . menu _pattern : m ;
// create the submenu
if ( typeof menus [ m ] . submenus [ chart . submenu ] === 'undefined' ) {
menus [ m ] . submenus [ chart . submenu ] = {
priority : chart . priority ,
charts : [ ] ,
title : null ,
info : netdataDashboard . submenuInfo ( menu _key , chart . submenu ) ,
height : netdataDashboard . submenuHeight ( menu _key , chart . submenu , menus [ m ] . height )
} ;
} 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 [ 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 ) {
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 {
menu _key = ( typeof ( menus [ m ] . menu _pattern ) !== 'undefined' ) ? menus [ m ] . menu _pattern : m ;
menus [ m ] . submenus [ s ] . title = netdataDashboard . submenuTitle ( menu _key , s ) ;
}
}
}
renderPage ( menus , data ) ;
}
// ----------------------------------------------------------------------------
function loadJs ( url , callback ) {
$ . ajax ( {
url : url . startsWith ( "http" ) ? url : transformWithOldSuffix ( url ) ,
cache : true ,
dataType : "script" ,
2022-05-23 16:02:11 +08:00
xhrFields : { withCredentials : true } // required for the cookie
2021-09-24 23:37:27 +08:00
} )
. fail ( function ( ) {
alert ( 'Cannot load required JS library: ' + url ) ;
} )
. always ( function ( ) {
if ( typeof callback === 'function' ) {
callback ( ) ;
}
} )
}
var clipboardLoaded = false ;
function loadClipboard ( callback ) {
if ( clipboardLoaded === false ) {
clipboardLoaded = true ;
loadJs ( 'lib/clipboard-polyfill-be05dad.js' , callback ) ;
} else {
callback ( ) ;
}
}
var bootstrapTableLoaded = false ;
function loadBootstrapTable ( callback ) {
if ( bootstrapTableLoaded === false ) {
bootstrapTableLoaded = true ;
loadJs ( 'lib/bootstrap-table-1.11.0.min.js' , function ( ) {
loadJs ( 'lib/bootstrap-table-export-1.11.0.min.js' , function ( ) {
loadJs ( 'lib/tableExport-1.6.0.min.js' , callback ) ;
} )
} ) ;
} else {
callback ( ) ;
}
}
var bootstrapSliderLoaded = false ;
function loadBootstrapSlider ( callback ) {
if ( bootstrapSliderLoaded === false ) {
bootstrapSliderLoaded = true ;
loadJs ( 'lib/bootstrap-slider-10.0.0.min.js' , function ( ) {
NETDATA . _loadCSS ( transformWithOldSuffix ( "css/bootstrap-slider-10.0.0.min.css" ) ) ;
callback ( ) ;
} ) ;
} else {
callback ( ) ;
}
}
var lzStringLoaded = false ;
function loadLzString ( callback ) {
if ( lzStringLoaded === false ) {
lzStringLoaded = true ;
loadJs ( 'lib/lz-string-1.4.4.min.js' , callback ) ;
} else {
callback ( ) ;
}
}
var pakoLoaded = false ;
function loadPako ( callback ) {
if ( pakoLoaded === false ) {
pakoLoaded = true ;
loadJs ( 'lib/pako-1.0.6.min.js' , callback ) ;
} else {
callback ( ) ;
}
}
// ----------------------------------------------------------------------------
function clipboardCopy ( text ) {
clipboard . writeText ( text ) ;
}
function clipboardCopyBadgeEmbed ( url ) {
clipboard . writeText ( '<embed src="' + url + '" type="image/svg+xml" height="20"/>' ) ;
}
// ----------------------------------------------------------------------------
function alarmsUpdateModal ( ) {
var active = '<h3>触发警报</h3><table class="table">' ;
var all = '<h3>所有作用中的警报</h3><div class="panel-group" id="alarms_all_accordion" role="tablist" aria-multiselectable="true">' ;
2022-05-23 16:02:11 +08:00
var footer = '<hr/><a href="https://github.com/netdata/netdata/tree/master/web/api/badges#netdata-badges" target="_blank">netdata badges</a> 会自动重新整理。不同颜色分表代表的警报状态: <span style="color: #e05d44"><b> 红色 </b></span> 表示重大, <span style="color:#fe7d37"><b> 橘色 </b></span> 表示警告, <span style="color: #4c1"><b> 绿色 </b></span> 表示良好, <span style="color: #9f9f9f"><b> 灰色 </b></span> 表示未定义 (例如无资料或无状态), <span style="color: #000"><b> 黑色 </b></span> 表示尚未初始化。您可以复制这里的网址并将它们嵌入到任一个网页。<br/>netdata 能够发送这些警报通知。请参阅 <a href="https://github.com/netdata/netdata/blob/master/health/notifications/health_alarm_notify.conf" target="_blank">这个设定档</a> 了解更多资讯。' ;
2021-09-24 23:37:27 +08:00
loadClipboard ( function ( ) {
} ) ;
NETDATA . alarms . get ( 'all' , function ( data ) {
options . alarm _families = [ ] ;
alarmsCallback ( data ) ;
if ( data === null ) {
document . getElementById ( 'alarms_active' ) . innerHTML =
document . getElementById ( 'alarms_all' ) . innerHTML =
2022-05-23 16:02:11 +08:00
document . getElementById ( 'alarms_log' ) . innerHTML =
'failed to load alarm data!' ;
2021-09-24 23:37:27 +08:00
return ;
}
function alarmid4human ( id ) {
if ( id === 0 ) {
return '-' ;
}
return id . toString ( ) ;
}
function timestamp4human ( timestamp , space ) {
if ( timestamp === 0 ) {
return '-' ;
}
if ( typeof space === 'undefined' ) {
space = ' ' ;
}
var t = new Date ( timestamp * 1000 ) ;
var now = new Date ( ) ;
if ( t . toDateString ( ) === now . toDateString ( ) ) {
return t . toLocaleTimeString ( ) ;
}
return t . toLocaleDateString ( ) + space + t . toLocaleTimeString ( ) ;
}
function alarm _lookup _explain ( alarm , chart ) {
var dimensions = ' of all values ' ;
if ( chart . dimensions . length > 1 ) {
dimensions = ' of the sum of all dimensions ' ;
}
if ( typeof alarm . lookup _dimensions !== 'undefined' ) {
var d = alarm . lookup _dimensions . replace ( /|/g , ',' ) ;
var x = d . split ( ',' ) ;
if ( x . length > 1 ) {
dimensions = 'of the sum of dimensions <code>' + alarm . lookup _dimensions + '</code> ' ;
} else {
dimensions = 'of all values of dimension <code>' + alarm . lookup _dimensions + '</code> ' ;
}
}
return '<code>' + alarm . lookup _method + '</code> '
+ dimensions + ', of chart <code>' + alarm . chart + '</code>'
+ ', starting <code>' + NETDATA . seconds4human ( alarm . lookup _after + alarm . lookup _before , { space : ' ' } ) + '</code> and up to <code>' + NETDATA . seconds4human ( alarm . lookup _before , { space : ' ' } ) + '</code>'
+ ( ( alarm . lookup _options ) ? ( ', with options <code>' + alarm . lookup _options . replace ( / /g , ', ' ) + '</code>' ) : '' )
+ '.' ;
}
function alarm _to _html ( alarm , full ) {
var chart = options . data . charts [ alarm . chart ] ;
if ( typeof ( chart ) === 'undefined' ) {
chart = options . data . charts _by _name [ alarm . chart ] ;
if ( typeof ( chart ) === 'undefined' ) {
// this means the charts loaded are incomplete
// probably netdata was restarted and more alarms
// are now available.
console . log ( 'Cannot find chart ' + alarm . chart + ', you probably need to refresh the page.' ) ;
return '' ;
}
}
var has _alarm = ( typeof alarm . warn !== 'undefined' || typeof alarm . crit !== 'undefined' ) ;
var badge _url = NETDATA . alarms . server + '/api/v1/badge.svg?chart=' + alarm . chart + '&alarm=' + alarm . name + '&refresh=auto' ;
var action _buttons = '<br/> <br/>role: <b>' + alarm . recipient + '</b><br/> <br/>'
+ '<div class="action-button ripple" title="click to scroll the dashboard to the chart of this alarm" data-toggle="tooltip" data-placement="bottom" onClick="scrollToChartAfterHidingModal(\'' + alarm . chart + '\', ' + alarm . last _status _change * 1000 + ', \'' + alarm . status + '\'); $(\'#alarmsModal\').modal(\'hide\'); return false;"><i class="fab fa-periscope"></i></div>'
+ '<div class="action-button ripple" title="click to copy to the clipboard the URL of this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopy(\'' + badge _url + '\'); return false;"><i class="far fa-copy"></i></div>'
+ '<div class="action-button ripple" title="click to copy to the clipboard an auto-refreshing <code>embed</code> html element for this badge" data-toggle="tooltip" data-placement="bottom" onClick="clipboardCopyBadgeEmbed(\'' + badge _url + '\'); return false;"><i class="fas fa-copy"></i></div>' ;
var html = '<tr><td class="text-center" style="vertical-align:middle" width="40%"><b>' + alarm . chart + '</b><br/> <br/><embed src="' + badge _url + '" type="image/svg+xml" height="20"/><br/> <br/><span style="font-size: 18px">' + alarm . info + '</span>' + action _buttons + '</td>'
+ '<td><table class="table">'
+ ( ( typeof alarm . warn !== 'undefined' ) ? ( '<tr><td width="10%" style="text-align:right">warning when</td><td><span style="font-family: monospace; color:#fe7d37; font-weight: bold;">' + alarm . warn + '</span></td></tr>' ) : '' )
+ ( ( typeof alarm . crit !== 'undefined' ) ? ( '<tr><td width="10%" style="text-align:right">critical when</td><td><span style="font-family: monospace; color: #e05d44; font-weight: bold;">' + alarm . crit + '</span></td></tr>' ) : '' ) ;
if ( full === true ) {
var units = chart . units ;
if ( units === '%' ) {
units = '%' ;
}
html += ( ( typeof alarm . lookup _after !== 'undefined' ) ? ( '<tr><td width="10%" style="text-align:right">db lookup</td><td>' + alarm _lookup _explain ( alarm , chart ) + '</td></tr>' ) : '' )
+ ( ( typeof alarm . calc !== 'undefined' ) ? ( '<tr><td width="10%" style="text-align:right">calculation</td><td><span style="font-family: monospace;">' + alarm . calc + '</span></td></tr>' ) : '' )
+ ( ( chart . green !== null ) ? ( '<tr><td width="10%" style="text-align:right">green threshold</td><td><code>' + chart . green + ' ' + units + '</code></td></tr>' ) : '' )
+ ( ( chart . red !== null ) ? ( '<tr><td width="10%" style="text-align:right">red threshold</td><td><code>' + chart . red + ' ' + units + '</code></td></tr>' ) : '' ) ;
}
if ( alarm . warn _repeat _every > 0 ) {
html += '<tr><td width="10%" style="text-align:right">repeat warning</td><td>' + NETDATA . seconds4human ( alarm . warn _repeat _every ) + '</td></tr>' ;
}
if ( alarm . crit _repeat _every > 0 ) {
html += '<tr><td width="10%" style="text-align:right">repeat critical</td><td>' + NETDATA . seconds4human ( alarm . crit _repeat _every ) + '</td></tr>' ;
}
var delay = '' ;
if ( ( alarm . delay _up _duration > 0 || alarm . delay _down _duration > 0 ) && alarm . delay _multiplier !== 0 && alarm . delay _max _duration > 0 ) {
if ( alarm . delay _up _duration === alarm . delay _down _duration ) {
delay += '<small><br/>hysteresis ' + NETDATA . seconds4human ( alarm . delay _up _duration , {
space : ' ' ,
negative _suffix : ''
} ) ;
} else {
delay = '<small><br/>hysteresis ' ;
if ( alarm . delay _up _duration > 0 ) {
delay += 'on escalation <code>' + NETDATA . seconds4human ( alarm . delay _up _duration , {
space : ' ' ,
negative _suffix : ''
} ) + '</code>, ' ;
}
if ( alarm . delay _down _duration > 0 ) {
delay += 'on recovery <code>' + NETDATA . seconds4human ( alarm . delay _down _duration , {
space : ' ' ,
negative _suffix : ''
} ) + '</code>, ' ;
}
}
if ( alarm . delay _multiplier !== 1.0 ) {
delay += 'multiplied by <code>' + alarm . delay _multiplier . toString ( ) + '</code>' ;
delay += ', up to <code>' + NETDATA . seconds4human ( alarm . delay _max _duration , {
space : ' ' ,
negative _suffix : ''
} ) + '</code>' ;
}
delay += '</small>' ;
}
html += '<tr><td width="10%" style="text-align:right">check every</td><td>' + NETDATA . seconds4human ( alarm . update _every , {
space : ' ' ,
negative _suffix : ''
} ) + '</td></tr>'
+ ( ( has _alarm === true ) ? ( '<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm . exec + '</span>' + delay + '</td></tr>' ) : '' )
+ '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm . source + '</span></td></tr>'
+ '</table></td></tr>' ;
return html ;
}
function alarm _family _show ( id ) {
var html = '<table class="table">' ;
var family = options . alarm _families [ id ] ;
var len = family . arr . length ;
while ( len -- ) {
var alarm = family . arr [ len ] ;
html += alarm _to _html ( alarm , true ) ;
}
html += '</table>' ;
$ ( '#alarm_all_' + id . toString ( ) ) . html ( html ) ;
enableTooltipsAndPopovers ( ) ;
}
// find the proper family of each alarm
var x , family , alarm ;
var count _active = 0 ;
var count _all = 0 ;
var families = { } ;
var families _sort = [ ] ;
for ( x in data . alarms ) {
if ( ! data . alarms . hasOwnProperty ( x ) ) {
continue ;
}
alarm = data . alarms [ x ] ;
family = alarm . family ;
// find the chart
var chart = options . data . charts [ alarm . chart ] ;
if ( typeof chart === 'undefined' ) {
chart = options . data . charts _by _name [ alarm . chart ] ;
}
// not found - this should never happen!
if ( typeof chart === 'undefined' ) {
console . log ( 'WARNING: alarm ' + x + ' is linked to chart ' + alarm . chart + ', which is not found in the list of chart got from the server.' ) ;
chart = { priority : 9999999 } ;
}
else if ( typeof chart . menu !== 'undefined' && typeof chart . submenu !== 'undefined' )
// the family based on the chart
{
family = chart . menu + ' - ' + chart . submenu ;
}
if ( typeof families [ family ] === 'undefined' ) {
families [ family ] = {
name : family ,
arr : [ ] ,
priority : chart . priority
} ;
families _sort . push ( families [ family ] ) ;
}
if ( chart . priority < families [ family ] . priority ) {
families [ family ] . priority = chart . priority ;
}
families [ family ] . arr . unshift ( alarm ) ;
}
// sort the families, like the dashboard menu does
var families _sorted = families _sort . sort ( function ( a , b ) {
if ( a . priority < b . priority ) {
return - 1 ;
}
if ( a . priority > b . priority ) {
return 1 ;
}
return naturalSortCompare ( a . name , b . name ) ;
} ) ;
var i = 0 ;
var fc = 0 ;
var len = families _sorted . length ;
while ( len -- ) {
family = families _sorted [ i ++ ] . name ;
var active _family _added = false ;
var expanded = 'true' ;
var collapsed = '' ;
var cin = 'in' ;
if ( fc !== 0 ) {
all += "</table></div></div></div>" ;
expanded = 'false' ;
collapsed = 'class="collapsed"' ;
cin = '' ;
}
all += '<div class="panel panel-default"><div class="panel-heading" role="tab" id="alarm_all_heading_' + fc . toString ( ) + '"><h4 class="panel-title"><a ' + collapsed + ' role="button" data-toggle="collapse" data-parent="#alarms_all_accordion" href="#alarm_all_' + fc . toString ( ) + '" aria-expanded="' + expanded + '" aria-controls="alarm_all_' + fc . toString ( ) + '">' + family . toString ( ) + '</a></h4></div><div id="alarm_all_' + fc . toString ( ) + '" class="panel-collapse collapse ' + cin + '" role="tabpanel" aria-labelledby="alarm_all_heading_' + fc . toString ( ) + '" data-alarm-id="' + fc . toString ( ) + '"><div class="panel-body" id="alarm_all_body_' + fc . toString ( ) + '">' ;
options . alarm _families [ fc ] = families [ family ] ;
fc ++ ;
var arr = families [ family ] . arr ;
var c = arr . length ;
while ( c -- ) {
alarm = arr [ c ] ;
if ( alarm . status === 'WARNING' || alarm . status === 'CRITICAL' ) {
if ( ! active _family _added ) {
active _family _added = true ;
active += '<tr><th class="text-center" colspan="2"><h4>' + family + '</h4></th></tr>' ;
}
count _active ++ ;
active += alarm _to _html ( alarm , true ) ;
}
count _all ++ ;
}
}
active += "</table>" ;
if ( families _sorted . length > 0 ) {
all += "</div></div></div>" ;
}
all += "</div>" ;
if ( ! count _active ) {
active += '<div style="width:100%; height: 100px; text-align: center;"><span style="font-size: 50px;"><i class="fas fa-thumbs-up"></i></span><br/>一切正常。没有警报。</div>' ;
} else {
active += footer ;
}
if ( ! count _all ) {
all += "<h4>此系统中没有运行警报。</h4>" ;
} else {
all += footer ;
}
document . getElementById ( 'alarms_active' ) . innerHTML = active ;
document . getElementById ( 'alarms_all' ) . innerHTML = all ;
enableTooltipsAndPopovers ( ) ;
if ( families _sorted . length > 0 ) {
alarm _family _show ( 0 ) ;
}
// register bootstrap events
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 ) ;
} ) ;
$accordion . on ( 'hidden.bs.collapse' , function ( d ) {
var target = $ ( d . target ) ;
var id = $ ( target ) . data ( 'alarm-id' ) ;
$ ( '#alarm_all_' + id . toString ( ) ) . html ( '' ) ;
} ) ;
document . getElementById ( 'alarms_log' ) . innerHTML = '<h3>警报记录</h3><table id="alarms_log_table"></table>' ;
loadBootstrapTable ( function ( ) {
$ ( '#alarms_log_table' ) . bootstrapTable ( {
url : NETDATA . alarms . server + '/api/v1/alarm_log?all' ,
cache : false ,
pagination : true ,
pageSize : 10 ,
showPaginationSwitch : false ,
search : true ,
searchTimeOut : 300 ,
searchAlign : 'left' ,
showColumns : true ,
showExport : true ,
exportDataType : 'basic' ,
exportOptions : {
fileName : 'netdata_alarm_log'
} ,
onClickRow : function ( row , $element , field ) {
void ( field ) ;
void ( $element ) ;
let main _url ;
let common _url = "&host=" + encodeURIComponent ( row [ 'hostname' ] ) + "&chart=" + encodeURIComponent ( row [ 'chart' ] ) + "&family=" + encodeURIComponent ( row [ 'family' ] ) + "&alarm=" + encodeURIComponent ( row [ 'name' ] ) + "&alarm_unique_id=" + row [ 'unique_id' ] + "&alarm_id=" + row [ 'alarm_id' ] + "&alarm_event_id=" + row [ 'alarm_event_id' ] + "&alarm_when=" + row [ 'when' ] ;
if ( NETDATA . registry . isUsingGlobalRegistry ( ) && NETDATA . registry . machine _guid != null ) {
main _url = "https://netdata.cloud/alarms/redirect?agentID=" + NETDATA . registry . machine _guid + common _url ;
} else {
main _url = NETDATA . registry . server + "/goto-host-from-alarm.html?" + common _url ;
}
window . open ( main _url , "_blank" ) ;
} ,
rowStyle : function ( row , index ) {
void ( index ) ;
switch ( row . status ) {
case 'CRITICAL' :
return { classes : 'danger' } ;
break ;
case 'WARNING' :
return { classes : 'warning' } ;
break ;
case 'UNDEFINED' :
return { classes : 'info' } ;
break ;
case 'CLEAR' :
return { classes : 'success' } ;
break ;
}
return { } ;
} ,
showFooter : false ,
showHeader : true ,
showRefresh : true ,
showToggle : false ,
sortable : true ,
silentSort : false ,
columns : [
{
field : 'when' ,
title : '事件日期' ,
valign : 'middle' ,
titleTooltip : 'The date and time the even took place' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return timestamp4human ( value , ' ' ) ;
} ,
align : 'center' ,
switchable : false ,
sortable : true
} ,
{
field : 'hostname' ,
title : '主机' ,
valign : 'middle' ,
titleTooltip : 'The host that generated this event' ,
align : 'center' ,
visible : false ,
sortable : true
} ,
{
field : 'unique_id' ,
title : '唯一 ID' ,
titleTooltip : 'The host unique ID for this event' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return alarmid4human ( value ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'alarm_id' ,
title : '警报 ID' ,
titleTooltip : 'The ID of the alarm that generated this event' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return alarmid4human ( value ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'alarm_event_id' ,
title : '警报事件 ID' ,
titleTooltip : 'The incremental ID of this event for the given alarm' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return alarmid4human ( value ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'chart' ,
title : '图表' ,
titleTooltip : 'The chart the alarm is attached to' ,
align : 'center' ,
valign : 'middle' ,
switchable : false ,
sortable : true
} ,
{
field : 'family' ,
title : 'Family' ,
titleTooltip : 'The family of the chart the alarm is attached to' ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'name' ,
title : '警报' ,
titleTooltip : 'The alarm name that generated this event' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return value . toString ( ) . replace ( /_/g , ' ' ) ;
} ,
align : 'center' ,
valign : 'middle' ,
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' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'value' ,
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
} ,
{
field : 'units' ,
title : '单位' ,
titleTooltip : 'The units of the value of the alarm' ,
align : 'left' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'old_status' ,
title : '先前状态' ,
titleTooltip : 'The status of the alarm, just before this event' ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'status' ,
title : '状态' ,
titleTooltip : 'The status of the alarm, that was set due to this event' ,
align : 'center' ,
valign : 'middle' ,
switchable : false ,
sortable : true
} ,
{
field : 'duration' ,
title : 'Last Duration' ,
titleTooltip : 'The duration the alarm was at its previous state, just before this event' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
2022-05-23 16:02:11 +08:00
return NETDATA . seconds4human ( value , { negative _suffix : '' , space : ' ' , now : 'no time' } ) ;
2021-09-24 23:37:27 +08:00
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'non_clear_duration' ,
title : 'Raised Duration' ,
titleTooltip : 'The duration the alarm was raised, just before this event' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
2022-05-23 16:02:11 +08:00
return NETDATA . seconds4human ( value , { negative _suffix : '' , space : ' ' , now : 'no time' } ) ;
2021-09-24 23:37:27 +08:00
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'recipient' ,
title : 'Recipient' ,
titleTooltip : 'The recipient of this event' ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'processed' ,
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 {
return 'PENDING' ;
}
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'updated' ,
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 {
return 'CURRENT' ;
}
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'updated_by_id' ,
title : 'Updated By ID' ,
titleTooltip : 'The unique ID of the event that obsoleted this one' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return alarmid4human ( value ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'updates_id' ,
title : 'Updates ID' ,
titleTooltip : 'The unique ID of the event obsoleted because of this event' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return alarmid4human ( value ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'exec' ,
title : 'Script' ,
titleTooltip : 'The script to handle the event notification' ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'exec_run' ,
title : 'Script Run At' ,
titleTooltip : 'The date and time the script has been ran' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
return timestamp4human ( value , ' ' ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'exec_code' ,
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 {
return 'FAILED (with code ' + value . toString ( ) + ')' ;
}
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'delay' ,
title : 'Script Delay' ,
titleTooltip : 'The hysteresis of the notification' ,
formatter : function ( value , row , index ) {
void ( row ) ;
void ( index ) ;
2022-05-23 16:02:11 +08:00
return NETDATA . seconds4human ( value , { negative _suffix : '' , space : ' ' , now : 'no time' } ) ;
2021-09-24 23:37:27 +08:00
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
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 ) {
void ( row ) ;
void ( index ) ;
return timestamp4human ( value , ' ' ) ;
} ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'info' ,
title : '说明' ,
titleTooltip : 'A short description of the alarm' ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
} ,
{
field : 'source' ,
title : '警报来源' ,
titleTooltip : 'The source of configuration of the alarm' ,
align : 'center' ,
valign : 'middle' ,
visible : false ,
sortable : true
}
]
} ) ;
// console.log($('#alarms_log_table').bootstrapTable('getOptions'));
} ) ;
} ) ;
}
function alarmsCallback ( data ) {
var count = 0 , x ;
for ( x in data . alarms ) {
if ( ! data . alarms . hasOwnProperty ( x ) ) {
continue ;
}
var alarm = data . alarms [ x ] ;
if ( alarm . status === 'WARNING' || alarm . status === 'CRITICAL' ) {
count ++ ;
}
}
if ( count > 0 ) {
document . getElementById ( 'alarms_count_badge' ) . innerHTML = count . toString ( ) ;
} else {
document . getElementById ( 'alarms_count_badge' ) . innerHTML = '' ;
}
}
function initializeDynamicDashboardWithData ( data ) {
if ( data !== null ) {
options . hostname = data . hostname ;
options . data = data ;
options . version = data . version ;
options . release _channel = data . release _channel ;
netdataDashboard . os = data . os ;
if ( typeof data . hosts !== 'undefined' ) {
options . hosts = data . hosts ;
}
// update the dashboard hostname
document . getElementById ( 'hostname' ) . innerHTML = '<span id="hostnametext">' + options . hostname + ( ( netdataSnapshotData !== null ) ? ' (snap)' : '' ) . toString ( ) + '</span> <strong class="caret">' ;
document . getElementById ( 'hostname' ) . href = NETDATA . serverDefault ;
document . getElementById ( 'netdataVersion' ) . innerHTML = options . version ;
if ( netdataSnapshotData !== null ) {
$ ( '#alarmsButton' ) . hide ( ) ;
$ ( '#updateButton' ) . hide ( ) ;
// $('#loadButton').hide();
$ ( '#saveButton' ) . hide ( ) ;
$ ( '#printButton' ) . hide ( ) ;
}
// update the dashboard title
document . title = options . hostname + ' netdata 仪表板' ;
// 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 ) ;
// Ensure MyNetdata menu is rendered with latest host info #5370
renderMyNetdataMenu ( isSignedIn ( ) ? cloudAgents : registryAgents ) ;
}
}
2022-05-23 16:02:11 +08:00
// an object to keep initialization configuration
2021-09-24 23:37:27 +08:00
// needed due to the async nature of the XSS modal
var initializeConfig = {
url : null ,
custom _info : true ,
} ;
function loadCustomDashboardInfo ( url , callback ) {
loadJs ( url , function ( ) {
$ . extend ( true , netdataDashboard , customDashboard ) ;
callback ( ) ;
} ) ;
}
function initializeChartsAndCustomInfo ( ) {
NETDATA . alarms . callback = alarmsCallback ;
// download all the charts the server knows
NETDATA . chartRegistry . downloadAll ( initializeConfig . url , function ( data ) {
if ( data !== null ) {
if ( initializeConfig . custom _info === true && typeof data . custom _info !== 'undefined' && data . custom _info !== "" && netdataSnapshotData === null ) {
//console.log('loading custom dashboard decorations from server ' + initializeConfig.url);
loadCustomDashboardInfo ( NETDATA . serverDefault + data . custom _info , function ( ) {
initializeDynamicDashboardWithData ( data ) ;
} ) ;
} else {
//console.log('not loading custom dashboard decorations from server ' + initializeConfig.url);
initializeDynamicDashboardWithData ( data ) ;
}
}
} ) ;
}
function xssModalDisableXss ( ) {
//console.log('disabling xss checks');
NETDATA . xss . enabled = false ;
NETDATA . xss . enabled _for _data = false ;
initializeConfig . custom _info = true ;
initializeChartsAndCustomInfo ( ) ;
return false ;
}
function xssModalKeepXss ( ) {
//console.log('keeping xss checks');
NETDATA . xss . enabled = true ;
NETDATA . xss . enabled _for _data = true ;
initializeConfig . custom _info = false ;
initializeChartsAndCustomInfo ( ) ;
return false ;
}
function initializeDynamicDashboard ( netdata _url ) {
if ( typeof netdata _url === 'undefined' || netdata _url === null ) {
netdata _url = NETDATA . serverDefault ;
}
initializeConfig . url = netdata _url ;
// initialize clickable alarms
NETDATA . alarms . chart _div _offset = - 50 ;
NETDATA . alarms . chart _div _id _prefix = 'chart_' ;
NETDATA . alarms . chart _div _animation _duration = 0 ;
NETDATA . pause ( function ( ) {
if ( typeof netdataCheckXSS !== 'undefined' && netdataCheckXSS === true ) {
//$("#loadOverlay").css("display","none");
document . getElementById ( 'netdataXssModalServer' ) . innerText = initializeConfig . url ;
$ ( '#xssModal' ) . modal ( 'show' ) ;
} else {
initializeChartsAndCustomInfo ( ) ;
}
} ) ;
}
// ----------------------------------------------------------------------------
function versionLog ( msg ) {
document . getElementById ( 'versionCheckLog' ) . innerHTML = msg ;
}
// New way of checking for updates, based only on versions
function versionsMatch ( v1 , v2 ) {
if ( v1 == v2 ) {
return true ;
} else {
let s1 = v1 . split ( '.' ) ;
let s2 = v2 . split ( '.' ) ;
// Check major version
let n1 = parseInt ( s1 [ 0 ] . substring ( 1 , 2 ) , 10 ) ;
let n2 = parseInt ( s2 [ 0 ] . substring ( 1 , 2 ) , 10 ) ;
if ( n1 < n2 ) return false ;
else if ( n1 > n2 ) return true ;
// Check minor version
n1 = parseInt ( s1 [ 1 ] , 10 ) ;
n2 = parseInt ( s2 [ 1 ] , 10 ) ;
if ( n1 < n2 ) return false ;
else if ( n1 > n2 ) return true ;
// Split patch: format could be e.g. 0-22-nightly
s1 = s1 [ 2 ] . split ( '-' ) ;
s2 = s2 [ 2 ] . split ( '-' ) ;
n1 = parseInt ( s1 [ 0 ] , 10 ) ;
n2 = parseInt ( s2 [ 0 ] , 10 ) ;
if ( n1 < n2 ) return false ;
else if ( n1 > n2 ) return true ;
n1 = ( s1 . length > 1 ) ? parseInt ( s1 [ 1 ] , 10 ) : 0 ;
n2 = ( s2 . length > 1 ) ? parseInt ( s2 [ 1 ] , 10 ) : 0 ;
if ( n1 < n2 ) return false ;
else return true ;
}
}
function getGithubLatestVersion ( callback ) {
versionLog ( '正在从 github 下载最新版本 ID...' ) ;
$ . ajax ( {
url : 'https://api.github.com/repos/netdata/netdata/releases/latest' ,
async : true ,
cache : false
} )
. done ( function ( data ) {
data = data . tag _name . replace ( /(\r\n|\n|\r| |\t)/gm , "" ) ;
versionLog ( '从 github 取得最新版本是 ' + data ) ;
callback ( data ) ;
} )
. fail ( function ( ) {
2022-05-23 16:02:11 +08:00
versionLog ( '从 github 下载最新版本 ID 失败!' ) ;
2021-09-24 23:37:27 +08:00
callback ( null ) ;
} ) ;
}
function getGCSLatestVersion ( callback ) {
versionLog ( 'Downloading latest version id from GCS...' ) ;
$ . ajax ( {
url : "https://www.googleapis.com/storage/v1/b/netdata-nightlies/o/latest-version.txt" ,
async : true ,
cache : false
} )
. done ( function ( response ) {
$ . ajax ( {
url : response . mediaLink ,
async : true ,
cache : false
} )
. done ( function ( data ) {
data = data . replace ( /(\r\n|\n|\r| |\t)/gm , "" ) ;
versionLog ( 'Latest nightly version from GCS is ' + data ) ;
callback ( data ) ;
} )
. fail ( function ( xhr , textStatus , errorThrown ) {
versionLog ( 'Failed to download the latest nightly version id from GCS!' ) ;
callback ( null ) ;
} ) ;
} )
. fail ( function ( xhr , textStatus , errorThrown ) {
versionLog ( 'Failed to download the latest nightly version from GCS!' ) ;
callback ( null ) ;
} ) ;
}
function checkForUpdateByVersion ( force , callback ) {
if ( options . release _channel === 'stable' ) {
getGithubLatestVersion ( function ( sha2 ) {
callback ( options . version , sha2 ) ;
} ) ;
} else {
getGCSLatestVersion ( function ( sha2 ) {
callback ( options . version , sha2 ) ;
} ) ;
}
return null ;
}
function notifyForUpdate ( force ) {
versionLog ( '<p>正在检查更新...</p>' ) ;
var now = Date . now ( ) ;
if ( typeof force === 'undefined' || force !== true ) {
var last = loadLocalStorage ( 'last_update_check' ) ;
if ( typeof last === 'string' ) {
last = parseInt ( last ) ;
} else {
last = 0 ;
}
if ( now - last < 3600000 * 8 ) {
// no need to check it - too soon
return ;
}
}
checkForUpdateByVersion ( force , function ( sha1 , sha2 ) {
var save = false ;
if ( sha1 === null ) {
save = false ;
versionLog ( '<p><big>取得您的 netdata 版本失败!</big></p><p>You can always get the latest netdata from <a href="https://github.com/netdata/netdata" target="_blank">its github page</a>.</p>' ) ;
} else if ( sha2 === null ) {
save = false ;
2022-05-23 16:02:11 +08:00
versionLog ( '<p><big>从 github 取得 netdata 最新版本失败。</big></p><p>您也可以从 <a href="https://github.com/netdata/netdata" target="_blank">its github page</a> 取得最新 netdata 版本。</p>' ) ;
2021-09-24 23:37:27 +08:00
} else if ( versionsMatch ( sha1 , sha2 ) ) {
save = true ;
2022-05-23 16:02:11 +08:00
versionLog ( '<p><big>您已经是最新版本的 netdata! </big></p><p>还没有更新?<br/>或许,我们还需要一些动力继续前进!</p><p>如果您还没有做好更新的准备,请您 <a href="https://github.com/netdata/netdata" target="_blank">到 github 给 netdata <b><i class="fas fa-star"></i></b> at its github page</a>.</p>' ) ;
2021-09-24 23:37:27 +08:00
} else {
save = true ;
var compare = 'https://learn.netdata.cloud/docs/agent/changelog/' ;
versionLog ( '<p><big><strong>New version of netdata available!</strong></big></p><p>Latest version: <b><code>' + sha2 + '</code></b></p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> and<br/><a href="https://github.com/netdata/netdata/tree/master/packaging/installer/UPDATE.md" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated is generally a good idea.</p>' ) ;
document . getElementById ( 'update_badge' ) . innerHTML = '!' ;
}
if ( save ) {
saveLocalStorage ( 'last_update_check' , now . toString ( ) ) ;
}
} ) ;
}
// ----------------------------------------------------------------------------
// printing dashboards
function showPageFooter ( ) {
document . getElementById ( 'footer' ) . style . display = 'block' ;
}
function printPreflight ( ) {
var url = document . location . origin . toString ( ) + document . location . pathname . toString ( ) + document . location . search . toString ( ) + urlOptions . genHash ( ) + ';mode=print' ;
var width = 990 ;
var height = screen . height * 90 / 100 ;
//console.log(url);
//console.log(document.location);
window . open ( url , '' , 'width=' + width . toString ( ) + ',height=' + height . toString ( ) + ',menubar=no,toolbar=no,personalbar=no,location=no,resizable=no,scrollbars=yes,status=no,chrome=yes,centerscreen=yes,attention=yes,dialog=yes' ) ;
$ ( '#printPreflightModal' ) . modal ( 'hide' ) ;
}
function printPage ( ) {
var print _is _rendering = true ;
$ ( '#printModal' ) . on ( 'hide.bs.modal' , function ( e ) {
if ( print _is _rendering === true ) {
e . preventDefault ( ) ;
return false ;
}
return true ;
} ) ;
$ ( '#printModal' ) . on ( 'show.bs.modal' , function ( ) {
var print _options = {
stop _updates _when _focus _is _lost : false ,
update _only _visible : false ,
sync _selection : false ,
eliminate _zero _dimensions : false ,
pan _and _zoom _data _padding : false ,
show _help : false ,
legend _toolbox : false ,
resize _charts : false ,
pixels _per _point : 1
} ;
var x ;
for ( x in print _options ) {
if ( print _options . hasOwnProperty ( x ) ) {
NETDATA . options . current [ x ] = print _options [ x ] ;
}
}
NETDATA . parseDom ( ) ;
showPageFooter ( ) ;
NETDATA . globalSelectionSync . stop ( ) ;
NETDATA . globalPanAndZoom . setMaster ( NETDATA . options . targets [ 0 ] , urlOptions . after , urlOptions . before ) ;
// NETDATA.onresize();
var el = document . getElementById ( 'printModalProgressBar' ) ;
var eltxt = document . getElementById ( 'printModalProgressBarText' ) ;
function update _chart ( idx ) {
var state = NETDATA . options . targets [ -- idx ] ;
var pcent = ( NETDATA . options . targets . length - idx ) * 100 / NETDATA . options . targets . length ;
$ ( el ) . css ( 'width' , pcent + '%' ) . attr ( 'aria-valuenow' , pcent ) ;
eltxt . innerText = Math . round ( pcent ) . toString ( ) + '%, ' + state . id ;
setTimeout ( function ( ) {
state . updateChart ( function ( ) {
NETDATA . options . targets [ idx ] . resizeForPrint ( ) ;
if ( idx > 0 ) {
update _chart ( idx ) ;
} else {
print _is _rendering = false ;
$ ( '#printModal' ) . modal ( 'hide' ) ;
window . print ( ) ;
window . close ( ) ;
}
} )
} , 0 ) ;
}
print _is _rendering = true ;
update _chart ( NETDATA . options . targets . length ) ;
} ) ;
$ ( '#printModal' ) . modal ( 'show' ) ;
}
// --------------------------------------------------------------------
function jsonStringifyFn ( obj ) {
return JSON . stringify ( obj , function ( key , value ) {
return ( typeof value === 'function' ) ? value . toString ( ) : value ;
} ) ;
}
function jsonParseFn ( str ) {
return JSON . parse ( str , function ( key , value ) {
if ( typeof value != 'string' ) {
return value ;
}
return ( value . substring ( 0 , 8 ) == 'function' ) ? eval ( '(' + value + ')' ) : value ;
} ) ;
}
// --------------------------------------------------------------------
var snapshotOptions = {
bytes _per _chart : 2048 ,
compressionDefault : 'pako.deflate.base64' ,
compressions : {
'none' : {
bytes _per _point _memory : 5.2 ,
bytes _per _point _disk : 5.6 ,
compress : function ( s ) {
return s ;
} ,
compressed _length : function ( s ) {
return s . length ;
} ,
uncompress : function ( s ) {
return s ;
}
} ,
'pako.deflate.base64' : {
bytes _per _point _memory : 1.8 ,
bytes _per _point _disk : 1.9 ,
compress : function ( s ) {
2022-05-23 16:02:11 +08:00
return btoa ( pako . deflate ( s , { to : 'string' } ) ) ;
2021-09-24 23:37:27 +08:00
} ,
compressed _length : function ( s ) {
return s . length ;
} ,
uncompress : function ( s ) {
2022-05-23 16:02:11 +08:00
return pako . inflate ( atob ( s ) , { to : 'string' } ) ;
2021-09-24 23:37:27 +08:00
}
} ,
'pako.deflate' : {
bytes _per _point _memory : 1.4 ,
bytes _per _point _disk : 3.2 ,
compress : function ( s ) {
2022-05-23 16:02:11 +08:00
return pako . deflate ( s , { to : 'string' } ) ;
2021-09-24 23:37:27 +08:00
} ,
compressed _length : function ( s ) {
return s . length ;
} ,
uncompress : function ( s ) {
2022-05-23 16:02:11 +08:00
return pako . inflate ( s , { to : 'string' } ) ;
2021-09-24 23:37:27 +08:00
}
} ,
'lzstring.utf16' : {
bytes _per _point _memory : 1.7 ,
bytes _per _point _disk : 2.6 ,
compress : function ( s ) {
return LZString . compressToUTF16 ( s ) ;
} ,
compressed _length : function ( s ) {
return s . length * 2 ;
} ,
uncompress : function ( s ) {
return LZString . decompressFromUTF16 ( s ) ;
}
} ,
'lzstring.base64' : {
bytes _per _point _memory : 2.1 ,
bytes _per _point _disk : 2.3 ,
compress : function ( s ) {
return LZString . compressToBase64 ( s ) ;
} ,
compressed _length : function ( s ) {
return s . length ;
} ,
uncompress : function ( s ) {
return LZString . decompressFromBase64 ( s ) ;
}
} ,
'lzstring.uri' : {
bytes _per _point _memory : 2.1 ,
bytes _per _point _disk : 2.3 ,
compress : function ( s ) {
return LZString . compressToEncodedURIComponent ( s ) ;
} ,
compressed _length : function ( s ) {
return s . length ;
} ,
uncompress : function ( s ) {
return LZString . decompressFromEncodedURIComponent ( s ) ;
}
}
}
} ;
// --------------------------------------------------------------------
// loading snapshots
function loadSnapshotModalLog ( priority , msg ) {
document . getElementById ( 'loadSnapshotStatus' ) . className = "alert alert-" + priority ;
document . getElementById ( 'loadSnapshotStatus' ) . innerHTML = msg ;
}
var tmpSnapshotData = null ;
function loadSnapshot ( ) {
$ ( '#loadSnapshotImport' ) . addClass ( 'disabled' ) ;
if ( tmpSnapshotData === null ) {
loadSnapshotPreflightEmpty ( ) ;
loadSnapshotModalLog ( 'danger' , 'no data have been loaded' ) ;
return ;
}
loadPako ( function ( ) {
loadLzString ( function ( ) {
loadSnapshotModalLog ( 'info' , 'Please wait, activating snapshot...' ) ;
$ ( '#loadSnapshotModal' ) . modal ( 'hide' ) ;
netdataShowAlarms = false ;
netdataRegistry = false ;
netdataServer = tmpSnapshotData . server ;
NETDATA . serverDefault = netdataServer ;
document . getElementById ( 'charts_div' ) . innerHTML = '' ;
document . getElementById ( 'sidebar' ) . innerHTML = '' ;
NETDATA . globalReset ( ) ;
if ( typeof tmpSnapshotData . hash !== 'undefined' ) {
urlOptions . hash = tmpSnapshotData . hash ;
} else {
urlOptions . hash = '#' ;
}
if ( typeof tmpSnapshotData . info !== 'undefined' ) {
var info = jsonParseFn ( tmpSnapshotData . info ) ;
if ( typeof info . menu !== 'undefined' ) {
netdataDashboard . menu = info . menu ;
}
if ( typeof info . submenu !== 'undefined' ) {
netdataDashboard . submenu = info . submenu ;
}
if ( typeof info . context !== 'undefined' ) {
netdataDashboard . context = info . context ;
}
}
if ( typeof tmpSnapshotData . compression !== 'string' ) {
tmpSnapshotData . compression = 'none' ;
}
if ( typeof snapshotOptions . compressions [ tmpSnapshotData . compression ] === 'undefined' ) {
alert ( 'unknown compression method: ' + tmpSnapshotData . compression ) ;
tmpSnapshotData . compression = 'none' ;
}
tmpSnapshotData . uncompress = snapshotOptions . compressions [ tmpSnapshotData . compression ] . uncompress ;
netdataSnapshotData = tmpSnapshotData ;
urlOptions . after = tmpSnapshotData . after _ms ;
urlOptions . before = tmpSnapshotData . before _ms ;
if ( typeof tmpSnapshotData . highlight _after _ms !== 'undefined'
&& tmpSnapshotData . highlight _after _ms !== null
&& tmpSnapshotData . highlight _after _ms > 0
&& typeof tmpSnapshotData . highlight _before _ms !== 'undefined'
&& tmpSnapshotData . highlight _before _ms !== null
&& tmpSnapshotData . highlight _before _ms > 0
) {
urlOptions . highlight _after = tmpSnapshotData . highlight _after _ms ;
urlOptions . highlight _before = tmpSnapshotData . highlight _before _ms ;
urlOptions . highlight = true ;
} else {
urlOptions . highlight _after = 0 ;
urlOptions . highlight _before = 0 ;
urlOptions . highlight = false ;
}
netdataCheckXSS = false ; // disable the modal - this does not affect XSS checks, since dashboard.js is already loaded
NETDATA . xss . enabled = true ; // we should not do any remote requests, but if we do, check them
NETDATA . xss . enabled _for _data = true ; // check also snapshot data - that have been excluded from the initial check, due to compression
loadSnapshotPreflightEmpty ( ) ;
initializeDynamicDashboard ( ) ;
} ) ;
} ) ;
} ;
function loadSnapshotPreflightFile ( file ) {
var filename = NETDATA . xss . string ( file . name ) ;
var fr = new FileReader ( ) ;
fr . onload = function ( e ) {
document . getElementById ( 'loadSnapshotFilename' ) . innerHTML = filename ;
var result = null ;
try {
result = NETDATA . xss . checkAlways ( 'snapshot' , JSON . parse ( e . target . result ) , /^(snapshot\.info|snapshot\.data)$/ ) ;
//console.log(result);
var date _after = new Date ( result . after _ms ) ;
var date _before = new Date ( result . before _ms ) ;
if ( typeof result . charts _ok === 'undefined' ) {
result . charts _ok = 'unknown' ;
}
if ( typeof result . charts _failed === 'undefined' ) {
result . charts _failed = 0 ;
}
if ( typeof result . compression === 'undefined' ) {
result . compression = 'none' ;
}
if ( typeof result . data _size === 'undefined' ) {
result . data _size = 0 ;
}
document . getElementById ( 'loadSnapshotFilename' ) . innerHTML = '<code>' + filename + '</code>' ;
document . getElementById ( 'loadSnapshotHostname' ) . innerHTML = '<b>' + result . hostname + '</b>, netdata version: <b>' + result . netdata _version . toString ( ) + '</b>' ;
document . getElementById ( 'loadSnapshotURL' ) . innerHTML = result . url ;
document . getElementById ( 'loadSnapshotCharts' ) . innerHTML = result . charts . charts _count . toString ( ) + ' charts, ' + result . charts . dimensions _count . toString ( ) + ' dimensions, ' + result . data _points . toString ( ) + ' points per dimension, ' + Math . round ( result . duration _ms / result . data _points ) . toString ( ) + ' ms per point' ;
document . getElementById ( 'loadSnapshotInfo' ) . innerHTML = 'version: <b>' + result . snapshot _version . toString ( ) + '</b>, includes <b>' + result . charts _ok . toString ( ) + '</b> unique chart data queries ' + ( ( result . charts _failed > 0 ) ? ( '<b>' + result . charts _failed . toString ( ) + '</b> failed' ) : '' ) . toString ( ) + ', compressed with <code>' + result . compression . toString ( ) + '</code>, data size ' + ( Math . round ( result . data _size * 100 / 1024 / 1024 ) / 100 ) . toString ( ) + ' MB' ;
document . getElementById ( 'loadSnapshotTimeRange' ) . innerHTML = '<b>' + NETDATA . dateTime . localeDateString ( date _after ) + ' ' + NETDATA . dateTime . localeTimeString ( date _after ) + '</b> to <b>' + NETDATA . dateTime . localeDateString ( date _before ) + ' ' + NETDATA . dateTime . localeTimeString ( date _before ) + '</b>' ;
document . getElementById ( 'loadSnapshotComments' ) . innerHTML = ( ( result . comments ) ? result . comments : '' ) . toString ( ) ;
loadSnapshotModalLog ( 'success' , 'File loaded, click <b>Import</b> to render it!' ) ;
$ ( '#loadSnapshotImport' ) . removeClass ( 'disabled' ) ;
tmpSnapshotData = result ;
}
catch ( e ) {
console . log ( e ) ;
document . getElementById ( 'loadSnapshotStatus' ) . className = "alert alert-danger" ;
document . getElementById ( 'loadSnapshotStatus' ) . innerHTML = "Failed to parse this file!" ;
$ ( '#loadSnapshotImport' ) . addClass ( 'disabled' ) ;
}
}
//console.log(file);
fr . readAsText ( file ) ;
} ;
function loadSnapshotPreflightEmpty ( ) {
document . getElementById ( 'loadSnapshotFilename' ) . innerHTML = '' ;
document . getElementById ( 'loadSnapshotHostname' ) . innerHTML = '' ;
document . getElementById ( 'loadSnapshotURL' ) . innerHTML = '' ;
document . getElementById ( 'loadSnapshotCharts' ) . innerHTML = '' ;
document . getElementById ( 'loadSnapshotInfo' ) . innerHTML = '' ;
document . getElementById ( 'loadSnapshotTimeRange' ) . innerHTML = '' ;
document . getElementById ( 'loadSnapshotComments' ) . innerHTML = '' ;
loadSnapshotModalLog ( 'success' , 'Browse for a snapshot file (or drag it and drop it here), then click <b>Import</b> to render it.' ) ;
$ ( '#loadSnapshotImport' ) . addClass ( 'disabled' ) ;
} ;
var loadSnapshotDragAndDropInitialized = false ;
function loadSnapshotDragAndDropSetup ( ) {
if ( loadSnapshotDragAndDropInitialized === false ) {
loadSnapshotDragAndDropInitialized = true ;
$ ( '#loadSnapshotDragAndDrop' )
. on ( 'drag dragstart dragend dragover dragenter dragleave drop' , function ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
} )
. on ( 'drop' , function ( e ) {
if ( e . originalEvent . dataTransfer . files . length ) {
loadSnapshotPreflightFile ( e . originalEvent . dataTransfer . files . item ( 0 ) ) ;
} else {
loadSnapshotPreflightEmpty ( ) ;
loadSnapshotModalLog ( 'danger' , 'No file selected' ) ;
}
} ) ;
}
} ;
function loadSnapshotPreflight ( ) {
var files = document . getElementById ( 'loadSnapshotSelectFiles' ) . files ;
if ( files . length <= 0 ) {
loadSnapshotPreflightEmpty ( ) ;
loadSnapshotModalLog ( 'danger' , 'No file selected' ) ;
return ;
}
loadSnapshotModalLog ( 'info' , 'Loading file...' ) ;
loadSnapshotPreflightFile ( files . item ( 0 ) ) ;
}
// --------------------------------------------------------------------
// saving snapshots
var saveSnapshotStop = false ;
function saveSnapshotCancel ( ) {
saveSnapshotStop = true ;
}
var saveSnapshotModalInitialized = false ;
function saveSnapshotModalSetup ( ) {
if ( saveSnapshotModalInitialized === false ) {
saveSnapshotModalInitialized = true ;
$ ( '#saveSnapshotModal' )
. on ( 'hide.bs.modal' , saveSnapshotCancel )
. on ( 'show.bs.modal' , saveSnapshotModalInit )
. on ( 'shown.bs.modal' , function ( ) {
$ ( '#saveSnapshotResolutionSlider' ) . find ( ".slider-handle:first" ) . attr ( "tabindex" , 1 ) ;
document . getElementById ( 'saveSnapshotComments' ) . focus ( ) ;
} ) ;
}
} ;
function saveSnapshotModalLog ( priority , msg ) {
document . getElementById ( 'saveSnapshotStatus' ) . className = "alert alert-" + priority ;
document . getElementById ( 'saveSnapshotStatus' ) . innerHTML = msg ;
}
function saveSnapshotModalShowExpectedSize ( ) {
var points = Math . round ( saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint ) ;
var priority = 'info' ;
var msg = 'A moderate snapshot.' ;
var sizemb = Math . round (
( options . data . charts _count * snapshotOptions . bytes _per _chart
+ options . data . dimensions _count * points * snapshotOptions . compressions [ saveSnapshotCompression ] . bytes _per _point _disk )
* 10 / 1024 / 1024 ) / 10 ;
var memmb = Math . round (
( options . data . charts _count * snapshotOptions . bytes _per _chart
+ options . data . dimensions _count * points * snapshotOptions . compressions [ saveSnapshotCompression ] . bytes _per _point _memory )
* 10 / 1024 / 1024 ) / 10 ;
if ( sizemb < 10 ) {
priority = 'success' ;
msg = 'A nice small snapshot!' ;
}
if ( sizemb > 50 ) {
priority = 'warning' ;
msg = 'Will stress your browser...' ;
}
if ( sizemb > 100 ) {
priority = 'danger' ;
msg = 'Hm... good luck...' ;
}
saveSnapshotModalLog ( priority , 'The snapshot will have ' + points . toString ( ) + ' points per dimension. Expected size on disk ' + sizemb + ' MB, at browser memory ' + memmb + ' MB.<br/>' + msg ) ;
}
var saveSnapshotCompression = snapshotOptions . compressionDefault ;
function saveSnapshotSetCompression ( name ) {
saveSnapshotCompression = name ;
document . getElementById ( 'saveSnapshotCompressionName' ) . innerHTML = saveSnapshotCompression ;
saveSnapshotModalShowExpectedSize ( ) ;
}
var saveSnapshotSlider = null ;
var saveSnapshotSelectedSecondsPerPoint = 1 ;
var saveSnapshotViewDuration = 1 ;
function saveSnapshotModalInit ( ) {
$ ( '#saveSnapshotModalProgressSection' ) . hide ( ) ;
$ ( '#saveSnapshotResolutionRadio' ) . show ( ) ;
saveSnapshotModalLog ( 'info' , 'Select resolution and click <b>Save</b>' ) ;
$ ( '#saveSnapshotExport' ) . removeClass ( 'disabled' ) ;
loadBootstrapSlider ( function ( ) {
saveSnapshotViewDuration = options . duration ;
var start _ms = Math . round ( Date . now ( ) - saveSnapshotViewDuration * 1000 ) ;
if ( NETDATA . globalPanAndZoom . isActive ( ) === true ) {
saveSnapshotViewDuration = Math . round ( ( NETDATA . globalPanAndZoom . force _before _ms - NETDATA . globalPanAndZoom . force _after _ms ) / 1000 ) ;
start _ms = NETDATA . globalPanAndZoom . force _after _ms ;
}
var start _date = new Date ( start _ms ) ;
var yyyymmddhhssmm = start _date . getFullYear ( ) + NETDATA . zeropad ( start _date . getMonth ( ) + 1 ) + NETDATA . zeropad ( start _date . getDate ( ) ) + '-' + NETDATA . zeropad ( start _date . getHours ( ) ) + NETDATA . zeropad ( start _date . getMinutes ( ) ) + NETDATA . zeropad ( start _date . getSeconds ( ) ) ;
document . getElementById ( 'saveSnapshotFilename' ) . value = 'netdata-' + options . hostname . toString ( ) + '-' + yyyymmddhhssmm . toString ( ) + '-' + saveSnapshotViewDuration . toString ( ) + '.snapshot' ;
saveSnapshotSetCompression ( saveSnapshotCompression ) ;
var min = options . update _every ;
var max = Math . round ( saveSnapshotViewDuration / 100 ) ;
if ( NETDATA . globalPanAndZoom . isActive ( ) === false ) {
max = Math . round ( saveSnapshotViewDuration / 50 ) ;
}
var view = Math . round ( saveSnapshotViewDuration / Math . round ( $ ( document . getElementById ( 'charts_div' ) ) . width ( ) / 2 ) ) ;
// console.log('view duration: ' + saveSnapshotViewDuration + ', min: ' + min + ', max: ' + max + ', view: ' + view);
if ( max < 10 ) {
max = 10 ;
}
if ( max < min ) {
max = min ;
}
if ( view < min ) {
view = min ;
}
if ( view > max ) {
view = max ;
}
if ( saveSnapshotSlider !== null ) {
saveSnapshotSlider . destroy ( ) ;
}
saveSnapshotSlider = new Slider ( '#saveSnapshotResolutionSlider' , {
ticks : [ min , view , max ] ,
min : min ,
max : max ,
step : options . update _every ,
value : view ,
scale : ( max > 100 ) ? 'logarithmic' : 'linear' ,
tooltip : 'always' ,
formatter : function ( value ) {
if ( value < 1 ) {
value = 1 ;
}
if ( value < options . data . update _every ) {
value = options . data . update _every ;
}
saveSnapshotSelectedSecondsPerPoint = value ;
saveSnapshotModalShowExpectedSize ( ) ;
var seconds = ' seconds ' ;
if ( value === 1 ) {
seconds = ' second ' ;
}
return value + seconds + 'per point' + ( ( value === options . data . update _every ) ? ', server default' : '' ) . toString ( ) ;
}
} ) ;
} ) ;
}
function saveSnapshot ( ) {
loadPako ( function ( ) {
loadLzString ( function ( ) {
saveSnapshotStop = false ;
$ ( '#saveSnapshotModalProgressSection' ) . show ( ) ;
$ ( '#saveSnapshotResolutionRadio' ) . hide ( ) ;
$ ( '#saveSnapshotExport' ) . addClass ( 'disabled' ) ;
var filename = document . getElementById ( 'saveSnapshotFilename' ) . value ;
// console.log(filename);
saveSnapshotModalLog ( 'info' , 'Generating snapshot as <code>' + filename . toString ( ) + '</code>' ) ;
var save _options = {
stop _updates _when _focus _is _lost : false ,
update _only _visible : false ,
sync _selection : false ,
eliminate _zero _dimensions : true ,
pan _and _zoom _data _padding : false ,
show _help : false ,
legend _toolbox : false ,
resize _charts : false ,
pixels _per _point : 1
} ;
var backedup _options = { } ;
var x ;
for ( x in save _options ) {
if ( save _options . hasOwnProperty ( x ) ) {
backedup _options [ x ] = NETDATA . options . current [ x ] ;
NETDATA . options . current [ x ] = save _options [ x ] ;
}
}
var el = document . getElementById ( 'saveSnapshotModalProgressBar' ) ;
var eltxt = document . getElementById ( 'saveSnapshotModalProgressBarText' ) ;
options . data . charts _by _name = null ;
var saveData = {
hostname : options . hostname ,
server : NETDATA . serverDefault ,
netdata _version : options . data . version ,
snapshot _version : 1 ,
after _ms : Date . now ( ) - options . duration * 1000 ,
before _ms : Date . now ( ) ,
highlight _after _ms : urlOptions . highlight _after ,
highlight _before _ms : urlOptions . highlight _before ,
duration _ms : options . duration * 1000 ,
update _every _ms : options . update _every * 1000 ,
data _points : 0 ,
url : ( ( urlOptions . server !== null ) ? urlOptions . server : document . location . origin . toString ( ) + document . location . pathname . toString ( ) + document . location . search . toString ( ) ) . toString ( ) ,
comments : document . getElementById ( 'saveSnapshotComments' ) . value . toString ( ) ,
hash : urlOptions . hash ,
charts : options . data ,
info : jsonStringifyFn ( {
menu : netdataDashboard . menu ,
submenu : netdataDashboard . submenu ,
context : netdataDashboard . context
} ) ,
charts _ok : 0 ,
charts _failed : 0 ,
compression : saveSnapshotCompression ,
data _size : 0 ,
data : { }
} ;
if ( typeof snapshotOptions . compressions [ saveData . compression ] === 'undefined' ) {
alert ( 'unknown compression method: ' + saveData . compression ) ;
saveData . compression = 'none' ;
}
var compress = snapshotOptions . compressions [ saveData . compression ] . compress ;
var compressed _length = snapshotOptions . compressions [ saveData . compression ] . compressed _length ;
function pack _api1 _v1 _chart _data ( state ) {
if ( state . library _name === null || state . data === null ) {
return ;
}
var data = state . data ;
state . data = null ;
data . state = null ;
var str = JSON . stringify ( data ) ;
if ( typeof str === 'string' ) {
var cstr = compress ( str ) ;
saveData . data [ state . chartDataUniqueID ( ) ] = cstr ;
return compressed _length ( cstr ) ;
} else {
return 0 ;
}
}
var clearPanAndZoom = false ;
if ( NETDATA . globalPanAndZoom . isActive ( ) === false ) {
NETDATA . globalPanAndZoom . setMaster ( NETDATA . options . targets [ 0 ] , saveData . after _ms , saveData . before _ms ) ;
clearPanAndZoom = true ;
}
saveData . after _ms = NETDATA . globalPanAndZoom . force _after _ms ;
saveData . before _ms = NETDATA . globalPanAndZoom . force _before _ms ;
saveData . duration _ms = saveData . before _ms - saveData . after _ms ;
saveData . data _points = Math . round ( ( saveData . before _ms - saveData . after _ms ) / ( saveSnapshotSelectedSecondsPerPoint * 1000 ) ) ;
saveSnapshotModalLog ( 'info' , 'Generating snapshot with ' + saveData . data _points . toString ( ) + ' data points per dimension...' ) ;
var charts _count = 0 ;
var charts _ok = 0 ;
var charts _failed = 0 ;
function saveSnapshotRestore ( ) {
$ ( '#saveSnapshotModal' ) . modal ( 'hide' ) ;
// restore the options
var x ;
for ( x in backedup _options ) {
if ( backedup _options . hasOwnProperty ( x ) ) {
NETDATA . options . current [ x ] = backedup _options [ x ] ;
}
}
$ ( el ) . css ( 'width' , '0%' ) . attr ( 'aria-valuenow' , 0 ) ;
eltxt . innerText = '0%' ;
if ( clearPanAndZoom ) {
NETDATA . globalPanAndZoom . clearMaster ( ) ;
}
NETDATA . options . force _data _points = 0 ;
NETDATA . options . fake _chart _rendering = false ;
NETDATA . onscroll _updater _enabled = true ;
NETDATA . onresize ( ) ;
NETDATA . unpause ( ) ;
$ ( '#saveSnapshotExport' ) . removeClass ( 'disabled' ) ;
}
NETDATA . globalSelectionSync . stop ( ) ;
NETDATA . options . force _data _points = saveData . data _points ;
NETDATA . options . fake _chart _rendering = true ;
NETDATA . onscroll _updater _enabled = false ;
NETDATA . abortAllRefreshes ( ) ;
var size = 0 ;
var info = ' Resolution: <b>' + saveSnapshotSelectedSecondsPerPoint . toString ( ) + ( ( saveSnapshotSelectedSecondsPerPoint === 1 ) ? ' second ' : ' seconds ' ) . toString ( ) + 'per point</b>.' ;
function update _chart ( idx ) {
if ( saveSnapshotStop === true ) {
saveSnapshotModalLog ( 'info' , 'Cancelled!' ) ;
saveSnapshotRestore ( ) ;
return ;
}
var state = NETDATA . options . targets [ -- idx ] ;
var pcent = ( NETDATA . options . targets . length - idx ) * 100 / NETDATA . options . targets . length ;
$ ( el ) . css ( 'width' , pcent + '%' ) . attr ( 'aria-valuenow' , pcent ) ;
eltxt . innerText = Math . round ( pcent ) . toString ( ) + '%, ' + state . id ;
setTimeout ( function ( ) {
charts _count ++ ;
state . isVisible ( true ) ;
state . current . force _after _ms = saveData . after _ms ;
state . current . force _before _ms = saveData . before _ms ;
state . updateChart ( function ( status , reason ) {
state . current . force _after _ms = null ;
state . current . force _before _ms = null ;
if ( status === true ) {
charts _ok ++ ;
// state.log('ok');
size += pack _api1 _v1 _chart _data ( state ) ;
} else {
charts _failed ++ ;
state . log ( 'failed to be updated: ' + reason ) ;
}
saveSnapshotModalLog ( ( charts _failed ) ? 'danger' : 'info' , 'Generated snapshot data size <b>' + ( Math . round ( size * 100 / 1024 / 1024 ) / 100 ) . toString ( ) + ' MB</b>. ' + ( ( charts _failed ) ? ( charts _failed . toString ( ) + ' charts have failed to be downloaded' ) : '' ) . toString ( ) + info ) ;
if ( idx > 0 ) {
update _chart ( idx ) ;
} else {
saveData . charts _ok = charts _ok ;
saveData . charts _failed = charts _failed ;
saveData . data _size = size ;
// console.log(saveData.compression + ': ' + (size / (options.data.dimensions_count * Math.round(saveSnapshotViewDuration / saveSnapshotSelectedSecondsPerPoint))).toString());
// save it
// console.log(saveData);
saveObjectToClient ( saveData , filename ) ;
if ( charts _failed > 0 ) {
alert ( charts _failed . toString ( ) + ' failed to be downloaded' ) ;
}
saveSnapshotRestore ( ) ;
saveData = null ;
}
} )
} , 0 ) ;
}
update _chart ( NETDATA . options . targets . length ) ;
} ) ;
} ) ;
}
// --------------------------------------------------------------------
// activate netdata on the page
function dashboardSettingsSetup ( ) {
var update _options _modal = function ( ) {
// console.log('update_options_modal');
var sync _option = function ( option ) {
var self = $ ( '#' + option ) ;
if ( self . prop ( 'checked' ) !== NETDATA . getOption ( option ) ) {
// 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' ) ;
} ;
var units _sync _option = function ( option ) {
var self = $ ( '#' + option ) ;
if ( self . prop ( 'checked' ) !== ( NETDATA . getOption ( 'units' ) === 'auto' ) ) {
self . bootstrapToggle ( NETDATA . getOption ( 'units' ) === 'auto' ? 'on' : 'off' ) ;
}
if ( self . prop ( 'checked' ) === true ) {
$ ( '#settingsLocaleTempRow' ) . show ( ) ;
$ ( '#settingsLocaleTimeRow' ) . show ( ) ;
} else {
$ ( '#settingsLocaleTempRow' ) . hide ( ) ;
$ ( '#settingsLocaleTimeRow' ) . hide ( ) ;
}
} ;
var temp _sync _option = function ( option ) {
var self = $ ( '#' + option ) ;
if ( self . prop ( 'checked' ) !== ( NETDATA . getOption ( 'temperature' ) === 'celsius' ) ) {
self . bootstrapToggle ( NETDATA . getOption ( 'temperature' ) === 'celsius' ? 'on' : 'off' ) ;
}
} ;
var timezone _sync _option = function ( option ) {
var self = $ ( '#' + option ) ;
document . getElementById ( 'browser_timezone' ) . innerText = NETDATA . options . browser _timezone ;
document . getElementById ( 'server_timezone' ) . innerText = NETDATA . options . server _timezone ;
document . getElementById ( 'current_timezone' ) . innerText = ( NETDATA . options . current . timezone === 'default' ) ? 'unset, using browser default' : NETDATA . options . current . timezone ;
if ( self . prop ( 'checked' ) === NETDATA . dateTime . using _timezone ) {
self . bootstrapToggle ( NETDATA . dateTime . using _timezone ? 'off' : 'on' ) ;
}
} ;
sync _option ( 'eliminate_zero_dimensions' ) ;
sync _option ( 'destroy_on_hide' ) ;
sync _option ( 'async_on_scroll' ) ;
sync _option ( 'parallel_refresher' ) ;
sync _option ( 'concurrent_refreshes' ) ;
sync _option ( 'sync_selection' ) ;
sync _option ( 'sync_pan_and_zoom' ) ;
sync _option ( 'stop_updates_when_focus_is_lost' ) ;
sync _option ( 'smooth_plot' ) ;
sync _option ( 'pan_and_zoom_data_padding' ) ;
sync _option ( 'show_help' ) ;
sync _option ( 'seconds_as_time' ) ;
theme _sync _option ( 'netdata_theme_control' ) ;
units _sync _option ( 'units_conversion' ) ;
temp _sync _option ( 'units_temp' ) ;
timezone _sync _option ( 'local_timezone' ) ;
if ( NETDATA . getOption ( 'parallel_refresher' ) === false ) {
$ ( '#concurrent_refreshes_row' ) . hide ( ) ;
} else {
$ ( '#concurrent_refreshes_row' ) . show ( ) ;
}
} ;
NETDATA . setOption ( 'setOptionCallback' , update _options _modal ) ;
// handle options changes
$ ( '#eliminate_zero_dimensions' ) . change ( function ( ) {
NETDATA . setOption ( 'eliminate_zero_dimensions' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#destroy_on_hide' ) . change ( function ( ) {
NETDATA . setOption ( 'destroy_on_hide' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#async_on_scroll' ) . change ( function ( ) {
NETDATA . setOption ( 'async_on_scroll' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#parallel_refresher' ) . change ( function ( ) {
NETDATA . setOption ( 'parallel_refresher' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#concurrent_refreshes' ) . change ( function ( ) {
NETDATA . setOption ( 'concurrent_refreshes' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#sync_selection' ) . change ( function ( ) {
NETDATA . setOption ( 'sync_selection' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#sync_pan_and_zoom' ) . change ( function ( ) {
NETDATA . setOption ( 'sync_pan_and_zoom' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#stop_updates_when_focus_is_lost' ) . change ( function ( ) {
urlOptions . update _always = ! $ ( this ) . prop ( 'checked' ) ;
urlOptions . hashUpdate ( ) ;
NETDATA . setOption ( 'stop_updates_when_focus_is_lost' , ! urlOptions . update _always ) ;
} ) ;
$ ( '#smooth_plot' ) . change ( function ( ) {
NETDATA . setOption ( 'smooth_plot' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#pan_and_zoom_data_padding' ) . change ( function ( ) {
NETDATA . setOption ( 'pan_and_zoom_data_padding' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#seconds_as_time' ) . change ( function ( ) {
NETDATA . setOption ( 'seconds_as_time' , $ ( this ) . prop ( 'checked' ) ) ;
} ) ;
$ ( '#local_timezone' ) . change ( function ( ) {
if ( $ ( this ) . prop ( 'checked' ) ) {
selected _server _timezone ( 'default' , true ) ;
} else {
selected _server _timezone ( 'default' , false ) ;
}
} ) ;
$ ( '#units_conversion' ) . change ( function ( ) {
NETDATA . setOption ( 'units' , $ ( this ) . prop ( 'checked' ) ? 'auto' : 'original' ) ;
} ) ;
$ ( '#units_temp' ) . change ( function ( ) {
NETDATA . setOption ( 'temperature' , $ ( this ) . prop ( 'checked' ) ? 'celsius' : 'fahrenheit' ) ;
} ) ;
$ ( '#show_help' ) . change ( function ( ) {
urlOptions . help = $ ( this ) . prop ( 'checked' ) ;
urlOptions . hashUpdate ( ) ;
NETDATA . setOption ( 'show_help' , urlOptions . help ) ;
netdataReload ( ) ;
} ) ;
// this has to be the last
// it reloads the page
$ ( '#netdata_theme_control' ) . change ( function ( ) {
urlOptions . theme = $ ( this ) . prop ( 'checked' ) ? 'slate' : 'white' ;
urlOptions . hashUpdate ( ) ;
if ( setTheme ( urlOptions . theme ) ) {
netdataReload ( ) ;
}
} ) ;
}
function scrollDashboardTo ( ) {
if ( netdataSnapshotData !== null && typeof netdataSnapshotData . hash !== 'undefined' ) {
//console.log(netdataSnapshotData.hash);
scrollToId ( netdataSnapshotData . hash . replace ( '#' , '' ) ) ;
} else {
// check if we have to jump to a specific section
scrollToId ( urlOptions . hash . replace ( '#' , '' ) ) ;
if ( urlOptions . chart !== null ) {
NETDATA . alarms . scrollToChart ( urlOptions . chart ) ;
//urlOptions.hash = '#' + NETDATA.name2id('menu_' + charts[c].menu + '_submenu_' + charts[c].submenu);
//urlOptions.hash = '#chart_' + NETDATA.name2id(urlOptions.chart);
//console.log('hash = ' + urlOptions.hash);
}
}
}
var modalHiddenCallback = null ;
function scrollToChartAfterHidingModal ( chart , alarmDate , alarmStatus ) {
modalHiddenCallback = function ( ) {
NETDATA . alarms . scrollToChart ( chart , alarmDate ) ;
if ( [ 'WARNING' , 'CRITICAL' ] . includes ( alarmStatus ) ) {
const currentChartState = NETDATA . options . targets . find (
( chartState ) => chartState . id === chart ,
)
const twoMinutes = 2 * 60 * 1000
NETDATA . globalPanAndZoom . setMaster (
currentChartState ,
alarmDate - twoMinutes ,
alarmDate + twoMinutes ,
)
}
} ;
}
// ----------------------------------------------------------------------------
function enableTooltipsAndPopovers ( ) {
$ ( '[data-toggle="tooltip"]' ) . tooltip ( {
animated : 'fade' ,
trigger : 'hover' ,
html : true ,
2022-05-23 16:02:11 +08:00
delay : { show : 500 , hide : 0 } ,
2021-09-24 23:37:27 +08:00
container : 'body'
} ) ;
$ ( '[data-toggle="popover"]' ) . popover ( ) ;
}
// ----------------------------------------------------------------------------
var runOnceOnDashboardLastRun = 0 ;
function runOnceOnDashboardWithjQuery ( ) {
if ( runOnceOnDashboardLastRun !== 0 ) {
scrollDashboardTo ( ) ;
// restore the scrollspy at the proper position
$ ( document . body ) . scrollspy ( 'refresh' ) ;
$ ( document . body ) . scrollspy ( 'process' ) ;
return ;
}
runOnceOnDashboardLastRun = Date . now ( ) ;
// ------------------------------------------------------------------------
// bootstrap modals
// prevent bootstrap modals from scrolling the page
// maintains the current scroll position
// https://stackoverflow.com/a/34754029/4525767
var scrollPos = 0 ;
var modal _depth = 0 ; // how many modals are currently open
var modal _shown = false ; // set to true, if a modal is shown
var netdata _paused _on _modal = false ; // set to true, if the modal paused netdata
var scrollspyOffset = $ ( window ) . height ( ) / 3 ; // will be updated below - the offset of scrollspy to select an item
$ ( '.modal' )
. on ( 'show.bs.modal' , function ( ) {
if ( modal _depth === 0 ) {
scrollPos = window . scrollY ;
$ ( 'body' ) . css ( {
overflow : 'hidden' ,
position : 'fixed' ,
top : - scrollPos
} ) ;
modal _shown = true ;
if ( NETDATA . options . pauseCallback === null ) {
NETDATA . pause ( function ( ) {
} ) ;
netdata _paused _on _modal = true ;
} else {
netdata _paused _on _modal = false ;
}
}
modal _depth ++ ;
//console.log(urlOptions.after);
} )
. on ( 'hide.bs.modal' , function ( ) {
modal _depth -- ;
if ( modal _depth <= 0 ) {
modal _depth = 0 ;
$ ( 'body' )
. css ( {
overflow : '' ,
position : '' ,
top : ''
} ) ;
// scroll to the position we had open before the modal
$ ( 'html, body' )
2022-05-23 16:02:11 +08:00
. animate ( { scrollTop : scrollPos } , 0 ) ;
2021-09-24 23:37:27 +08:00
// unpause netdata, if we paused it
if ( netdata _paused _on _modal === true ) {
NETDATA . unpause ( ) ;
netdata _paused _on _modal = false ;
}
// restore the scrollspy at the proper position
$ ( document . body ) . scrollspy ( 'process' ) ;
}
//console.log(urlOptions.after);
} )
. on ( 'hidden.bs.modal' , function ( ) {
if ( modal _depth === 0 ) {
modal _shown = false ;
}
if ( typeof modalHiddenCallback === 'function' ) {
modalHiddenCallback ( ) ;
}
modalHiddenCallback = null ;
//console.log(urlOptions.after);
} ) ;
// ------------------------------------------------------------------------
// sidebar / affix
if ( shouldShowSignInBanner ( ) ) {
const el = document . getElementById ( "sign-in-banner" ) ;
if ( el ) {
el . style . display = "initial" ;
el . classList . add ( ` theme- ${ netdataTheme } ` ) ;
}
}
$ ( '#sidebar' )
. affix ( {
offset : {
top : ( isdemo ( ) ) ? 150 : 0 ,
bottom : 0
}
} )
. on ( 'affixed.bs.affix' , function ( ) {
// fix scrolling of very long affix lists
// http://stackoverflow.com/questions/21691585/bootstrap-3-1-0-affix-too-long
$ ( this ) . removeAttr ( 'style' ) ;
} )
. on ( 'affix-top.bs.affix' , function ( ) {
// fix bootstrap affix click bug
// https://stackoverflow.com/a/37847981/4525767
if ( modal _shown ) {
return false ;
}
} )
. on ( 'activate.bs.scrollspy' , function ( e ) {
// change the URL based on the current position of the screen
if ( modal _shown === false ) {
var el = $ ( e . target ) ;
var hash = el . find ( 'a' ) . attr ( 'href' ) ;
if ( typeof hash === 'string' && hash . substring ( 0 , 1 ) === '#' && urlOptions . hash . startsWith ( hash + '_submenu_' ) === false ) {
urlOptions . hash = hash ;
urlOptions . hashUpdate ( ) ;
}
}
} ) ;
Ps . initialize ( document . getElementById ( 'sidebar' ) , {
wheelSpeed : 0.5 ,
wheelPropagation : true ,
swipePropagation : true ,
minScrollbarLength : null ,
maxScrollbarLength : null ,
useBothWheelAxes : false ,
suppressScrollX : true ,
suppressScrollY : false ,
scrollXMarginOffset : 0 ,
scrollYMarginOffset : 0 ,
theme : 'default'
} ) ;
// ------------------------------------------------------------------------
// scrollspy
if ( scrollspyOffset > 250 ) {
scrollspyOffset = 250 ;
}
if ( scrollspyOffset < 75 ) {
scrollspyOffset = 75 ;
}
document . body . setAttribute ( 'data-offset' , scrollspyOffset ) ;
// scroll the dashboard, before activating the scrollspy, so that our
// hash will not be updated before we got the chance to scroll to it
scrollDashboardTo ( ) ;
$ ( document . body ) . scrollspy ( {
target : '#sidebar' ,
offset : scrollspyOffset // controls the diff of the <hX> element to the top, to select it
} ) ;
// ------------------------------------------------------------------------
// my-netdata menu
Ps . initialize ( document . getElementById ( 'my-netdata-dropdown-content' ) , {
wheelSpeed : 1 ,
wheelPropagation : false ,
swipePropagation : false ,
minScrollbarLength : null ,
maxScrollbarLength : null ,
useBothWheelAxes : false ,
suppressScrollX : true ,
suppressScrollY : false ,
scrollXMarginOffset : 0 ,
scrollYMarginOffset : 0 ,
theme : 'default'
} ) ;
$ ( '#myNetdataDropdownParent' )
. on ( 'show.bs.dropdown' , function ( ) {
var hash = urlOptions . genHash ( ) ;
$ ( '.registry_link' ) . each ( function ( idx ) {
this . setAttribute ( 'href' , this . getAttribute ( "href" ) . replace ( /#.*$/ , hash ) ) ;
} ) ;
NETDATA . pause ( function ( ) {
} ) ;
} )
. on ( 'shown.bs.dropdown' , function ( ) {
Ps . update ( document . getElementById ( 'my-netdata-dropdown-content' ) ) ;
myNetdataMenuDidShow ( ) ;
} )
. on ( 'hidden.bs.dropdown' , function ( ) {
NETDATA . unpause ( ) ;
} ) ;
$ ( '#deleteRegistryModal' )
. on ( 'hidden.bs.modal' , function ( ) {
deleteRegistryGuid = null ;
} ) ;
// ------------------------------------------------------------------------
// update modal
$ ( '#updateModal' )
. on ( 'show.bs.modal' , function ( ) {
versionLog ( 'checking, please wait...' ) ;
} )
. on ( 'shown.bs.modal' , function ( ) {
notifyForUpdate ( true ) ;
} ) ;
// ------------------------------------------------------------------------
// alarms modal
$ ( '#alarmsModal' )
. on ( 'shown.bs.modal' , function ( ) {
alarmsUpdateModal ( ) ;
} )
. on ( 'hidden.bs.modal' , function ( ) {
document . getElementById ( 'alarms_active' ) . innerHTML =
document . getElementById ( 'alarms_all' ) . innerHTML =
2022-05-23 16:02:11 +08:00
document . getElementById ( 'alarms_log' ) . innerHTML =
'loading...' ;
2021-09-24 23:37:27 +08:00
} ) ;
// ------------------------------------------------------------------------
dashboardSettingsSetup ( ) ;
loadSnapshotDragAndDropSetup ( ) ;
saveSnapshotModalSetup ( ) ;
showPageFooter ( ) ;
// ------------------------------------------------------------------------
// https://github.com/viralpatel/jquery.shorten/blob/master/src/jquery.shorten.js
$ . fn . shorten = function ( settings ) {
"use strict" ;
var config = {
showChars : 750 ,
minHideChars : 10 ,
ellipsesText : "..." ,
moreText : '<i class="fas fa-expand"></i> show more information' ,
lessText : '<i class="fas fa-compress"></i> show less information' ,
onLess : function ( ) {
NETDATA . onscroll ( ) ;
} ,
onMore : function ( ) {
NETDATA . onscroll ( ) ;
} ,
errMsg : null ,
force : false
} ;
if ( settings ) {
$ . extend ( config , settings ) ;
}
if ( $ ( this ) . data ( 'jquery.shorten' ) && ! config . force ) {
return false ;
}
$ ( this ) . data ( 'jquery.shorten' , true ) ;
$ ( document ) . off ( "click" , '.morelink' ) ;
$ ( document ) . on ( {
click : function ( ) {
var $this = $ ( this ) ;
if ( $this . hasClass ( 'less' ) ) {
$this . removeClass ( 'less' ) ;
$this . html ( config . moreText ) ;
2022-05-23 16:02:11 +08:00
$this . parent ( ) . prev ( ) . animate ( { 'height' : '0' + '%' } , 0 , function ( ) {
2021-09-24 23:37:27 +08:00
$this . parent ( ) . prev ( ) . prev ( ) . show ( ) ;
} ) . hide ( 0 , function ( ) {
config . onLess ( ) ;
} ) ;
} else {
$this . addClass ( 'less' ) ;
$this . html ( config . lessText ) ;
2022-05-23 16:02:11 +08:00
$this . parent ( ) . prev ( ) . animate ( { 'height' : '100' + '%' } , 0 , function ( ) {
2021-09-24 23:37:27 +08:00
$this . parent ( ) . prev ( ) . prev ( ) . hide ( ) ;
} ) . show ( 0 , function ( ) {
config . onMore ( ) ;
} ) ;
}
return false ;
}
} , '.morelink' ) ;
return this . each ( function ( ) {
var $this = $ ( this ) ;
var content = $this . html ( ) ;
var contentlen = $this . text ( ) . length ;
if ( contentlen > config . showChars + config . minHideChars ) {
var c = content . substr ( 0 , config . showChars ) ;
if ( c . indexOf ( '<' ) >= 0 ) // If there's HTML don't want to cut it
{
var inTag = false ; // I'm in a tag?
var bag = '' ; // Put the characters to be shown here
var countChars = 0 ; // Current bag size
var openTags = [ ] ; // Stack for opened tags, so I can close them later
var tagName = null ;
for ( var i = 0 , r = 0 ; r <= config . showChars ; i ++ ) {
if ( content [ i ] === '<' && ! inTag ) {
inTag = true ;
// This could be "tag" or "/tag"
tagName = content . substring ( i + 1 , content . indexOf ( '>' , i ) ) ;
// If its a closing tag
if ( tagName [ 0 ] === '/' ) {
if ( tagName !== ( '/' + openTags [ 0 ] ) ) {
config . errMsg = 'ERROR en HTML: the top of the stack should be the tag that closes' ;
} else {
openTags . shift ( ) ; // Pops the last tag from the open tag stack (the tag is closed in the retult HTML!)
}
} else {
// There are some nasty tags that don't have a close tag like <br/>
if ( tagName . toLowerCase ( ) !== 'br' ) {
openTags . unshift ( tagName ) ; // Add to start the name of the tag that opens
}
}
}
if ( inTag && content [ i ] === '>' ) {
inTag = false ;
}
if ( inTag ) {
bag += content . charAt ( i ) ;
} else {
// Add tag name chars to the result
r ++ ;
if ( countChars <= config . showChars ) {
bag += content . charAt ( i ) ; // Fix to ie 7 not allowing you to reference string characters using the []
countChars ++ ;
} else {
// Now I have the characters needed
if ( openTags . length > 0 ) {
// I have unclosed tags
//console.log('They were open tags');
//console.log(openTags);
for ( var j = 0 ; j < openTags . length ; j ++ ) {
//console.log('Cierro tag ' + openTags[j]);
bag += '</' + openTags [ j ] + '>' ; // Close all tags that were opened
// You could shift the tag from the stack to check if you end with an empty stack, that means you have closed all open tags
}
break ;
}
}
}
}
c = $ ( '<div/>' ) . html ( bag + '<span class="ellip">' + config . ellipsesText + '</span>' ) . html ( ) ;
} else {
c += config . ellipsesText ;
}
var html = '<div class="shortcontent">' + c +
'</div><div class="allcontent">' + content +
'</div><span><a href="javascript://nop/" class="morelink">' + config . moreText + '</a></span>' ;
$this . html ( html ) ;
$this . find ( ".allcontent" ) . hide ( ) ; // Hide all text
$ ( '.shortcontent p:last' , $this ) . css ( 'margin-bottom' , 0 ) ; //Remove bottom margin on last paragraph as it's likely shortened
}
} ) ;
} ;
}
function finalizePage ( ) {
// resize all charts - without starting the background thread
// this has to be done while NETDATA is paused
2022-05-23 16:02:11 +08:00
// if we omit this, the affix menu will be wrong, since all
2021-09-24 23:37:27 +08:00
// the Dom elements are initially zero-sized
NETDATA . parseDom ( ) ;
// ------------------------------------------------------------------------
NETDATA . globalPanAndZoom . callback = null ;
NETDATA . globalChartUnderlay . callback = null ;
if ( urlOptions . pan _and _zoom === true && NETDATA . options . targets . length > 0 ) {
NETDATA . globalPanAndZoom . setMaster ( NETDATA . options . targets [ 0 ] , urlOptions . after , urlOptions . before ) ;
}
// callback for us to track PanAndZoom operations
NETDATA . globalPanAndZoom . callback = urlOptions . netdataPanAndZoomCallback ;
NETDATA . globalChartUnderlay . callback = urlOptions . netdataHighlightCallback ;
// ------------------------------------------------------------------------
// let it run (update the charts)
NETDATA . unpause ( ) ;
runOnceOnDashboardWithjQuery ( ) ;
$ ( ".shorten" ) . shorten ( ) ;
enableTooltipsAndPopovers ( ) ;
if ( isdemo ( ) ) {
// do not to give errors on netdata demo servers for 60 seconds
NETDATA . options . current . retries _on _data _failures = 60 ;
// google analytics when this is used for the home page of the demo sites
// this does not run on user's installations
setTimeout ( function ( ) {
( function ( i , s , o , g , r , a , m ) {
i [ 'GoogleAnalyticsObject' ] = r ;
i [ r ] = i [ r ] || function ( ) {
( i [ r ] . q = i [ r ] . q || [ ] ) . push ( arguments )
} , i [ r ] . l = 1 * new Date ( ) ;
a = s . createElement ( o ) ,
m = s . getElementsByTagName ( o ) [ 0 ] ;
a . async = 1 ;
a . src = g ;
m . parentNode . insertBefore ( a , m )
} ) ( window , document , 'script' , 'https://www.google-analytics.com/analytics.js' , 'ga' ) ;
ga ( 'create' , 'UA-64295674-3' , 'auto' ) ;
ga ( 'send' , 'pageview' , '/demosite/' + window . location . host ) ;
} , 2000 ) ;
} else {
notifyForUpdate ( ) ;
}
if ( urlOptions . show _alarms === true ) {
setTimeout ( function ( ) {
$ ( '#alarmsModal' ) . modal ( 'show' ) ;
} , 1000 ) ;
}
NETDATA . onresizeCallback = function ( ) {
Ps . update ( document . getElementById ( 'sidebar' ) ) ;
Ps . update ( document . getElementById ( 'my-netdata-dropdown-content' ) ) ;
} ;
NETDATA . onresizeCallback ( ) ;
if ( netdataSnapshotData !== null ) {
NETDATA . globalPanAndZoom . setMaster ( NETDATA . options . targets [ 0 ] , netdataSnapshotData . after _ms , netdataSnapshotData . before _ms ) ;
}
//if (urlOptions.nowelcome !== true) {
// setTimeout(function () {
// $('#welcomeModal').modal();
// }, 2000);
//}
// var netdataEnded = performance.now();
// console.log('start up time: ' + (netdataEnded - netdataStarted).toString() + ' ms');
}
function resetDashboardOptions ( ) {
var help = NETDATA . options . current . show _help ;
NETDATA . resetOptions ( ) ;
if ( setTheme ( 'slate' ) ) {
netdataReload ( ) ;
}
if ( help !== NETDATA . options . current . show _help ) {
netdataReload ( ) ;
}
}
// callback to add the dashboard info to the
// parallel javascript downloader in netdata
var netdataPrepCallback = function ( ) {
NETDATA . requiredCSS . push ( {
url : NETDATA . serverStatic + 'css/bootstrap-toggle-2.2.2.min.css' ,
isAlreadyLoaded : function ( ) {
return false ;
}
} ) ;
NETDATA . requiredJs . push ( {
url : NETDATA . serverStatic + 'lib/bootstrap-toggle-2.2.2.min.js' ,
isAlreadyLoaded : function ( ) {
return false ;
}
} ) ;
NETDATA . requiredJs . push ( {
url : NETDATA . serverStatic + 'dashboard_info.js?v20181019-1' ,
async : false ,
isAlreadyLoaded : function ( ) {
return false ;
}
} ) ;
if ( isdemo ( ) ) {
document . getElementById ( 'masthead' ) . style . display = 'block' ;
} else {
if ( urlOptions . update _always === true ) {
NETDATA . setOption ( 'stop_updates_when_focus_is_lost' , ! urlOptions . update _always ) ;
}
}
} ;
var selected _server _timezone = function ( timezone , status ) {
//console.log('called with timezone: ' + timezone + ", status: " + ((typeof status === 'undefined')?'undefined':status).toString());
// clear the error
document . getElementById ( 'timezone_error_message' ) . innerHTML = '' ;
if ( typeof status === 'undefined' ) {
// the user selected a timezone from the menu
NETDATA . setOption ( 'user_set_server_timezone' , timezone ) ;
if ( NETDATA . dateTime . init ( timezone ) === false ) {
NETDATA . dateTime . init ( ) ;
if ( ! $ ( '#local_timezone' ) . prop ( 'checked' ) ) {
$ ( '#local_timezone' ) . bootstrapToggle ( 'on' ) ;
}
document . getElementById ( 'timezone_error_message' ) . innerHTML = 'Ooops! That timezone was not accepted by your browser. Please open a github issue to help us fix it.' ;
NETDATA . setOption ( 'user_set_server_timezone' , NETDATA . options . server _timezone ) ;
} else {
if ( $ ( '#local_timezone' ) . prop ( 'checked' ) ) {
$ ( '#local_timezone' ) . bootstrapToggle ( 'off' ) ;
}
}
} else if ( status === true ) {
// the user wants the browser default timezone to be activated
NETDATA . dateTime . init ( ) ;
} else {
// the user wants the server default timezone to be activated
//console.log('found ' + NETDATA.options.current.user_set_server_timezone);
if ( NETDATA . options . current . user _set _server _timezone === 'default' ) {
NETDATA . options . current . user _set _server _timezone = NETDATA . options . server _timezone ;
}
timezone = NETDATA . options . current . user _set _server _timezone ;
if ( NETDATA . dateTime . init ( timezone ) === false ) {
NETDATA . dateTime . init ( ) ;
if ( ! $ ( '#local_timezone' ) . prop ( 'checked' ) ) {
$ ( '#local_timezone' ) . bootstrapToggle ( 'on' ) ;
}
document . getElementById ( 'timezone_error_message' ) . innerHTML = 'Sorry. The timezone "' + timezone . toString ( ) + '" is not accepted by your browser. Please select one from the list.' ;
NETDATA . setOption ( 'user_set_server_timezone' , NETDATA . options . server _timezone ) ;
}
}
document . getElementById ( 'current_timezone' ) . innerText = ( NETDATA . options . current . timezone === 'default' ) ? 'unset, using browser default' : NETDATA . options . current . timezone ;
return false ;
} ;
// our entry point
// var netdataStarted = performance.now();
var netdataCallback = initializeDynamicDashboard ;
// =================================================================================================
// netdata.cloud
let registryAgents = [ ] ;
let cloudAgents = [ ] ;
let myNetdataMenuFilterValue = "" ;
let cloudAccountID = null ;
let cloudAccountName = null ;
let cloudToken = null ;
/// Enforces a maximum string length while retaining the prefix and the postfix of
/// the string.
function truncateString ( str , maxLength ) {
if ( str . length <= maxLength ) {
return str ;
}
const spanLength = Math . floor ( ( maxLength - 3 ) / 2 ) ;
return ` ${ str . substring ( 0 , spanLength ) } ... ${ str . substring ( str . length - spanLength ) } ` ;
}
// -------------------------------------------------------------------------------------------------
// netdata.cloud API Client
// -------------------------------------------------------------------------------------------------
function isValidAgent ( a ) {
return a . urls != null && a . urls . length > 0 ;
}
// https://github.com/netdata/hub/issues/146
function getCloudAccountAgents ( ) {
if ( ! isSignedIn ( ) ) {
return [ ] ;
}
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
return fetch (
` ${ NETDATA . registry . cloudBaseURL } /api/v1/accounts/ ${ cloudAccountID } /agents ` ,
{
method : "GET" ,
mode : "cors" ,
headers : {
"Authorization" : ` Bearer ${ cloudToken } `
}
}
2022-05-23 16:02:11 +08:00
) . then ( ( response ) => {
2021-09-24 23:37:27 +08:00
if ( ! response . ok ) {
throw Error ( "Cannot fetch known accounts" ) ;
}
return response . json ( ) ;
} ) . then ( ( payload ) => {
const agents = payload . result ? payload . result . agents : null ;
if ( ! agents ) {
return [ ] ;
}
return agents . filter ( ( a ) => isValidAgent ( a ) ) . map ( ( a ) => {
return {
"guid" : a . id ,
"name" : a . name ,
"url" : a . urls [ 0 ] ,
"alternate_urls" : a . urls
}
} )
} ) . catch ( function ( error ) {
console . log ( error ) ;
return null ;
} ) ;
}
/** Updates the lastAccessTime and accessCount properties of the agent for the account. */
function touchAgent ( ) {
if ( ! isSignedIn ( ) ) {
return [ ] ;
}
const touchUrl = ` ${ NETDATA . registry . cloudBaseURL } /api/v1/agents/ ${ NETDATA . registry . machine _guid } /touch?account_id= ${ cloudAccountID } ` ;
return fetch (
touchUrl ,
{
method : "post" ,
body : "" ,
mode : "cors" ,
headers : {
"Authorization" : ` Bearer ${ cloudToken } `
}
}
) . then ( ( response ) => {
if ( ! response . ok ) {
throw Error ( "Cannot touch agent" + JSON . stringify ( response ) ) ;
}
return response . json ( ) ;
} ) . then ( ( payload ) => {
} ) . catch ( function ( error ) {
console . log ( error ) ;
return null ;
} ) ;
}
// https://github.com/netdata/hub/issues/128
function postCloudAccountAgents ( agentsToSync ) {
if ( ! isSignedIn ( ) ) {
return [ ] ;
}
const maskedURL = NETDATA . registry . MASKED _DATA ;
const agents = agentsToSync . map ( ( a ) => {
const urls = a . alternate _urls . filter ( ( url ) => url != maskedURL ) ;
return {
"id" : a . guid ,
"name" : a . name ,
"urls" : urls
}
} ) . filter ( ( a ) => isValidAgent ( a ) )
const payload = {
"accountID" : cloudAccountID ,
"agents" : agents ,
"merge" : false ,
} ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
return fetch (
` ${ NETDATA . registry . cloudBaseURL } /api/v1/accounts/ ${ cloudAccountID } /agents ` ,
{
method : "POST" ,
mode : "cors" ,
headers : {
"Content-Type" : "application/json; charset=utf-8" ,
"Authorization" : ` Bearer ${ cloudToken } `
} ,
body : JSON . stringify ( payload )
}
) . then ( ( response ) => {
return response . json ( ) ;
} ) . then ( ( payload ) => {
const agents = payload . result ? payload . result . agents : null ;
if ( ! agents ) {
return [ ] ;
}
return agents . filter ( ( a ) => isValidAgent ( a ) ) . map ( ( a ) => {
return {
"guid" : a . id ,
"name" : a . name ,
"url" : a . urls [ 0 ] ,
"alternate_urls" : a . urls
}
2022-05-23 16:02:11 +08:00
} )
2021-09-24 23:37:27 +08:00
} ) ;
}
function deleteCloudAgentURL ( agentID , url ) {
if ( ! isSignedIn ( ) ) {
return [ ] ;
}
return fetch (
` ${ NETDATA . registry . cloudBaseURL } /api/v1/accounts/ ${ cloudAccountID } /agents/ ${ agentID } /url?value= ${ encodeURIComponent ( url ) } ` ,
{
method : "DELETE" ,
mode : "cors" ,
headers : {
"Content-Type" : "application/json; charset=utf-8" ,
"Authorization" : ` Bearer ${ cloudToken } `
} ,
}
) . then ( ( response ) => {
return response . json ( ) ;
} ) . then ( ( payload ) => {
const count = payload . result ? payload . result . count : 0 ;
return count ;
} ) ;
}
// -------------------------------------------------------------------------------------------------
function signInDidClick ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
if ( ! NETDATA . registry . isUsingGlobalRegistry ( ) ) {
// If user is using a private registry, request his consent for
// synchronizing with cloud.
showSignInModal ( ) ;
return ;
}
signIn ( ) ;
}
function shouldShowSignInBanner ( ) {
return false ;
}
function closeSignInBanner ( ) {
localStorage . setItem ( "signInBannerClosed" , "true" ) ;
const el = document . getElementById ( "sign-in-banner" ) ;
if ( el ) {
el . style . display = "none" ;
}
}
function closeSignInBannerDidClick ( e ) {
closeSignInBanner ( ) ;
}
function signOutDidClick ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
signOut ( ) ;
}
// -------------------------------------------------------------------------------------------------
function updateMyNetdataAfterFilterChange ( ) {
const machinesEl = document . getElementById ( "my-netdata-menu-machines" )
machinesEl . innerHTML = renderMachines ( cloudAgents ) ;
if ( options . hosts . length > 1 ) {
const streamedEl = document . getElementById ( "my-netdata-menu-streamed" )
2022-05-23 16:02:11 +08:00
streamedEl . innerHTML = renderStreamedHosts ( options ) ;
2021-09-24 23:37:27 +08:00
}
}
function myNetdataMenuDidShow ( ) {
const filterEl = document . getElementById ( "my-netdata-menu-filter-input" ) ;
if ( filterEl ) {
filterEl . focus ( ) ;
}
}
function myNetdataFilterDidChange ( e ) {
const inputEl = e . target ;
setTimeout ( ( ) => {
myNetdataMenuFilterValue = inputEl . value ;
2022-05-23 16:02:11 +08:00
updateMyNetdataAfterFilterChange ( ) ;
2021-09-24 23:37:27 +08:00
} , 1 ) ;
}
function myNetdataFilterClearDidClick ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
const inputEl = document . getElementById ( "my-netdata-menu-filter-input" ) ;
inputEl . value = "" ;
myNetdataMenuFilterValue = "" ;
2022-05-23 16:02:11 +08:00
updateMyNetdataAfterFilterChange ( ) ;
2021-09-24 23:37:27 +08:00
inputEl . focus ( ) ;
}
// -------------------------------------------------------------------------------------------------
function clearCloudVariables ( ) {
cloudAccountID = null ;
cloudAccountName = null ;
cloudToken = null ;
}
function clearCloudLocalStorageItems ( ) {
localStorage . removeItem ( "cloud.baseURL" ) ;
localStorage . removeItem ( "cloud.agentID" ) ;
localStorage . removeItem ( "cloud.sync" ) ;
}
function signIn ( ) {
const url = ` ${ NETDATA . registry . cloudBaseURL } /account/sign-in-agent?id= ${ NETDATA . registry . machine _guid } &name= ${ encodeURIComponent ( NETDATA . registry . hostname ) } &origin= ${ encodeURIComponent ( window . location . origin + "/" ) } ` ;
window . open ( url ) ;
}
function signOut ( ) {
cloudSSOSignOut ( ) ;
}
function handleMessage ( e ) {
switch ( e . data . type ) {
case "sign-in" :
handleSignInMessage ( e ) ;
break ;
case "sign-out" :
handleSignOutMessage ( e ) ;
break ;
default :
return ;
}
}
function handleSignInMessage ( e ) {
closeSignInBanner ( ) ;
localStorage . setItem ( "cloud.baseURL" , NETDATA . registry . cloudBaseURL ) ;
cloudAccountID = e . data . accountID ;
cloudAccountName = e . data . accountName ;
cloudToken = e . data . token ;
netdataRegistryCallback ( registryAgents ) ;
if ( e . data . redirectURI && ! window . location . href . includes ( e . data . redirectURI ) ) {
// lgtm false-positive - redirectURI does not come from user input, but from iframe callback
window . location . replace ( e . data . redirectURI ) ; // lgtm[js/client-side-unvalidated-url-redirection]
}
}
function handleSignOutMessage ( e ) {
clearCloudVariables ( ) ;
renderMyNetdataMenu ( registryAgents ) ;
}
function isSignedIn ( ) {
return cloudToken != null && cloudAccountID != null ;
}
function sortedArraysEqual ( a , b ) {
if ( a . length != b . length ) return false ;
for ( var i = 0 ; i < a . length ; ++ i ) {
if ( a [ i ] !== b [ i ] ) return false ;
}
return true ;
}
// If merging is needed returns the merged agents set, otherwise returns null.
function mergeAgents ( cloud , local ) {
let dirty = false ;
const union = new Map ( ) ;
for ( const cagent of cloud ) {
union . set ( cagent . guid , cagent ) ;
}
for ( const lagent of local ) {
const cagent = union . get ( lagent . guid ) ;
if ( cagent ) {
for ( const u of lagent . alternate _urls ) {
if ( u === NETDATA . registry . MASKED _DATA ) { // TODO: temp until registry is updated.
continue ;
}
if ( ! cagent . alternate _urls . includes ( u ) ) {
dirty = true ;
cagent . alternate _urls . push ( u ) ;
}
}
} else {
dirty = true ;
union . set ( lagent . guid , lagent ) ;
}
}
if ( dirty ) {
return Array . from ( union . values ( ) ) ;
}
return null ;
}
function showSignInModal ( ) {
document . getElementById ( "sim-registry" ) . innerHTML = NETDATA . registry . server ;
$ ( "#signInModal" ) . modal ( "show" ) ;
}
function explicitlySignIn ( ) {
$ ( "#signInModal" ) . modal ( "hide" ) ;
signIn ( ) ;
}
function showSyncModal ( ) {
document . getElementById ( "sync-registry-modal-registry" ) . innerHTML = NETDATA . registry . server ;
$ ( "#syncRegistryModal" ) . modal ( "show" ) ;
}
function explicitlySyncAgents ( ) {
$ ( "#syncRegistryModal" ) . modal ( "hide" ) ;
const json = localStorage . getItem ( "cloud.sync" ) ;
2022-05-23 16:02:11 +08:00
const sync = json ? JSON . parse ( json ) : { } ;
2021-09-24 23:37:27 +08:00
delete sync [ cloudAccountID ] ;
localStorage . setItem ( "cloud.sync" , JSON . stringify ( sync ) ) ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
NETDATA . registry . init ( ) ;
}
function syncAgents ( callback ) {
const json = localStorage . getItem ( "cloud.sync" ) ;
2022-05-23 16:02:11 +08:00
const sync = json ? JSON . parse ( json ) : { } ;
2021-09-24 23:37:27 +08:00
const currentAgent = {
guid : NETDATA . registry . machine _guid ,
name : NETDATA . registry . hostname ,
url : NETDATA . serverDefault ,
alternate _urls : [ NETDATA . serverDefault ] ,
}
2022-05-23 16:02:11 +08:00
const localAgents = sync [ cloudAccountID ]
? [ currentAgent ]
2021-09-24 23:37:27 +08:00
: registryAgents . concat ( [ currentAgent ] ) ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
console . log ( "Checking if sync is needed." , localAgents ) ;
const agentsToSync = mergeAgents ( cloudAgents , localAgents ) ;
2022-05-23 16:02:11 +08:00
if ( ( ! sync [ cloudAccountID ] ) || agentsToSync ) {
2021-09-24 23:37:27 +08:00
sync [ cloudAccountID ] = new Date ( ) . getTime ( ) ;
localStorage . setItem ( "cloud.sync" , JSON . stringify ( sync ) ) ;
}
if ( agentsToSync ) {
console . log ( "Synchronizing with netdata.cloud." ) ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
postCloudAccountAgents ( agentsToSync ) . then ( ( agents ) => {
// TODO: clear syncTime on error!
cloudAgents = agents ;
callback ( cloudAgents ) ;
} ) ;
2022-05-23 16:02:11 +08:00
return
}
2021-09-24 23:37:27 +08:00
callback ( cloudAgents ) ;
}
let isCloudSSOInitialized = false ;
function cloudSSOInit ( ) {
const iframeEl = document . getElementById ( "ssoifrm" ) ;
const url = ` ${ NETDATA . registry . cloudBaseURL } /account/sso-agent?id= ${ NETDATA . registry . machine _guid } ` ;
iframeEl . src = url ;
isCloudSSOInitialized = true ;
}
function cloudSSOSignOut ( ) {
const iframe = document . getElementById ( "ssoifrm" ) ;
const url = ` ${ NETDATA . registry . cloudBaseURL } /account/sign-out-agent ` ;
iframe . src = url ;
}
function initCloud ( ) {
if ( ! NETDATA . registry . isCloudEnabled ) {
clearCloudVariables ( ) ;
clearCloudLocalStorageItems ( ) ;
return ;
}
if ( NETDATA . registry . cloudBaseURL != localStorage . getItem ( "cloud.baseURL" ) ) {
clearCloudVariables ( ) ;
clearCloudLocalStorageItems ( ) ;
if ( NETDATA . registry . cloudBaseURL ) {
localStorage . setItem ( "cloud.baseURL" , NETDATA . registry . cloudBaseURL ) ;
}
}
if ( ! isCloudSSOInitialized ) {
cloudSSOInit ( ) ;
}
touchAgent ( ) ;
}
// This callback is called after NETDATA.registry is initialized.
function netdataRegistryCallback ( machinesArray ) {
localStorage . setItem ( "cloud.agentID" , NETDATA . registry . machine _guid ) ;
initCloud ( ) ;
2022-05-23 16:02:11 +08:00
registryAgents = machinesArray ;
2021-09-24 23:37:27 +08:00
if ( isSignedIn ( ) ) {
2022-05-23 16:02:11 +08:00
// We call getCloudAccountAgents() here because it requires that
2021-09-24 23:37:27 +08:00
// NETDATA.registry is initialized.
clearMyNetdataMenu ( ) ;
getCloudAccountAgents ( ) . then ( ( agents ) => {
if ( ! agents ) {
errorMyNetdataMenu ( ) ;
return ;
}
2022-05-23 16:02:11 +08:00
cloudAgents = agents ;
2021-09-24 23:37:27 +08:00
syncAgents ( ( agents ) => {
const agentsMap = { }
for ( const agent of agents ) {
agentsMap [ agent . guid ] = agent ;
}
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
NETDATA . registry . machines = agentsMap ;
NETDATA . registry . machines _array = agents ;
2022-05-23 16:02:11 +08:00
renderMyNetdataMenu ( agents ) ;
2021-09-24 23:37:27 +08:00
} ) ;
} ) ;
} else {
renderMyNetdataMenu ( machinesArray )
}
} ;
2022-05-23 16:02:11 +08:00
// If we know the cloudBaseURL and agentID from local storage render (eagerly)
// the account ui before receiving the definitive response from the web server.
2021-09-24 23:37:27 +08:00
// This improves the perceived performance.
function tryFastInitCloud ( ) {
const baseURL = localStorage . getItem ( "cloud.baseURL" ) ;
const agentID = localStorage . getItem ( "cloud.agentID" ) ;
if ( baseURL && agentID ) {
NETDATA . registry . cloudBaseURL = baseURL ;
NETDATA . registry . machine _guid = agentID ;
NETDATA . registry . isCloudEnabled = true ;
2022-05-23 16:02:11 +08:00
2021-09-24 23:37:27 +08:00
initCloud ( ) ;
}
}
function initializeApp ( ) {
2022-05-23 16:02:11 +08:00
window . addEventListener ( "message" , handleMessage , false ) ;
2021-09-24 23:37:27 +08:00
2022-05-23 16:02:11 +08:00
// tryFastInitCloud();
2021-09-24 23:37:27 +08:00
}
if ( document . readyState === "complete" ) {
initializeApp ( ) ;
} else {
document . addEventListener ( "readystatechange" , ( ) => {
if ( document . readyState === "complete" ) {
initializeApp ( ) ;
}
} ) ;
}