import { AggregationsSingleBucketAggregateBase , AggregationsTopHitsAggregate , AggregationsRateAggregate , SearchResponse , } from '@elastic/elasticsearch/lib/api/typesWithBodyKey' ; import { PackagePolicyServiceInterface } from '../../../fleet/server' ; import { getRouteMetric } from '../routes/usage' ; import { ElasticsearchClient , SavedObjectsClientContract } from '../../../../../src/core/server' ; import { ListResult , PackagePolicy , PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../fleet/common' ; import { OSQUERY_INTEGRATION_NAME } from '../../common' ; import { METRICS_INDICES } from './constants' ; import { AgentInfo , BeatMetricsUsage , LiveQueryUsage } from './types' ; interface PolicyLevelUsage { scheduled_queries ? : ScheduledQueryUsageMetrics ; agent_info ? : AgentInfo ; } export async function getPolicyLevelUsage ( esClient , soClient , packagePolicyService ? ) { if ( ! packagePolicyService ) { return { } ; } const packagePolicies = await packagePolicyService . list ( soClient , { kuery : `${ PACKAGE_POLICY_SAVED_OBJECT_TYPE }.package.name:${ OSQUERY_INTEGRATION_NAME }` , perPage : 10000 , } ) ; const result = { scheduled_queries : getScheduledQueryUsage ( packagePolicies ) , } ; const agentResponse = await esClient . search < unknown , { policied : AggregationsSingleBucketAggregateBase ; } > ( { body : { size : 0 , query : { match : { active : true , } , } , aggs : { policied : { filter : { terms : { policy_id : packagePolicies . items . map ( ( p ) => p . policy_id ) , } , } , } , } , } , index : '.fleet-agents' , ignore_unavailable : true , } ) ; const policied = agentResponse . body . aggregations ?. policied ; if ( policied && typeof policied . doc_count === 'number' ) { result . agent_info = { enrolled : policied . doc_count , } ; } return result ; } export function getPackageVersions ( packagePolicies ) { return packagePolicies . items . reduce ( ( acc , item ) => { if ( item . package ) { acc [ item . package . version ] = ( acc [ item . package . version ] ?? 0 ) + 1 ; } return acc ; } , { } as { [ version ] : number ; } ) ; } interface ScheduledQueryUsageMetrics { queryGroups : { total : number ; empty : number ; } ; } export function getScheduledQueryUsage ( packagePolicies ) { return packagePolicies . items . reduce ( ( acc , item ) => { ++ acc . queryGroups . total ; const policyAgents = item . inputs . reduce ( ( sum , input ) => sum + input . streams . length , 0 ) ; if ( policyAgents === 0 ) { ++ acc . queryGroups . empty ; } return acc ; } , { queryGroups : { total : 0 , empty : 0 , } , } as ScheduledQueryUsageMetrics ) ; } export async function getLiveQueryUsage ( soClient , esClient ) { const { body : metricResponse } = await esClient . search < unknown , { queries : AggregationsSingleBucketAggregateBase ; } > ( { body : { size : 0 , aggs : { queries : { filter : { term : { input_type : 'osquery' , } , } , } , } , } , index : '.fleet-actions' , ignore_unavailable : true , } ) ; const result = { session : await getRouteMetric ( soClient , 'live_query' ) , } ; const esQueries = metricResponse . aggregations ?. queries ; if ( esQueries && typeof esQueries . doc_count === 'number' ) { result . cumulative = { queries : esQueries . doc_count , } ; } return result ; } interface BeatUsageAggs { lastDay : { max_rss ? : AggregationsRateAggregate ; max_cpu ? : AggregationsRateAggregate ; latest ? : AggregationsTopHitsAggregate ; avg_rss ? : AggregationsRateAggregate ; avg_cpu ? : AggregationsRateAggregate ; } ; } export function extractBeatUsageMetrics ( metricResponse ) { const lastDayAggs = metricResponse . aggregations ?. lastDay ; const result = { memory : { rss : { } , } , cpu : { } , } ; if ( lastDayAggs ) { if ( lastDayAggs . max_rss !== undefined ) { result . memory . rss . max = lastDayAggs . max_rss . value ; } if ( lastDayAggs . avg_rss !== undefined ) { result . memory . rss . avg = lastDayAggs . max_rss . value ; } if ( lastDayAggs . max_cpu !== undefined ) { result . cpu . max = lastDayAggs . max_cpu . value ; } if ( lastDayAggs . avg_cpu !== undefined ) { result . cpu . avg = lastDayAggs . max_cpu . value ; } if ( lastDayAggs . latest !== undefined ) { const latest = lastDayAggs . latest . hits . hits [ 0 ] ?. _source ?. monitoring . metrics . beat ; if ( latest ) { result . cpu . latest = latest . cpu . total . time . ms ; result . memory . rss . latest = latest . memstats . rss ; } } } return result ; } export async function getBeatUsage ( esClient ) { const { body : metricResponse } = await esClient . search < unknown , BeatUsageAggs > ( { body : { size : 0 , aggs : { lastDay : { filter : { range : { '@timestamp' : { gte : 'now-24h' , lte : 'now' , } , } , } , aggs : { latest : { top_hits : { sort : [ { '@timestamp' : { order : 'desc' , } , } , ] , size : 1 , } , } , max_rss : { max : { field : 'monitoring.metrics.beat.memstats.rss' , } , } , avg_rss : { avg : { field : 'monitoring.metrics.beat.memstats.rss' , } , } , max_cpu : { max : { field : 'monitoring.metrics.beat.cpu.total.time.ms' , } , } , avg_cpu : { avg : { field : 'monitoring.metrics.beat.cpu.total.time.ms' , } , } , } , } , } , } , index : METRICS_INDICES , ignore_unavailable : true , } ) ; return extractBeatUsageMetrics ( metricResponse ) ; }