k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/csi/csi_client.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package csi 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io" 24 "net" 25 "strconv" 26 "sync" 27 28 csipbv1 "github.com/container-storage-interface/spec/lib/go/csi" 29 "google.golang.org/grpc" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/credentials/insecure" 32 "google.golang.org/grpc/status" 33 api "k8s.io/api/core/v1" 34 "k8s.io/apimachinery/pkg/api/resource" 35 utilfeature "k8s.io/apiserver/pkg/util/feature" 36 "k8s.io/klog/v2" 37 "k8s.io/kubernetes/pkg/features" 38 "k8s.io/kubernetes/pkg/volume" 39 volumetypes "k8s.io/kubernetes/pkg/volume/util/types" 40 ) 41 42 type csiClient interface { 43 NodeGetInfo(ctx context.Context) ( 44 nodeID string, 45 maxVolumePerNode int64, 46 accessibleTopology map[string]string, 47 err error) 48 49 // The caller is responsible for checking whether the driver supports 50 // applying FSGroup by calling NodeSupportsVolumeMountGroup(). 51 // If the driver does not, fsGroup must be set to nil. 52 NodePublishVolume( 53 ctx context.Context, 54 volumeid string, 55 readOnly bool, 56 stagingTargetPath string, 57 targetPath string, 58 accessMode api.PersistentVolumeAccessMode, 59 publishContext map[string]string, 60 volumeContext map[string]string, 61 secrets map[string]string, 62 fsType string, 63 mountOptions []string, 64 fsGroup *int64, 65 ) error 66 67 NodeExpandVolume(ctx context.Context, rsOpts csiResizeOptions) (resource.Quantity, error) 68 NodeUnpublishVolume( 69 ctx context.Context, 70 volID string, 71 targetPath string, 72 ) error 73 74 // The caller is responsible for checking whether the driver supports 75 // applying FSGroup by calling NodeSupportsVolumeMountGroup(). 76 // If the driver does not, fsGroup must be set to nil. 77 NodeStageVolume(ctx context.Context, 78 volID string, 79 publishVolumeInfo map[string]string, 80 stagingTargetPath string, 81 fsType string, 82 accessMode api.PersistentVolumeAccessMode, 83 secrets map[string]string, 84 volumeContext map[string]string, 85 mountOptions []string, 86 fsGroup *int64, 87 ) error 88 89 NodeGetVolumeStats( 90 ctx context.Context, 91 volID string, 92 targetPath string, 93 ) (*volume.Metrics, error) 94 NodeUnstageVolume(ctx context.Context, volID, stagingTargetPath string) error 95 NodeSupportsStageUnstage(ctx context.Context) (bool, error) 96 NodeSupportsNodeExpand(ctx context.Context) (bool, error) 97 NodeSupportsVolumeStats(ctx context.Context) (bool, error) 98 NodeSupportsSingleNodeMultiWriterAccessMode(ctx context.Context) (bool, error) 99 NodeSupportsVolumeMountGroup(ctx context.Context) (bool, error) 100 } 101 102 // Strongly typed address 103 type csiAddr string 104 105 // Strongly typed driver name 106 type csiDriverName string 107 108 // csiClient encapsulates all csi-plugin methods 109 type csiDriverClient struct { 110 driverName csiDriverName 111 addr csiAddr 112 metricsManager *MetricsManager 113 nodeV1ClientCreator nodeV1ClientCreator 114 } 115 116 type csiResizeOptions struct { 117 volumeID string 118 volumePath string 119 stagingTargetPath string 120 fsType string 121 accessMode api.PersistentVolumeAccessMode 122 newSize resource.Quantity 123 mountOptions []string 124 secrets map[string]string 125 } 126 127 var _ csiClient = &csiDriverClient{} 128 129 type nodeV1ClientCreator func(addr csiAddr, metricsManager *MetricsManager) ( 130 nodeClient csipbv1.NodeClient, 131 closer io.Closer, 132 err error, 133 ) 134 135 type nodeV1AccessModeMapper func(am api.PersistentVolumeAccessMode) csipbv1.VolumeCapability_AccessMode_Mode 136 137 // newV1NodeClient creates a new NodeClient with the internally used gRPC 138 // connection set up. It also returns a closer which must be called to close 139 // the gRPC connection when the NodeClient is not used anymore. 140 // This is the default implementation for the nodeV1ClientCreator, used in 141 // newCsiDriverClient. 142 func newV1NodeClient(addr csiAddr, metricsManager *MetricsManager) (nodeClient csipbv1.NodeClient, closer io.Closer, err error) { 143 var conn *grpc.ClientConn 144 conn, err = newGrpcConn(addr, metricsManager) 145 if err != nil { 146 return nil, nil, err 147 } 148 149 nodeClient = csipbv1.NewNodeClient(conn) 150 return nodeClient, conn, nil 151 } 152 153 func newCsiDriverClient(driverName csiDriverName) (*csiDriverClient, error) { 154 if driverName == "" { 155 return nil, fmt.Errorf("driver name is empty") 156 } 157 158 existingDriver, driverExists := csiDrivers.Get(string(driverName)) 159 if !driverExists { 160 return nil, fmt.Errorf("driver name %s not found in the list of registered CSI drivers", driverName) 161 } 162 163 nodeV1ClientCreator := newV1NodeClient 164 return &csiDriverClient{ 165 driverName: driverName, 166 addr: csiAddr(existingDriver.endpoint), 167 nodeV1ClientCreator: nodeV1ClientCreator, 168 metricsManager: NewCSIMetricsManager(string(driverName)), 169 }, nil 170 } 171 172 func (c *csiDriverClient) NodeGetInfo(ctx context.Context) ( 173 nodeID string, 174 maxVolumePerNode int64, 175 accessibleTopology map[string]string, 176 err error) { 177 klog.V(4).InfoS(log("calling NodeGetInfo rpc")) 178 179 var getNodeInfoError error 180 nodeID, maxVolumePerNode, accessibleTopology, getNodeInfoError = c.nodeGetInfoV1(ctx) 181 if getNodeInfoError != nil { 182 klog.InfoS("Error calling CSI NodeGetInfo()", "err", getNodeInfoError.Error()) 183 } 184 return nodeID, maxVolumePerNode, accessibleTopology, getNodeInfoError 185 } 186 187 func (c *csiDriverClient) nodeGetInfoV1(ctx context.Context) ( 188 nodeID string, 189 maxVolumePerNode int64, 190 accessibleTopology map[string]string, 191 err error) { 192 193 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 194 if err != nil { 195 return "", 0, nil, err 196 } 197 defer closer.Close() 198 199 res, err := nodeClient.NodeGetInfo(ctx, &csipbv1.NodeGetInfoRequest{}) 200 if err != nil { 201 return "", 0, nil, err 202 } 203 204 topology := res.GetAccessibleTopology() 205 if topology != nil { 206 accessibleTopology = topology.Segments 207 } 208 return res.GetNodeId(), res.GetMaxVolumesPerNode(), accessibleTopology, nil 209 } 210 211 func (c *csiDriverClient) NodePublishVolume( 212 ctx context.Context, 213 volID string, 214 readOnly bool, 215 stagingTargetPath string, 216 targetPath string, 217 accessMode api.PersistentVolumeAccessMode, 218 publishContext map[string]string, 219 volumeContext map[string]string, 220 secrets map[string]string, 221 fsType string, 222 mountOptions []string, 223 fsGroup *int64, 224 ) error { 225 klog.V(4).InfoS(log("calling NodePublishVolume rpc"), "volID", volID, "targetPath", targetPath) 226 if volID == "" { 227 return errors.New("missing volume id") 228 } 229 if targetPath == "" { 230 return errors.New("missing target path") 231 } 232 233 if c.nodeV1ClientCreator == nil { 234 return errors.New("failed to call NodePublishVolume. nodeV1ClientCreator is nil") 235 } 236 237 accessModeMapper, err := c.getNodeV1AccessModeMapper(ctx) 238 if err != nil { 239 return err 240 } 241 242 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 243 if err != nil { 244 return err 245 } 246 defer closer.Close() 247 248 req := &csipbv1.NodePublishVolumeRequest{ 249 VolumeId: volID, 250 TargetPath: targetPath, 251 Readonly: readOnly, 252 PublishContext: publishContext, 253 VolumeContext: volumeContext, 254 Secrets: secrets, 255 VolumeCapability: &csipbv1.VolumeCapability{ 256 AccessMode: &csipbv1.VolumeCapability_AccessMode{ 257 Mode: accessModeMapper(accessMode), 258 }, 259 }, 260 } 261 if stagingTargetPath != "" { 262 req.StagingTargetPath = stagingTargetPath 263 } 264 265 if fsType == fsTypeBlockName { 266 req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{ 267 Block: &csipbv1.VolumeCapability_BlockVolume{}, 268 } 269 } else { 270 mountVolume := &csipbv1.VolumeCapability_MountVolume{ 271 FsType: fsType, 272 MountFlags: mountOptions, 273 } 274 if fsGroup != nil { 275 mountVolume.VolumeMountGroup = strconv.FormatInt(*fsGroup, 10 /* base */) 276 } 277 req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{ 278 Mount: mountVolume, 279 } 280 } 281 282 _, err = nodeClient.NodePublishVolume(ctx, req) 283 if err != nil && !isFinalError(err) { 284 return volumetypes.NewUncertainProgressError(err.Error()) 285 } 286 return err 287 } 288 289 func (c *csiDriverClient) NodeExpandVolume(ctx context.Context, opts csiResizeOptions) (resource.Quantity, error) { 290 if c.nodeV1ClientCreator == nil { 291 return opts.newSize, fmt.Errorf("version of CSI driver does not support volume expansion") 292 } 293 294 if opts.volumeID == "" { 295 return opts.newSize, errors.New("missing volume id") 296 } 297 if opts.volumePath == "" { 298 return opts.newSize, errors.New("missing volume path") 299 } 300 301 if opts.newSize.Value() < 0 { 302 return opts.newSize, errors.New("size can not be less than 0") 303 } 304 305 accessModeMapper, err := c.getNodeV1AccessModeMapper(ctx) 306 if err != nil { 307 return opts.newSize, err 308 } 309 310 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 311 if err != nil { 312 return opts.newSize, err 313 } 314 defer closer.Close() 315 316 req := &csipbv1.NodeExpandVolumeRequest{ 317 VolumeId: opts.volumeID, 318 VolumePath: opts.volumePath, 319 CapacityRange: &csipbv1.CapacityRange{RequiredBytes: opts.newSize.Value()}, 320 VolumeCapability: &csipbv1.VolumeCapability{ 321 AccessMode: &csipbv1.VolumeCapability_AccessMode{ 322 Mode: accessModeMapper(opts.accessMode), 323 }, 324 }, 325 Secrets: opts.secrets, 326 } 327 328 // not all CSI drivers support NodeStageUnstage and hence the StagingTargetPath 329 // should only be set when available 330 if opts.stagingTargetPath != "" { 331 req.StagingTargetPath = opts.stagingTargetPath 332 } 333 334 if opts.fsType == fsTypeBlockName { 335 req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{ 336 Block: &csipbv1.VolumeCapability_BlockVolume{}, 337 } 338 } else { 339 req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{ 340 Mount: &csipbv1.VolumeCapability_MountVolume{ 341 FsType: opts.fsType, 342 MountFlags: opts.mountOptions, 343 }, 344 } 345 } 346 347 resp, err := nodeClient.NodeExpandVolume(ctx, req) 348 if err != nil { 349 if !isFinalError(err) { 350 return opts.newSize, volumetypes.NewUncertainProgressError(err.Error()) 351 } 352 return opts.newSize, err 353 } 354 355 updatedQuantity := resource.NewQuantity(resp.CapacityBytes, resource.BinarySI) 356 return *updatedQuantity, nil 357 } 358 359 func (c *csiDriverClient) NodeUnpublishVolume(ctx context.Context, volID string, targetPath string) error { 360 klog.V(4).InfoS(log("calling NodeUnpublishVolume rpc"), "volID", volID, "targetPath", targetPath) 361 if volID == "" { 362 return errors.New("missing volume id") 363 } 364 if targetPath == "" { 365 return errors.New("missing target path") 366 } 367 if c.nodeV1ClientCreator == nil { 368 return errors.New("nodeV1ClientCreate is nil") 369 } 370 371 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 372 if err != nil { 373 return err 374 } 375 defer closer.Close() 376 377 req := &csipbv1.NodeUnpublishVolumeRequest{ 378 VolumeId: volID, 379 TargetPath: targetPath, 380 } 381 382 _, err = nodeClient.NodeUnpublishVolume(ctx, req) 383 return err 384 } 385 386 func (c *csiDriverClient) NodeStageVolume(ctx context.Context, 387 volID string, 388 publishContext map[string]string, 389 stagingTargetPath string, 390 fsType string, 391 accessMode api.PersistentVolumeAccessMode, 392 secrets map[string]string, 393 volumeContext map[string]string, 394 mountOptions []string, 395 fsGroup *int64, 396 ) error { 397 klog.V(4).InfoS(log("calling NodeStageVolume rpc"), "volID", volID, "stagingTargetPath", stagingTargetPath) 398 if volID == "" { 399 return errors.New("missing volume id") 400 } 401 if stagingTargetPath == "" { 402 return errors.New("missing staging target path") 403 } 404 if c.nodeV1ClientCreator == nil { 405 return errors.New("nodeV1ClientCreate is nil") 406 } 407 408 accessModeMapper, err := c.getNodeV1AccessModeMapper(ctx) 409 if err != nil { 410 return err 411 } 412 413 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 414 if err != nil { 415 return err 416 } 417 defer closer.Close() 418 419 req := &csipbv1.NodeStageVolumeRequest{ 420 VolumeId: volID, 421 PublishContext: publishContext, 422 StagingTargetPath: stagingTargetPath, 423 VolumeCapability: &csipbv1.VolumeCapability{ 424 AccessMode: &csipbv1.VolumeCapability_AccessMode{ 425 Mode: accessModeMapper(accessMode), 426 }, 427 }, 428 Secrets: secrets, 429 VolumeContext: volumeContext, 430 } 431 432 if fsType == fsTypeBlockName { 433 req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Block{ 434 Block: &csipbv1.VolumeCapability_BlockVolume{}, 435 } 436 } else { 437 mountVolume := &csipbv1.VolumeCapability_MountVolume{ 438 FsType: fsType, 439 MountFlags: mountOptions, 440 } 441 if fsGroup != nil { 442 mountVolume.VolumeMountGroup = strconv.FormatInt(*fsGroup, 10 /* base */) 443 } 444 req.VolumeCapability.AccessType = &csipbv1.VolumeCapability_Mount{ 445 Mount: mountVolume, 446 } 447 } 448 449 _, err = nodeClient.NodeStageVolume(ctx, req) 450 if err != nil && !isFinalError(err) { 451 return volumetypes.NewUncertainProgressError(err.Error()) 452 } 453 return err 454 } 455 456 func (c *csiDriverClient) NodeUnstageVolume(ctx context.Context, volID, stagingTargetPath string) error { 457 klog.V(4).InfoS(log("calling NodeUnstageVolume rpc"), "volID", volID, "stagingTargetPath", stagingTargetPath) 458 if volID == "" { 459 return errors.New("missing volume id") 460 } 461 if stagingTargetPath == "" { 462 return errors.New("missing staging target path") 463 } 464 if c.nodeV1ClientCreator == nil { 465 return errors.New("nodeV1ClientCreate is nil") 466 } 467 468 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 469 if err != nil { 470 return err 471 } 472 defer closer.Close() 473 474 req := &csipbv1.NodeUnstageVolumeRequest{ 475 VolumeId: volID, 476 StagingTargetPath: stagingTargetPath, 477 } 478 _, err = nodeClient.NodeUnstageVolume(ctx, req) 479 return err 480 } 481 482 func (c *csiDriverClient) NodeSupportsNodeExpand(ctx context.Context) (bool, error) { 483 return c.nodeSupportsCapability(ctx, csipbv1.NodeServiceCapability_RPC_EXPAND_VOLUME) 484 } 485 486 func (c *csiDriverClient) NodeSupportsStageUnstage(ctx context.Context) (bool, error) { 487 return c.nodeSupportsCapability(ctx, csipbv1.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME) 488 } 489 490 func (c *csiDriverClient) getNodeV1AccessModeMapper(ctx context.Context) (nodeV1AccessModeMapper, error) { 491 supported, err := c.NodeSupportsSingleNodeMultiWriterAccessMode(ctx) 492 if err != nil { 493 return nil, err 494 } 495 if supported { 496 return asSingleNodeMultiWriterCapableCSIAccessModeV1, nil 497 } 498 return asCSIAccessModeV1, nil 499 } 500 501 func asCSIAccessModeV1(am api.PersistentVolumeAccessMode) csipbv1.VolumeCapability_AccessMode_Mode { 502 switch am { 503 case api.ReadWriteOnce: 504 return csipbv1.VolumeCapability_AccessMode_SINGLE_NODE_WRITER 505 case api.ReadOnlyMany: 506 return csipbv1.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY 507 case api.ReadWriteMany: 508 return csipbv1.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER 509 // This mapping exists to enable CSI drivers that lack the 510 // SINGLE_NODE_MULTI_WRITER capability to work with the 511 // ReadWriteOncePod access mode. 512 case api.ReadWriteOncePod: 513 return csipbv1.VolumeCapability_AccessMode_SINGLE_NODE_WRITER 514 } 515 return csipbv1.VolumeCapability_AccessMode_UNKNOWN 516 } 517 518 func asSingleNodeMultiWriterCapableCSIAccessModeV1(am api.PersistentVolumeAccessMode) csipbv1.VolumeCapability_AccessMode_Mode { 519 switch am { 520 case api.ReadWriteOnce: 521 return csipbv1.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER 522 case api.ReadOnlyMany: 523 return csipbv1.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY 524 case api.ReadWriteMany: 525 return csipbv1.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER 526 case api.ReadWriteOncePod: 527 return csipbv1.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER 528 } 529 return csipbv1.VolumeCapability_AccessMode_UNKNOWN 530 } 531 532 func newGrpcConn(addr csiAddr, metricsManager *MetricsManager) (*grpc.ClientConn, error) { 533 network := "unix" 534 klog.V(4).InfoS(log("creating new gRPC connection"), "protocol", network, "endpoint", addr) 535 536 return grpc.Dial( 537 string(addr), 538 grpc.WithAuthority("localhost"), 539 grpc.WithTransportCredentials(insecure.NewCredentials()), 540 grpc.WithContextDialer(func(ctx context.Context, target string) (net.Conn, error) { 541 return (&net.Dialer{}).DialContext(ctx, network, target) 542 }), 543 grpc.WithChainUnaryInterceptor(metricsManager.RecordMetricsInterceptor), 544 ) 545 } 546 547 // CSI client getter with cache. 548 // This provides a method to initialize CSI client with driver name and caches 549 // it for later use. When CSI clients have not been discovered yet (e.g. 550 // on kubelet restart), client initialization will fail. Users of CSI client (e.g. 551 // mounter manager and block mapper) can use this to delay CSI client 552 // initialization until needed. 553 type csiClientGetter struct { 554 sync.RWMutex 555 csiClient csiClient 556 driverName csiDriverName 557 } 558 559 func (c *csiClientGetter) Get() (csiClient, error) { 560 c.RLock() 561 if c.csiClient != nil { 562 c.RUnlock() 563 return c.csiClient, nil 564 } 565 c.RUnlock() 566 c.Lock() 567 defer c.Unlock() 568 // Double-checking locking criterion. 569 if c.csiClient != nil { 570 return c.csiClient, nil 571 } 572 csi, err := newCsiDriverClient(c.driverName) 573 if err != nil { 574 return nil, err 575 } 576 c.csiClient = csi 577 return c.csiClient, nil 578 } 579 580 func (c *csiDriverClient) NodeSupportsVolumeStats(ctx context.Context) (bool, error) { 581 return c.nodeSupportsCapability(ctx, csipbv1.NodeServiceCapability_RPC_GET_VOLUME_STATS) 582 } 583 584 func (c *csiDriverClient) NodeSupportsSingleNodeMultiWriterAccessMode(ctx context.Context) (bool, error) { 585 return c.nodeSupportsCapability(ctx, csipbv1.NodeServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER) 586 } 587 588 func (c *csiDriverClient) NodeGetVolumeStats(ctx context.Context, volID string, targetPath string) (*volume.Metrics, error) { 589 klog.V(4).InfoS(log("calling NodeGetVolumeStats rpc"), "volID", volID, "targetPath", targetPath) 590 if volID == "" { 591 return nil, errors.New("missing volume id") 592 } 593 if targetPath == "" { 594 return nil, errors.New("missing target path") 595 } 596 if c.nodeV1ClientCreator == nil { 597 return nil, errors.New("nodeV1ClientCreate is nil") 598 } 599 600 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 601 if err != nil { 602 return nil, err 603 } 604 defer closer.Close() 605 606 req := &csipbv1.NodeGetVolumeStatsRequest{ 607 VolumeId: volID, 608 VolumePath: targetPath, 609 } 610 611 resp, err := nodeClient.NodeGetVolumeStats(ctx, req) 612 if err != nil { 613 return nil, err 614 } 615 usages := resp.GetUsage() 616 if usages == nil { 617 return nil, fmt.Errorf("failed to get usage from response. usage is nil") 618 } 619 metrics := &volume.Metrics{ 620 Used: resource.NewQuantity(int64(0), resource.BinarySI), 621 Capacity: resource.NewQuantity(int64(0), resource.BinarySI), 622 Available: resource.NewQuantity(int64(0), resource.BinarySI), 623 InodesUsed: resource.NewQuantity(int64(0), resource.BinarySI), 624 Inodes: resource.NewQuantity(int64(0), resource.BinarySI), 625 InodesFree: resource.NewQuantity(int64(0), resource.BinarySI), 626 } 627 628 if utilfeature.DefaultFeatureGate.Enabled(features.CSIVolumeHealth) { 629 isSupportNodeVolumeCondition, err := c.nodeSupportsVolumeCondition(ctx) 630 if err != nil { 631 return nil, err 632 } 633 634 if isSupportNodeVolumeCondition { 635 abnormal, message := resp.VolumeCondition.GetAbnormal(), resp.VolumeCondition.GetMessage() 636 metrics.Abnormal, metrics.Message = &abnormal, &message 637 } 638 } 639 640 for _, usage := range usages { 641 if usage == nil { 642 continue 643 } 644 unit := usage.GetUnit() 645 switch unit { 646 case csipbv1.VolumeUsage_BYTES: 647 metrics.Available = resource.NewQuantity(usage.GetAvailable(), resource.BinarySI) 648 metrics.Capacity = resource.NewQuantity(usage.GetTotal(), resource.BinarySI) 649 metrics.Used = resource.NewQuantity(usage.GetUsed(), resource.BinarySI) 650 case csipbv1.VolumeUsage_INODES: 651 metrics.InodesFree = resource.NewQuantity(usage.GetAvailable(), resource.BinarySI) 652 metrics.Inodes = resource.NewQuantity(usage.GetTotal(), resource.BinarySI) 653 metrics.InodesUsed = resource.NewQuantity(usage.GetUsed(), resource.BinarySI) 654 default: 655 klog.ErrorS(nil, "unknown unit in VolumeUsage", "unit", unit.String()) 656 } 657 658 } 659 return metrics, nil 660 } 661 662 func (c *csiDriverClient) nodeSupportsVolumeCondition(ctx context.Context) (bool, error) { 663 return c.nodeSupportsCapability(ctx, csipbv1.NodeServiceCapability_RPC_VOLUME_CONDITION) 664 } 665 666 func (c *csiDriverClient) NodeSupportsVolumeMountGroup(ctx context.Context) (bool, error) { 667 return c.nodeSupportsCapability(ctx, csipbv1.NodeServiceCapability_RPC_VOLUME_MOUNT_GROUP) 668 } 669 670 func (c *csiDriverClient) nodeSupportsCapability(ctx context.Context, capabilityType csipbv1.NodeServiceCapability_RPC_Type) (bool, error) { 671 klog.V(4).Info(log("calling NodeGetCapabilities rpc to determine if the node service has %s capability", capabilityType)) 672 capabilities, err := c.nodeGetCapabilities(ctx) 673 if err != nil { 674 return false, err 675 } 676 677 for _, capability := range capabilities { 678 if capability == nil || capability.GetRpc() == nil { 679 continue 680 } 681 if capability.GetRpc().GetType() == capabilityType { 682 return true, nil 683 } 684 } 685 return false, nil 686 } 687 688 func (c *csiDriverClient) nodeGetCapabilities(ctx context.Context) ([]*csipbv1.NodeServiceCapability, error) { 689 if c.nodeV1ClientCreator == nil { 690 return []*csipbv1.NodeServiceCapability{}, errors.New("nodeV1ClientCreate is nil") 691 } 692 693 nodeClient, closer, err := c.nodeV1ClientCreator(c.addr, c.metricsManager) 694 if err != nil { 695 return []*csipbv1.NodeServiceCapability{}, err 696 } 697 defer closer.Close() 698 699 req := &csipbv1.NodeGetCapabilitiesRequest{} 700 resp, err := nodeClient.NodeGetCapabilities(ctx, req) 701 if err != nil { 702 return []*csipbv1.NodeServiceCapability{}, err 703 } 704 return resp.GetCapabilities(), nil 705 } 706 707 func isFinalError(err error) bool { 708 // Sources: 709 // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md 710 // https://github.com/container-storage-interface/spec/blob/master/spec.md 711 st, ok := status.FromError(err) 712 if !ok { 713 // This is not gRPC error. The operation must have failed before gRPC 714 // method was called, otherwise we would get gRPC error. 715 // We don't know if any previous volume operation is in progress, be on the safe side. 716 return false 717 } 718 switch st.Code() { 719 case codes.Canceled, // gRPC: Client Application cancelled the request 720 codes.DeadlineExceeded, // gRPC: Timeout 721 codes.Unavailable, // gRPC: Server shutting down, TCP connection broken - previous volume operation may be still in progress. 722 codes.ResourceExhausted, // gRPC: Server temporarily out of resources - previous volume operation may be still in progress. 723 codes.Aborted: // CSI: Operation pending for volume 724 return false 725 } 726 // All other errors mean that operation either did not 727 // even start or failed. It is for sure not in progress. 728 return true 729 }