Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ profile hda_sudo {
/run/systemd/resolve/stub-resolv.conf r,
/usr/bin/ipmitool rPx -> hda_ipmitool,
/usr/bin/lshw rPx -> hda_lshw,
/usr/bin/rpc ix,
/usr/bin/sudo mr,
/usr/libexec/sudo/libsudo_util.so.* mr,
/usr/sbin/dmidecode rPx -> hda_dmidecode,
Expand Down
2 changes: 1 addition & 1 deletion hardware-discovery-agent/config/sudoers.d/hd-agent
Original file line number Diff line number Diff line change
@@ -1 +1 @@
hd-agent ALL=(root) NOPASSWD:/usr/sbin/dmidecode,/usr/bin/ipmitool,/usr/bin/lshw,/usr/sbin/lshw
hd-agent ALL=(root) NOPASSWD:/usr/sbin/dmidecode,/usr/bin/ipmitool,/usr/bin/lshw,/usr/sbin/lshw,/usr/bin/rpc
8 changes: 7 additions & 1 deletion hardware-discovery-agent/internal/comms/comms.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: (C) 2025 Intel Corporation
// SPDX-FileCopyrightText: (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
package comms

Expand All @@ -16,6 +16,7 @@ import (
"google.golang.org/grpc/credentials"

"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/cpu"
"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/device"
"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/disk"
"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/gpu"
"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/logger"
Expand Down Expand Up @@ -357,5 +358,10 @@ func GenerateSystemInfoRequest(executor utils.CmdExecutor) *proto.SystemInfo {
log.Errorf("unable to get usb description : %v", err)
}

_, err = device.GetDeviceInfo(executor)
if err != nil {
log.Errorf("unable to get device description : %v", err)
}

return parseSystemInfo(sn, productName, bmcAddr, osInfo, biosInfo, cpu, storage, gpu, mem, networkList, bmType, usbList)
}
126 changes: 114 additions & 12 deletions hardware-discovery-agent/internal/comms/comms_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: (C) 2025 Intel Corporation
// SPDX-FileCopyrightText: (C) 2026 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
package comms_test

Expand Down Expand Up @@ -426,6 +426,18 @@ func getUsbInfo() []*proto.SystemUSB {
return usbInfo
}

func TestGenerateUpdateDeviceRequestSuccessAllInfo(t *testing.T) {
network.ReadFile = mockedReadFile
network.ReadDir = mockedReadDir
network.Readlink = mockedReadlink
network.CollectEthtoolData = mockedCollectEthtoolData
network.Stat = mockedStat
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassed)
expected := expectedSystemInfoResult("12A34B5", "Test Product", "192.168.1.50", getOsInfo(), getBiosInfo(), getCpuInfo(), getStorageInfo(), getGpuInfo(), 17179869184, getNetworkInfo(), proto.BmInfo_IPMI, getUsbInfo())
require.NotNil(t, json)
assert.Equal(t, expected, json)
}

func TestGenerateUpdateDeviceRequestErr(t *testing.T) {
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandFailed)
osKern := proto.OsKernel{}
Expand All @@ -444,31 +456,55 @@ func TestGenerateUpdateDeviceRequestErr(t *testing.T) {
assert.Equal(t, expected, json)
}

func TestGenerateUpdateDeviceRequestSuccessAllInfo(t *testing.T) {
network.ReadFile = mockedReadFile
network.ReadDir = mockedReadDir
network.Readlink = mockedReadlink
network.CollectEthtoolData = mockedCollectEthtoolData
network.Stat = mockedStat
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassed)
expected := expectedSystemInfoResult("12A34B5", "Test Product", "192.168.1.50", getOsInfo(), getBiosInfo(), getCpuInfo(), getStorageInfo(), getGpuInfo(), 17179869184, getNetworkInfo(), proto.BmInfo_IPMI, getUsbInfo())
func TestGenerateUpdateDeviceRequestSuccessStorageOnly(t *testing.T) {
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassedStorageOnly)
osKern := proto.OsKernel{}
osRelease := proto.OsRelease{}
osInfo := &proto.OsInfo{
Kernel: &osKern,
Release: &osRelease,
}
cpu := &proto.SystemCPU{}
gpu := []*proto.SystemGPU{}
networks := []*proto.SystemNetwork{}
usbInfo := []*proto.SystemUSB{}
expected := expectedSystemInfoResult("", "", "", osInfo, &proto.BiosInfo{}, cpu, getStorageInfo(), gpu, uint64(0), networks, proto.BmInfo_NONE, usbInfo)
require.NotNil(t, json)
assert.Equal(t, expected, json)
}

func TestGenerateUpdateDeviceRequestSuccessStorageOnly(t *testing.T) {
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassedStorageOnly)
func TestGeneraeUpdateDeviceRequestSuccessSerialNumberOnly(t *testing.T) {
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassedSerialNumberOnly)
osKern := proto.OsKernel{}
osRelease := proto.OsRelease{}
osInfo := &proto.OsInfo{
Kernel: &osKern,
Release: &osRelease,
}
cpu := &proto.SystemCPU{}
storage := []*proto.SystemDisk{}
gpu := []*proto.SystemGPU{}
networks := []*proto.SystemNetwork{}
usbInfo := []*proto.SystemUSB{}
expected := expectedSystemInfoResult("", "", "", osInfo, &proto.BiosInfo{}, cpu, getStorageInfo(), gpu, uint64(0), networks, proto.BmInfo_NONE, usbInfo)
expected := expectedSystemInfoResult("12A34B5", "", "", osInfo, &proto.BiosInfo{}, cpu, storage, gpu, uint64(0), networks, proto.BmInfo_NONE, usbInfo)
require.NotNil(t, json)
assert.Equal(t, expected, json)
}

func TestGenerateUpdateDeviceRequestSuccessProductNameOnly(t *testing.T) {
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassedProductNameOnly)
osKern := proto.OsKernel{}
osRelease := proto.OsRelease{}
osInfo := &proto.OsInfo{
Kernel: &osKern,
Release: &osRelease,
}
cpu := &proto.SystemCPU{}
storage := []*proto.SystemDisk{}
gpu := []*proto.SystemGPU{}
networks := []*proto.SystemNetwork{}
usbInfo := []*proto.SystemUSB{}
expected := expectedSystemInfoResult("", "Test Product", "", osInfo, &proto.BiosInfo{}, cpu, storage, gpu, uint64(0), networks, proto.BmInfo_NONE, usbInfo)
require.NotNil(t, json)
assert.Equal(t, expected, json)
}
Expand Down Expand Up @@ -594,6 +630,24 @@ func TestGenerateUpdateDeviceRequestSuccessUsbInfoOnly(t *testing.T) {
assert.Equal(t, expected, json)
}

func TestGenerateUpdateDeviceRequestSuccessDeviceInfoOnly(t *testing.T) {
json := comms.GenerateSystemInfoRequest(testCmdExecutorCommandPassedDeviceOnly)
osKern := proto.OsKernel{}
osRelease := proto.OsRelease{}
osInfo := &proto.OsInfo{
Kernel: &osKern,
Release: &osRelease,
}
cpu := &proto.SystemCPU{}
storage := []*proto.SystemDisk{}
gpu := []*proto.SystemGPU{}
networks := []*proto.SystemNetwork{}
usbInfo := []*proto.SystemUSB{}
expected := expectedSystemInfoResult("", "", "", osInfo, &proto.BiosInfo{}, cpu, storage, gpu, uint64(0), networks, proto.BmInfo_NONE, usbInfo)
require.NotNil(t, json)
assert.Equal(t, expected, json)
}

func testCmd(testFunc string, command string, args ...string) *exec.Cmd {
cs := []string{fmt.Sprintf("-test.run=%s", testFunc), "--", command}
cs = append(cs, args...)
Expand Down Expand Up @@ -644,6 +698,8 @@ func testCmdExecutorCommandPassed(command string, args ...string) *exec.Cmd {
return testCmd("TestGenerateUpdateDeviceRequestCommandGpuDetails", command, args...)
} else if strings.Contains(args[0], "ipmitool") {
return testCmd("TestGenerateUpdateDeviceRequestCommandIpmiDetails", command, args...)
} else if strings.Contains(args[0], "rpc") {
return testCmd("TestGenerateUpdateDeviceRequestCommandDeviceDetails", command, args...)
} else {
if strings.Contains(args[2], "bios-version") {
return testCmd("TestGenerateUpdateDeviceRequestCommandBiosVersion", command, args...)
Expand All @@ -670,6 +726,30 @@ func testCmdExecutorCommandPassedStorageOnly(command string, args ...string) *ex
}
}

func testCmdExecutorCommandPassedSerialNumberOnly(command string, args ...string) *exec.Cmd {
if strings.Contains(command, "sudo") && strings.Contains(args[0], "dmidecode") {
if strings.Contains(args[2], "system-serial-number") {
return testCmd("TestGenerateUpdateDeviceRequestCommandSystemSerialNumber", command, args...)
} else {
return testCmd("TestGenerateUpdateDeviceRequestCommandFailed", command, args...)
}
} else {
return testCmd("TestGenerateUpdateDeviceRequestCommandFailed", command, args...)
}
}

func testCmdExecutorCommandPassedProductNameOnly(command string, args ...string) *exec.Cmd {
if strings.Contains(command, "sudo") && strings.Contains(args[0], "dmidecode") {
if strings.Contains(args[2], "system-product-name") {
return testCmd("TestGenerateUpdateDeviceRequestCommandSystemProductName", command, args...)
} else {
return testCmd("TestGenerateUpdateDeviceRequestCommandFailed", command, args...)
}
} else {
return testCmd("TestGenerateUpdateDeviceRequestCommandFailed", command, args...)
}
}

func testCmdExecutorCommandPassedOsOnly(command string, args ...string) *exec.Cmd {
if strings.Contains(command, "uname") {
if strings.Contains(args[0], "-r") {
Expand Down Expand Up @@ -760,6 +840,18 @@ func testCmdExecutorCommandPassedUsbOnly(command string, args ...string) *exec.C
}
}

func testCmdExecutorCommandPassedDeviceOnly(command string, args ...string) *exec.Cmd {
if strings.Contains(command, "sudo") {
if strings.Contains(args[0], "rpc") {
return testCmd("TestGenerateUpdateDeviceRequestCommandDeviceDetails", command, args...)
} else {
return testCmd("TestGenerateUpdateDeviceRequestCommandFailed", command, args...)
}
} else {
return testCmd("TestGenerateUpdateDeviceRequestCommandFailed", command, args...)
}
}

func testCmdExecutorCommandFailed(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestGenerateUpdateDeviceRequestCommandFailed", "--", command}
cs = append(cs, args...)
Expand Down Expand Up @@ -788,6 +880,16 @@ func TestGenerateUpdateDeviceRequestCommandCoreDetails(t *testing.T) {
os.Exit(0)
}

func TestGenerateUpdateDeviceRequestCommandDeviceDetails(t *testing.T) {
if os.Getenv("GO_TEST_PROCESS") != "1" {
return
}
testData, err := os.ReadFile("../../test/data/mock_amtinfo.json")
require.NoError(t, err)
fmt.Fprintf(os.Stdout, "%v", string(testData))
os.Exit(0)
}

func TestGenerateUpdateDeviceRequestCommandDiskDetails(t *testing.T) {
if os.Getenv("GO_TEST_PROCESS") != "1" {
return
Expand Down
56 changes: 56 additions & 0 deletions hardware-discovery-agent/internal/device/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: (C) 2026 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0

package device

import (
"encoding/json"
"fmt"

"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/system"
"github.com/open-edge-platform/edge-node-agents/hardware-discovery-agent/internal/utils"
)

type RASInfo struct {
NetworkStatus string `json:"networkStatus"`
RemoteStatus string `json:"remoteStatus"`
RemoteTrigger string `json:"remoteTrigger"`
MPSHostname string `json:"mpsHostname"`
}

type AMTInfo struct {
Version string `json:"amt"`
Hostname string `json:"hostnameOS"`
OperationalState string `json:"operationalState"`
BuildNumber string `json:"buildNumber"`
Sku string `json:"sku"`
Features string `json:"features"`
Uuid string `json:"uuid"`
ControlMode string `json:"controlMode"`
DNSSuffix string `json:"dnsSuffix"`
RAS RASInfo `json:"ras"`
}

func GetDeviceInfo(executor utils.CmdExecutor) (AMTInfo, error) {
var amtInfo AMTInfo
dataBytes, err := utils.ReadFromCommand(executor, "sudo", "rpc", "amtinfo", "-json")
if err != nil {
return amtInfo, fmt.Errorf("failed to read data from command; error: %w", err)
}

err = json.Unmarshal(dataBytes, &amtInfo)
if err != nil {
return amtInfo, fmt.Errorf("failed to parse data from command; error: %w", err)
}

if amtInfo.Uuid == "" {
systemId, err := system.GetSystemUUID(executor)
if err != nil {
return AMTInfo{}, fmt.Errorf("failed to retrieve system uuid; error: %w", err)
}
amtInfo.Uuid = systemId
}

return amtInfo, nil
}
Loading