diff --git a/Makefile b/Makefile index 5611f9aa..6da7a538 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,12 @@ help: all: clean bin openshift push openshift-all: clean openshift push -bin: controller node +bin: protoc controller node + +protoc: + @echo "" + @echo "[] protocol buffers" + protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./pkg/node_service/node_servicepb/node_rpc.proto controller: @echo "" diff --git a/helm/csi-charts/templates/daemonset.yaml b/helm/csi-charts/templates/daemonset.yaml index 21effbd4..6d4a6de8 100644 --- a/helm/csi-charts/templates/daemonset.yaml +++ b/helm/csi-charts/templates/daemonset.yaml @@ -38,6 +38,17 @@ spec: - -bind=unix://{{ .Values.kubeletPath }}/plugins/csi-exos-x.seagate.com/csi.sock - -chroot=/host {{- include "csidriver.extraArgs" .Values.node | indent 10 }} + env: + - name: CSI_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: CSI_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: CSI_NODE_SERVICE_PORT + value: "978" securityContext: privileged: true volumeMounts: diff --git a/helm/csi-charts/templates/deployment.yaml b/helm/csi-charts/templates/deployment.yaml index ad71994b..464f02d9 100644 --- a/helm/csi-charts/templates/deployment.yaml +++ b/helm/csi-charts/templates/deployment.yaml @@ -29,6 +29,9 @@ spec: - seagate-exos-x-csi-controller - -bind=unix:///csi/csi.sock {{- include "csidriver.extraArgs" .Values.controller | indent 10 }} + env: + - name: CSI_NODE_SERVICE_PORT + value: "978" volumeMounts: - name: socket-dir mountPath: /csi diff --git a/pkg/common/driver.go b/pkg/common/driver.go index 52b511a5..d536771b 100644 --- a/pkg/common/driver.go +++ b/pkg/common/driver.go @@ -47,6 +47,11 @@ const ( MaximumLUN = 255 VolumeNameMaxLength = 31 VolumePrefixMaxLength = 3 + + //If changed, must also be updated in helm charts + NodeIPEnvVar = "CSI_NODE_IP" + NodeNameEnvVar = "CSI_NODE_NAME" + NodeServicePortEnvVar = "CSI_NODE_SERVICE_PORT" ) // Driver contains main resources needed by the driver and references the underlying specific driver diff --git a/pkg/common/identity.go b/pkg/common/identity.go index 236b2567..4eb5f9d0 100644 --- a/pkg/common/identity.go +++ b/pkg/common/identity.go @@ -32,13 +32,6 @@ func (driver *Driver) GetPluginCapabilities(ctx context.Context, req *csi.GetPlu }, }, }, - { - Type: &csi.PluginCapability_Service_{ - Service: &csi.PluginCapability_Service{ - Type: csi.PluginCapability_Service_VOLUME_ACCESSIBILITY_CONSTRAINTS, - }, - }, - }, }, }, nil } diff --git a/pkg/controller/provisioner.go b/pkg/controller/provisioner.go index d001c767..81b01a86 100644 --- a/pkg/controller/provisioner.go +++ b/pkg/controller/provisioner.go @@ -71,17 +71,6 @@ func (controller *Controller) CreateVolume(ctx context.Context, req *csi.CreateV // Extract the storage interface protocol to be used for this volume (iscsi, fc, sas, etc) storageProtocol := storage.ValidateStorageProtocol(parameters[common.StorageProtocolKey]) - klog.Infof("Create Volume -- Requisite Topology: %v", req.GetAccessibilityRequirements().GetRequisite()) - klog.Infof("Create Volume -- Preferred Topology: %v", req.GetAccessibilityRequirements().GetPreferred()) - - //insert topology keys into the parameters map, so they will be available in ControllerPublishVolume - accessibleTopology, err := parseTopology(req.GetAccessibilityRequirements().GetRequisite(), storageProtocol, ¶meters) - if err != nil { - klog.Errorf("parseTopology() returned error: %v", err) - return nil, err - } - klog.V(5).Infof("accessibleTopology: %v", accessibleTopology) - if !common.ValidateName(volumeName) { return nil, status.Error(codes.InvalidArgument, "volume name contains invalid characters") } @@ -164,11 +153,10 @@ func (controller *Controller) CreateVolume(ctx context.Context, req *csi.CreateV volume := &csi.CreateVolumeResponse{ Volume: &csi.Volume{ - VolumeId: volumeId, - VolumeContext: parameters, - AccessibleTopology: accessibleTopology, - CapacityBytes: req.GetCapacityRange().GetRequiredBytes(), - ContentSource: req.GetVolumeContentSource(), + VolumeId: volumeId, + VolumeContext: parameters, + CapacityBytes: req.GetCapacityRange().GetRequiredBytes(), + ContentSource: req.GetVolumeContentSource(), }, } diff --git a/pkg/controller/publisher.go b/pkg/controller/publisher.go index c9d1b2a9..bfd756b7 100644 --- a/pkg/controller/publisher.go +++ b/pkg/controller/publisher.go @@ -2,64 +2,16 @@ package controller import ( "context" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strings" "github.com/Seagate/seagate-exos-x-csi/pkg/common" + "github.com/Seagate/seagate-exos-x-csi/pkg/node_service" + pb "github.com/Seagate/seagate-exos-x-csi/pkg/node_service/node_servicepb" "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog/v2" ) -// getConnectorInfoPath -func (driver *Controller) getConnectorInfoPath(volumeID string) string { - return fmt.Sprintf("%s/%s.json", driver.runPath, volumeID) -} - -// Read connector json file and return initiator address info for the given volume -func (driver *Controller) readInitiatorMapFromFile(filePath string, volumeID string) ([]string, error) { - klog.Infof("Reading initiator value for volume %v from file %v", volumeID, filePath) - f, err := ioutil.ReadFile(filePath) - if err != nil { - return nil, err - } - initiatorMap := make(map[string][]string) - err = json.Unmarshal(f, &initiatorMap) - if err != nil { - return nil, fmt.Errorf("error unmarshaling initiator info file for specified volume ID %v", volumeID) - } - initiators, found := initiatorMap[volumeID] - if found { - return initiators, nil - } else { - return nil, fmt.Errorf("initiator value for volume ID %v not found", volumeID) - } -} - -// PersistConnector persists the provided Connector to the specified file -func persistInitiatorMap(volumeID string, initiators []string, filePath string) error { - initiatorMap := map[string][]string{ - volumeID: initiators, - } - f, err := os.Create(filePath) - if err != nil { - klog.Error("error encoding initiator info: %v", err) - return fmt.Errorf("error creating initiator map file %s: %s", filePath, err) - } - defer f.Close() - encoder := json.NewEncoder(f) - if err = encoder.Encode(initiatorMap); err != nil { - klog.Error("error encoding initiator info: %v", err) - return fmt.Errorf("error encoding initiator info: %v", err) - } - klog.Infof("wrote initiator persistence file at %s", filePath) - return nil -} - // ControllerPublishVolume attaches the given volume to the node func (driver *Controller) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { if len(req.GetVolumeId()) == 0 { @@ -71,35 +23,31 @@ func (driver *Controller) ControllerPublishVolume(ctx context.Context, req *csi. if req.GetVolumeCapability() == nil { return nil, status.Error(codes.InvalidArgument, "cannot publish volume without capabilities") } + + nodeIP := req.GetNodeId() parameters := req.GetVolumeContext() - initiatorMapNodeID := common.GetTopologyCompliantNodeID(req.GetNodeId()) - var initiatorNames []string + var reqType pb.InitiatorType + switch parameters[common.StorageProtocolKey] { + case common.StorageProtocolSAS: + reqType = pb.InitiatorType_SAS + case common.StorageProtocolFC: + reqType = pb.InitiatorType_FC + case common.StorageProtocolISCSI: + reqType = pb.InitiatorType_ISCSI + } - // Available initiators for the node are provided in parameters through NodeGetInfo - if parameters[common.StorageProtocolKey] == common.StorageProtocolSAS { - for key, val := range parameters { - if strings.Contains(key, common.TopologySASInitiatorLabel) && strings.Contains(key, initiatorMapNodeID) { - initiatorNames = append(initiatorNames, val) - } - } - } else if parameters[common.StorageProtocolKey] == common.StorageProtocolFC { - for key, val := range parameters { - if strings.Contains(key, common.TopologyFCInitiatorLabel) && strings.Contains(key, initiatorMapNodeID) { - initiatorNames = append(initiatorNames, val) - } - } - } else { - initiatorNames = []string{req.GetNodeId()} + initiators, err := node_service.GetNodeInitiators(nodeIP, reqType) + if err != nil { + klog.ErrorS(err, "error getting node initiators", "node-ip", nodeIP, "storage-protocol", reqType) + return nil, err } volumeName, _ := common.VolumeIdGetName(req.GetVolumeId()) - persistentInfoFilepath := driver.getConnectorInfoPath(req.GetVolumeId()) - persistInitiatorMap(volumeName, initiatorNames, persistentInfoFilepath) - klog.Infof("attach request for initiator(s) %v, volume id: %s", initiatorNames, volumeName) + klog.InfoS("attach request", "initiator(s)", initiators, "volume", volumeName) - lun, err := driver.client.PublishVolume(volumeName, initiatorNames) + lun, err := driver.client.PublishVolume(volumeName, initiators) if err != nil { return nil, err @@ -119,17 +67,30 @@ func (driver *Controller) ControllerUnpublishVolume(ctx context.Context, req *cs volumeName, _ := common.VolumeIdGetName(req.GetVolumeId()) var initiators []string - var err error - if protocol, _ := common.VolumeIdGetStorageProtocol(req.GetVolumeId()); protocol == common.StorageProtocolSAS { - initiators, err = driver.readInitiatorMapFromFile(driver.getConnectorInfoPath(req.GetVolumeId()), volumeName) - if err != nil { - return nil, fmt.Errorf("error retrieving initiator! cannot unpublish volume %v", volumeName) - } - } else { - initiators = []string{req.GetNodeId()} + + nodeIP := req.GetNodeId() + storageProtocol, err := common.VolumeIdGetStorageProtocol(req.GetVolumeId()) + if err != nil { + klog.ErrorS(err, "No storage protocol found in ControllerUnpublishVolume", "storage protocol", storageProtocol, "volume ID:", req.GetVolumeId()) + return nil, err + } + + var reqType pb.InitiatorType + switch storageProtocol { + case common.StorageProtocolSAS: + reqType = pb.InitiatorType_SAS + case common.StorageProtocolFC: + reqType = pb.InitiatorType_FC + case common.StorageProtocolISCSI: + reqType = pb.InitiatorType_ISCSI } - klog.Infof("unmapping volume %s from initiator %s", volumeName, initiators) + initiators, err = node_service.GetNodeInitiators(nodeIP, reqType) + if err != nil { + klog.ErrorS(err, "error getting initiators from the node", "nodeIP", nodeIP, "storage-protocol", reqType) + } + + klog.InfoS("unmapping volume from initiator", "volumeName", volumeName, "initiators", initiators) for _, initiator := range initiators { _, status, err := driver.client.UnmapVolume(volumeName, initiator) if err != nil { @@ -141,9 +102,6 @@ func (driver *Controller) ControllerUnpublishVolume(ctx context.Context, req *cs } } - persistentInfoFilepath := driver.getConnectorInfoPath(req.GetVolumeId()) - os.Remove(persistentInfoFilepath) - klog.Infof("successfully unmapped volume %s from all initiators", volumeName) return &csi.ControllerUnpublishVolumeResponse{}, nil } diff --git a/pkg/node/node.go b/pkg/node/node.go index 926f977b..e29f41c4 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -10,6 +10,7 @@ import ( "github.com/Seagate/csi-lib-iscsi/iscsi" "github.com/Seagate/seagate-exos-x-csi/pkg/common" + "github.com/Seagate/seagate-exos-x-csi/pkg/node_service" "github.com/Seagate/seagate-exos-x-csi/pkg/storage" "github.com/container-storage-interface/spec/lib/go/csi" "github.com/golang/protobuf/ptypes/wrappers" @@ -26,6 +27,8 @@ type Node struct { semaphore *semaphore.Weighted runPath string + nodeName string + nodeIP string } // New is a convenience function for creating a node driver @@ -34,10 +37,24 @@ func New() *Node { iscsi.EnableDebugLogging(os.Stderr) } + envNodeName, _ := os.LookupEnv(common.NodeNameEnvVar) + nodeIP, envFound := os.LookupEnv(common.NodeIPEnvVar) + if !envFound { + klog.InfoS("no Node IP found in environment. Using default") + nodeIP = "127.0.0.1" + } + envServicePort, envFound := os.LookupEnv(common.NodeServicePortEnvVar) + if !envFound { + klog.InfoS("no node service port found in environment. Using default") + envServicePort = "978" + } + node := &Node{ Driver: common.NewDriver(), semaphore: semaphore.NewWeighted(1), runPath: fmt.Sprintf("/var/run/%s", common.PluginName), + nodeName: envNodeName, + nodeIP: nodeIP, } if err := os.MkdirAll(node.runPath, 0755); err != nil { @@ -96,46 +113,17 @@ func New() *Node { csi.RegisterIdentityServer(node.Server, node) csi.RegisterNodeServer(node.Server, node) + // initialize node communication service + go node_service.ListenAndServe(envServicePort) + return node } // NodeGetInfo returns info about the node func (node *Node) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) { - initiatorName, err := readInitiatorName() - if err != nil { - return nil, status.Error(codes.FailedPrecondition, err.Error()) - } - - topology := map[string]string{} - sasAddresses, err := storage.GetSASInitiators() - if err != nil { - klog.Warningf("Error while searching for FC HBA Addresses: %s", err) - } - fcAddresses, err := storage.GetFCInitiators() - if err != nil { - klog.Warningf("Error while searching for FC HBA Addresses: %s", err) - } - - for i, sasAddr := range sasAddresses { - //maximum value length 63 chars - topoKey := fmt.Sprintf("%s/%s-%d", common.TopologyInitiatorPrefix, common.TopologySASInitiatorLabel, i) - topology[topoKey] = sasAddr - } - - for i, fcAddr := range fcAddresses { - topoKey := fmt.Sprintf("%s/%s-%d", common.TopologyInitiatorPrefix, common.TopologyFCInitiatorLabel, i) - topology[topoKey] = fcAddr - } - - topology[common.TopologyNodeIDKey] = common.GetTopologyCompliantNodeID(initiatorName) - - klog.Infof("Node Accessible Topology: %v", topology) return &csi.NodeGetInfoResponse{ - NodeId: initiatorName, + NodeId: node.nodeIP, MaxVolumesPerNode: 255, - AccessibleTopology: &csi.Topology{ - Segments: topology, - }, }, nil } diff --git a/pkg/node_service/node_service_client.go b/pkg/node_service/node_service_client.go new file mode 100644 index 00000000..438e700f --- /dev/null +++ b/pkg/node_service/node_service_client.go @@ -0,0 +1,42 @@ +package node_service + +import ( + "context" + "os" + "time" + + "github.com/Seagate/seagate-exos-x-csi/pkg/common" + pb "github.com/Seagate/seagate-exos-x-csi/pkg/node_service/node_servicepb" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "k8s.io/klog/v2" +) + +// Connect to the node_service gRPC server at the given address and retrieve initiators +func GetNodeInitiators(nodeAddress string, reqType pb.InitiatorType) ([]string, error) { + port, envFound := os.LookupEnv(common.NodeServicePortEnvVar) + if !envFound { + port = "978" + klog.InfoS("no node service port found in environment. using default", "port", port) + } + + nodeServiceAddr := nodeAddress + ":" + port + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + conn, err := grpc.Dial(nodeServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + klog.ErrorS(err, "Error connecting to node service", "node ip", nodeAddress, "port", port) + return nil, err + } + defer conn.Close() + + client := pb.NewNodeServiceClient(conn) + initiatorReq := pb.InitiatorRequest{Type: reqType} + initiators, err := client.GetInitiators(ctx, &initiatorReq) + if err != nil { + klog.ErrorS(err, "Error during GetInitiators", "initiatorReq", initiatorReq) + return nil, err + } + return initiators.Initiators, nil +} diff --git a/pkg/node_service/node_service_server.go b/pkg/node_service/node_service_server.go new file mode 100644 index 00000000..91f1aa57 --- /dev/null +++ b/pkg/node_service/node_service_server.go @@ -0,0 +1,49 @@ +package node_service + +import ( + "context" + "net" + + pb "github.com/Seagate/seagate-exos-x-csi/pkg/node_service/node_servicepb" + "github.com/Seagate/seagate-exos-x-csi/pkg/storage" + "google.golang.org/grpc" + "k8s.io/klog/v2" +) + +type server struct { + pb.UnimplementedNodeServiceServer +} + +func (s *server) GetInitiators(ctx context.Context, in *pb.InitiatorRequest) (*pb.Initiators, error) { + initiators := []string{} + var err error + switch in.GetType() { + case pb.InitiatorType_FC: + initiators, err = storage.GetFCInitiators() + case pb.InitiatorType_SAS: + initiators, err = storage.GetSASInitiators() + case pb.InitiatorType_ISCSI: + initiators, err = storage.GetISCSIInitiators() + case pb.InitiatorType_UNSPECIFIED: + klog.InfoS("Unspecified initiator type in initiator request") + } + if err != nil { + return nil, err + } + return &pb.Initiators{Initiators: initiators}, nil +} + +func (s *server) NotifyUnmap(ctx context.Context, in *pb.UnmappedVolume) (*pb.Ack, error) { + return &pb.Ack{Ack: 1}, nil +} + +func ListenAndServe(port string) { + lis, err := net.Listen("tcp", ":"+port) + if err != nil { + klog.ErrorS(err, "Node Service gRPC server failed to listen") + } + s := grpc.NewServer() + pb.RegisterNodeServiceServer(s, &server{}) + klog.V(0).InfoS("Node Service gRPC server listening", "address", lis.Addr()) + s.Serve(lis) +} diff --git a/pkg/node_service/node_servicepb/node_rpc.pb.go b/pkg/node_service/node_servicepb/node_rpc.pb.go new file mode 100644 index 00000000..934615a9 --- /dev/null +++ b/pkg/node_service/node_servicepb/node_rpc.pb.go @@ -0,0 +1,412 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v4.22.2 +// source: pkg/node_service/node_servicepb/node_rpc.proto + +package node_servicepb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type InitiatorType int32 + +const ( + InitiatorType_UNSPECIFIED InitiatorType = 0 + InitiatorType_FC InitiatorType = 1 + InitiatorType_SAS InitiatorType = 2 + InitiatorType_ISCSI InitiatorType = 3 +) + +// Enum value maps for InitiatorType. +var ( + InitiatorType_name = map[int32]string{ + 0: "UNSPECIFIED", + 1: "FC", + 2: "SAS", + 3: "ISCSI", + } + InitiatorType_value = map[string]int32{ + "UNSPECIFIED": 0, + "FC": 1, + "SAS": 2, + "ISCSI": 3, + } +) + +func (x InitiatorType) Enum() *InitiatorType { + p := new(InitiatorType) + *p = x + return p +} + +func (x InitiatorType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (InitiatorType) Descriptor() protoreflect.EnumDescriptor { + return file_pkg_node_service_node_servicepb_node_rpc_proto_enumTypes[0].Descriptor() +} + +func (InitiatorType) Type() protoreflect.EnumType { + return &file_pkg_node_service_node_servicepb_node_rpc_proto_enumTypes[0] +} + +func (x InitiatorType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use InitiatorType.Descriptor instead. +func (InitiatorType) EnumDescriptor() ([]byte, []int) { + return file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescGZIP(), []int{0} +} + +type InitiatorRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type InitiatorType `protobuf:"varint,1,opt,name=type,proto3,enum=node_service.InitiatorType" json:"type,omitempty"` +} + +func (x *InitiatorRequest) Reset() { + *x = InitiatorRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitiatorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitiatorRequest) ProtoMessage() {} + +func (x *InitiatorRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InitiatorRequest.ProtoReflect.Descriptor instead. +func (*InitiatorRequest) Descriptor() ([]byte, []int) { + return file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescGZIP(), []int{0} +} + +func (x *InitiatorRequest) GetType() InitiatorType { + if x != nil { + return x.Type + } + return InitiatorType_UNSPECIFIED +} + +type Initiators struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Initiators []string `protobuf:"bytes,1,rep,name=initiators,proto3" json:"initiators,omitempty"` +} + +func (x *Initiators) Reset() { + *x = Initiators{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Initiators) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Initiators) ProtoMessage() {} + +func (x *Initiators) ProtoReflect() protoreflect.Message { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Initiators.ProtoReflect.Descriptor instead. +func (*Initiators) Descriptor() ([]byte, []int) { + return file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescGZIP(), []int{1} +} + +func (x *Initiators) GetInitiators() []string { + if x != nil { + return x.Initiators + } + return nil +} + +type UnmappedVolume struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + VolumeName string `protobuf:"bytes,1,opt,name=volumeName,proto3" json:"volumeName,omitempty"` +} + +func (x *UnmappedVolume) Reset() { + *x = UnmappedVolume{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnmappedVolume) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnmappedVolume) ProtoMessage() {} + +func (x *UnmappedVolume) ProtoReflect() protoreflect.Message { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnmappedVolume.ProtoReflect.Descriptor instead. +func (*UnmappedVolume) Descriptor() ([]byte, []int) { + return file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescGZIP(), []int{2} +} + +func (x *UnmappedVolume) GetVolumeName() string { + if x != nil { + return x.VolumeName + } + return "" +} + +type Ack struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ack int32 `protobuf:"varint,1,opt,name=ack,proto3" json:"ack,omitempty"` +} + +func (x *Ack) Reset() { + *x = Ack{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Ack) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ack) ProtoMessage() {} + +func (x *Ack) ProtoReflect() protoreflect.Message { + mi := &file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ack.ProtoReflect.Descriptor instead. +func (*Ack) Descriptor() ([]byte, []int) { + return file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescGZIP(), []int{3} +} + +func (x *Ack) GetAck() int32 { + if x != nil { + return x.Ack + } + return 0 +} + +var File_pkg_node_service_node_servicepb_node_rpc_proto protoreflect.FileDescriptor + +var file_pkg_node_service_node_servicepb_node_rpc_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x70, 0x6b, 0x67, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x70, + 0x62, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x43, + 0x0a, 0x10, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1b, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, + 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0x2c, 0x0a, 0x0a, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, + 0x73, 0x22, 0x30, 0x0a, 0x0e, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x56, 0x6f, 0x6c, + 0x75, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x22, 0x17, 0x0a, 0x03, 0x41, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x2a, 0x3c, 0x0a, 0x0d, + 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, + 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x06, + 0x0a, 0x02, 0x46, 0x43, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x41, 0x53, 0x10, 0x02, 0x12, + 0x09, 0x0a, 0x05, 0x49, 0x53, 0x43, 0x53, 0x49, 0x10, 0x03, 0x32, 0x9c, 0x01, 0x0a, 0x0b, 0x4e, + 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4b, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x6f, + 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0b, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x12, 0x1c, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x55, 0x6e, 0x6d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x56, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x1a, 0x11, 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x41, 0x63, 0x6b, 0x22, 0x00, 0x42, 0x57, 0x5a, 0x55, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x53, 0x65, 0x61, 0x67, 0x61, 0x74, 0x65, 0x2f, + 0x73, 0x65, 0x61, 0x67, 0x61, 0x74, 0x65, 0x2d, 0x65, 0x78, 0x6f, 0x73, 0x2d, 0x78, 0x2d, 0x63, + 0x73, 0x69, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x75, 0x66, 0x66, + 0x65, 0x72, 0x73, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescOnce sync.Once + file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescData = file_pkg_node_service_node_servicepb_node_rpc_proto_rawDesc +) + +func file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescGZIP() []byte { + file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescOnce.Do(func() { + file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescData) + }) + return file_pkg_node_service_node_servicepb_node_rpc_proto_rawDescData +} + +var file_pkg_node_service_node_servicepb_node_rpc_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_pkg_node_service_node_servicepb_node_rpc_proto_goTypes = []interface{}{ + (InitiatorType)(0), // 0: node_service.InitiatorType + (*InitiatorRequest)(nil), // 1: node_service.InitiatorRequest + (*Initiators)(nil), // 2: node_service.Initiators + (*UnmappedVolume)(nil), // 3: node_service.UnmappedVolume + (*Ack)(nil), // 4: node_service.Ack +} +var file_pkg_node_service_node_servicepb_node_rpc_proto_depIdxs = []int32{ + 0, // 0: node_service.InitiatorRequest.type:type_name -> node_service.InitiatorType + 1, // 1: node_service.NodeService.GetInitiators:input_type -> node_service.InitiatorRequest + 3, // 2: node_service.NodeService.NotifyUnmap:input_type -> node_service.UnmappedVolume + 2, // 3: node_service.NodeService.GetInitiators:output_type -> node_service.Initiators + 4, // 4: node_service.NodeService.NotifyUnmap:output_type -> node_service.Ack + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_pkg_node_service_node_servicepb_node_rpc_proto_init() } +func file_pkg_node_service_node_servicepb_node_rpc_proto_init() { + if File_pkg_node_service_node_servicepb_node_rpc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitiatorRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Initiators); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnmappedVolume); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Ack); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pkg_node_service_node_servicepb_node_rpc_proto_rawDesc, + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_pkg_node_service_node_servicepb_node_rpc_proto_goTypes, + DependencyIndexes: file_pkg_node_service_node_servicepb_node_rpc_proto_depIdxs, + EnumInfos: file_pkg_node_service_node_servicepb_node_rpc_proto_enumTypes, + MessageInfos: file_pkg_node_service_node_servicepb_node_rpc_proto_msgTypes, + }.Build() + File_pkg_node_service_node_servicepb_node_rpc_proto = out.File + file_pkg_node_service_node_servicepb_node_rpc_proto_rawDesc = nil + file_pkg_node_service_node_servicepb_node_rpc_proto_goTypes = nil + file_pkg_node_service_node_servicepb_node_rpc_proto_depIdxs = nil +} diff --git a/pkg/node_service/node_servicepb/node_rpc.proto b/pkg/node_service/node_servicepb/node_rpc.proto new file mode 100644 index 00000000..9a012a0e --- /dev/null +++ b/pkg/node_service/node_servicepb/node_rpc.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; +package node_service; + +option go_package = "github.com/Seagate/seagate-exos-x-csi/pkg/node_service/protocolbuffers/node_servicepb"; + +service NodeService { + rpc GetInitiators(InitiatorRequest) returns (Initiators){} + rpc NotifyUnmap(UnmappedVolume) returns (Ack){} +} + +enum InitiatorType{ + UNSPECIFIED = 0; + FC = 1; + SAS = 2; + ISCSI = 3; +} + +message InitiatorRequest { + InitiatorType type = 1; +} + +message Initiators { + repeated string initiators = 1; +} + +message UnmappedVolume { + string volumeName = 1; +} + +message Ack { + int32 ack = 1; +} diff --git a/pkg/node_service/node_servicepb/node_rpc_grpc.pb.go b/pkg/node_service/node_servicepb/node_rpc_grpc.pb.go new file mode 100644 index 00000000..ebfbc97d --- /dev/null +++ b/pkg/node_service/node_servicepb/node_rpc_grpc.pb.go @@ -0,0 +1,141 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v4.22.2 +// source: pkg/node_service/node_servicepb/node_rpc.proto + +package node_servicepb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// NodeServiceClient is the client API for NodeService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type NodeServiceClient interface { + GetInitiators(ctx context.Context, in *InitiatorRequest, opts ...grpc.CallOption) (*Initiators, error) + NotifyUnmap(ctx context.Context, in *UnmappedVolume, opts ...grpc.CallOption) (*Ack, error) +} + +type nodeServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewNodeServiceClient(cc grpc.ClientConnInterface) NodeServiceClient { + return &nodeServiceClient{cc} +} + +func (c *nodeServiceClient) GetInitiators(ctx context.Context, in *InitiatorRequest, opts ...grpc.CallOption) (*Initiators, error) { + out := new(Initiators) + err := c.cc.Invoke(ctx, "/node_service.NodeService/GetInitiators", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nodeServiceClient) NotifyUnmap(ctx context.Context, in *UnmappedVolume, opts ...grpc.CallOption) (*Ack, error) { + out := new(Ack) + err := c.cc.Invoke(ctx, "/node_service.NodeService/NotifyUnmap", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// NodeServiceServer is the server API for NodeService service. +// All implementations must embed UnimplementedNodeServiceServer +// for forward compatibility +type NodeServiceServer interface { + GetInitiators(context.Context, *InitiatorRequest) (*Initiators, error) + NotifyUnmap(context.Context, *UnmappedVolume) (*Ack, error) + mustEmbedUnimplementedNodeServiceServer() +} + +// UnimplementedNodeServiceServer must be embedded to have forward compatible implementations. +type UnimplementedNodeServiceServer struct { +} + +func (UnimplementedNodeServiceServer) GetInitiators(context.Context, *InitiatorRequest) (*Initiators, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInitiators not implemented") +} +func (UnimplementedNodeServiceServer) NotifyUnmap(context.Context, *UnmappedVolume) (*Ack, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyUnmap not implemented") +} +func (UnimplementedNodeServiceServer) mustEmbedUnimplementedNodeServiceServer() {} + +// UnsafeNodeServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to NodeServiceServer will +// result in compilation errors. +type UnsafeNodeServiceServer interface { + mustEmbedUnimplementedNodeServiceServer() +} + +func RegisterNodeServiceServer(s grpc.ServiceRegistrar, srv NodeServiceServer) { + s.RegisterService(&NodeService_ServiceDesc, srv) +} + +func _NodeService_GetInitiators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InitiatorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NodeServiceServer).GetInitiators(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/node_service.NodeService/GetInitiators", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NodeServiceServer).GetInitiators(ctx, req.(*InitiatorRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NodeService_NotifyUnmap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnmappedVolume) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NodeServiceServer).NotifyUnmap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/node_service.NodeService/NotifyUnmap", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NodeServiceServer).NotifyUnmap(ctx, req.(*UnmappedVolume)) + } + return interceptor(ctx, in, info, handler) +} + +// NodeService_ServiceDesc is the grpc.ServiceDesc for NodeService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var NodeService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "node_service.NodeService", + HandlerType: (*NodeServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetInitiators", + Handler: _NodeService_GetInitiators_Handler, + }, + { + MethodName: "NotifyUnmap", + Handler: _NodeService_NotifyUnmap_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pkg/node_service/node_servicepb/node_rpc.proto", +} diff --git a/pkg/storage/iscsiNode.go b/pkg/storage/iscsiNode.go index a9eccd0b..9a463840 100644 --- a/pkg/storage/iscsiNode.go +++ b/pkg/storage/iscsiNode.go @@ -19,6 +19,7 @@ package storage import ( + "bufio" "context" "fmt" "os" @@ -310,11 +311,6 @@ func (iscsi *iscsiStorage) NodeExpandVolume(ctx context.Context, req *csi.NodeEx return nil, status.Error(codes.NotFound, fmt.Sprintf("node expand volume path not found for volume id (%s)", volumeName)) } - // TODO: Is a rescan needed - rescan a scsi device by writing 1 in /sys/class/scsi_device/h:c:t:l/device/rescan - // for i := range connector.Devices { - // connector.Devices[i].Rescan() - // } - if connector.Multipath { klog.V(2).Info("device is using multipath") if err := iscsilib.ResizeMultipathDevice(connector.DevicePath); err != nil { @@ -343,3 +339,28 @@ func (iscsi *iscsiStorage) NodeGetCapabilities(ctx context.Context, req *csi.Nod func (iscsi *iscsiStorage) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) { return nil, status.Error(codes.Unimplemented, "NodeGetInfo is not implemented") } + +func GetISCSIInitiators() ([]string, error) { + initiatorNameFilePath := "/etc/iscsi/initiatorname.iscsi" + file, err := os.Open(initiatorNameFilePath) + if err != nil { + return nil, err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if equal := strings.Index(line, "="); equal >= 0 { + if strings.TrimSpace(line[:equal]) == "InitiatorName" { + return []string{strings.TrimSpace(line[equal+1:])}, nil + } + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return nil, fmt.Errorf("InitiatorName key is missing from %s", initiatorNameFilePath) +}