diff --git a/service/envvars.go b/service/envvars.go index 48bc6253..13520279 100644 --- a/service/envvars.go +++ b/service/envvars.go @@ -118,9 +118,15 @@ const ( // EnvManagedArrays is an env variable with a list of space separated arrays. EnvManagedArrays = "X_CSI_MANAGED_ARRAYS" + // EnvKubeConfigPath indicates kubernetes configuration that has to be used by CSI Driver + EnvKubeConfigPath = "KUBECONFIG" + // EnvConfigFilePath is an env variable which contains the full path for the config file EnvConfigFilePath = "X_CSI_POWERMAX_CONFIG_PATH" + // EnvMaxVolumesPerNode specifies maximum number of volumes that controller can publish to the node. + EnvMaxVolumesPerNode = "X_CSI_MAX_VOLUMES_PER_NODE" + // EnvHealthMonitorEnabled is an env variable which indicated if volume health monitor is enabled EnvHealthMonitorEnabled = "X_CSI_HEALTH_MONITOR_ENABLED" diff --git a/service/node.go b/service/node.go index e6c86259..96fe86d7 100644 --- a/service/node.go +++ b/service/node.go @@ -1050,11 +1050,52 @@ func (s *service) NodeGetInfo( return nil, status.Error(codes.FailedPrecondition, "no topology keys could be generate") } + var maxPowerMaxVolumesPerNode int64 + labels, err := s.GetNodeLabels() + if err != nil { + log.Error("failed to get Node Labels with error", err.Error()) + return nil, err + } + if val, ok := labels["max-powermax-volumes-per-node"]; ok { + maxPowerMaxVolumesPerNode, err = strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid value '%s' specified for 'max-powermax-volumes-per-node' node label", val) + } + if s.opts.IsVsphereEnabled { + if maxPowerMaxVolumesPerNode <= 0 || maxPowerMaxVolumesPerNode > 60 { + log.Errorf("Node label max-powermax-volumes-per-node should not be greater than 60 or set to any negative value for RDM volumes, Setting to default value 60") + } + maxPowerMaxVolumesPerNode = 60 + } else { + if maxPowerMaxVolumesPerNode < 0 { + log.Errorf("Node label max-powermax-volumes-per-node should not be set to negative value, Using default value 0") + maxPowerMaxVolumesPerNode = 0 + } + } + log.Infof("node label 'max-powermax-volumes-per-node' is available and is set to value '%v'", maxPowerMaxVolumesPerNode) + } else { + // As per the csi spec the plugin MUST NOT set negative values to + // 'MaxVolumesPerNode' in the NodeGetInfoResponse response + log.Infof("Node label 'max-powermax-volumes-per-node' is not available. Retrieving the value from yaml file") + if s.opts.IsVsphereEnabled { + if s.opts.MaxVolumesPerNode <= 0 || s.opts.MaxVolumesPerNode > 60 { + log.Errorf("maxPowerMaxVolumesPerNode MUST NOT be greater than 60 or set to any negative value for RDM volumes. Setting to default value 60") + } + s.opts.MaxVolumesPerNode = 60 + } else { + if s.opts.MaxVolumesPerNode < 0 { + log.Errorf("maxPowerMaxVolumesPerNode MUST NOT be set to negative value, setting to default value 0") + s.opts.MaxVolumesPerNode = 0 + } + } + maxPowerMaxVolumesPerNode = s.opts.MaxVolumesPerNode + } return &csi.NodeGetInfoResponse{ NodeId: s.opts.NodeName, AccessibleTopology: &csi.Topology{ Segments: topology, }, + MaxVolumesPerNode: maxPowerMaxVolumesPerNode, }, nil } diff --git a/service/service.go b/service/service.go index 4a11930f..976621ea 100644 --- a/service/service.go +++ b/service/service.go @@ -34,6 +34,7 @@ import ( "time" "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/dell/csi-powermax/v2/k8sutils" "github.com/dell/gocsi" csictx "github.com/dell/gocsi/context" "github.com/dell/goiscsi" @@ -46,6 +47,7 @@ import ( migrext "github.com/dell/dell-csi-extensions/migration" csiext "github.com/dell/dell-csi-extensions/replication" pmax "github.com/dell/gopowermax/v2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Constants for the service @@ -95,6 +97,7 @@ type Opts struct { Password string SystemName string NodeName string + NodeFullName string TransportProtocol string DriverName string CHAPUserName string @@ -125,6 +128,8 @@ type Opts struct { VCenterHostURL string // vCenter host url VCenterHostUserName string // vCenter host username VCenterHostPassword string // vCenter password + MaxVolumesPerNode int64 // to specify volume limits + KubeConfigPath string // to specify k8s configuration to be used CSI driver } // NodeConfig defines rules for given node @@ -362,6 +367,7 @@ func (s *service) BeforeServe( if name, ok := csictx.LookupEnv(ctx, EnvNodeName); ok { shortHostName := strings.Split(name, ".")[0] opts.NodeName = shortHostName + opts.NodeFullName = name } if portgroups, ok := csictx.LookupEnv(ctx, EnvPortGroups); ok { tempList, err := s.parseCommaSeperatedList(portgroups) @@ -378,6 +384,10 @@ func (s *service) BeforeServe( os.Exit(1) } + if kubeConfigPath, ok := csictx.LookupEnv(ctx, EnvKubeConfigPath); ok { + opts.KubeConfigPath = kubeConfigPath + } + if replicationContextPrefix, ok := csictx.LookupEnv(ctx, EnvReplicationContextPrefix); ok { opts.ReplicationContextPrefix = replicationContextPrefix } @@ -385,6 +395,16 @@ func (s *service) BeforeServe( opts.ReplicationPrefix = replicationPrefix } + if MaxVolumesPerNode, ok := csictx.LookupEnv(ctx, EnvMaxVolumesPerNode); ok { + val, err := strconv.ParseInt(MaxVolumesPerNode, 10, 64) + if err != nil { + log.Warningf("error while parsing env variable '%s', %s, defaulting to 0", EnvMaxVolumesPerNode, err) + opts.MaxVolumesPerNode = 0 + } else { + opts.MaxVolumesPerNode = val + } + } + opts.TransportProtocol = s.getTransportProtocolFromEnv() opts.ProxyServiceHost, opts.ProxyServicePort, opts.UseProxy = s.getProxySettingsFromEnv() if !opts.UseProxy && !inducedMockReverseProxy { @@ -792,3 +812,19 @@ func getLogFields(ctx context.Context) log.Fields { fields["RequestID"] = csiReqID return fields } + +func (s *service) GetNodeLabels() (map[string]string, error) { + k8sclientset, err := k8sutils.CreateKubeClientSet(s.opts.KubeConfigPath) + if err != nil { + log.Errorf("init client failed: '%s'", err.Error()) + return nil, err + } + // access the API to fetch node object + node, err := k8sclientset.CoreV1().Nodes().Get(context.TODO(), s.opts.NodeFullName, v1.GetOptions{}) + if err != nil { + return nil, err + } + log.Debugf("Node %s details\n", node) + + return node.Labels, nil +}