Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
/comp/metadata/clusterchecks @DataDog/container-platform
/comp/metadata/haagent @DataDog/ndm-core
/comp/metadata/hostgpu @DataDog/ebpf-platform
/comp/metadata/hostsysteminfo @DataDog/windows-products
/comp/metadata/packagesigning @DataDog/agent-delivery
/comp/process/connectionscheck @DataDog/cloud-network-monitoring @DataDog/universal-service-monitoring
/comp/trace/etwtracer @DataDog/windows-products
Expand Down Expand Up @@ -531,6 +532,7 @@
/pkg/fleet/installer/setup/djm/ @DataDog/fleet @DataDog/data-jobs-monitoring
/pkg/inventory/ @DataDog/windows-products
/pkg/inventory/software/ @DataDog/windows-products
/pkg/inventory/systeminfo/ @DataDog/windows-products
/pkg/opentelemetry-mapping-go @DataDog/opentelemetry-agent
/pkg/pidfile/ @DataDog/agent-runtimes
/pkg/persistentcache/ @DataDog/agent-runtimes
Expand Down
17 changes: 17 additions & 0 deletions cmd/agent/subcommands/diagnose/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,22 @@ This command print the security-agent metadata payload. This payload is used by
},
}

payloadHostSystemInfoCmd := &cobra.Command{
Use: "host-system-info",
Short: "[internal] Print the host system info metadata payload.",
Long: `
This command print the host system info metadata payload.`,
RunE: func(_ *cobra.Command, _ []string) error {
return fxutil.OneShot(printPayload,
fx.Supply(payloadName("host-system-info")),
fx.Supply(command.GetDefaultCoreBundleParams(cliParams.GlobalParams)),
core.Bundle(),
secretnoopfx.Module(),
ipcfx.ModuleReadOnly(),
)
},
}

showPayloadCommand.AddCommand(payloadV5Cmd)
showPayloadCommand.AddCommand(payloadGohaiCmd)
showPayloadCommand.AddCommand(payloadInventoriesAgentCmd)
Expand All @@ -378,6 +394,7 @@ This command print the security-agent metadata payload. This payload is used by
showPayloadCommand.AddCommand(payloadSecurityAgentCmd)
showPayloadCommand.AddCommand(agentTelemetryCmd)
showPayloadCommand.AddCommand(agentFullTelemetryCmd)
showPayloadCommand.AddCommand(payloadHostSystemInfoCmd)
diagnoseCommand.AddCommand(showPayloadCommand)

return []*cobra.Command{diagnoseCommand}
Expand Down
8 changes: 8 additions & 0 deletions cmd/agent/subcommands/diagnose/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,11 @@ func TestShowFullAgentTelemetryCommand(t *testing.T) {
func() {},
)
}

func TestShowMetadataHostSystemInfoCommand(t *testing.T) {
fxutil.TestOneShotSubcommand(t,
Commands(&command.GlobalParams{}),
[]string{"diagnose", "show-metadata", "host-system-info"},
printPayload,
func(_ core.BundleParams) {})
}
6 changes: 6 additions & 0 deletions comp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,12 @@ Package host implements a component to generate the 'host' metadata payload (als

Package hostgpu exposes the interface for the component to generate the 'host_gpu' metadata payload for inventory.

### [comp/metadata/hostsysteminfo](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/hostsysteminfo)

*Datadog Team*: windows-products

Package hostsysteminfo exposes the interface for the component to generate the 'host_system_info' metadata payload for inventory.

### [comp/metadata/inventoryagent](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/inventoryagent)

Package inventoryagent implements a component to generate the 'datadog_agent' metadata payload for inventory.
Expand Down
2 changes: 2 additions & 0 deletions comp/metadata/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
haagentmetadatafx "github.com/DataDog/datadog-agent/comp/metadata/haagent/fx"
"github.com/DataDog/datadog-agent/comp/metadata/host/hostimpl"
hostgpu "github.com/DataDog/datadog-agent/comp/metadata/hostgpu/fx"
hostsysteminfo "github.com/DataDog/datadog-agent/comp/metadata/hostsysteminfo/fx"
"github.com/DataDog/datadog-agent/comp/metadata/inventoryagent/inventoryagentimpl"
"github.com/DataDog/datadog-agent/comp/metadata/inventorychecks/inventorychecksimpl"
"github.com/DataDog/datadog-agent/comp/metadata/inventoryhost/inventoryhostimpl"
Expand All @@ -34,6 +35,7 @@ func Bundle() fxutil.BundleOptions {
inventoryagentimpl.Module(),
inventoryhostimpl.Module(),
hostgpu.Module(),
hostsysteminfo.Module(),
inventorychecksimpl.Module(),
inventoryotelimpl.Module(),
packagesigningimpl.Module(),
Expand Down
90 changes: 90 additions & 0 deletions comp/metadata/hostsysteminfo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Host System Info Payload

This package populates the host system information fields in the `End User Device Monitoring` product in Datadog.

This is enabled only for the `end_user_device` infrastructure mode.

The payload is sent every 1 hour (see `inventories_max_interval` in the config) or whenever it's updated with at most 1 update every 1 hour (see `inventories_min_interval`).

# Content

The payload contains physical system identification attributes collected from the host system, including manufacturer details, model information, serial numbers, and chassis type. This information is useful for asset management, hardware inventory tracking, and fleet management in end-user device monitoring scenarios.

## System Information Collection

The system information is collected using platform-specific APIs:
- **Windows**: WMI queries (`Win32_ComputerSystem`, `Win32_BIOS`, `Win32_SystemEnclosure`)
- **MacOS**: IOKit queries (`IOPlatformExpertDevice`, `product`)
- **Linux/Unix**: Will not run as it is currently not implemented

Collection includes:
- Manufacturer name (for example, Dell, Lenovo, HP, Amazon EC2)
- Model number and name
- Serial number
- System SKU/Identifier
- Chassis type (Desktop, Laptop, Virtual Machine, Other)

## Configuration

System info metadata collection can be controlled using:
- `infrastructure_mode: end_user_device` - Required to enable this feature

# Format

The payload is a JSON dict with the following fields:

- `hostname` - **string**: the hostname of the agent as shown on the status page.
- `uuid` - **string**: a unique identifier of the agent, used in case the hostname is empty.
- `timestamp` - **int**: the timestamp when the payload was created (Unix nanoseconds).
- `host_system_info` - **dict of string to JSON type**:
- `manufacturer` - **string**: The company brand name under which the device is marketed.
- `model_number` - **string**: Company's specific model number of device.
- `serial_number` - **string**: The serial number assigned from the company and is accessible on the exterior of the device.
- `model_name` - **string**: The model name of the current device.
- `chassis_type` - **string**: The chassis type of the current device. One of: "Desktop", "Laptop", "Virtual Machine", or "Other".
- `identifier` - **string**: the system SKU number or other unique identifier.

## Virtual Machine Detection

The payload includes special logic to detect virtual machines:
- Hyper-V and Azure VMs are detected via the model name "Virtual Machine"
- AWS EC2 instances are detected via the manufacturer "Amazon EC2"
- When detected, the `chassis_type` is set to "Virtual Machine"

## Example Payload

Here is an example of a host system info payload for a physical laptop:

```json
{
"hostname": "LAPTOP-123456",
"timestamp": 1767996703894578400,
"host_system_info_metadata": {
"manufacturer": "LENOVO",
"model_number": "ABC123",
"serial_number": "DEF456",
"model_name": "ThinkPad T14s Gen 5",
"chassis_type": "Laptop",
"identifier": "LENOVO_MT_21LS_BU_Think_FM_ThinkPad T14s Gen 5"
},
"uuid": "1234-5678-abcd-efgh"
}
```

Here is an example for a virtual machine:

```json
{
"hostname": "WIN-VM",
"timestamp": 1767998956607294100,
"host_system_info_metadata": {
"manufacturer": "Microsoft Corporation",
"model_number": "Virtual Machine",
"serial_number": "XYZ789",
"model_name": "Virtual Machine",
"chassis_type": "Virtual Machine",
"identifier": "None"
},
"uuid": "abcd-1234-efgh-5678"
}
```
13 changes: 13 additions & 0 deletions comp/metadata/hostsysteminfo/def/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

// Package hostsysteminfo exposes the interface for the component to generate the 'host_system_info' metadata payload for inventory.
package hostsysteminfo

// team: windows-products

// Component is the component type.
type Component interface {
}
18 changes: 18 additions & 0 deletions comp/metadata/hostsysteminfo/fx/fx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2024-present Datadog, Inc.

// Package fx provides the fx module for the host system info metadata component
package fx

import (
hostsysteminfoimpl "github.com/DataDog/datadog-agent/comp/metadata/hostsysteminfo/impl"
"github.com/DataDog/datadog-agent/pkg/util/fxutil"
)

// Module defines the fx options for this component.
func Module() fxutil.Module {
return fxutil.Component(
fxutil.ProvideComponentConstructor(hostsysteminfoimpl.NewSystemInfoProvider))
}
175 changes: 175 additions & 0 deletions comp/metadata/hostsysteminfo/impl/hostsysteminfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

// Package hostsysteminfoimpl implements a component to generate the 'host_system_info' metadata payload for inventory.
package hostsysteminfoimpl

import (
"context"
"encoding/json"
"net/http"
"runtime"
"time"

api "github.com/DataDog/datadog-agent/comp/api/api/def"
"github.com/DataDog/datadog-agent/comp/core/config"
flaretypes "github.com/DataDog/datadog-agent/comp/core/flare/types"
"github.com/DataDog/datadog-agent/comp/core/hostname/hostnameinterface"
ipc "github.com/DataDog/datadog-agent/comp/core/ipc/def"
log "github.com/DataDog/datadog-agent/comp/core/log/def"
hostsysteminfo "github.com/DataDog/datadog-agent/comp/metadata/hostsysteminfo/def"
"github.com/DataDog/datadog-agent/comp/metadata/internal/util"
"github.com/DataDog/datadog-agent/comp/metadata/runner/runnerimpl"
"github.com/DataDog/datadog-agent/pkg/inventory/systeminfo"
"github.com/DataDog/datadog-agent/pkg/serializer"
"github.com/DataDog/datadog-agent/pkg/serializer/marshaler"
httputils "github.com/DataDog/datadog-agent/pkg/util/http"
"github.com/DataDog/datadog-agent/pkg/util/uuid"
)

const flareFileName = "hostsysteminfo.json"

type hostSystemInfoMetadata struct {
Manufacturer string `json:"manufacturer"`
ModelNumber string `json:"model_number"`
SerialNumber string `json:"serial_number"`
ModelName string `json:"model_name"`
ChassisType string `json:"chassis_type"`
Identifier string `json:"identifier"`
}

type hostSystemInfo struct {
util.InventoryPayload

log log.Component
conf config.Component
hostname string
data *hostSystemInfoMetadata
}

type Payload struct {
Hostname string `json:"hostname"`
Timestamp int64 `json:"timestamp"`
Metadata *hostSystemInfoMetadata `json:"host_system_info_metadata"`
UUID string `json:"uuid"`
}

func (p *Payload) MarshalJSON() ([]byte, error) {
type PayloadAlias Payload
return json.Marshal((*PayloadAlias)(p))
}

type Requires struct {
Log log.Component
Config config.Component
Serializer serializer.MetricSerializer
Hostname hostnameinterface.Component
IPCClient ipc.HTTPClient
}

type Provides struct {
Comp hostsysteminfo.Component
Provider runnerimpl.Provider
FlareProvider flaretypes.Provider
Endpoint api.AgentEndpointProvider
}

func NewSystemInfoProvider(deps Requires) Provides {
hname, _ := deps.Hostname.Get(context.Background())
hh := &hostSystemInfo{
log: deps.Log,
conf: deps.Config,
hostname: hname,
data: &hostSystemInfoMetadata{},
}
hh.InventoryPayload = util.CreateInventoryPayload(deps.Config, deps.Log, deps.Serializer, hh.getPayload, flareFileName)

// Override default intervals for system info metadata
// System info changes infrequently, so check less often
hh.InventoryPayload.MinInterval = 1 * time.Hour
hh.InventoryPayload.MaxInterval = 1 * time.Hour

// Only enable system info metadata collection for end user device infrastructure mode on Windows and Darwin
infraMode := deps.Config.GetString("infrastructure_mode")
isEndUserDevice := infraMode == "end_user_device"
isSupportedOS := runtime.GOOS == "windows" || runtime.GOOS == "darwin"
hh.InventoryPayload.Enabled = hh.InventoryPayload.Enabled && isEndUserDevice && isSupportedOS

var provider runnerimpl.Provider
if hh.InventoryPayload.Enabled {
provider = hh.MetadataProvider()
deps.Log.Info("System info metadata collection enabled for end user device mode")
} else {
if !isSupportedOS {
deps.Log.Debugf("System info metadata collection disabled: only supported on Windows and macOS (current OS: %s)", runtime.GOOS)
} else {
deps.Log.Debugf("System info metadata collection disabled: infrastructure_mode is '%s' (requires 'end_user_device')", infraMode)
}
}

return Provides{
Comp: hh,
Provider: provider,
FlareProvider: hh.FlareProvider(),
Endpoint: api.NewAgentEndpointProvider(hh.writePayloadAsJSON, "/metadata/host-system-info", "GET"),
}
}

func (hh *hostSystemInfo) fillData() error {
// System info collection is only supported on Windows and Darwin
if runtime.GOOS != "windows" && runtime.GOOS != "darwin" {
hh.log.Debugf("System information collection not supported on %s", runtime.GOOS)
hh.data = &hostSystemInfoMetadata{}
return nil
}

sysInfo, err := systeminfo.Collect()
if err != nil {
hh.log.Errorf("Failed to collect system information: %v", err)
hh.data = &hostSystemInfoMetadata{}
return err
}

// Handle case where collection returns nil data
if sysInfo == nil {
hh.log.Debug("System information collection returned no data")
hh.data = &hostSystemInfoMetadata{}
return nil
}

hh.data.Manufacturer = sysInfo.Manufacturer
hh.data.ModelNumber = sysInfo.ModelNumber
hh.data.SerialNumber = sysInfo.SerialNumber
hh.data.ModelName = sysInfo.ModelName
hh.data.ChassisType = sysInfo.ChassisType
hh.data.Identifier = sysInfo.Identifier

return nil
}

func (hh *hostSystemInfo) writePayloadAsJSON(w http.ResponseWriter, _ *http.Request) {
// GetAsJSON calls getPayload which already scrub the data
scrubbed, err := hh.GetAsJSON()
if err != nil {
httputils.SetJSONError(w, err, 500)
return
}
w.Write(scrubbed)
}

func (hh *hostSystemInfo) getPayload() marshaler.JSONMarshaler {
// Try to collect system info data
if err := hh.fillData(); err != nil {
hh.log.Debugf("Skipping system info metadata payload due to collection failure: %v", err)
return nil
}

return &Payload{
Hostname: hh.hostname,
Timestamp: time.Now().UnixNano(),
Metadata: hh.data,
UUID: uuid.GetUUID(),
}
}
Loading