diff --git a/providers/os/resources/cpu.go b/providers/os/resources/cpu.go new file mode 100644 index 0000000000..5d73beec88 --- /dev/null +++ b/providers/os/resources/cpu.go @@ -0,0 +1,396 @@ +// Copyright (c) Mondoo, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package resources + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "strconv" + "strings" + + "go.mondoo.com/mql/v13/llx" + "go.mondoo.com/mql/v13/providers-sdk/v1/inventory" + "go.mondoo.com/mql/v13/providers-sdk/v1/plugin" + "go.mondoo.com/mql/v13/providers/os/connection/shared" + "go.mondoo.com/mql/v13/providers/os/resources/powershell" + "go.mondoo.com/mql/v13/providers/os/resources/procfs" +) + +type cpuInfo struct { + Cores int64 + Manufacturer string + Model string + ProcessorCount int64 +} + +func initMachineCpu(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { + if len(args) > 2 { + return args, nil, nil + } + + conn := runtime.Connection.(shared.Connection) + pf := conn.Asset().Platform + + var info *cpuInfo + var err error + + switch { + case pf.IsFamily("darwin"): + info, err = getCpuInfoMacos(conn) + case pf.IsFamily("windows"): + info, err = getCpuInfoWindows(conn) + case pf.Name == "freebsd": + info, err = getCpuInfoFreeBSD(conn) + case pf.Name == "aix": + info, err = getCpuInfoAIX(conn) + case pf.Name == "solaris": + info, err = getCpuInfoSolaris(conn) + case pf.IsFamily(inventory.FAMILY_UNIX): + info, err = getCpuInfoLinux(conn) + default: + return nil, nil, fmt.Errorf("unsupported platform for cpu info: %s", pf.Name) + } + if err != nil { + return nil, nil, err + } + + return map[string]*llx.RawData{ + "coreCount": llx.IntData(info.Cores), + "manufacturer": llx.StringData(info.Manufacturer), + "model": llx.StringData(info.Model), + "processorCount": llx.IntData(info.ProcessorCount), + }, nil, nil +} + +func getCpuInfoLinux(conn shared.Connection) (*cpuInfo, error) { + f, err := conn.FileSystem().Open("/proc/cpuinfo") + if err != nil { + return nil, err + } + defer f.Close() + + parsed, err := procfs.ParseCpuInfo(f) + if err != nil { + return nil, err + } + + if len(parsed.Processors) == 0 { + return nil, errors.New("no processors found in /proc/cpuinfo") + } + + // Count physical CPU packages (sockets) by unique physical_id values. + // Count physical cores by deduplicating (physical_id, core_id) pairs. + // On ARM or in containers where these fields aren't populated, + // fall back to 1 socket and using processor count as core count. + type coreKey struct { + physicalID uint + coreID uint + } + seenCores := map[coreKey]struct{}{} + seenSockets := map[uint]struct{}{} + hasCoreInfo := false + for _, p := range parsed.Processors { + if p.CPUCores > 0 || p.Siblings > 0 { + hasCoreInfo = true + } + seenCores[coreKey{p.PhysicalID, p.CoreID}] = struct{}{} + seenSockets[p.PhysicalID] = struct{}{} + } + + info := &cpuInfo{ + Manufacturer: normalizeManufacturer(parsed.Processors[0].VendorID), + Model: parsed.Processors[0].ModelName, + } + + if hasCoreInfo { + info.Cores = int64(len(seenCores)) + info.ProcessorCount = int64(len(seenSockets)) + } else { + // ARM or minimal /proc/cpuinfo: each processor entry is a core, assume 1 socket + info.Cores = int64(len(parsed.Processors)) + info.ProcessorCount = 1 + } + + return info, nil +} + +func getCpuInfoMacos(conn shared.Connection) (*cpuInfo, error) { + cmd, err := conn.RunCommand("sysctl -n machdep.cpu.brand_string hw.physicalcpu") + if err != nil { + return nil, err + } + if cmd.ExitStatus != 0 { + stderr, _ := io.ReadAll(cmd.Stderr) + return nil, fmt.Errorf("sysctl failed: %s", string(stderr)) + } + + data, err := io.ReadAll(cmd.Stdout) + if err != nil { + return nil, err + } + + lines := strings.Split(strings.TrimSpace(string(data)), "\n") + if len(lines) < 2 { + return nil, fmt.Errorf("unexpected sysctl output: %s", string(data)) + } + + brandString := strings.TrimSpace(lines[0]) + physicalCPU, err := strconv.ParseInt(strings.TrimSpace(lines[1]), 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse hw.physicalcpu: %w", err) + } + + info := &cpuInfo{ + Model: brandString, + Cores: physicalCPU, + ProcessorCount: 1, // Macs always have a single CPU package + } + + // Extract manufacturer from brand string + if strings.Contains(brandString, "Intel") { + info.Manufacturer = "Intel" + } else if strings.Contains(brandString, "Apple") { + info.Manufacturer = "Apple" + } else if strings.Contains(brandString, "AMD") { + info.Manufacturer = "AMD" + } + + return info, nil +} + +const cpuWindowsScript = ` +$cpu = @(Get-CimInstance -ClassName Win32_Processor) +$result = @{ + Name = $cpu[0].Name + Manufacturer = $cpu[0].Manufacturer + NumberOfCores = ($cpu | Measure-Object -Property NumberOfCores -Sum).Sum + ProcessorCount = $cpu.Count +} +$result | ConvertTo-Json +` + +func getCpuInfoWindows(conn shared.Connection) (*cpuInfo, error) { + cmd, err := conn.RunCommand(powershell.Encode(cpuWindowsScript)) + if err != nil { + return nil, err + } + if cmd.ExitStatus != 0 { + stderr, _ := io.ReadAll(cmd.Stderr) + return nil, fmt.Errorf("failed to retrieve cpu info: %s", string(stderr)) + } + + var result struct { + Name string `json:"Name"` + Manufacturer string `json:"Manufacturer"` + NumberOfCores int64 `json:"NumberOfCores"` + ProcessorCount int64 `json:"ProcessorCount"` + } + + data, err := io.ReadAll(cmd.Stdout) + if err != nil { + return nil, err + } + + if err := json.Unmarshal(data, &result); err != nil { + return nil, fmt.Errorf("failed to parse cpu info: %w", err) + } + + return &cpuInfo{ + Model: strings.TrimSpace(result.Name), + Manufacturer: normalizeManufacturer(strings.TrimSpace(result.Manufacturer)), + Cores: result.NumberOfCores, + ProcessorCount: result.ProcessorCount, + }, nil +} + +func getCpuInfoFreeBSD(conn shared.Connection) (*cpuInfo, error) { + cmd, err := conn.RunCommand("sysctl -n hw.model kern.smp.cores") + if err != nil { + return nil, err + } + if cmd.ExitStatus != 0 { + stderr, _ := io.ReadAll(cmd.Stderr) + return nil, fmt.Errorf("sysctl failed: %s", string(stderr)) + } + + data, err := io.ReadAll(cmd.Stdout) + if err != nil { + return nil, err + } + + lines := strings.Split(strings.TrimSpace(string(data)), "\n") + if len(lines) < 2 { + return nil, fmt.Errorf("unexpected sysctl output: %s", string(data)) + } + + model := strings.TrimSpace(lines[0]) + cores, err := strconv.ParseInt(strings.TrimSpace(lines[1]), 10, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse kern.smp.cores: %w", err) + } + + // FreeBSD doesn't expose socket/package count directly; default to 1 + info := &cpuInfo{ + Model: model, + Cores: cores, + ProcessorCount: 1, + } + + // Extract manufacturer from model string + if strings.Contains(model, "Intel") { + info.Manufacturer = "Intel" + } else if strings.Contains(model, "AMD") { + info.Manufacturer = "AMD" + } + + return info, nil +} + +func getCpuInfoAIX(conn shared.Connection) (*cpuInfo, error) { + // Use prtconf for processor type + cmd, err := conn.RunCommand("prtconf") + if err != nil { + return nil, err + } + if cmd.ExitStatus != 0 { + stderr, _ := io.ReadAll(cmd.Stderr) + return nil, fmt.Errorf("prtconf failed: %s", string(stderr)) + } + + data, err := io.ReadAll(cmd.Stdout) + if err != nil { + return nil, err + } + + info := &cpuInfo{ + Manufacturer: "IBM", + ProcessorCount: 1, + } + + for _, line := range strings.Split(string(data), "\n") { + line = strings.TrimSpace(line) + if k, v, ok := strings.Cut(line, ":"); ok { + k = strings.TrimSpace(k) + v = strings.TrimSpace(v) + switch k { + case "Processor Type": + info.Model = v + } + } + } + + // Use lsdev to count physical processor devices (proc0, proc1, ...). + // prtconf's "Number Of Processors" can report virtual/logical processors + // on SMT-enabled POWER systems, so lsdev is more reliable for core count. + cmd, err = conn.RunCommand("lsdev -Cc processor") + if err != nil { + return nil, err + } + if cmd.ExitStatus != 0 { + stderr, _ := io.ReadAll(cmd.Stderr) + return nil, fmt.Errorf("lsdev failed: %s", string(stderr)) + } + + lsdevData, err := io.ReadAll(cmd.Stdout) + if err != nil { + return nil, err + } + + var cores int64 + for _, line := range strings.Split(strings.TrimSpace(string(lsdevData)), "\n") { + if strings.HasPrefix(line, "proc") { + cores++ + } + } + if cores > 0 { + info.Cores = cores + } + + return info, nil +} + +func getCpuInfoSolaris(conn shared.Connection) (*cpuInfo, error) { + // psrinfo -pv gives per-physical-processor details including model. + // Example output: + // The physical processor has 4 cores and 8 virtual processors (0-7) + // x86 (GenuineIntel 206D7 family 6 model 45 step 7 clock 2600 MHz) + // Intel(r) Xeon(r) CPU E5-2670 0 @ 2.60GHz + cmd, err := conn.RunCommand("psrinfo -pv") + if err != nil { + return nil, err + } + if cmd.ExitStatus != 0 { + stderr, _ := io.ReadAll(cmd.Stderr) + return nil, fmt.Errorf("psrinfo failed: %s", string(stderr)) + } + + data, err := io.ReadAll(cmd.Stdout) + if err != nil { + return nil, err + } + + info := &cpuInfo{} + var totalCores int64 + var sockets int64 + + for _, line := range strings.Split(string(data), "\n") { + trimmed := strings.TrimSpace(line) + + // "The physical processor has N cores and M virtual processors (...)" + if strings.HasPrefix(trimmed, "The physical processor has") { + sockets++ + // Extract core count + if idx := strings.Index(trimmed, " cores"); idx > 0 { + // Find the number before " cores" + prefix := trimmed[:idx] + parts := strings.Fields(prefix) + if len(parts) > 0 { + if n, err := strconv.ParseInt(parts[len(parts)-1], 10, 64); err == nil { + totalCores += n + } + } + } + } + + // The indented model line (deepest indent, no parens) e.g.: + // Intel(r) Xeon(r) CPU E5-2670 0 @ 2.60GHz + if info.Model == "" && !strings.HasPrefix(trimmed, "The ") && + !strings.HasPrefix(trimmed, "x86 ") && !strings.HasPrefix(trimmed, "sparc ") && + trimmed != "" && !strings.HasPrefix(trimmed, "(") { + info.Model = trimmed + } + } + + if sockets > 0 { + info.ProcessorCount = sockets + } else { + info.ProcessorCount = 1 + } + info.Cores = totalCores + + // Extract manufacturer from model string + if strings.Contains(info.Model, "Intel") { + info.Manufacturer = "Intel" + } else if strings.Contains(info.Model, "AMD") { + info.Manufacturer = "AMD" + } else if strings.Contains(info.Model, "SPARC") || strings.Contains(info.Model, "sparc") { + info.Manufacturer = "Oracle" + } + + return info, nil +} + +// normalizeManufacturer maps CPUID vendor strings to human-readable names. +func normalizeManufacturer(vendor string) string { + switch vendor { + case "GenuineIntel": + return "Intel" + case "AuthenticAMD": + return "AMD" + default: + return vendor + } +} diff --git a/providers/os/resources/cpu_test.go b/providers/os/resources/cpu_test.go new file mode 100644 index 0000000000..734e4a6849 --- /dev/null +++ b/providers/os/resources/cpu_test.go @@ -0,0 +1,172 @@ +// Copyright (c) Mondoo, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package resources + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.mondoo.com/mql/v13/providers-sdk/v1/inventory" + "go.mondoo.com/mql/v13/providers/os/connection/mock" +) + +func TestGetCpuInfoLinuxX64(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithPath("./procfs/testdata/cpu-info-x64.toml")) + require.NoError(t, err) + + info, err := getCpuInfoLinux(conn) + require.NoError(t, err) + + assert.Equal(t, "Intel", info.Manufacturer) + assert.Equal(t, "Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz", info.Model) + assert.Equal(t, int64(2), info.ProcessorCount) + assert.Equal(t, int64(2), info.Cores) +} + +func TestGetCpuInfoMacosAppleSilicon(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithData(&mock.TomlData{ + Commands: map[string]*mock.Command{ + "sysctl -n machdep.cpu.brand_string hw.physicalcpu": { + Stdout: "Apple M4 Pro\n14\n", + }, + }, + })) + require.NoError(t, err) + + info, err := getCpuInfoMacos(conn) + require.NoError(t, err) + + assert.Equal(t, "Apple", info.Manufacturer) + assert.Equal(t, "Apple M4 Pro", info.Model) + assert.Equal(t, int64(1), info.ProcessorCount) + assert.Equal(t, int64(14), info.Cores) +} + +func TestGetCpuInfoMacosIntel(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithData(&mock.TomlData{ + Commands: map[string]*mock.Command{ + "sysctl -n machdep.cpu.brand_string hw.physicalcpu": { + Stdout: "Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz\n8\n", + }, + }, + })) + require.NoError(t, err) + + info, err := getCpuInfoMacos(conn) + require.NoError(t, err) + + assert.Equal(t, "Intel", info.Manufacturer) + assert.Equal(t, "Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz", info.Model) + assert.Equal(t, int64(1), info.ProcessorCount) + assert.Equal(t, int64(8), info.Cores) +} + +func TestGetCpuInfoLinuxAMD(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithPath("./procfs/testdata/cpu-info-amd.toml")) + require.NoError(t, err) + + info, err := getCpuInfoLinux(conn) + require.NoError(t, err) + + assert.Equal(t, "AMD", info.Manufacturer) + assert.Equal(t, "AMD EPYC 7763 64-Core Processor", info.Model) + assert.Equal(t, int64(1), info.ProcessorCount) + assert.Equal(t, int64(2), info.Cores) +} + +func TestGetCpuInfoFreeBSD(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithData(&mock.TomlData{ + Commands: map[string]*mock.Command{ + "sysctl -n hw.model kern.smp.cores": { + Stdout: "Intel(R) Xeon(R) CPU E3-1220 v5 @ 3.00GHz\n4\n", + }, + }, + })) + require.NoError(t, err) + + info, err := getCpuInfoFreeBSD(conn) + require.NoError(t, err) + + assert.Equal(t, "Intel", info.Manufacturer) + assert.Equal(t, "Intel(R) Xeon(R) CPU E3-1220 v5 @ 3.00GHz", info.Model) + assert.Equal(t, int64(1), info.ProcessorCount) + assert.Equal(t, int64(4), info.Cores) +} + +func TestGetCpuInfoAIX(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithData(&mock.TomlData{ + Commands: map[string]*mock.Command{ + "prtconf": { + Stdout: "System Model: IBM,9009-42A\n" + + "Processor Type: PowerPC_POWER9\n" + + "Number Of Processors: 48\n" + + "Processor Clock Speed: 3800 MHz\n" + + "CPU Type: 64-bit\n", + }, + "lsdev -Cc processor": { + Stdout: "proc0 Available 00-00 Processor\n" + + "proc4 Available 00-04 Processor\n" + + "proc8 Available 00-08 Processor\n" + + "proc12 Available 00-12 Processor\n" + + "proc16 Available 00-16 Processor\n" + + "proc20 Available 00-20 Processor\n" + + "proc24 Available 00-24 Processor\n" + + "proc28 Available 00-28 Processor\n" + + "proc32 Available 00-32 Processor\n" + + "proc36 Available 00-36 Processor\n" + + "proc40 Available 00-40 Processor\n" + + "proc44 Available 00-44 Processor\n", + }, + }, + })) + require.NoError(t, err) + + info, err := getCpuInfoAIX(conn) + require.NoError(t, err) + + assert.Equal(t, "IBM", info.Manufacturer) + assert.Equal(t, "PowerPC_POWER9", info.Model) + assert.Equal(t, int64(1), info.ProcessorCount) + // 12 physical cores from lsdev, not 48 logical from prtconf + assert.Equal(t, int64(12), info.Cores) +} + +func TestGetCpuInfoSolaris(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithData(&mock.TomlData{ + Commands: map[string]*mock.Command{ + "psrinfo -pv": { + Stdout: "The physical processor has 4 cores and 8 virtual processors (0-7)\n" + + " x86 (GenuineIntel 206D7 family 6 model 45 step 7 clock 2600 MHz)\n" + + " Intel(r) Xeon(r) CPU E5-2670 0 @ 2.60GHz\n" + + "The physical processor has 4 cores and 8 virtual processors (8-15)\n" + + " x86 (GenuineIntel 206D7 family 6 model 45 step 7 clock 2600 MHz)\n" + + " Intel(r) Xeon(r) CPU E5-2670 0 @ 2.60GHz\n", + }, + }, + })) + require.NoError(t, err) + + info, err := getCpuInfoSolaris(conn) + require.NoError(t, err) + + assert.Equal(t, "Intel", info.Manufacturer) + assert.Equal(t, "Intel(r) Xeon(r) CPU E5-2670 0 @ 2.60GHz", info.Model) + assert.Equal(t, int64(2), info.ProcessorCount) + assert.Equal(t, int64(8), info.Cores) +} + +func TestGetCpuInfoLinuxArm(t *testing.T) { + conn, err := mock.New(0, &inventory.Asset{}, mock.WithPath("./procfs/testdata/cpu-info-aarch64.toml")) + require.NoError(t, err) + + info, err := getCpuInfoLinux(conn) + require.NoError(t, err) + + assert.Equal(t, "", info.Manufacturer) + assert.Equal(t, "", info.Model) + assert.Equal(t, int64(1), info.ProcessorCount) + // ARM: no CPUCores info, falls back to processor count for cores + assert.Equal(t, int64(2), info.Cores) +} diff --git a/providers/os/resources/os.lr b/providers/os/resources/os.lr index 80b3ee5cfc..e89777c7ae 100644 --- a/providers/os/resources/os.lr +++ b/providers/os/resources/os.lr @@ -225,6 +225,18 @@ machine.chassis { assetTag string } +// CPU information +machine.cpu @defaults("manufacturer model processorCount coreCount") { + // CPU manufacturer + manufacturer string + // CPU model name + model string + // Number of physical CPU packages (sockets) + processorCount int + // Total number of physical CPU cores + coreCount int +} + // Operating system information os { // Pretty hostname on macOS/Linux or device name on Windows diff --git a/providers/os/resources/os.lr.go b/providers/os/resources/os.lr.go index 35f4e3407b..404f593daf 100644 --- a/providers/os/resources/os.lr.go +++ b/providers/os/resources/os.lr.go @@ -34,6 +34,7 @@ const ( ResourceMachineSystem string = "machine.system" ResourceMachineBaseboard string = "machine.baseboard" ResourceMachineChassis string = "machine.chassis" + ResourceMachineCpu string = "machine.cpu" ResourceOs string = "os" ResourceOsUpdate string = "os.update" ResourceOsBase string = "os.base" @@ -278,6 +279,10 @@ func init() { Init: initMachineChassis, Create: createMachineChassis, }, + "machine.cpu": { + Init: initMachineCpu, + Create: createMachineCpu, + }, "os": { // to override args, implement: initOs(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createOs, @@ -1257,6 +1262,18 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "machine.chassis.assetTag": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlMachineChassis).GetAssetTag()).ToDataRes(types.String) }, + "machine.cpu.manufacturer": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMachineCpu).GetManufacturer()).ToDataRes(types.String) + }, + "machine.cpu.model": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMachineCpu).GetModel()).ToDataRes(types.String) + }, + "machine.cpu.processorCount": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMachineCpu).GetProcessorCount()).ToDataRes(types.Int) + }, + "machine.cpu.coreCount": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlMachineCpu).GetCoreCount()).ToDataRes(types.Int) + }, "os.name": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlOs).GetName()).ToDataRes(types.String) }, @@ -4344,6 +4361,26 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool{ r.(*mqlMachineChassis).AssetTag, ok = plugin.RawToTValue[string](v.Value, v.Error) return }, + "machine.cpu.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMachineCpu).__id, ok = v.Value.(string) + return + }, + "machine.cpu.manufacturer": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMachineCpu).Manufacturer, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "machine.cpu.model": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMachineCpu).Model, ok = plugin.RawToTValue[string](v.Value, v.Error) + return + }, + "machine.cpu.processorCount": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMachineCpu).ProcessorCount, ok = plugin.RawToTValue[int64](v.Value, v.Error) + return + }, + "machine.cpu.coreCount": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlMachineCpu).CoreCount, ok = plugin.RawToTValue[int64](v.Value, v.Error) + return + }, "os.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlOs).__id, ok = v.Value.(string) return @@ -9846,6 +9883,65 @@ func (c *mqlMachineChassis) GetAssetTag() *plugin.TValue[string] { return &c.AssetTag } +// mqlMachineCpu for the machine.cpu resource +type mqlMachineCpu struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlMachineCpuInternal it will be used here + Manufacturer plugin.TValue[string] + Model plugin.TValue[string] + ProcessorCount plugin.TValue[int64] + CoreCount plugin.TValue[int64] +} + +// createMachineCpu creates a new instance of this resource +func createMachineCpu(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlMachineCpu{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + // to override __id implement: id() (string, error) + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("machine.cpu", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlMachineCpu) MqlName() string { + return "machine.cpu" +} + +func (c *mqlMachineCpu) MqlID() string { + return c.__id +} + +func (c *mqlMachineCpu) GetManufacturer() *plugin.TValue[string] { + return &c.Manufacturer +} + +func (c *mqlMachineCpu) GetModel() *plugin.TValue[string] { + return &c.Model +} + +func (c *mqlMachineCpu) GetProcessorCount() *plugin.TValue[int64] { + return &c.ProcessorCount +} + +func (c *mqlMachineCpu) GetCoreCount() *plugin.TValue[int64] { + return &c.CoreCount +} + // mqlOs for the os resource type mqlOs struct { MqlRuntime *plugin.Runtime diff --git a/providers/os/resources/os.lr.versions b/providers/os/resources/os.lr.versions index a679d7e062..9a98f87d7f 100644 --- a/providers/os/resources/os.lr.versions +++ b/providers/os/resources/os.lr.versions @@ -441,6 +441,11 @@ machine.chassis.assetTag 9.0.0 machine.chassis.manufacturer 9.0.0 machine.chassis.serial 9.0.0 machine.chassis.version 9.0.0 +machine.cpu 13.2.3 +machine.cpu.coreCount 13.2.3 +machine.cpu.manufacturer 13.2.3 +machine.cpu.model 13.2.3 +machine.cpu.processorCount 13.2.3 machine.system 9.0.0 machine.system.family 9.0.0 machine.system.manufacturer 9.0.0 diff --git a/providers/os/resources/procfs/testdata/cpu-info-amd.toml b/providers/os/resources/procfs/testdata/cpu-info-amd.toml new file mode 100644 index 0000000000..3566924604 --- /dev/null +++ b/providers/os/resources/procfs/testdata/cpu-info-amd.toml @@ -0,0 +1,54 @@ +[files."/proc/cpuinfo"] +content = """ +processor : 0 +vendor_id : AuthenticAMD +cpu family : 25 +model : 1 +model name : AMD EPYC 7763 64-Core Processor +stepping : 1 +cpu MHz : 2450.000 +cache size : 512 KB +physical id : 0 +siblings : 2 +core id : 0 +cpu cores : 2 +apicid : 0 +initial apicid : 0 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext ssbd ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass +bogomips : 4890.86 +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: + +processor : 1 +vendor_id : AuthenticAMD +cpu family : 25 +model : 1 +model name : AMD EPYC 7763 64-Core Processor +stepping : 1 +cpu MHz : 2450.000 +cache size : 512 KB +physical id : 0 +siblings : 2 +core id : 1 +cpu cores : 2 +apicid : 1 +initial apicid : 1 +fpu : yes +fpu_exception : yes +cpuid level : 13 +wp : yes +flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext ssbd ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves +bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass +bogomips : 4890.86 +clflush size : 64 +cache_alignment : 64 +address sizes : 48 bits physical, 48 bits virtual +power management: +"""