2018-04-22 19:10:29 +00:00
/ *
* Wazuh app - Module for agent info fetching functions
* Copyright ( C ) 2018 Wazuh , Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* Find more information about this on the LICENSE file .
* /
2018-04-22 13:41:10 +00:00
import cron from 'node-cron'
import needle from 'needle'
import getPath from '../util/get-path'
import colors from 'ansicolors'
import log from './logger'
import ElasticWrapper from './lib/elastic-wrapper'
import monitoringTemplate from './integration-files/monitoring-template'
import packageJSON from '../package.json'
2018-04-10 15:11:29 +00:00
2018-04-21 10:09:55 +00:00
export default ( server , options ) => {
2018-04-21 11:31:47 +00:00
const blueWazuh = colors . blue ( 'wazuh' ) ;
const index _pattern = "wazuh-monitoring-3.x-*" ;
const index _prefix = "wazuh-monitoring-3.x-" ;
2017-12-11 10:45:32 +00:00
// Elastic JS Client
2018-04-10 15:11:29 +00:00
const wzWrapper = new ElasticWrapper ( server ) ;
2017-12-11 10:45:32 +00:00
// Initialize
let agentsArray = [ ] ;
2018-04-12 13:49:29 +00:00
2017-12-11 10:45:32 +00:00
let fDate = new Date ( ) . toISOString ( ) . replace ( /T/ , '-' ) . replace ( /\..+/ , '' ) . replace ( /-/g , '.' ) . replace ( /:/g , '' ) . slice ( 0 , - 7 ) ;
let todayIndex = index _prefix + fDate ;
// Check status and get agent status array
2018-03-19 11:21:44 +00:00
const checkStatus = async ( apiEntry , maxSize , offset ) => {
try {
if ( ! maxSize ) {
throw new Error ( 'You must provide a max size' )
}
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
const payload = {
offset : offset ? offset : 0 ,
limit : ( 250 < maxSize ) ? 250 : maxSize
} ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
const options = {
headers : {
'wazuh-app-version' : packageJSON . version
} ,
username : apiEntry . user ,
password : apiEntry . password ,
rejectUnauthorized : ! apiEntry . insecure
} ;
2018-04-22 19:10:29 +00:00
2018-03-19 12:18:56 +00:00
const response = await needle ( 'get' , ` ${ getPath ( apiEntry ) } /agents ` , payload , options ) ;
2017-12-11 10:45:32 +00:00
2018-03-19 11:21:44 +00:00
if ( ! response . error && response . body . data . items ) {
2017-12-11 10:45:32 +00:00
agentsArray = agentsArray . concat ( response . body . data . items ) ;
if ( ( payload . limit + payload . offset ) < maxSize ) {
2018-03-19 11:21:44 +00:00
return checkStatus ( apiEntry , response . body . data . totalItems , payload . limit + payload . offset ) ;
2017-12-11 10:45:32 +00:00
} else {
2018-04-17 09:39:45 +00:00
await saveStatus ( apiEntry . clusterName ) ;
2017-12-11 10:45:32 +00:00
}
} else {
2018-03-19 11:21:44 +00:00
throw new Error ( 'Can not access Wazuh API' )
2017-12-11 10:45:32 +00:00
}
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
return ;
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][checkStatus]' , error . message || error ) ;
2018-04-11 14:41:14 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , error . message || error ) ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
// Check API status twice and get agents total items
2018-03-19 11:21:44 +00:00
const checkAndSaveStatus = async apiEntry => {
try {
const payload = {
'offset' : 0 ,
'limit' : 1
} ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
const options = {
headers : {
'wazuh-app-version' : packageJSON . version
} ,
username : apiEntry . user ,
password : apiEntry . password ,
rejectUnauthorized : ! apiEntry . insecure
} ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
const response = await needle ( 'get' , ` ${ getPath ( apiEntry ) } /agents ` , payload , options )
2018-04-22 19:10:29 +00:00
2018-04-17 09:39:45 +00:00
const isCluster = await needle ( 'get' , ` ${ getPath ( apiEntry ) } /cluster/status ` , { } , options )
2018-04-22 19:10:29 +00:00
const clusterName = ( isCluster && isCluster . body && isCluster . body . data && isCluster . body . data . enabled === 'yes' ) ?
2018-04-17 09:39:45 +00:00
await needle ( 'get' , ` ${ getPath ( apiEntry ) } /cluster/node ` , { } , options ) :
false ;
apiEntry . clusterName = clusterName && clusterName . body && clusterName . body . data && clusterName . body . data . cluster ?
clusterName . body . data . cluster :
false ;
2017-12-11 10:45:32 +00:00
if ( ! response . error && response . body . data && response . body . data . totalItems ) {
checkStatus ( apiEntry , response . body . data . totalItems ) ;
} else {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][checkAndSaveStatus]' , 'Wazuh API credentials not found or are not correct. Open the app in your browser and configure it to start monitoring agents.' ) ;
2017-12-11 10:45:32 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , 'Wazuh API credentials not found or are not correct. Open the app in your browser and configure it to start monitoring agents.' ) ;
}
2018-03-19 11:21:44 +00:00
return ;
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][checkAndSaveStatus]' , error . message || error ) ;
2018-03-19 11:21:44 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , error . message || error ) ;
2018-04-22 19:10:29 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
// Load Wazuh API credentials from Elasticsearch document
2018-03-19 11:21:44 +00:00
const loadCredentials = async apiEntries => {
try {
if ( typeof apiEntries === 'undefined' || ! ( 'hits' in apiEntries ) ) return ;
const filteredApis = apiEntries . hits . filter ( ( element , index , self ) =>
index === self . findIndex ( ( t ) => (
2018-04-22 19:10:29 +00:00
t . _source . api _user === element . _source . api _user &&
2018-03-19 11:21:44 +00:00
t . _source . api _password === element . _source . api _password &&
2018-04-22 19:10:29 +00:00
t . _source . url === element . _source . url &&
2018-03-19 11:21:44 +00:00
t . _source . api _port === element . _source . api _port
) )
) ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
for ( let element of filteredApis ) {
let apiEntry = {
'user' : element . _source . api _user ,
'password' : Buffer . from ( element . _source . api _password , 'base64' ) . toString ( "ascii" ) ,
'url' : element . _source . url ,
'port' : element . _source . api _port ,
'insecure' : element . _source . insecure
} ;
if ( apiEntry . error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][loadCredentials]' , apiEntry . error || apiEntry ) ;
2018-03-19 11:21:44 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , ` Error getting wazuh-api data: ${ apiEntry . error } ` ) ;
break ;
}
await checkAndSaveStatus ( apiEntry ) ;
2017-12-11 10:45:32 +00:00
}
2018-04-21 11:31:47 +00:00
return { result : 'ok' }
2018-03-19 11:21:44 +00:00
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][loadCredentials]' , error . message || error ) ;
2018-03-19 11:21:44 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , error . message || error ) ;
2017-12-11 10:45:32 +00:00
}
} ;
// Get API configuration from elastic and callback to loadCredentials
2018-03-22 16:10:18 +00:00
const getConfig = async ( ) => {
2018-03-19 11:21:44 +00:00
try {
2018-04-10 15:11:29 +00:00
const data = await wzWrapper . getWazuhAPIEntries ( ) ;
2018-04-22 19:10:29 +00:00
2017-12-21 16:15:13 +00:00
if ( data . hits . total > 0 ) {
2018-03-22 16:10:18 +00:00
return data . hits ;
2017-12-11 10:45:32 +00:00
}
2018-03-19 11:21:44 +00:00
2018-04-25 08:39:10 +00:00
log ( '[monitoring][getConfig]' , 'There is no Wazuh API entries yet' , 'info' ) ;
2018-03-22 16:10:18 +00:00
return {
error : 'no credentials' ,
error _code : 1
} ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][getConfig]' , error . message || error ) ;
2018-03-22 16:10:18 +00:00
return {
error : 'no elasticsearch' ,
error _code : 2
} ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
// fetchAgents on demand
2018-04-21 11:31:47 +00:00
const fetchAgentsExternal = async ( ) => {
2018-03-22 16:10:18 +00:00
try {
const data = await getConfig ( ) ;
return loadCredentials ( data ) ;
} catch ( error ) {
return Promise . reject ( error ) ;
}
} ;
2017-12-11 10:45:32 +00:00
// Configure Kibana patterns.
2018-03-19 11:21:44 +00:00
const configureKibana = async ( ) => {
try {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][configureKibana]' , ` Creating index pattern: ${ index _pattern } ` , 'info' ) ;
2018-03-19 11:21:44 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , ` Creating index pattern: ${ index _pattern } ` ) ;
2018-04-22 19:10:29 +00:00
2018-04-10 15:11:29 +00:00
await wzWrapper . createMonitoringIndexPattern ( index _pattern ) ;
2018-04-12 13:49:29 +00:00
log ( '[monitoring][configureKibana]' , ` Created index pattern: ${ index _pattern } ` , 'info' ) ;
2018-03-12 11:57:14 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , ` Created index pattern: ${ index _pattern } ` ) ;
2018-04-09 16:34:16 +00:00
2018-03-19 11:21:44 +00:00
return ;
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][configureKibana]' , error . message || error ) ;
2017-12-11 10:45:32 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , 'Error creating index-pattern due to ' + error ) ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
// Creating wazuh-monitoring index
2018-04-17 09:39:45 +00:00
const createIndex = async ( todayIndex , clusterName ) => {
2018-03-19 11:21:44 +00:00
try {
2018-04-10 15:11:29 +00:00
await wzWrapper . createIndexByName ( todayIndex ) ;
2018-04-12 13:49:29 +00:00
log ( '[monitoring][createIndex]' , 'Successfully created today index.' , 'info' ) ;
2017-12-11 10:45:32 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , 'Successfully created today index.' ) ;
2018-04-17 09:39:45 +00:00
await insertDocument ( todayIndex , clusterName ) ;
2018-03-19 11:21:44 +00:00
return ;
} catch ( error ) {
2018-04-25 08:39:10 +00:00
log ( '[monitoring][createIndex]' , ` Could not create ${ todayIndex } index on elasticsearch due to ${ error . message || error } ` ) ;
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , ` Could not create ${ todayIndex } index on elasticsearch due to ${ error . message || error } ` ) ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
// Inserting one document per agent into Elastic. Bulk.
2018-04-17 09:39:45 +00:00
const insertDocument = async ( todayIndex , clusterName ) => {
2018-03-19 11:21:44 +00:00
try {
let body = '' ;
if ( agentsArray . length > 0 ) {
2018-04-17 09:39:45 +00:00
const managerName = agentsArray [ 0 ] . name ;
2018-03-19 11:21:44 +00:00
for ( let element of agentsArray ) {
body += '{ "index": { "_index": "' + todayIndex + '", "_type": "wazuh-agent" } }\n' ;
let date = new Date ( Date . now ( ) ) . toISOString ( ) ;
2018-04-17 07:21:49 +00:00
element [ '@timestamp' ] = date ;
element . host = managerName ;
2018-04-17 09:39:45 +00:00
element . cluster = { name : clusterName ? clusterName : 'disabled' } ;
2018-03-19 11:21:44 +00:00
body += JSON . stringify ( element ) + "\n" ;
}
if ( body === '' ) return ;
2018-04-22 19:10:29 +00:00
2018-04-10 15:11:29 +00:00
const response = await wzWrapper . pushBulkAnyIndex ( todayIndex , body ) ;
2017-12-11 10:45:32 +00:00
2018-03-19 11:21:44 +00:00
agentsArray . length = 0 ;
}
return ;
} catch ( error ) {
2018-04-25 08:39:10 +00:00
log ( '[monitoring][insertDocument]' , ` Error inserting agent data into elasticsearch. Bulk request failed due to ${ error . message || error } ` ) ;
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , ` Error inserting agent data into elasticsearch. Bulk request failed due to ${ error . message || error } ` ) ;
2017-12-11 10:45:32 +00:00
}
} ;
// Save agent status into elasticsearch, create index and/or insert document
2018-04-17 09:39:45 +00:00
const saveStatus = async clusterName => {
2018-03-19 11:21:44 +00:00
try {
fDate = new Date ( ) . toISOString ( ) . replace ( /T/ , '-' ) . replace ( /\..+/ , '' ) . replace ( /-/g , '.' ) . replace ( /:/g , '' ) . slice ( 0 , - 7 ) ;
todayIndex = index _prefix + fDate ;
2018-04-22 19:10:29 +00:00
2018-04-10 15:11:29 +00:00
const result = await wzWrapper . checkIfIndexExists ( todayIndex ) ;
2018-04-17 09:39:45 +00:00
result ? await insertDocument ( todayIndex , clusterName ) : await createIndex ( todayIndex , clusterName ) ;
2018-03-19 11:21:44 +00:00
return ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][saveStatus]' , ` Could not check if the index ${ todayIndex } exists due to ${ error . message || error } ` ) ;
2018-04-25 08:39:10 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , ` Could not check if the index ${ todayIndex } exists due to ${ error . message || error } ` ) ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
2018-03-19 11:21:44 +00:00
const createWazuhMonitoring = async ( ) => {
try {
2018-04-22 19:10:29 +00:00
2018-03-19 11:56:31 +00:00
try {
2018-04-10 15:11:29 +00:00
await wzWrapper . deleteMonitoring ( ) ;
2018-04-12 13:49:29 +00:00
log ( '[monitoring][createWazuhMonitoring]' , 'Successfully deleted old wazuh-monitoring pattern.' , 'info' ) ;
2018-03-19 11:56:31 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , "Successfully deleted old wazuh-monitoring pattern." ) ;
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][createWazuhMonitoring]' , 'No need to delete old wazuh-monitoring pattern.' , 'info' ) ;
2018-03-19 11:56:31 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , "No need to delete old wazuh-monitoring pattern." ) ;
}
2018-03-19 11:21:44 +00:00
await configureKibana ( ) ;
return ;
} catch ( error ) {
return Promise . reject ( error ) ;
}
}
const checkTemplate = async ( ) => {
try {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][checkTemplate]' , 'Updating wazuh-monitoring template...' , 'info' ) ;
2018-03-19 14:11:36 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , "Updating wazuh-monitoring template..." ) ;
2018-04-10 15:11:29 +00:00
const data = await wzWrapper . putMonitoringTemplate ( monitoringTemplate ) ;
2018-03-19 11:21:44 +00:00
return ;
} catch ( error ) {
2018-04-25 08:39:10 +00:00
log ( '[monitoring][checkTemplate]' , ` Something went wrong updating wazuh-monitoring template... ${ error . message || error } ` ) ;
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , ` Something went wrong updating wazuh-monitoring template... ${ error . message || error } ` ) ;
2018-03-19 11:21:44 +00:00
return Promise . reject ( error ) ;
}
}
// Main. First execution when installing / loading App.
const init = async ( ) => {
try {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][init]' , 'Creating/Updating wazuh-agent template...' , 'info' ) ;
2018-03-19 11:21:44 +00:00
await checkTemplate ( ) ;
2018-04-12 13:49:29 +00:00
log ( '[monitoring][init]' , 'Creating today index...' , 'info' ) ;
2018-03-19 11:21:44 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , 'Creating today index...' ) ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
await saveStatus ( ) ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
const patternId = 'index-pattern:' + index _pattern ;
2018-03-19 11:56:31 +00:00
2018-03-21 15:03:33 +00:00
// Checks if wazuh-monitoring index pattern is already created, if it fails create it
try {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][init]' , 'Checking if wazuh-monitoring pattern exists...' , 'info' ) ;
2018-03-21 15:03:33 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , 'Checking if wazuh-monitoring pattern exists...' ) ;
2018-04-10 15:11:29 +00:00
await wzWrapper . getIndexPatternUsingGet ( patternId ) ;
2018-03-21 15:03:33 +00:00
} catch ( error ) {
2018-04-12 14:33:00 +00:00
log ( '[monitoring][init]' , 'Didn\'t find wazuh-monitoring pattern for Kibana v6.x. Proceeding to create it...' , 'info' ) ;
2018-03-21 15:03:33 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , "Didn't find wazuh-monitoring pattern for Kibana v6.x. Proceeding to create it..." ) ;
return createWazuhMonitoring ( ) ;
}
2018-04-12 13:49:29 +00:00
log ( '[monitoring][init]' , 'Skipping wazuh-monitoring pattern creation. Already exists.' , 'info' ) ;
2018-03-21 15:03:33 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , 'Skipping wazuh-monitoring creation. Already exists.' ) ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
return ;
2018-04-22 19:10:29 +00:00
2018-03-19 11:21:44 +00:00
} catch ( error ) {
2018-03-21 15:03:33 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'error' ] , error . message || error ) ;
2018-04-12 13:49:29 +00:00
log ( '[monitoring][init]' , error . message || error ) ;
2018-03-21 15:03:33 +00:00
return ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
2017-06-01 15:08:10 +00:00
2018-04-13 15:26:41 +00:00
// Check Elasticsearch Server status and Kibana index presence
2018-03-19 11:21:44 +00:00
const checkElasticsearchServer = async ( ) => {
try {
2018-04-13 15:26:41 +00:00
const data = await wzWrapper . checkIfIndexExists ( wzWrapper . WZ _KIBANA _INDEX ) ;
2018-04-10 15:11:29 +00:00
2018-03-19 11:21:44 +00:00
if ( data ) {
const pluginsData = await server . plugins . elasticsearch . waitUntilReady ( ) ;
return pluginsData ;
}
return Promise . reject ( data ) ;
} catch ( error ) {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][checkElasticsearchServer]' , error . message || error ) ;
2018-03-19 11:21:44 +00:00
return Promise . reject ( error ) ;
}
2017-11-15 09:35:23 +00:00
}
2017-12-11 10:45:32 +00:00
// Wait until Kibana server is ready
2018-03-19 11:21:44 +00:00
const checkKibanaStatus = async ( ) => {
try {
2018-04-12 13:49:29 +00:00
log ( '[monitoring][checkKibanaStatus]' , 'Waiting for Kibana and Elasticsearch servers to be ready...' , 'info' ) ;
2018-03-19 11:21:44 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , 'Waiting for Kibana and Elasticsearch servers to be ready...' ) ;
await checkElasticsearchServer ( ) ;
await init ( ) ;
return ;
} catch ( error ) {
2018-04-25 08:39:10 +00:00
log ( '[monitoring][checkKibanaStatus]' , 'Waiting for Kibana and Elasticsearch servers to be ready...' , 'info' ) ;
2018-04-04 09:13:34 +00:00
server . log ( [ blueWazuh , 'monitoring' , 'info' ] , 'Waiting for Kibana and Elasticsearch servers to be ready...' , 'info' ) ;
2017-11-15 09:35:23 +00:00
setTimeout ( ( ) => checkKibanaStatus ( ) , 3000 ) ;
2018-03-19 11:21:44 +00:00
}
2017-12-11 10:45:32 +00:00
} ;
2017-06-01 15:08:10 +00:00
2017-12-11 10:45:32 +00:00
// Check Kibana index and if it is prepared, start the initialization of Wazuh App.
2018-04-21 11:31:47 +00:00
if ( ! options ) checkKibanaStatus ( ) ;
2017-06-01 15:08:10 +00:00
2018-04-12 13:49:29 +00:00
const cronTask = async ( ) => {
try {
2018-04-25 08:51:06 +00:00
const template = await wzWrapper . getTemplateByName ( 'wazuh-agent' ) ;
// Prevents to insert monitoring indices without the proper template inserted
if ( typeof template === 'object' &&
typeof template [ 'wazuh-agent' ] !== 'undefined' &&
typeof template [ 'wazuh-agent' ] . index _patterns !== 'undefined' ) {
agentsArray = [ ] ;
const data = await getConfig ( ) ;
await loadCredentials ( data ) ;
} else {
log ( '[monitoring][cronTask]' , 'No wazuh-agent template found, not inserting monitoring data' , 'info' ) ;
server . log ( [ blueWazuh , 'monitoring [cronTask]' , 'info' ] , 'No wazuh-agent template found, not inserting monitoring data' )
}
2018-04-12 13:49:29 +00:00
return ;
} catch ( error ) {
log ( '[monitoring][cronTask]' , error . message || error ) ;
server . log ( [ blueWazuh , 'monitoring [cronTask]' , 'error' ] , error . message || error )
}
}
2018-04-21 11:31:47 +00:00
if ( ! options ) cronTask ( )
2017-12-11 10:45:32 +00:00
// Cron tab for getting agent status.
2018-04-21 11:31:47 +00:00
if ( ! options ) cron . schedule ( '0 */10 * * * *' , cronTask , true ) ;
return fetchAgentsExternal ;
2017-12-11 10:45:32 +00:00
} ;