Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"os/signal"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/fsnotify/fsnotify"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
"ascend-common/api"
"ascend-common/common-utils/hwlog"
"ascend-common/common-utils/utils"
)
var (
dpRegexp = map[string]*regexp.Regexp{
"nodeName": regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`),
"namespace": regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`),
"fullPodName": regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`),
"vir910": regexp.MustCompile("Ascend910-([2-6]|8|10|12|16)c"),
"vir310p": regexp.MustCompile("Ascend310P-(1|2|4)c"),
api.Ascend910Lowercase: regexp.MustCompile(`^` + api.Ascend910 + `-\d+`),
api.Ascend310Lowercase: regexp.MustCompile(`^` + api.Ascend310 + `-\d+`),
api.Ascend310PLowercase: regexp.MustCompile(`^` + api.Ascend310P + `-\d+`),
api.NPULowerCase: regexp.MustCompile(`^` + api.NPULowerCase + `-\d+`),
}
updateTriggerChan = make(chan struct{}, 1)
)
type ServerInfo struct {
ServerID string
HostIp string
DeviceType string
SuperPodID int32
RackId int32
ServerIndex string
ServerIP string
}
func GetPattern() map[string]*regexp.Regexp {
return dpRegexp
}
var (
allDeviceInfoLock sync.Mutex
)
func LockAllDeviceInfo() {
allDeviceInfoLock.Lock()
}
func UnlockAllDeviceInfo() {
allDeviceInfoLock.Unlock()
}
func SetAscendRuntimeEnv(devices []int, ascendRuntimeOptions string, resp *v1beta1.ContainerAllocateResponse) {
if resp == nil {
hwlog.RunLog.Error("resp is nil")
return
}
if len((*resp).Envs) == 0 {
(*resp).Envs = make(map[string]string, runtimeEnvNum+SlowNodeStepTimeEnvNum)
}
var deviceStr []string
for _, id := range devices {
deviceStr = append(deviceStr, strconv.Itoa(id))
}
(*resp).Envs[AscendVisibleDevicesEnv] = strings.Join(deviceStr, ",")
(*resp).Envs[ascendRuntimeOptionsEnv] = ascendRuntimeOptions
if ParamOption.RealCardType == api.Ascend310B {
(*resp).Envs[ascendAllowLinkEnv] = "True"
}
if !ParamOption.PresetVDevice {
(*resp).Envs[api.MsLocalWorkerEnv] = strconv.Itoa(len(deviceStr))
(*resp).Envs[api.MsWorkerNumEnv] = strconv.Itoa(len(deviceStr))
(*resp).Envs[api.PtWorldSizeEnv] = strconv.Itoa(len(deviceStr))
(*resp).Envs[api.PtLocalWorldSizeEnv] = strconv.Itoa(len(deviceStr))
(*resp).Envs[api.PtLocalRankEnv] = localRankStr(len(deviceStr))
(*resp).Envs[api.TfWorkerSizeEnv] = strconv.Itoa(len(deviceStr))
(*resp).Envs[api.TfLocalWorkerEnv] = strconv.Itoa(len(deviceStr))
}
hwlog.RunLog.Infof("allocate resp env: %s; %s", (*resp).Envs[AscendVisibleDevicesEnv], ascendRuntimeOptions)
}
func localRankStr(req int) string {
rankStr := ""
for i := 0; i < req-1; i++ {
rankStr += strconv.Itoa(i) + ","
}
rankStr += strconv.Itoa(req - 1)
return rankStr
}
func MakeDataHash(data interface{}) string {
var dataBuffer []byte
if dataBuffer = MarshalData(data); len(dataBuffer) == 0 {
return ""
}
h := sha256.New()
if _, err := h.Write(dataBuffer); err != nil {
hwlog.RunLog.Error("hash data error")
return ""
}
sum := h.Sum(nil)
return hex.EncodeToString(sum)
}
func MarshalData(data interface{}) []byte {
dataBuffer, err := json.Marshal(data)
if err != nil {
hwlog.RunLog.Errorf("marshal data err: %v", err)
return nil
}
return dataBuffer
}
func MapDeepCopy(source map[string]string) map[string]string {
dest := make(map[string]string, len(source))
if source == nil {
return dest
}
for key, value := range source {
dest[key] = value
}
return dest
}
func GetPodAnnotationByDeviceType(pod *v1.Pod, deviceType string) (string, error) {
if pod == nil {
return "", fmt.Errorf("invalid pod")
}
annotationTag := fmt.Sprintf("%s%s", api.ResourceNamePrefix, deviceType)
annotation, exist := pod.Annotations[annotationTag]
if !exist {
return "", fmt.Errorf("cannot find the annotation")
}
if len(annotation) > PodAnnotationMaxLength {
return "", fmt.Errorf("pod annotation size out of memory")
}
return annotation, nil
}
func GetDeviceFromPodAnnotation(pod *v1.Pod, deviceType string) ([]string, error) {
if pod == nil {
return nil, fmt.Errorf("param pod is nil")
}
annotation, err := GetPodAnnotationByDeviceType(pod, deviceType)
if err != nil {
return nil, err
}
return strings.Split(annotation, CommaSepDev), nil
}
func setDeviceByPathWhen200RC(defaultDevices *[]string) {
setDeviceByPath(defaultDevices, HiAi200RCEventSched)
setDeviceByPath(defaultDevices, HiAi200RCHiDvpp)
setDeviceByPath(defaultDevices, HiAi200RCLog)
setDeviceByPath(defaultDevices, HiAi200RCMemoryBandwidth)
setDeviceByPath(defaultDevices, HiAi200RCSVM0)
setDeviceByPath(defaultDevices, HiAi200RCTsAisle)
setDeviceByPath(defaultDevices, HiAi200RCUpgrade)
}
func setDeviceByPath(defaultDevices *[]string, device string) {
if _, err := os.Stat(device); err == nil {
*defaultDevices = append(*defaultDevices, device)
}
}
func GetDefaultDevices(getFdFlag bool) ([]string, error) {
davinciManager, err := getDavinciManagerPath()
if err != nil {
return nil, err
}
var defaultDevices []string
defaultDevices = append(defaultDevices, davinciManager)
setDeviceByPath(&defaultDevices, HiAIHDCDevice)
setDeviceByPath(&defaultDevices, HiAISVMDevice)
if getFdFlag {
setDeviceByPathWhen200RC(&defaultDevices)
}
if _, err := os.Stat(DvppCmdlistDevice); err != nil {
hwlog.RunLog.Warnf("device %s not exist", DvppCmdlistDevice)
} else {
defaultDevices = append(defaultDevices, DvppCmdlistDevice)
}
var productType string
if len(ParamOption.ProductTypes) == 1 {
productType = ParamOption.ProductTypes[0]
}
if productType == Atlas200ISoc {
socDefaultDevices, err := set200SocDefaultDevices()
if err != nil {
hwlog.RunLog.Errorf("get 200I soc default devices failed, err: %v", err)
return nil, err
}
defaultDevices = append(defaultDevices, socDefaultDevices...)
}
if ParamOption.RealCardType == api.Ascend310B {
a310BDefaultDevices := set310BDefaultDevices()
defaultDevices = append(defaultDevices, a310BDefaultDevices...)
}
return defaultDevices, nil
}
func getDavinciManagerPath() (string, error) {
if ParamOption.RealCardType == api.Ascend310B {
if _, err := os.Stat(HiAIManagerDeviceDocker); err == nil {
return HiAIManagerDeviceDocker, nil
}
hwlog.RunLog.Warn("get npu manager docker failed")
}
if _, err := os.Stat(HiAIManagerDevice); err != nil {
return "", err
}
return HiAIManagerDevice, nil
}
func set200SocDefaultDevices() ([]string, error) {
var socDefaultDevices = []string{
Atlas200ISocVPC,
Atlas200ISocVDEC,
Atlas200ISocSYS,
Atlas200ISocSpiSmbus,
Atlas200ISocUserConfig,
HiAi200RCTsAisle,
HiAi200RCSVM0,
HiAi200RCLog,
HiAi200RCMemoryBandwidth,
HiAi200RCUpgrade,
}
for _, devPath := range socDefaultDevices {
if _, err := os.Stat(devPath); err != nil {
return nil, err
}
}
var socOptionsDevices = []string{
HiAi200RCEventSched,
Atlas200ISocXSMEM,
}
for _, devPath := range socOptionsDevices {
if _, err := os.Stat(devPath); err != nil {
hwlog.RunLog.Warnf("device %s not exist", devPath)
continue
}
socDefaultDevices = append(socDefaultDevices, devPath)
}
return socDefaultDevices, nil
}
func set310BDefaultDevices() []string {
var a310BDefaultDevices = []string{
Atlas310BPngd,
Atlas310BVenc,
HiAi200RCUpgrade,
Atlas200ISocSYS,
HiAi200RCSVM0,
Atlas200ISocVDEC,
Atlas200ISocVPC,
HiAi200RCTsAisle,
HiAi200RCLog,
Atlas310BAcodec,
Atlas310BAi,
Atlas310BAo,
Atlas310BVo,
Atlas310BHdmi,
}
var available310BDevices []string
for _, devPath := range a310BDefaultDevices {
if _, err := os.Stat(devPath); err != nil {
hwlog.RunLog.Warnf("device %s not exist", devPath)
continue
}
available310BDevices = append(available310BDevices, devPath)
}
return available310BDevices
}
func getNPUResourceNumOfPod(pod *v1.Pod, deviceType string) int64 {
containers := pod.Spec.Containers
if len(containers) > MaxContainerLimit {
hwlog.RunLog.Error("The number of container exceeds the upper limit")
return int64(0)
}
var total int64
annotationTag := fmt.Sprintf("%s%s", api.ResourceNamePrefix, deviceType)
for _, container := range containers {
val, ok := container.Resources.Limits[v1.ResourceName(annotationTag)]
if !ok {
continue
}
limitsDevNum := val.Value()
if limitsDevNum < 0 || limitsDevNum > int64(MaxDevicesNum*MaxAICoreNum) {
hwlog.RunLog.Errorf("apply devices number should be in the range of [0, %d]", MaxDevicesNum*MaxAICoreNum)
return int64(0)
}
total += limitsDevNum
}
return total
}
func isAscendAssignedPod(pod *v1.Pod, deviceType string) bool {
if IsVirtualDev(deviceType) {
return true
}
annotationTag := fmt.Sprintf("%s%s", api.ResourceNamePrefix, deviceType)
if _, ok := pod.ObjectMeta.Annotations[annotationTag]; !ok {
hwlog.RunLog.Debugf("no assigned flag, pod Name: %s, pod NameSpace: %s", pod.Name, pod.Namespace)
return false
}
return true
}
func isShouldDeletePod(pod *v1.Pod) bool {
if pod.DeletionTimestamp != nil {
return true
}
if len(pod.Status.ContainerStatuses) > MaxContainerLimit {
hwlog.RunLog.Error("The number of container exceeds the upper limit")
return true
}
for _, status := range pod.Status.ContainerStatuses {
if status.State.Waiting != nil &&
strings.Contains(status.State.Waiting.Message, "PreStartContainer check failed") {
return true
}
}
return pod.Status.Reason == "UnexpectedAdmissionError"
}
func FilterPods(pods []v1.Pod, deviceType string, conditionFunc func(pod *v1.Pod) bool) []v1.Pod {
var res = make([]v1.Pod, 0)
for _, pod := range pods {
hwlog.RunLog.Debugf("pod: %s, %s", pod.Name, pod.Status.Phase)
if getNPUResourceNumOfPod(&pod, deviceType) == 0 || !isAscendAssignedPod(&pod,
deviceType) || isShouldDeletePod(&pod) {
continue
}
if conditionFunc != nil && !conditionFunc(&pod) {
continue
}
res = append(res, pod)
}
return res
}
func VerifyPathAndPermission(verifyPath string, waitSecond int) (string, bool) {
hwlog.RunLog.Debug("starting check device socket file path.")
absVerifyPath, err := filepath.Abs(verifyPath)
if err != nil {
hwlog.RunLog.Error("abs current path failed")
return "", false
}
pathInfo, err := os.Stat(absVerifyPath)
if err != nil {
for i := 0; i < waitSecond; i++ {
time.Sleep(time.Second)
pathInfo, err = os.Stat(absVerifyPath)
if err == nil {
break
}
}
if err != nil {
hwlog.RunLog.Error("file path not exist")
return "", false
}
}
realPath, err := filepath.EvalSymlinks(absVerifyPath)
if err != nil || absVerifyPath != realPath {
hwlog.RunLog.Error("Symlinks is not allowed")
return "", false
}
stat, ok := pathInfo.Sys().(*syscall.Stat_t)
if !ok || stat.Uid != RootUID || stat.Gid != RootGID {
hwlog.RunLog.Error("Non-root owner group of the path")
return "", false
}
return realPath, true
}
func CheckPodNameAndSpace(podPara string, maxLength int) error {
if len(podPara) > maxLength {
return fmt.Errorf("para length %d is bigger than %d", len(podPara), maxLength)
}
patternMap := GetPattern()
pattern := patternMap["namespace"]
if maxLength == PodNameMaxLength {
pattern = patternMap["fullPodName"]
}
if match := pattern.MatchString(podPara); !match {
return fmt.Errorf("podPara %s is illegal", podPara)
}
return nil
}
func CheckDeviceName(deviceName, deviceRunMode string) bool {
patternMap := GetPattern()
runModeRegexpMap := map[string]string{
api.Ascend910: api.Ascend910Lowercase,
api.Ascend310: api.Ascend310Lowercase,
api.Ascend310P: api.Ascend310PLowercase,
}
pattern := patternMap[runModeRegexpMap[deviceRunMode]]
if !pattern.MatchString(deviceName) {
if deviceRunMode == api.Ascend910 && ParamOption.RealCardType == api.Ascend910A5 {
npuPattern := patternMap[api.NPULowerCase]
if npuPattern.MatchString(deviceName) {
return true
}
}
hwlog.RunLog.Warnf("in %s device run mode, device name %s is illegal", deviceRunMode, deviceName)
return false
}
return true
}
func NewFileWatch() (*FileWatch, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
return &FileWatch{FileWatcher: watcher}, nil
}
func (fw *FileWatch) WatchFile(fileName string) error {
if _, err := os.Stat(fileName); err != nil {
return err
}
return fw.FileWatcher.Add(fileName)
}
func NewSignWatcher(osSigns ...os.Signal) chan os.Signal {
signChan := make(chan os.Signal, 1)
for _, sign := range osSigns {
signal.Notify(signChan, sign)
}
return signChan
}
func GetPodConfiguration(phyDevMapVirtualDev map[int]int, devices map[int]string, podName string,
info ServerInfo, allDevices []NpuDevice) string {
var sortDevicesKey []int
for deviceID := range devices {
sortDevicesKey = append(sortDevicesKey, deviceID)
}
sort.Ints(sortDevicesKey)
instance := Instance{
PodName: podName,
ServerID: info.ServerID,
ServerIP: info.ServerIP,
HostIP: info.HostIp,
SuperPodId: info.SuperPodID,
RackId: info.RackId,
ServerIndex: info.ServerIndex,
}
for _, deviceID := range sortDevicesKey {
if !IsVirtualDev(info.DeviceType) {
device := getDeviceFromAllDeviceInfo(deviceID, devices, allDevices)
instance.Devices = append(instance.Devices, device)
continue
}
phyID, exist := phyDevMapVirtualDev[deviceID]
if !exist {
hwlog.RunLog.Warn("virtual device not found phyid")
continue
}
instance.Devices = append(instance.Devices, Device{
DeviceID: fmt.Sprintf("%d", phyID),
DeviceIP: devices[deviceID],
})
}
instanceByte, err := json.Marshal(instance)
if err != nil {
hwlog.RunLog.Errorf("Transform marshal failed, err: %v", err)
return ""
}
return string(instanceByte)
}
func getDeviceFromAllDeviceInfo(deviceID int, devices map[int]string, allDevices []NpuDevice) Device {
device := Device{
DeviceID: fmt.Sprintf("%d", deviceID),
DeviceIP: devices[deviceID],
SuperDeviceID: strconv.Itoa(SdIdAbnormal),
}
for _, npuDevice := range allDevices {
if deviceID == int(npuDevice.PhyID) {
device.SuperDeviceID = strconv.Itoa(int(npuDevice.SuperDeviceID))
device.LevelList = npuDevice.LevelList
return device
}
}
hwlog.RunLog.Warnf("device[%d] not exists in npuBaseInfo", deviceID)
return device
}
func CheckFileUserSameWithProcess(loggerPath string) bool {
curUid := os.Getuid()
if curUid == RootUID {
return true
}
pathInfo, err := os.Lstat(loggerPath)
if err != nil {
path := filepath.Dir(loggerPath)
pathInfo, err = os.Lstat(path)
if err != nil {
fmt.Printf("get logger path stat failed, error is %v\n", err)
return false
}
}
stat, ok := pathInfo.Sys().(*syscall.Stat_t)
if !ok {
fmt.Printf("get logger file stat failed\n")
return false
}
if int(stat.Uid) != curUid || int(stat.Gid) != curUid {
fmt.Printf("check log file failed, owner not right\n")
return false
}
return true
}
func IsContainAtlas300IDuo() bool {
for _, product := range ParamOption.ProductTypes {
if product == Atlas300IDuo {
return true
}
}
return false
}
func IsContainAll300IDuo() bool {
if len(ParamOption.ProductTypes) == 0 {
return false
}
for _, product := range ParamOption.ProductTypes {
hwlog.RunLog.Infof("ProductTypes, %s\n", product)
if product != Atlas300IDuo {
return false
}
}
return true
}
func RecordFaultInfoList(devFaultInfoList []*TaskDevInfo) {
for _, devFaultInfo := range devFaultInfoList {
if devFaultInfo == nil {
continue
}
hexErrorCode := strings.ToUpper(Int64Tool.ToHexString(devFaultInfo.ErrorCode))
hwlog.RunLog.Infof("rank id: %d, log id: %d, policy: %s, error code: %s",
devFaultInfo.RankId, devFaultInfo.LogicId, devFaultInfo.Policy, hexErrorCode)
}
}
func Int32Join(data []int32, sep string) string {
strData := make([]string, 0, len(data))
for _, val := range data {
strData = append(strData, strconv.Itoa(int(val)))
}
return strings.Join(strData, sep)
}
func GetPodNameFromEnv() (string, error) {
podName := os.Getenv("HOSTNAME")
if err := CheckPodNameAndSpace(podName, PodNameMaxLength); err != nil {
return "", fmt.Errorf("check pod name failed: %w", err)
}
return podName, nil
}
func GetDeviceRunMode() (string, error) {
devType := ParamOption.RealCardType
switch devType {
case api.Ascend310, api.Ascend310B:
return api.Ascend310, nil
case api.Ascend910A, api.Ascend910B, api.Ascend910A3, api.Ascend910A5:
return api.Ascend910, nil
case api.Ascend310P:
return api.Ascend310P, nil
default:
hwlog.RunLog.Errorf("found an unsupported device type %s", devType)
return "", fmt.Errorf("%v is a unsupported device type", devType)
}
}
func IntInList(num int32, list []int32) bool {
for _, val := range list {
if val == num {
return true
}
}
return false
}
func GetJobNameOfPod(pod *v1.Pod) string {
if pod == nil {
return ""
}
var jobLabelKeys = []string{
ResetTaskNameKey,
ResetTaskNameKeyInLabel,
DeploymentNameKeyInLabel,
}
for _, key := range jobLabelKeys {
if val, exists := pod.Labels[key]; exists {
return val
}
}
return ""
}
func GetSyncMapLen(m *sync.Map) int {
count := 0
m.Range(func(k, v interface{}) bool {
count++
return true
})
return count
}
func ObjToString(data interface{}) string {
var dataBuffer []byte
if dataBuffer = MarshalData(data); len(dataBuffer) == 0 {
return ""
}
return string(dataBuffer)
}
func Keys[T comparable, U any](mp map[T]U) []T {
result := make([]T, 0, len(mp))
for key := range mp {
result = append(result, key)
}
return result
}
func CompareStringSetMap(map1, map2 map[string]sets.String) bool {
if map1 == nil && map2 == nil {
return true
}
if map1 == nil || map2 == nil {
return false
}
if len(map1) != len(map2) {
return false
}
for key, value := range map1 {
if val, exists := map2[key]; !exists || !val.Equal(value) {
return false
}
}
return true
}
func TriggerUpdate(msg string) {
select {
case updateTriggerChan <- struct{}{}:
hwlog.RunLog.Infof("update signal send, %s", msg)
default:
hwlog.RunLog.Debugf("update signal exists, receive %s", msg)
}
}
func GetUpdateChan() chan struct{} {
return updateTriggerChan
}
func IsSoftShareDevJob(pod *v1.Pod) bool {
if pod == nil {
return false
}
_, hasSchedulingPolicy := pod.Annotations[api.SchedulerSoftShareDevPolicyKey]
_, hasAicoreQuota := pod.Annotations[api.SchedulerSoftShareDevAicoreQuotaKey]
_, hasHbmQuota := pod.Annotations[api.SchedulerSoftShareDevHbmQuotaKey]
return hasSchedulingPolicy && hasAicoreQuota && hasHbmQuota
}
func IsSupportSoftShareDevice() bool {
return ParamOption.ShareCount == api.SoftShareDeviceCount && ParamOption.SoftShareDevConfigDir != ""
}
func ConvertSchedulingPolicyToIntStr(schedulingPolicy string) string {
var schedulingPolicyMap = map[string]string{
api.SoftShareDeviceSchedulingPolicyFixedShare: api.SoftShareDeviceSchedulingPolicyFixedShareInt,
api.SoftShareDeviceSchedulingPolicyElastic: api.SoftShareDeviceSchedulingPolicyElasticInt,
api.SoftShareDeviceSchedulingPolicyBestEffort: api.SoftShareDeviceSchedulingPolicyBestEffortInt,
}
if schedulingPolicy == "" {
hwlog.RunLog.Warn("scheduling policy is empty, use unknown policy")
return ""
}
if val, exist := schedulingPolicyMap[schedulingPolicy]; exist {
return val
}
hwlog.RunLog.Warnf("unknown scheduling policy: %s, return empty string", schedulingPolicy)
return ""
}
func GetMaxVirtualIDByPhysicalID(physicalID int) (int, error) {
baseDir, err := getNPUInfoConfigBaseDir()
if err != nil {
return MinVirtualID, err
}
virtualIDs, err := collectMatchedVirtualIDs(baseDir, physicalID)
if err != nil {
return MinVirtualID, err
}
maxVID := calculateMaxVirtualID(virtualIDs)
return maxVID, nil
}
func getNPUInfoConfigBaseDir() (string, error) {
baseDirPath := filepath.Dir(SoftShareDevNPUInfoConfigParentDirPath)
npuInfoCfgBaseDirPath, err := utils.RealDirChecker(baseDirPath, true, false)
if err != nil {
return "", fmt.Errorf("stat npu info config dir %s failed: %v", npuInfoCfgBaseDirPath, err)
}
return npuInfoCfgBaseDirPath, nil
}
func collectMatchedVirtualIDs(baseDir string, physicalID int) ([]int, error) {
entries, err := os.ReadDir(baseDir)
if err != nil {
return nil, fmt.Errorf("read base dir %s failed: %v", baseDir, err)
}
var virtualIDs []int
physicalIDStr := strconv.Itoa(physicalID)
for _, entry := range entries {
if !entry.IsDir() {
continue
}
nsAndJobDirPath := filepath.Join(baseDir, entry.Name())
subEntries, err := os.ReadDir(nsAndJobDirPath)
if err != nil {
hwlog.RunLog.Errorf("read dir %s failed: %v, skip\n", nsAndJobDirPath, err)
continue
}
for _, subEntry := range subEntries {
if !subEntry.IsDir() {
continue
}
vid, err := parseVirtualIDFromDirName(subEntry.Name(), physicalIDStr)
if err != nil {
hwlog.RunLog.Errorf("parse virtualID from dir %s failed: %v, skip\n", subEntry.Name(), err)
continue
}
if vid >= 0 {
virtualIDs = append(virtualIDs, vid)
}
}
}
return virtualIDs, nil
}
func parseVirtualIDFromDirName(dirName, physicalIDStr string) (int, error) {
parts := strings.Split(dirName, UnderLine)
if len(parts) != SoftShareDevNPUInfoConfigParentDirSplitLen {
return -1, fmt.Errorf("invalid dir name format %s (expect PhysicalID%sVirtualID)", dirName, UnderLine)
}
if parts[0] != physicalIDStr {
return -1, nil
}
vid, err := strconv.Atoi(parts[1])
if err != nil {
return -1, fmt.Errorf("invalid VirtualID %s", parts[1])
}
return vid, nil
}
func calculateMaxVirtualID(virtualIDs []int) int {
maxVID := -1
for _, vid := range virtualIDs {
if vid > maxVID {
maxVID = vid
}
}
return maxVID
}
func SliceEqual[T comparable](a, b []T) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}