cli: add functions to detect MThreads AI Book (#2883)

* feat(connector): add functions to detect Mthreads AI Book and RockChip models

* feat(gpu): add MThreads GPU support and related modules

* fix(cli): remove redundant import of amdgpu package

* chore(cli): remove unnecessary tasks when adding node

---------

Co-authored-by: dkeven <dkvvven@gmail.com>
This commit is contained in:
eball 2026-04-14 19:53:08 +08:00 committed by GitHub
parent ebfa23852e
commit 51214c3d80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 191 additions and 26 deletions

View file

@ -0,0 +1,57 @@
package connector
import (
"fmt"
"os/exec"
)
func isMthreadsAIBook(cmdExec func(s string) (string, error)) bool {
dmidecode, err := cmdExec("command -v dmidecode")
if err != nil {
fmt.Printf("Error executing dmidecode command: %v\n", err)
return false
}
if len(dmidecode) == 0 {
fmt.Println("dmidecode command not found, cannot determine if it's an Mthreads AI Book.")
return false
}
output, err := cmdExec("dmidecode -s processor-manufacturer")
if err != nil {
fmt.Printf("Error executing dmidecode to get processor manufacturer: %v\n", err)
return false
}
return output == "AIBOOK"
}
func isMthreadsAIBookM1000(cmdExec func(s string) (string, error)) bool {
if !isMthreadsAIBook(cmdExec) {
return false
}
output, err := cmdExec("dmidecode -s processor-version")
if err != nil {
fmt.Printf("Error executing dmidecode to get processor version: %v\n", err)
return false
}
return output == "M1000"
}
func IsMThreadsAIBookM1000Local() bool {
return isMthreadsAIBookM1000(func(s string) (string, error) {
out, err := exec.Command("sh", "-c", s).Output()
if err != nil {
return "", err
}
return string(out), nil
})
}
func IsMThreadsAIBookM1000(execRuntime Runtime) bool {
return isMthreadsAIBookM1000(func(s string) (string, error) {
return execRuntime.GetRunner().SudoCmd(s, false, false)
})
}

View file

@ -0,0 +1,37 @@
package connector
import "strings"
func isRockChip(execRuntime Runtime) bool {
// check the /proc/device-tree/model exists and contains "rockchip"
out, err := execRuntime.GetRunner().SudoCmd("cat /proc/device-tree/model 2>/dev/null || true", false, false)
if err != nil {
return false
}
if out != "" && strings.Contains(strings.ToLower(out), "rockchip") {
return true
}
return false
}
func isRockChipRK3588(execRuntime Runtime) bool {
if !isRockChip(execRuntime) {
return false
}
out, err := execRuntime.GetRunner().SudoCmd("cat /proc/device-tree/model 2>/dev/null || true", false, false)
if err != nil {
return false
}
if out != "" && strings.Contains(strings.ToLower(out), "rk3588") {
return true
}
return false
}
func getRockChipModel(execRuntime Runtime) string {
out, err := execRuntime.GetRunner().SudoCmd("cat /proc/device-tree/model 2>/dev/null || true", false, false)
if err != nil {
return ""
}
return strings.TrimSpace(out)
}

View file

@ -81,6 +81,7 @@ type Systems interface {
IsAmdApu() bool
IsAmdGPU() bool
IsAmdGPUOrAPU() bool
IsMThreadsM1000() bool
IsUbuntu() bool
IsDebian() bool
@ -257,6 +258,10 @@ func (s *SystemInfo) IsAmdGPUOrAPU() bool {
return s.CpuInfo.HasAmdAPU || s.HasAmdGPU
}
func (s *SystemInfo) IsMThreadsM1000() bool {
return s.CpuInfo.IsMThreadsM1000
}
func (s *SystemInfo) IsUbuntu() bool {
return s.HostInfo.OsPlatformFamily == common.Ubuntu
}
@ -469,6 +474,7 @@ type CpuInfo struct {
CpuPhysicalCount int `json:"cpu_physical_count"`
IsGB10Chip bool `json:"is_gb10_chip,omitempty"`
HasAmdAPU bool `json:"has_amd_apu,omitempty"`
IsMThreadsM1000 bool `json:"is_mthreads_m1000,omitempty"`
}
// Not considering the case where AMD GPU and AMD APU coexist.
@ -537,6 +543,9 @@ func getCpu() *CpuInfo {
hasAmdAPU = false
}
// check if it is mthreads m1000
ret.IsMThreadsM1000 = IsMThreadsAIBookM1000Local()
ret.IsGB10Chip = isGB10Chip
ret.HasAmdAPU = hasAmdAPU

View file

@ -0,0 +1,33 @@
package mtgpu
import (
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/task"
)
// InstallMThreadsPluginModule installs MThreads GPU device plugin on Kubernetes.
type InstallMThreadsPluginModule struct {
common.KubeModule
Skip bool // conditional execution based on GPU enablement
}
func (m *InstallMThreadsPluginModule) IsSkip() bool {
return m.Skip
}
func (m *InstallMThreadsPluginModule) Init() {
m.Name = "InstallMThreadsPlugin"
// update node with MThreads GPU labels
updateNode := &task.RemoteTask{
Name: "UpdateNodeMThreadsGPUInfo",
Hosts: m.Runtime.GetHostsByRole(common.Master),
Action: new(UpdateNodeMThreadsGPUInfo),
Parallel: false,
Retry: 1,
}
m.Tasks = []task.Interface{
updateNode,
}
}

View file

@ -0,0 +1,39 @@
package mtgpu
import (
"context"
"github.com/beclab/Olares/cli/pkg/clientset"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/gpu"
"k8s.io/utils/ptr"
"github.com/pkg/errors"
)
// UpdateNodeMThreadsGPUInfo updates Kubernetes node labels with MThreads GPU information.
type UpdateNodeMThreadsGPUInfo struct {
common.KubeAction
}
func (u *UpdateNodeMThreadsGPUInfo) Execute(runtime connector.Runtime) error {
client, err := clientset.NewKubeClient()
if err != nil {
return errors.Wrap(errors.WithStack(err), "kubeclient create error")
}
// Check if MThreads AI Book M1000 exists
m1000Exists := connector.IsMThreadsAIBookM1000(runtime)
if !m1000Exists {
logger.Info("MThreads AI Book M1000 is not detected")
return nil
}
if runtime.GetSystemInfo().IsAmdApu() {
return gpu.UpdateNodeGpuLabel(context.Background(), client.Kubernetes(), nil, nil, nil, ptr.To(gpu.MThreadsM1000Type))
}
return nil
}

View file

@ -367,8 +367,6 @@ func (u *UpdateNodeGPUInfo) Execute(runtime connector.Runtime) error {
// TODO:
gpuType := NvidiaCardType
switch {
case runtime.GetSystemInfo().IsAmdApu():
gpuType = AmdApuCardType
case runtime.GetSystemInfo().IsGB10Chip():
gpuType = GB10ChipType
}

View file

@ -12,9 +12,10 @@ var (
)
const (
NvidiaCardType = "nvidia" // handling by HAMi
AmdGpuCardType = "amd-gpu" //
AmdApuCardType = "amd-apu" // AMD APU with integrated GPU , AI Max 395 etc.
GB10ChipType = "nvidia-gb10" // NVIDIA GB10 Superchip & unified system memory
StrixHaloChipType = "strix-halo" // AMD Strix Halo GPU & unified system memory
NvidiaCardType = "nvidia" // handling by HAMi
AmdGpuCardType = "amd-gpu" //
AmdApuCardType = "amd-apu" // AMD APU with integrated GPU , AI Max 395 etc.
GB10ChipType = "nvidia-gb10" // NVIDIA GB10 Superchip & unified system memory
StrixHaloChipType = "strix-halo" // AMD Strix Halo GPU & unified system memory
MThreadsM1000Type = "mthreads-m1000" // MThreads M1000 AI Book with integrated GPU
)

View file

@ -1,14 +1,11 @@
package cluster
import (
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/bootstrap/os"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/logger"
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/core/pipeline"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/pkg/gpu"
"github.com/beclab/Olares/cli/pkg/k3s"
"github.com/beclab/Olares/cli/pkg/kubernetes"
"github.com/beclab/Olares/cli/pkg/manifest"
@ -79,23 +76,10 @@ func (m *AddNodeModule) Init() {
&k3s.JoinNodesModule{},
}
}
m.underlyingModules = append(m.underlyingModules, &gpu.NodeLabelingModule{})
for _, underlyingModule := range m.underlyingModules {
underlyingModule.Default(m.Runtime, m.PipelineCache, m.ModuleCache)
underlyingModule.AutoAssert()
underlyingModule.Init()
m.Tasks = append(m.Tasks, underlyingModule.GetTasks()...)
}
m.Tasks = append(m.Tasks,
&task.RemoteTask{
Name: "UpdateNodeGPUInfo",
Action: new(gpu.UpdateNodeGPUInfo),
Retry: 1,
},
&task.LocalTask{
Name: "UpdateNodeAmdGPUInfo",
Action: new(amdgpu.UpdateNodeAmdGPUInfo),
Retry: 1,
},
)
}

View file

@ -1,10 +1,11 @@
package cluster
import (
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/gpu"
"github.com/beclab/Olares/cli/pkg/gpu/amdgpu"
"github.com/beclab/Olares/cli/pkg/gpu/mtgpu"
"github.com/beclab/Olares/cli/pkg/kubesphere/plugins"
"github.com/beclab/Olares/cli/pkg/manifest"
"github.com/beclab/Olares/cli/pkg/storage"
@ -65,6 +66,12 @@ func (l *linuxInstallPhaseBuilder) installGpuPlugin() phase {
}
return true
}()},
&mtgpu.InstallMThreadsPluginModule{Skip: func() bool {
if l.runtime.GetSystemInfo().IsMThreadsM1000() {
return false
}
return true
}()},
}
}

View file

@ -3,7 +3,6 @@ package system
import (
"strings"
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/bootstrap/os"
"github.com/beclab/Olares/cli/pkg/bootstrap/patch"
"github.com/beclab/Olares/cli/pkg/bootstrap/precheck"
@ -12,6 +11,7 @@ import (
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/daemon"
"github.com/beclab/Olares/cli/pkg/gpu"
"github.com/beclab/Olares/cli/pkg/gpu/amdgpu"
"github.com/beclab/Olares/cli/pkg/images"
"github.com/beclab/Olares/cli/pkg/k3s"
"github.com/beclab/Olares/cli/pkg/manifest"

View file

@ -3,7 +3,6 @@ package pipelines
import (
"strings"
"github.com/beclab/Olares/cli/pkg/amdgpu"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/action"
"github.com/beclab/Olares/cli/pkg/core/connector"
@ -11,6 +10,7 @@ import (
"github.com/beclab/Olares/cli/pkg/core/module"
"github.com/beclab/Olares/cli/pkg/core/pipeline"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/pkg/gpu/amdgpu"
)
type singleTaskModule struct {