Agentless Telemetry

This commit is contained in:
Ido Cohen 2025-03-12 09:17:31 +02:00 committed by GitHub
parent 78fb6883f1
commit f4c9a700d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 158 additions and 9 deletions

View file

@ -3981,6 +3981,58 @@
}
}
}
},
"agentless_agents": {
"properties": {
"total_enrolled": {
"type": "long",
"_meta": {
"description": "The total number of enrolled agents, in any state"
}
},
"healthy": {
"type": "long",
"_meta": {
"description": "The total number of enrolled agents in a healthy state"
}
},
"unhealthy": {
"type": "long",
"_meta": {
"description": "The total number of enrolled agents in an unhealthy state"
}
},
"updating": {
"type": "long",
"_meta": {
"description": "The total number of enrolled agents in an updating state"
}
},
"offline": {
"type": "long",
"_meta": {
"description": "The total number of enrolled agents currently offline"
}
},
"inactive": {
"type": "long",
"_meta": {
"description": "The total number of of enrolled agents currently inactive"
}
},
"unenrolled": {
"type": "long",
"_meta": {
"description": "The total number of agents currently unenrolled"
}
},
"total_all_statuses": {
"type": "long",
"_meta": {
"description": "The total number of agents in any state, both enrolled and inactive"
}
}
}
}
}
},

View file

@ -7,9 +7,9 @@
import type { SavedObjectsClient, ElasticsearchClient } from '@kbn/core/server';
import { AGENTS_INDEX } from '../../common';
import { AGENTS_INDEX, LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../common';
import * as AgentService from '../services/agents';
import { appContextService } from '../services';
import { agentPolicyService, appContextService } from '../services';
import { getAgentStatusForAgentPolicy } from '../services/agents';
export interface AgentStatus {
@ -28,7 +28,8 @@ export interface AgentUsage extends AgentStatus {
export const getAgentUsage = async (
soClient?: SavedObjectsClient,
esClient?: ElasticsearchClient
esClient?: ElasticsearchClient,
onlyAgentless?: boolean
): Promise<AgentUsage> => {
// TODO: unsure if this case is possible at all.
if (!soClient || !esClient) {
@ -44,8 +45,26 @@ export const getAgentUsage = async (
};
}
let agentPolicyIds;
if (onlyAgentless) {
const agentPolicies = await agentPolicyService.list(soClient, {
perPage: 1000, // avoiding pagination
withPackagePolicies: true,
kuery: `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}.supports_agentless:true`,
});
agentPolicyIds = agentPolicies.items.map((agentPolicy) => agentPolicy.id);
}
const { total, inactive, online, error, offline, updating, unenrolled } =
await AgentService.getAgentStatusForAgentPolicy(esClient, soClient);
await AgentService.getAgentStatusForAgentPolicy(
esClient,
soClient,
undefined,
undefined,
undefined,
agentPolicyIds
);
return {
total_enrolled: total,
healthy: online,

View file

@ -12,12 +12,19 @@ import { getPackageSavedObjects } from '../services/epm/packages/get';
import { agentPolicyService } from '../services';
import type { NewPackagePolicy } from '../types';
import type { AgentUsage } from './agent_collectors';
export interface PackageUsage {
name: string;
version: string;
enabled: boolean;
}
export interface AgentlessUsage {
agentlessPackages: PackageUsage[];
agentlessAgents: AgentUsage;
}
export const getPackageUsage = async (soClient?: SavedObjectsClient): Promise<PackageUsage[]> => {
if (!soClient) {
return [];
@ -34,17 +41,35 @@ export const getPackageUsage = async (soClient?: SavedObjectsClient): Promise<Pa
const packagesInAgentPolicies = agentPolicies.items.map((agentPolicy) => {
const packagePolicies: NewPackagePolicy[] = agentPolicy.package_policies as NewPackagePolicy[];
return packagePolicies
.map((packagePolicy) => packagePolicy.package?.name)
.filter((packageName): packageName is string => packageName !== undefined);
.filter(
(packagePolicy): packagePolicy is NewPackagePolicy =>
packagePolicy.package?.name !== undefined && packagePolicy.supports_agentless !== true
)
.map((packagePolicy) => packagePolicy.package?.name);
});
const enabledPackages = _.uniq(_.flatten(packagesInAgentPolicies));
const packagesInAgentlessPolicies = agentPolicies.items.map((agentPolicy) => {
const packagePolicies: NewPackagePolicy[] = agentPolicy.package_policies as NewPackagePolicy[];
return packagePolicies
.filter(
(packagePolicy): packagePolicy is NewPackagePolicy =>
packagePolicy.package?.name !== undefined && packagePolicy.supports_agentless === true
)
.map((packagePolicy) => packagePolicy.package?.name);
});
const enabledAgentlessPackages = _.uniq(_.flatten(packagesInAgentlessPolicies));
return packagesSavedObjects.saved_objects.map((p) => {
const agentBased = enabledPackages.includes(p.attributes.name);
const agentless = enabledAgentlessPackages.includes(p.attributes.name);
const enabled = agentBased || agentless;
return {
name: p.attributes.name,
version: p.attributes.version,
enabled: enabledPackages.includes(p.attributes.name),
enabled,
...(enabled && agentBased && { agent_based: true }), // Only include if true
...(enabled && agentless && { agentless: true }), // Only include if true
};
});
};

View file

@ -34,6 +34,7 @@ export interface Usage {
agents: AgentUsage;
packages: PackageUsage[];
fleet_server: FleetServerUsage;
agentless_agents: AgentUsage;
}
export interface FleetUsage extends Usage, AgentData {
@ -75,18 +76,20 @@ export const fetchFleetUsage = async (
license_issued_to: (await esClient.license.get()).license.issued_to,
deployment_id: appContextService.getCloud()?.deploymentId,
integrations_details: await getIntegrationsDetails(soClient),
agentless_agents: await getAgentUsage(soClient, esClient, true),
};
return usage;
};
// used by kibana daily collector
const fetchUsage = async (core: CoreSetup, config: FleetConfigType) => {
const fetchUsage = async (core: CoreSetup, config: FleetConfigType): Promise<Usage> => {
const [soClient, esClient] = await getInternalClients(core);
const usage = {
agents_enabled: getIsAgentsEnabled(config),
agents: await getAgentUsage(soClient, esClient),
fleet_server: await getFleetServerUsage(soClient, esClient),
packages: await getPackageUsage(soClient),
agentless_agents: await getAgentUsage(soClient, esClient, true),
};
return usage;
};
@ -236,6 +239,56 @@ export function registerFleetUsageCollector(
enabled: { type: 'boolean' },
},
},
agentless_agents: {
total_enrolled: {
type: 'long',
_meta: {
description: 'The total number of enrolled agents, in any state',
},
},
healthy: {
type: 'long',
_meta: {
description: 'The total number of enrolled agents in a healthy state',
},
},
unhealthy: {
type: 'long',
_meta: {
description: 'The total number of enrolled agents in an unhealthy state',
},
},
updating: {
type: 'long',
_meta: {
description: 'The total number of enrolled agents in an updating state',
},
},
offline: {
type: 'long',
_meta: {
description: 'The total number of enrolled agents currently offline',
},
},
inactive: {
type: 'long',
_meta: {
description: 'The total number of of enrolled agents currently inactive',
},
},
unenrolled: {
type: 'long',
_meta: {
description: 'The total number of agents currently unenrolled',
},
},
total_all_statuses: {
type: 'long',
_meta: {
description: 'The total number of agents in any state, both enrolled and inactive',
},
},
},
},
});