Skip to content

Commit 2c39a41

Browse files
committed
fix(setting): remove CPU mask validation
A Longhorn system backup might be restored on a system with fewer CPU cores. In such cases, CPU mask validation could cause the system restore to fail. To prevent this, we should remove CPU mask validation. If the user misconfigures the mask, the SPDK process in the instance manager pod will fail, preventing v2 volume creation. Longhorn 11933 Signed-off-by: Derek Su <derek.su@suse.com>
1 parent e44a7ef commit 2c39a41

2 files changed

Lines changed: 0 additions & 130 deletions

File tree

datastore/longhorn.go

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"math/bits"
87
"math/rand"
98
"net"
109
"net/url"
@@ -587,69 +586,6 @@ func (s *DataStore) ValidateSetting(name, value string) (err error) {
587586
}
588587
}
589588

590-
case types.SettingNameDataEngineCPUMask:
591-
definition, ok := types.GetSettingDefinition(types.SettingNameDataEngineCPUMask)
592-
if !ok {
593-
return fmt.Errorf("setting %v is not found", types.SettingNameDataEngineCPUMask)
594-
}
595-
var values map[longhorn.DataEngineType]any
596-
if types.IsJSONFormat(value) {
597-
values, err = types.ParseDataEngineSpecificSetting(definition, value)
598-
} else {
599-
values, err = types.ParseSettingSingleValue(definition, value)
600-
}
601-
if err != nil {
602-
return errors.Wrapf(err, "failed to parse value %v for setting %v", value, types.SettingNameDataEngineCPUMask)
603-
}
604-
605-
v2DataEngineEnabled, err := s.GetSettingAsBool(types.SettingNameV2DataEngine)
606-
if err != nil {
607-
return errors.Wrapf(err, "failed to get setting %v for setting validation", types.SettingNameV2DataEngine)
608-
}
609-
if !v2DataEngineEnabled {
610-
logrus.Infof("Skipping validating setting %v since v2 data engine is not enabled", types.SettingNameDataEngineCPUMask)
611-
return nil
612-
}
613-
614-
for dataEngine, raw := range values {
615-
cpuMask, ok := raw.(string)
616-
if !ok {
617-
return fmt.Errorf("setting %v value %v is not a string for data engine %v", types.SettingNameDataEngineCPUMask, raw, dataEngine)
618-
}
619-
620-
lhNodes, err := s.ListNodesRO()
621-
if err != nil {
622-
return errors.Wrapf(err, "failed to list nodes for %v setting validation for data engine %v", types.SettingNameDataEngineCPUMask, dataEngine)
623-
}
624-
625-
// Ensure if the CPU mask can be satisfied on each node
626-
for _, lhNode := range lhNodes {
627-
if isUnavailable, err := s.IsNodeDownOrDeletedOrMissingManager(lhNode.Name); err != nil {
628-
return errors.Wrapf(err, "failed to check if node %v is down or deleted", lhNode.Name)
629-
} else if isUnavailable {
630-
continue
631-
}
632-
633-
kubeNode, err := s.GetKubernetesNodeRO(lhNode.Name)
634-
if err != nil {
635-
if apierrors.IsNotFound(err) {
636-
logrus.Warnf("Kubernetes node %s not found, skipping CPU mask validation for this node for data engine %v", lhNode.Name, dataEngine)
637-
continue
638-
}
639-
return errors.Wrapf(err, "failed to get Kubernetes node %s for %v setting validation for data engine %v", lhNode.Name, types.SettingNameDataEngineCPUMask, dataEngine)
640-
}
641-
642-
if val, ok := kubeNode.Labels[types.NodeDisableV2DataEngineLabelKey]; ok && val == types.NodeDisableV2DataEngineLabelKeyTrue {
643-
// V2 data engine is disabled on this node, don't worry about cpu mask
644-
continue
645-
}
646-
647-
if err := s.ValidateCPUMask(kubeNode, cpuMask); err != nil {
648-
return err
649-
}
650-
}
651-
}
652-
653589
case types.SettingNameAutoCleanupSystemGeneratedSnapshot:
654590
disablePurgeValue, err := s.GetSettingAsBool(types.SettingNameDisableSnapshotPurge)
655591
if err != nil {
@@ -790,51 +726,6 @@ func (s *DataStore) ValidateV2DataEngineEnabled(dataEngineEnabled bool) (ims []*
790726
return
791727
}
792728

793-
func (s *DataStore) ValidateCPUMask(kubeNode *corev1.Node, value string) error {
794-
if value == "" {
795-
return fmt.Errorf("failed to validate CPU mask: cannot be empty")
796-
}
797-
798-
// CPU mask must start with 0x
799-
cpuMaskRegex := regexp.MustCompile(`^0x[1-9a-fA-F][0-9a-fA-F]*$`)
800-
if !cpuMaskRegex.MatchString(value) {
801-
return fmt.Errorf("invalid CPU mask: %s", value)
802-
}
803-
804-
maskValue, err := strconv.ParseUint(value[2:], 16, 64) // skip 0x prefix
805-
if err != nil {
806-
return errors.Wrapf(err, "failed to parse CPU mask %v", value)
807-
}
808-
809-
// Validate the mask value is not larger than the number of available CPUs
810-
numCPUs, err := s.getMinNumCPUsFromAvailableNodes()
811-
if err != nil {
812-
return errors.Wrap(err, "failed to get minimum number of CPUs for CPU mask validation")
813-
}
814-
815-
maxCPUMaskValue := (1 << numCPUs) - 1
816-
if maskValue > uint64(maxCPUMaskValue) {
817-
return fmt.Errorf("CPU mask exceeds the maximum allowed value %v for the current system: %s", maxCPUMaskValue, value)
818-
}
819-
820-
// CPU mask currently only supports v2 data engine
821-
guaranteedInstanceManagerCPUInPercentage, err := s.GetSettingAsFloatByDataEngine(types.SettingNameGuaranteedInstanceManagerCPU, longhorn.DataEngineTypeV2)
822-
if err != nil {
823-
return errors.Wrapf(err, "failed to get %v setting for guaranteed instance manager CPU validation for data engine %v",
824-
types.SettingNameGuaranteedInstanceManagerCPU, longhorn.DataEngineTypeV2)
825-
}
826-
827-
guaranteedInstanceManagerCPU := float64(kubeNode.Status.Allocatable.Cpu().MilliValue()) * guaranteedInstanceManagerCPUInPercentage / 100
828-
829-
numMilliCPUsRequrestedByMaskValue := calculateMilliCPUs(maskValue)
830-
if numMilliCPUsRequrestedByMaskValue > int(guaranteedInstanceManagerCPU) {
831-
return fmt.Errorf("number of CPUs (%v) requested by CPU mask (%v) is larger than the %v setting value (%v)",
832-
numMilliCPUsRequrestedByMaskValue, value, types.SettingNameGuaranteedInstanceManagerCPU, guaranteedInstanceManagerCPU)
833-
}
834-
835-
return nil
836-
}
837-
838729
func (s *DataStore) getMinNumCPUsFromAvailableNodes() (int64, error) {
839730
kubeNodes, err := s.ListKubeNodesRO()
840731
if err != nil {
@@ -873,16 +764,6 @@ func (s *DataStore) getMinNumCPUsFromAvailableNodes() (int64, error) {
873764
return minNumCPUs, nil
874765
}
875766

876-
func calculateMilliCPUs(mask uint64) int {
877-
// Count the number of set bits in the mask
878-
setBits := bits.OnesCount64(mask)
879-
880-
// Each set bit represents 1000 milliCPUs
881-
numMilliCPUsRequestedByMaskValue := setBits * 1000
882-
883-
return numMilliCPUsRequestedByMaskValue
884-
}
885-
886767
func (s *DataStore) AreAllRWXVolumesDetached() (bool, error) {
887768
volumes, err := s.ListVolumesRO()
888769
if err != nil {

webhook/resources/instancemanager/validator.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,5 @@ func (i *instanceManagerValidator) validate(im *longhorn.InstanceManager) error
7979
return fmt.Errorf("data engine for instanceManager %s is not set", im.Name)
8080
}
8181

82-
if im.Spec.DataEngineSpec.V2.CPUMask != "" {
83-
kubeNode, err := i.ds.GetKubernetesNodeRO(im.Spec.NodeID)
84-
if err != nil {
85-
return werror.NewInvalidError(fmt.Sprintf("failed to get kube node %s: %v", im.Spec.NodeID, err), "")
86-
}
87-
88-
if err := i.ds.ValidateCPUMask(kubeNode, im.Spec.DataEngineSpec.V2.CPUMask); err != nil {
89-
return werror.NewInvalidError(err.Error(), "")
90-
}
91-
}
92-
9382
return nil
9483
}

0 commit comments

Comments
 (0)