Skip to content

Commit

Permalink
Send device scan status (#31894)
Browse files Browse the repository at this point in the history
  • Loading branch information
NouhaManai96 authored Jan 6, 2025
1 parent a539ab5 commit 0fd815b
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 24 deletions.
68 changes: 58 additions & 10 deletions cmd/agent/subcommands/snmp/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ package snmp
import (
"errors"
"fmt"
"net"
"os"
"strconv"

"github.com/DataDog/datadog-agent/cmd/agent/command"
"github.com/DataDog/datadog-agent/comp/aggregator"
"github.com/DataDog/datadog-agent/comp/aggregator/demultiplexer/demultiplexerimpl"
Expand All @@ -30,11 +26,15 @@ import (
compressionfx "github.com/DataDog/datadog-agent/comp/serializer/compression/fx"
snmpscan "github.com/DataDog/datadog-agent/comp/snmpscan/def"
snmpscanfx "github.com/DataDog/datadog-agent/comp/snmpscan/fx"
"github.com/DataDog/datadog-agent/pkg/networkdevice/metadata"
"github.com/DataDog/datadog-agent/pkg/snmp/snmpparse"
"github.com/DataDog/datadog-agent/pkg/util/fxutil"

"github.com/spf13/cobra"
"go.uber.org/fx"
"net"
"os"
"strconv"
"time"
)

const (
Expand Down Expand Up @@ -302,16 +302,64 @@ func scanDevice(connParams *snmpparse.SNMPConfig, args argsType, snmpScanner snm
// newSNMP only returns config errors, so any problem is a usage error
return configErr{err}
}
if err := snmp.Connect(); err != nil {
namespace := conf.GetString("network_devices.namespace")
deviceID := namespace + ":" + connParams.IPAddress
// Since the snmp connection can take a while, start by sending an in progress status for the start of the scan
// before connecting to the agent
inProgressStatusPayload := metadata.NetworkDevicesMetadata{
DeviceScanStatus: &metadata.ScanStatusMetadata{
DeviceID: deviceID,
ScanStatus: metadata.ScanStatusInProgress,
},
CollectTimestamp: time.Now().Unix(),
Namespace: namespace,
}
if err = snmpScanner.SendPayload(inProgressStatusPayload); err != nil {
return fmt.Errorf("unable to send in progress status: %v", err)
}
if err = snmp.Connect(); err != nil {
// Send an error status if we can't connect to the agent
errorStatusPayload := metadata.NetworkDevicesMetadata{
DeviceScanStatus: &metadata.ScanStatusMetadata{
DeviceID: deviceID,
ScanStatus: metadata.ScanStatusError,
},
CollectTimestamp: time.Now().Unix(),
Namespace: namespace,
}
if err = snmpScanner.SendPayload(errorStatusPayload); err != nil {
return fmt.Errorf("unable to send error status: %v", err)
}
return fmt.Errorf("unable to connect to SNMP agent on %s:%d: %w", snmp.LocalAddr, snmp.Port, err)
}

namespace := conf.GetString("network_devices.namespace")

err = snmpScanner.RunDeviceScan(snmp, namespace, connParams.IPAddress)
err = snmpScanner.RunDeviceScan(snmp, namespace, deviceID)
if err != nil {
// Send an error status if we can't scan the device
errorStatusPayload := metadata.NetworkDevicesMetadata{
DeviceScanStatus: &metadata.ScanStatusMetadata{
DeviceID: deviceID,
ScanStatus: metadata.ScanStatusError,
},
CollectTimestamp: time.Now().Unix(),
Namespace: namespace,
}
if err = snmpScanner.SendPayload(errorStatusPayload); err != nil {
return fmt.Errorf("unable to send error status: %v", err)
}
return fmt.Errorf("unable to perform device scan: %v", err)
}
// Send a completed status if the scan was successful
completedStatusPayload := metadata.NetworkDevicesMetadata{
DeviceScanStatus: &metadata.ScanStatusMetadata{
DeviceID: deviceID,
ScanStatus: metadata.ScanStatusCompleted,
},
CollectTimestamp: time.Now().Unix(),
Namespace: namespace,
}
if err = snmpScanner.SendPayload(completedStatusPayload); err != nil {
return fmt.Errorf("unable to send completed status: %v", err)
}
return nil
}

Expand Down
4 changes: 3 additions & 1 deletion comp/snmpscan/def/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package snmpscan

import (
"github.com/DataDog/datadog-agent/pkg/networkdevice/metadata"
"github.com/gosnmp/gosnmp"
)

Expand All @@ -15,6 +16,7 @@ import (
// Component is the component type.
type Component interface {
// Triggers a device scan
RunDeviceScan(snmpConection *gosnmp.GoSNMP, deviceNamespace string, deviceIPAddress string) error
RunDeviceScan(snmpConection *gosnmp.GoSNMP, deviceNamespace string, deviceID string) error
RunSnmpWalk(snmpConection *gosnmp.GoSNMP, firstOid string) error
SendPayload(payload metadata.NetworkDevicesMetadata) error
}
16 changes: 3 additions & 13 deletions comp/snmpscan/impl/devicescan.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@
package snmpscanimpl

import (
"encoding/json"
"time"

"github.com/DataDog/datadog-agent/comp/forwarder/eventplatform"
"github.com/DataDog/datadog-agent/pkg/logs/message"
"github.com/DataDog/datadog-agent/pkg/networkdevice/metadata"
"github.com/DataDog/datadog-agent/pkg/snmp/gosnmplib"
"github.com/gosnmp/gosnmp"
)

func (s snmpScannerImpl) RunDeviceScan(snmpConnection *gosnmp.GoSNMP, deviceNamespace string, deviceIPAddress string) error {
func (s snmpScannerImpl) RunDeviceScan(snmpConnection *gosnmp.GoSNMP, deviceNamespace string, deviceID string) error {
// execute the scan
pdus, err := gatherPDUs(snmpConnection)
if err != nil {
return err
}

deviceID := deviceNamespace + ":" + deviceIPAddress
var deviceOids []*metadata.DeviceOID
for _, pdu := range pdus {
record, err := metadata.DeviceOIDFromPDU(deviceID, pdu)
Expand All @@ -35,15 +32,8 @@ func (s snmpScannerImpl) RunDeviceScan(snmpConnection *gosnmp.GoSNMP, deviceName

metadataPayloads := metadata.BatchDeviceScan(deviceNamespace, time.Now(), metadata.PayloadMetadataBatchSize, deviceOids)
for _, payload := range metadataPayloads {
payloadBytes, err := json.Marshal(payload)
err := s.SendPayload(payload)
if err != nil {
s.log.Errorf("Error marshalling device metadata: %v", err)
continue
}
m := message.NewMessage(payloadBytes, nil, "", 0)
s.log.Debugf("Device OID metadata payload is %d bytes", len(payloadBytes))
s.log.Tracef("Device OID metadata payload: %s", string(payloadBytes))
if err := s.epforwarder.SendEventPlatformEventBlocking(m, eventplatform.EventTypeNetworkDevicesMetadata); err != nil {
return err
}
}
Expand Down
28 changes: 28 additions & 0 deletions comp/snmpscan/impl/sendpayload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 snmpscanimpl

import (
"encoding/json"
"github.com/DataDog/datadog-agent/comp/forwarder/eventplatform"
"github.com/DataDog/datadog-agent/pkg/logs/message"
"github.com/DataDog/datadog-agent/pkg/networkdevice/metadata"
)

func (s snmpScannerImpl) SendPayload(payload metadata.NetworkDevicesMetadata) error {
payloadBytes, err := json.Marshal(payload)
if err != nil {
s.log.Errorf("Error marshalling device metadata: %v", err)
return nil
}
m := message.NewMessage(payloadBytes, nil, "", 0)
s.log.Debugf("Device metadata payload is %d bytes", len(payloadBytes))
s.log.Tracef("Device metadata payload: %s", string(payloadBytes))
if err := s.epforwarder.SendEventPlatformEventBlocking(m, eventplatform.EventTypeNetworkDevicesMetadata); err != nil {
return err
}
return nil
}
4 changes: 4 additions & 0 deletions comp/snmpscan/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

log "github.com/DataDog/datadog-agent/comp/core/log/def"
snmpscan "github.com/DataDog/datadog-agent/comp/snmpscan/def"
"github.com/DataDog/datadog-agent/pkg/networkdevice/metadata"
"github.com/gosnmp/gosnmp"
)

Expand All @@ -38,3 +39,6 @@ func (m mock) RunDeviceScan(_ *gosnmp.GoSNMP, _ string, _ string) error {
func (m mock) RunSnmpWalk(_ *gosnmp.GoSNMP, _ string) error {
return nil
}
func (m mock) SendPayload(_ metadata.NetworkDevicesMetadata) error {
return nil
}
19 changes: 19 additions & 0 deletions pkg/networkdevice/metadata/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type NetworkDevicesMetadata struct {
NetflowExporters []NetflowExporter `json:"netflow_exporters,omitempty"`
Diagnoses []DiagnosisMetadata `json:"diagnoses,omitempty"`
DeviceOIDs []DeviceOID `json:"device_oids,omitempty"`
DeviceScanStatus *ScanStatusMetadata `json:"scan_status,omitempty"`
CollectTimestamp int64 `json:"collect_timestamp"`
}

Expand Down Expand Up @@ -81,6 +82,24 @@ type DeviceOID struct {
Value string `json:"value"`
}

// ScanStatus type for the different possible scan statuses
type ScanStatus string

const (
// ScanStatusInProgress represents a scan in progress
ScanStatusInProgress ScanStatus = "in progress"
// ScanStatusCompleted represents a completed scan
ScanStatusCompleted ScanStatus = "completed"
// ScanStatusError represents a scan error
ScanStatusError ScanStatus = "error"
)

// ScanStatusMetadata contains scan status metadata
type ScanStatusMetadata struct {
DeviceID string `json:"device_id"`
ScanStatus ScanStatus `json:"scan_status"`
}

// InterfaceMetadata contains interface metadata
type InterfaceMetadata struct {
DeviceID string `json:"device_id"`
Expand Down

0 comments on commit 0fd815b

Please sign in to comment.