77 "os"
88 "path/filepath"
99 "regexp"
10+ "slices"
1011 "strings"
1112
1213 "golang.org/x/sys/unix"
@@ -36,10 +37,9 @@ func newDeviceStats(statfs *unix.Statfs_t) *DeviceStats {
3637 }
3738}
3839
39- // CountNonVirtioBlockDevices returns the number of PCIe Root ports who
40- // are currently occupied by anything else than an VIRTIO 1.0 Block Device
41- // returns zero when something went wrong
42- func CountNonVirtioBlockDevices () (int64 , error ) {
40+ // CountFreePCIeSlots returns the number of PCIe Root ports who
41+ // are currently not occupied by anything.
42+ func CountFreePCIeSlots () (int64 , error ) {
4343 const pciPath = "/sys/bus/pci/devices"
4444
4545 // Get all PCI devices
@@ -48,7 +48,7 @@ func CountNonVirtioBlockDevices() (int64, error) {
4848 return 0 , fmt .Errorf ("failed to read PCI bus: %w" , err )
4949 }
5050
51- pcieSlotsOccupiedByNonBlockDevice := 0
51+ freePCIeSlots := 0
5252
5353 for _ , dev := range devices {
5454 devPath := filepath .Join (pciPath , dev .Name ())
@@ -71,23 +71,48 @@ func CountNonVirtioBlockDevices() (int64, error) {
7171 if err2 != nil {
7272 klog .Errorf ("failed to read dir %s : %v" , devPath , err2 )
7373 }
74- for _ , file := range files {
75- // Ignore PCI bus directories such as pci001 pci002 and pci010
76- // Devices must follow <domain:bus:device.function> format
77- if pciAddressRegex .MatchString (file .Name ()) {
78- isNonBlockDevice := IsNonBlockDevice (devPath , file )
79- if isNonBlockDevice {
80- pcieSlotsOccupiedByNonBlockDevice ++
81- }
82- break
83- }
74+ hasDownStreamFolder := slices .ContainsFunc (files , func (s os.DirEntry ) bool {
75+ return pciAddressRegex .MatchString (s .Name ())
76+ })
77+ if ! hasDownStreamFolder {
78+ freePCIeSlots += 1
8479 }
8580 } else {
8681 klog .V (4 ).Infof ("skipping class %s: path: %s" , class , devPath )
8782 }
8883 }
8984
90- return int64 (pcieSlotsOccupiedByNonBlockDevice ), nil
85+ return int64 (freePCIeSlots ), nil
86+ }
87+
88+ // CountLocalCSIVolumes tries to count how many volumes are mounted for a given driverName.
89+ func CountLocalCSIVolumes (driverName string ) (int64 , error ) {
90+ const kubeletDir = "/var/lib/kubelet"
91+ volumeCount := 0
92+ // The path where Kubelet mounts global tracking directories for a specific CSI driver
93+ targetDir := filepath .Join (kubeletDir , "plugins" , "kubernetes.io" , "csi" , driverName )
94+
95+ if _ , err := os .Stat (targetDir ); os .IsNotExist (err ) {
96+ return 0 , nil
97+ } else if err != nil {
98+ return 0 , fmt .Errorf ("failed to check directory: %w" , err )
99+ }
100+
101+ volumes , err := os .ReadDir (targetDir )
102+ if err != nil {
103+ return 0 , fmt .Errorf ("failed to read dir %s: %w" , targetDir , err )
104+ }
105+ for _ , vol := range volumes {
106+ // Check if volume has a "globalmount" dir to determine if it's mounted correctly
107+ globalMountPath := filepath .Join (vol .Name (), "globalmount" )
108+ if _ , err := os .Stat (globalMountPath ); os .IsNotExist (err ) {
109+ continue
110+ }
111+
112+ volumeCount ++
113+ }
114+
115+ return int64 (volumeCount ), nil
91116}
92117
93118func IsNonBlockDevice (devPath string , file os.DirEntry ) bool {
0 commit comments