@@ -21,6 +21,7 @@ const (
2121 ResourceMemory ResourceType = "memory"
2222 ResourceDisk ResourceType = "disk"
2323 ResourceNetwork ResourceType = "network"
24+ ResourceDiskIO ResourceType = "disk_io"
2425)
2526
2627// SourceType identifies how a resource capacity was determined.
@@ -100,6 +101,7 @@ type InstanceAllocation struct {
100101 VolumeOverlayBytes int64 // Sum of volume overlay sizes
101102 NetworkDownloadBps int64 // Download rate limit (external→VM)
102103 NetworkUploadBps int64 // Upload rate limit (VM→external)
104+ DiskIOBps int64 // Disk I/O rate limit (bytes/sec)
103105 State string // Only count running/paused/created instances
104106 VolumeBytes int64 // Sum of attached volume base sizes (for per-instance reporting)
105107}
@@ -211,6 +213,10 @@ func (m *Manager) Initialize(ctx context.Context) error {
211213 }
212214 m .resources [ResourceNetwork ] = net
213215
216+ // Discover disk I/O (reuses existing DiskIOCapacity method)
217+ diskIO := NewDiskIOResource (m .DiskIOCapacity (), m .instanceLister )
218+ m .resources [ResourceDiskIO ] = diskIO
219+
214220 return nil
215221}
216222
@@ -227,6 +233,8 @@ func (m *Manager) GetOversubRatio(rt ResourceType) float64 {
227233 ratio = m .cfg .OversubDisk
228234 case ResourceNetwork :
229235 ratio = m .cfg .OversubNetwork
236+ case ResourceDiskIO :
237+ ratio = m .cfg .OversubDiskIO
230238 default :
231239 return 1.0
232240 }
@@ -413,6 +421,20 @@ func (m *Manager) ValidateAllocation(ctx context.Context, vcpus int, memoryBytes
413421 }
414422 }
415423
424+ // Check Disk I/O
425+ if diskIOBps > 0 {
426+ status , err := m .GetStatus (ctx , ResourceDiskIO )
427+ if err != nil {
428+ return fmt .Errorf ("check disk I/O capacity: %w" , err )
429+ }
430+ if diskIOBps > status .Available {
431+ return fmt .Errorf ("insufficient disk I/O: requested %s/s, but only %s/s available (currently allocated: %s/s, effective limit: %s/s with %.1fx oversubscription)" ,
432+ datasize .ByteSize (diskIOBps ).HR (), datasize .ByteSize (status .Available ).HR (),
433+ datasize .ByteSize (status .Allocated ).HR (), datasize .ByteSize (status .EffectiveLimit ).HR (),
434+ status .OversubRatio )
435+ }
436+ }
437+
416438 // Check GPU if needed
417439 if needsGPU {
418440 gpuStatus := GetGPUStatus ()
0 commit comments