• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubevirt / kubevirt / e19f0223-9daf-4e5e-8fbd-3faa1908a371

20 May 2026 09:57PM UTC coverage: 71.877% (-0.004%) from 71.881%
e19f0223-9daf-4e5e-8fbd-3faa1908a371

push

prow

web-flow
Merge pull request #17405 from iholder101/beta-features-on-by-default

[Meta-VEP 229]: Enable Beta features by default

24 of 27 new or added lines in 6 files covered. (88.89%)

57 existing lines in 6 files now uncovered.

78021 of 108548 relevant lines covered (71.88%)

595.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.29
/pkg/virt-controller/services/renderresources.go
1
package services
2

3
import (
4
        "fmt"
5
        "strconv"
6
        "strings"
7

8
        "k8s.io/client-go/tools/cache"
9

10
        "kubevirt.io/client-go/log"
11

12
        k8sv1 "k8s.io/api/core/v1"
13
        "k8s.io/apimachinery/pkg/api/resource"
14
        v1 "kubevirt.io/api/core/v1"
15

16
        netvmispec "kubevirt.io/kubevirt/pkg/network/vmispec"
17
        "kubevirt.io/kubevirt/pkg/util"
18
        "kubevirt.io/kubevirt/pkg/util/hardware"
19
        virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
20
)
21

22
type ResourceRendererOption func(renderer *ResourceRenderer)
23

24
type ResourceRenderer struct {
25
        vmLimits           k8sv1.ResourceList
26
        vmRequests         k8sv1.ResourceList
27
        calculatedLimits   k8sv1.ResourceList
28
        calculatedRequests k8sv1.ResourceList
29
        resourceClaims     []k8sv1.ResourceClaim
30
}
31

32
type resourcePredicate func(*v1.VirtualMachineInstance) bool
33

34
type VMIResourcePredicates struct {
35
        resourceRules []VMIResourceRule
36
        vmi           *v1.VirtualMachineInstance
37
}
38

39
type VMIResourceRule struct {
40
        predicate resourcePredicate
41
        option    ResourceRendererOption
42
}
43

44
func not(p resourcePredicate) resourcePredicate {
620✔
45
        return func(vmi *v1.VirtualMachineInstance) bool {
1,240✔
46
                return !p(vmi)
620✔
47
        }
620✔
48
}
49
func NewVMIResourceRule(p resourcePredicate, option ResourceRendererOption) VMIResourceRule {
4,340✔
50
        return VMIResourceRule{predicate: p, option: option}
4,340✔
51
}
4,340✔
52

53
func doesVMIRequireDedicatedCPU(vmi *v1.VirtualMachineInstance) bool {
620✔
54
        return vmi.IsCPUDedicated()
620✔
55
}
620✔
56

57
func NewResourceRenderer(vmLimits k8sv1.ResourceList, vmRequests k8sv1.ResourceList, options ...ResourceRendererOption) *ResourceRenderer {
343✔
58
        limits := map[k8sv1.ResourceName]resource.Quantity{}
343✔
59
        requests := map[k8sv1.ResourceName]resource.Quantity{}
343✔
60
        copyResources(vmLimits, limits)
343✔
61
        copyResources(vmRequests, requests)
343✔
62

343✔
63
        resourceRenderer := &ResourceRenderer{
343✔
64
                vmLimits:           limits,
343✔
65
                vmRequests:         requests,
343✔
66
                calculatedLimits:   map[k8sv1.ResourceName]resource.Quantity{},
343✔
67
                calculatedRequests: map[k8sv1.ResourceName]resource.Quantity{},
343✔
68
                resourceClaims:     []k8sv1.ResourceClaim{},
343✔
69
        }
343✔
70

343✔
71
        for _, opt := range options {
1,785✔
72
                opt(resourceRenderer)
1,442✔
73
        }
1,442✔
74
        return resourceRenderer
343✔
75
}
76

77
func (rr *ResourceRenderer) Limits() k8sv1.ResourceList {
363✔
78
        podLimits := map[k8sv1.ResourceName]resource.Quantity{}
363✔
79
        copyResources(rr.calculatedLimits, podLimits)
363✔
80
        copyResources(rr.vmLimits, podLimits)
363✔
81
        return podLimits
363✔
82
}
363✔
83

84
func (rr *ResourceRenderer) Requests() k8sv1.ResourceList {
359✔
85
        podRequests := map[k8sv1.ResourceName]resource.Quantity{}
359✔
86
        copyResources(rr.calculatedRequests, podRequests)
359✔
87
        copyResources(rr.vmRequests, podRequests)
359✔
88
        return podRequests
359✔
89
}
359✔
90

91
func (rr *ResourceRenderer) Claims() []k8sv1.ResourceClaim {
337✔
92
        return rr.resourceClaims
337✔
93
}
337✔
94

95
func (rr *ResourceRenderer) ResourceRequirements() k8sv1.ResourceRequirements {
330✔
96
        return k8sv1.ResourceRequirements{
330✔
97
                Limits:   rr.Limits(),
330✔
98
                Requests: rr.Requests(),
330✔
99
                Claims:   rr.Claims(),
330✔
100
        }
330✔
101
}
330✔
102

103
func WithEphemeralStorageRequest() ResourceRendererOption {
311✔
104
        return func(renderer *ResourceRenderer) {
622✔
105
                // Add ephemeral storage request to container to be used by Kubevirt. This amount of ephemeral storage
311✔
106
                // should be added to the user's request.
311✔
107
                ephemeralStorageOverhead := resource.MustParse(ephemeralStorageOverheadSize)
311✔
108
                ephemeralStorageRequested := renderer.vmRequests[k8sv1.ResourceEphemeralStorage]
311✔
109
                ephemeralStorageRequested.Add(ephemeralStorageOverhead)
311✔
110
                renderer.vmRequests[k8sv1.ResourceEphemeralStorage] = ephemeralStorageRequested
311✔
111

311✔
112
                if ephemeralStorageLimit, ephemeralStorageLimitDefined := renderer.vmLimits[k8sv1.ResourceEphemeralStorage]; ephemeralStorageLimitDefined {
313✔
113
                        ephemeralStorageLimit.Add(ephemeralStorageOverhead)
2✔
114
                        renderer.vmLimits[k8sv1.ResourceEphemeralStorage] = ephemeralStorageLimit
2✔
115
                }
2✔
116
        }
117
}
118

119
// Helper function to extract IO thread CPU count from VMI
120
func getIOThreadsCount(vmi *v1.VirtualMachineInstance) int64 {
320✔
121
        if vmi == nil || vmi.Spec.Domain.IOThreads == nil ||
320✔
122
                vmi.Spec.Domain.IOThreads.SupplementalPoolThreadCount == nil {
634✔
123
                return 0
314✔
124
        }
314✔
125
        return int64(*vmi.Spec.Domain.IOThreads.SupplementalPoolThreadCount)
6✔
126
}
127

128
func WithoutDedicatedCPU(vmi *v1.VirtualMachineInstance, cpuAllocationRatio int, withCPULimits bool) ResourceRendererOption {
314✔
129
        return func(renderer *ResourceRenderer) {
621✔
130
                cpu := vmi.Spec.Domain.CPU
307✔
131
                vcpus := calcVCPUs(cpu)
307✔
132
                ioThreadCPUs := getIOThreadsCount(vmi) // Get IO thread count
307✔
133
                totalCPUs := vcpus + ioThreadCPUs      // Include IO threads
307✔
134
                if totalCPUs != 0 && cpuAllocationRatio > 0 {
602✔
135
                        val := float64(totalCPUs) / float64(cpuAllocationRatio)
295✔
136
                        vcpusStr := fmt.Sprintf("%g", val)
295✔
137
                        if val < 1 {
587✔
138
                                val *= 1000
292✔
139
                                vcpusStr = fmt.Sprintf("%gm", val)
292✔
140
                        }
292✔
141
                        renderer.calculatedRequests[k8sv1.ResourceCPU] = resource.MustParse(vcpusStr)
295✔
142
                        if withCPULimits {
299✔
143
                                renderer.calculatedLimits[k8sv1.ResourceCPU] = resource.MustParse(strconv.FormatInt(totalCPUs, 10))
4✔
144
                        }
4✔
145
                }
146
        }
147
}
148

149
func WithGPUsDevicePlugins(gpus []v1.GPU) ResourceRendererOption {
313✔
150
        return func(r *ResourceRenderer) {
317✔
151
                res := r.ResourceRequirements()
4✔
152
                for _, g := range gpus {
8✔
153
                        if g.DeviceName != "" && g.ClaimRequest == nil {
7✔
154
                                requestResource(&res, g.DeviceName)
3✔
155
                        }
3✔
156
                }
157
                copyResources(res.Limits, r.calculatedLimits)
4✔
158
                copyResources(res.Requests, r.calculatedRequests)
4✔
159
        }
160
}
161

162
func WithGPUsDRA(gpus []v1.GPU) ResourceRendererOption {
315✔
163
        return func(r *ResourceRenderer) {
320✔
164
                for _, g := range gpus {
12✔
165
                        if g.DeviceName == "" && g.ClaimRequest != nil {
13✔
166
                                claim := k8sv1.ResourceClaim{
6✔
167
                                        Name:    g.ClaimRequest.ClaimName,
6✔
168
                                        Request: g.ClaimRequest.RequestName,
6✔
169
                                }
6✔
170
                                r.resourceClaims = append(r.resourceClaims, claim)
6✔
171
                        }
6✔
172
                }
173
        }
174
}
175

176
// WithHostDevicesDevicePlugins adds resource requests/limits only for HostDevices managed by device plugins.
177
func WithHostDevicesDevicePlugins(hostDevices []v1.HostDevice) ResourceRendererOption {
313✔
178
        return func(r *ResourceRenderer) {
317✔
179
                resources := r.ResourceRequirements()
4✔
180
                for _, hd := range hostDevices {
8✔
181
                        if hd.DeviceName != "" && hd.ClaimRequest == nil {
7✔
182
                                requestResource(&resources, hd.DeviceName)
3✔
183
                        }
3✔
184
                }
185
                copyResources(resources.Limits, r.calculatedLimits)
4✔
186
                copyResources(resources.Requests, r.calculatedRequests)
4✔
187
        }
188
}
189

190
// WithHostDevicesDRA adds ResourceClaims for HostDevices provisioned via DRA.
191
func WithHostDevicesDRA(hostDevices []v1.HostDevice) ResourceRendererOption {
314✔
192
        return func(r *ResourceRenderer) {
318✔
193
                for _, hd := range hostDevices {
10✔
194
                        if hd.DeviceName == "" && hd.ClaimRequest != nil {
11✔
195
                                claim := k8sv1.ResourceClaim{
5✔
196
                                        Name:    hd.ClaimRequest.ClaimName,
5✔
197
                                        Request: hd.ClaimRequest.RequestName,
5✔
198
                                }
5✔
199
                                r.resourceClaims = append(r.resourceClaims, claim)
5✔
200
                        }
5✔
201
                }
202
        }
203
}
204

205
func WithHugePages(vmMemory *v1.Memory, memoryOverhead resource.Quantity) ResourceRendererOption {
310✔
206
        return func(renderer *ResourceRenderer) {
332✔
207
                hugepageType := k8sv1.ResourceName(k8sv1.ResourceHugePagesPrefix + vmMemory.Hugepages.PageSize)
22✔
208
                hugepagesMemReq := renderer.vmRequests.Memory()
22✔
209

22✔
210
                // If requested, use the guest memory to allocate hugepages
22✔
211
                if vmMemory != nil && vmMemory.Guest != nil {
24✔
212
                        requests := hugepagesMemReq.Value()
2✔
213
                        guest := vmMemory.Guest.Value()
2✔
214
                        if requests > guest {
4✔
215
                                hugepagesMemReq = vmMemory.Guest
2✔
216
                        }
2✔
217
                }
218
                renderer.calculatedRequests[hugepageType] = *hugepagesMemReq
22✔
219
                renderer.calculatedLimits[hugepageType] = *hugepagesMemReq
22✔
220

22✔
221
                reqMemDiff := resource.NewScaledQuantity(0, resource.Kilo)
22✔
222
                limMemDiff := resource.NewScaledQuantity(0, resource.Kilo)
22✔
223
                // In case the guest memory and the requested memory are different, add the difference
22✔
224
                // to the overhead
22✔
225
                if vmMemory != nil && vmMemory.Guest != nil {
24✔
226
                        requests := renderer.vmRequests.Memory().Value()
2✔
227
                        limits := renderer.vmLimits.Memory().Value()
2✔
228
                        guest := vmMemory.Guest.Value()
2✔
229
                        if requests > guest {
4✔
230
                                reqMemDiff.Add(*renderer.vmRequests.Memory())
2✔
231
                                reqMemDiff.Sub(*vmMemory.Guest)
2✔
232
                        }
2✔
233
                        if limits > guest {
4✔
234
                                limMemDiff.Add(*renderer.vmLimits.Memory())
2✔
235
                                limMemDiff.Sub(*vmMemory.Guest)
2✔
236
                        }
2✔
237
                }
238
                // Set requested memory equals to overhead memory
239
                reqMemDiff.Add(memoryOverhead)
22✔
240
                renderer.vmRequests[k8sv1.ResourceMemory] = *reqMemDiff
22✔
241
                if _, ok := renderer.vmLimits[k8sv1.ResourceMemory]; ok {
36✔
242
                        limMemDiff.Add(memoryOverhead)
14✔
243
                        renderer.vmLimits[k8sv1.ResourceMemory] = *limMemDiff
14✔
244
                }
14✔
245
        }
246
}
247

248
func WithMemoryRequests(vmiSpecMemory *v1.Memory, overcommit int) ResourceRendererOption {
310✔
249
        return func(renderer *ResourceRenderer) {
463✔
250
                limit, hasLimit := renderer.vmLimits[k8sv1.ResourceMemory]
153✔
251
                request, hasRequest := renderer.vmRequests[k8sv1.ResourceMemory]
153✔
252
                if hasLimit && !limit.IsZero() && (!hasRequest || request.IsZero()) {
161✔
253
                        renderer.vmRequests[k8sv1.ResourceMemory] = limit
8✔
254
                }
8✔
255

256
                if _, exists := renderer.vmRequests[k8sv1.ResourceMemory]; exists {
161✔
257
                        return
8✔
258
                }
8✔
259

260
                var memory *resource.Quantity
145✔
261
                if vmiSpecMemory != nil && vmiSpecMemory.Guest != nil {
148✔
262
                        memory = vmiSpecMemory.Guest
3✔
263
                } else if vmiSpecMemory != nil && vmiSpecMemory.Hugepages != nil {
149✔
264
                        if hugepagesSize, err := resource.ParseQuantity(vmiSpecMemory.Hugepages.PageSize); err == nil {
8✔
265
                                memory = &hugepagesSize
4✔
266
                        }
4✔
267
                }
268

269
                if memory != nil && memory.Value() > 0 {
152✔
270
                        if overcommit == 100 {
11✔
271
                                renderer.vmRequests[k8sv1.ResourceMemory] = *memory
4✔
272
                        } else {
7✔
273
                                value := (memory.Value() * int64(100)) / int64(overcommit)
3✔
274
                                renderer.vmRequests[k8sv1.ResourceMemory] = *resource.NewQuantity(value, memory.Format)
3✔
275
                        }
3✔
276
                }
277
        }
278
}
279

280
func WithMemoryOverhead(guestResourceSpec v1.ResourceRequirements, memoryOverhead resource.Quantity) ResourceRendererOption {
312✔
281
        return func(renderer *ResourceRenderer) {
602✔
282
                memoryRequest := renderer.vmRequests[k8sv1.ResourceMemory]
290✔
283
                if !guestResourceSpec.OvercommitGuestOverhead {
577✔
284
                        memoryRequest.Add(memoryOverhead)
287✔
285
                }
287✔
286
                renderer.vmRequests[k8sv1.ResourceMemory] = memoryRequest
290✔
287

290✔
288
                if memoryLimit, ok := renderer.vmLimits[k8sv1.ResourceMemory]; ok {
307✔
289
                        memoryLimit.Add(memoryOverhead)
17✔
290
                        renderer.vmLimits[k8sv1.ResourceMemory] = memoryLimit
17✔
291
                }
17✔
292
        }
293
}
294

295
func WithAutoMemoryLimits(namespace string, namespaceStore cache.Store) ResourceRendererOption {
313✔
296
        return func(renderer *ResourceRenderer) {
320✔
297
                requestRatio := getMemoryLimitsRatio(namespace, namespaceStore)
7✔
298
                memoryRequest := renderer.vmRequests[k8sv1.ResourceMemory]
7✔
299
                value := int64(float64(memoryRequest.Value()) * requestRatio)
7✔
300
                renderer.calculatedLimits[k8sv1.ResourceMemory] = *resource.NewQuantity(value, memoryRequest.Format)
7✔
301
        }
7✔
302
}
303

304
func WithCPUPinning(vmi *v1.VirtualMachineInstance, annotations map[string]string, additionalCPUs uint32) ResourceRendererOption {
316✔
305
        return func(renderer *ResourceRenderer) {
329✔
306
                cpu := vmi.Spec.Domain.CPU
13✔
307
                vcpus := hardware.GetNumberOfVCPUs(cpu)
13✔
308
                ioThreadCPUs := getIOThreadsCount(vmi)
13✔
309
                if vcpus != 0 {
23✔
310
                        totalCPUs := vcpus + ioThreadCPUs
10✔
311
                        renderer.vmLimits[k8sv1.ResourceCPU] = *resource.NewQuantity(totalCPUs, resource.BinarySI)
10✔
312
                        renderer.vmRequests[k8sv1.ResourceCPU] = *resource.NewQuantity(totalCPUs, resource.BinarySI) // Ensure requests match limits for dedicated CPUs
10✔
313
                } else {
13✔
314
                        ioThreadsCount := resource.NewQuantity(ioThreadCPUs, resource.BinarySI)
3✔
315
                        if cpuLimit, ok := renderer.vmLimits[k8sv1.ResourceCPU]; ok {
5✔
316
                                cpuLimit.Add(*ioThreadsCount)
2✔
317
                                renderer.vmLimits[k8sv1.ResourceCPU] = cpuLimit
2✔
318
                        }
2✔
319
                        if cpuRequest, ok := renderer.vmRequests[k8sv1.ResourceCPU]; ok {
5✔
320
                                cpuRequest.Add(*ioThreadsCount)
2✔
321
                                renderer.vmRequests[k8sv1.ResourceCPU] = cpuRequest
2✔
322
                        }
2✔
323
                }
324

325
                if cpu.IsolateEmulatorThread {
24✔
326
                        emulatorThreadCPUs := resource.NewQuantity(1, resource.BinarySI)
11✔
327
                        limits := renderer.vmLimits[k8sv1.ResourceCPU]
11✔
328
                        _, emulatorThreadCompleteToEvenParityAnnotationExists := annotations[v1.EmulatorThreadCompleteToEvenParity]
11✔
329
                        if emulatorThreadCompleteToEvenParityAnnotationExists &&
11✔
330
                                (limits.Value()+int64(additionalCPUs))%2 == 0 {
13✔
331
                                emulatorThreadCPUs = resource.NewQuantity(2, resource.BinarySI)
2✔
332
                        }
2✔
333
                        limits.Add(*emulatorThreadCPUs)
11✔
334
                        renderer.vmLimits[k8sv1.ResourceCPU] = limits
11✔
335
                        if cpuRequest, ok := renderer.vmRequests[k8sv1.ResourceCPU]; ok {
22✔
336
                                cpuRequest.Add(*emulatorThreadCPUs)
11✔
337
                                renderer.vmRequests[k8sv1.ResourceCPU] = cpuRequest
11✔
338
                        }
11✔
339
                }
340

341
                // Align memory limits with requests for consistency
342
                if memRequest, ok := renderer.vmRequests[k8sv1.ResourceMemory]; ok {
15✔
343
                        renderer.vmLimits[k8sv1.ResourceMemory] = memRequest
2✔
344
                }
2✔
345
        }
346
}
347

348
func WithNetworkResources(networkToResourceMap map[string]string) ResourceRendererOption {
312✔
349
        return func(renderer *ResourceRenderer) {
314✔
350
                resources := renderer.ResourceRequirements()
2✔
351
                for _, resourceName := range networkToResourceMap {
4✔
352
                        if resourceName != "" {
4✔
353
                                requestResource(&resources, resourceName)
2✔
354
                        }
2✔
355
                }
356
                copyResources(resources.Limits, renderer.calculatedLimits)
2✔
357
                copyResources(resources.Requests, renderer.calculatedRequests)
2✔
358
        }
359
}
360

361
func WithSEV() ResourceRendererOption {
311✔
362
        return func(renderer *ResourceRenderer) {
318✔
363
                resources := renderer.ResourceRequirements()
7✔
364
                requestResource(&resources, SevDevice)
7✔
365
                copyResources(resources.Limits, renderer.calculatedLimits)
7✔
366
                copyResources(resources.Requests, renderer.calculatedRequests)
7✔
367
        }
7✔
368
}
369

370
func WithTDX() ResourceRendererOption {
311✔
371
        return func(renderer *ResourceRenderer) {
314✔
372
                resources := renderer.ResourceRequirements()
3✔
373
                requestResource(&resources, TdxDevice)
3✔
374
                copyResources(resources.Limits, renderer.calculatedLimits)
3✔
375
                copyResources(resources.Requests, renderer.calculatedRequests)
3✔
376
        }
3✔
377
}
378

379
func WithPersistentReservation() ResourceRendererOption {
310✔
380
        return func(renderer *ResourceRenderer) {
310✔
381
                resources := renderer.ResourceRequirements()
×
382
                requestResource(&resources, PrDevice)
×
383
                copyResources(resources.Limits, renderer.calculatedLimits)
×
384
                copyResources(resources.Requests, renderer.calculatedRequests)
×
385
        }
×
386
}
387

388
func copyResources(srcResources, dstResources k8sv1.ResourceList) {
2,480✔
389
        for key, value := range srcResources {
5,262✔
390
                dstResources[key] = value
2,782✔
391
        }
2,782✔
392
}
393

394
// Request a resource by name. This function bumps the number of resources,
395
// both its limits and requests attributes.
396
//
397
// If we were operating with a regular resource (CPU, memory, network
398
// bandwidth), we would need to take care of QoS. For example,
399
// https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/#create-a-pod-that-gets-assigned-a-qos-class-of-guaranteed
400
// explains that when Limits are set but Requests are not then scheduler
401
// assumes that Requests are the same as Limits for a particular resource.
402
//
403
// But this function is not called for this standard resources but for
404
// resources managed by device plugins. The device plugin design document says
405
// the following on the matter:
406
// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/resource-management/device-plugin.md#end-user-story
407
//
408
// ```
409
// Devices can be selected using the same process as for OIRs in the pod spec.
410
// Devices have no impact on QOS. However, for the alpha, we expect the request
411
// to have limits == requests.
412
// ```
413
//
414
// Which suggests that, for resources managed by device plugins, 1) limits
415
// should be equal to requests; and 2) QoS rules do not apVFIO//
416
// Hence we don't copy Limits value to Requests if the latter is missing.
417
func requestResource(resources *k8sv1.ResourceRequirements, resourceName string) {
23✔
418
        name := k8sv1.ResourceName(resourceName)
23✔
419
        bumpResources(resources.Limits, name)
23✔
420
        bumpResources(resources.Requests, name)
23✔
421
}
23✔
422

423
func bumpResources(resources k8sv1.ResourceList, name k8sv1.ResourceName) {
46✔
424
        unitQuantity := *resource.NewQuantity(1, resource.DecimalSI)
46✔
425

46✔
426
        val, ok := resources[name]
46✔
427
        if ok {
54✔
428
                val.Add(unitQuantity)
8✔
429
                resources[name] = val
8✔
430
        } else {
46✔
431
                resources[name] = unitQuantity
38✔
432
        }
38✔
433
}
434

435
func calcVCPUs(cpu *v1.CPU) int64 {
307✔
436
        if cpu != nil {
343✔
437
                return hardware.GetNumberOfVCPUs(cpu)
36✔
438
        }
36✔
439
        return int64(1)
271✔
440
}
441

442
func getRequiredResources(vmi *v1.VirtualMachineInstance, hypervisorResource k8sv1.ResourceName, allowEmulation bool) k8sv1.ResourceList {
310✔
443
        res := k8sv1.ResourceList{}
310✔
444
        if netvmispec.RequiresTunDevice(vmi) {
619✔
445
                res[TunDevice] = resource.MustParse("1")
309✔
446
        }
309✔
447
        if netvmispec.RequiresVirtioNetDevice(vmi, allowEmulation) {
323✔
448
                // Note that about network interface, allowEmulation does not make
13✔
449
                // any difference on eventual Domain xml, but uniformly making
13✔
450
                // /dev/vhost-net unavailable and libvirt implicitly fallback
13✔
451
                // to use QEMU userland NIC emulation.
13✔
452
                res[VhostNetDevice] = resource.MustParse("1")
13✔
453
        }
13✔
454
        if !allowEmulation {
619✔
455
                res[hypervisorResource] = resource.MustParse("1")
309✔
456
        }
309✔
457
        if util.IsAutoAttachVSOCK(vmi) {
311✔
458
                res[VhostVsockDevice] = resource.MustParse("1")
1✔
459
        }
1✔
460
        return res
310✔
461
}
462

463
func WithVirtualizationResources(virtResources k8sv1.ResourceList) ResourceRendererOption {
310✔
464
        return func(renderer *ResourceRenderer) {
620✔
465
                copyResources(virtResources, renderer.vmLimits)
310✔
466
        }
310✔
467
}
468

469
func validatePermittedHostDevices(spec *v1.VirtualMachineInstanceSpec, config *virtconfig.ClusterConfig) error {
315✔
470
        errors := make([]string, 0)
315✔
471

315✔
472
        if hostDevs := config.GetPermittedHostDevices(); hostDevs != nil {
320✔
473
                // build a map of all permitted host devices
5✔
474
                supportedHostDevicesMap := make(map[string]bool)
5✔
475
                for _, dev := range hostDevs.PciHostDevices {
10✔
476
                        supportedHostDevicesMap[dev.ResourceName] = true
5✔
477
                }
5✔
478
                for _, dev := range hostDevs.MediatedDevices {
5✔
479
                        supportedHostDevicesMap[dev.ResourceName] = true
×
480
                }
×
481
                for _, dev := range hostDevs.USB {
5✔
482
                        supportedHostDevicesMap[dev.ResourceName] = true
×
483
                }
×
484
                //TODO @alayp: add proper validation for DRA GPUs in beta
485
                if !config.GPUsWithDRAGateEnabled() {
10✔
486
                        for _, hostDev := range spec.Domain.Devices.GPUs {
5✔
487
                                if _, exist := supportedHostDevicesMap[hostDev.DeviceName]; !exist {
×
488
                                        errors = append(errors, fmt.Sprintf("GPU %s is not permitted in permittedHostDevices configuration", hostDev.DeviceName))
×
489
                                }
×
490
                        }
491
                }
492
                for _, hostDev := range spec.Domain.Devices.HostDevices {
10✔
493
                        // skip host devices backed by DRA claims, since they are validated via DRA instead of the permittedHostDevices config
5✔
494
                        if config.HostDevicesWithDRAEnabled() && hostDev.ClaimRequest != nil {
6✔
495
                                continue
1✔
496
                        }
497
                        if _, exist := supportedHostDevicesMap[hostDev.DeviceName]; !exist {
6✔
498
                                errors = append(errors, fmt.Sprintf("HostDevice %s is not permitted in permittedHostDevices configuration", hostDev.DeviceName))
2✔
499
                        }
2✔
500
                }
501
        }
502

503
        if len(errors) != 0 {
317✔
504
                return fmt.Errorf("%s", strings.Join(errors, " "))
2✔
505
        }
2✔
506

507
        return nil
313✔
508
}
509

510
func sidecarResources(vmi *v1.VirtualMachineInstance, config *virtconfig.ClusterConfig) k8sv1.ResourceRequirements {
21✔
511
        resources := k8sv1.ResourceRequirements{
21✔
512
                Requests: k8sv1.ResourceList{},
21✔
513
                Limits:   k8sv1.ResourceList{},
21✔
514
        }
21✔
515
        if reqCpu := config.GetSupportContainerRequest(v1.SideCar, k8sv1.ResourceCPU); reqCpu != nil {
23✔
516
                resources.Requests[k8sv1.ResourceCPU] = *reqCpu
2✔
517
        }
2✔
518
        if reqMem := config.GetSupportContainerRequest(v1.SideCar, k8sv1.ResourceMemory); reqMem != nil {
23✔
519
                resources.Requests[k8sv1.ResourceMemory] = *reqMem
2✔
520
        }
2✔
521

522
        // add default cpu and memory limits to enable cpu pinning if requested
523
        // TODO(vladikr): make the hookSidecar express resources
524
        if vmi.IsCPUDedicated() || vmi.WantsToHaveQOSGuaranteed() {
25✔
525
                resources.Limits[k8sv1.ResourceCPU] = resource.MustParse("200m")
4✔
526
                if limCpu := config.GetSupportContainerLimit(v1.SideCar, k8sv1.ResourceCPU); limCpu != nil {
5✔
527
                        resources.Limits[k8sv1.ResourceCPU] = *limCpu
1✔
528
                }
1✔
529
                resources.Limits[k8sv1.ResourceMemory] = resource.MustParse("64M")
4✔
530
                if limMem := config.GetSupportContainerLimit(v1.SideCar, k8sv1.ResourceMemory); limMem != nil {
5✔
531
                        resources.Limits[k8sv1.ResourceMemory] = *limMem
1✔
532
                }
1✔
533
                resources.Requests[k8sv1.ResourceCPU] = resources.Limits[k8sv1.ResourceCPU]
4✔
534
                resources.Requests[k8sv1.ResourceMemory] = resources.Limits[k8sv1.ResourceMemory]
4✔
535
        } else {
17✔
536
                if limCpu := config.GetSupportContainerLimit(v1.SideCar, k8sv1.ResourceCPU); limCpu != nil {
18✔
537
                        resources.Limits[k8sv1.ResourceCPU] = *limCpu
1✔
538
                }
1✔
539
                if limMem := config.GetSupportContainerLimit(v1.SideCar, k8sv1.ResourceMemory); limMem != nil {
18✔
540
                        resources.Limits[k8sv1.ResourceMemory] = *limMem
1✔
541
                }
1✔
542
        }
543
        return resources
21✔
544
}
545

546
func initContainerResourceRequirementsForVMI(vmi *v1.VirtualMachineInstance, containerType v1.SupportContainerType, config *virtconfig.ClusterConfig) k8sv1.ResourceRequirements {
5✔
547
        if vmi.IsCPUDedicated() || vmi.WantsToHaveQOSGuaranteed() {
5✔
UNCOV
548
                return k8sv1.ResourceRequirements{
×
UNCOV
549
                        Limits:   initContainerDedicatedCPURequiredResources(containerType, config),
×
UNCOV
550
                        Requests: initContainerDedicatedCPURequiredResources(containerType, config),
×
UNCOV
551
                }
×
552
        } else {
5✔
553
                return k8sv1.ResourceRequirements{
5✔
554
                        Limits:   initContainerMinimalLimits(containerType, config),
5✔
555
                        Requests: initContainerMinimalRequests(containerType, config),
5✔
556
                }
5✔
557
        }
5✔
558
}
559

UNCOV
560
func initContainerDedicatedCPURequiredResources(containerType v1.SupportContainerType, config *virtconfig.ClusterConfig) k8sv1.ResourceList {
×
UNCOV
561
        res := k8sv1.ResourceList{
×
UNCOV
562
                k8sv1.ResourceCPU:    resource.MustParse("10m"),
×
UNCOV
563
                k8sv1.ResourceMemory: resource.MustParse("40M"),
×
UNCOV
564
        }
×
UNCOV
565
        if cpuLim := config.GetSupportContainerLimit(containerType, k8sv1.ResourceCPU); cpuLim != nil {
×
566
                res[k8sv1.ResourceCPU] = *cpuLim
×
567
        }
×
UNCOV
568
        if memLim := config.GetSupportContainerLimit(containerType, k8sv1.ResourceMemory); memLim != nil {
×
569
                res[k8sv1.ResourceMemory] = *memLim
×
570
        }
×
UNCOV
571
        return res
×
572
}
573

574
func initContainerMinimalLimits(containerType v1.SupportContainerType, config *virtconfig.ClusterConfig) k8sv1.ResourceList {
5✔
575
        res := k8sv1.ResourceList{
5✔
576
                k8sv1.ResourceCPU:    resource.MustParse("100m"),
5✔
577
                k8sv1.ResourceMemory: resource.MustParse("40M"),
5✔
578
        }
5✔
579
        if cpuLim := config.GetSupportContainerLimit(containerType, k8sv1.ResourceCPU); cpuLim != nil {
5✔
580
                res[k8sv1.ResourceCPU] = *cpuLim
×
581
        }
×
582
        if memLim := config.GetSupportContainerLimit(containerType, k8sv1.ResourceMemory); memLim != nil {
5✔
583
                res[k8sv1.ResourceMemory] = *memLim
×
584
        }
×
585
        return res
5✔
586
}
587

588
func initContainerMinimalRequests(containerType v1.SupportContainerType, config *virtconfig.ClusterConfig) k8sv1.ResourceList {
5✔
589
        res := k8sv1.ResourceList{
5✔
590
                k8sv1.ResourceCPU:    resource.MustParse("10m"),
5✔
591
                k8sv1.ResourceMemory: resource.MustParse("1M"),
5✔
592
        }
5✔
593
        if cpuReq := config.GetSupportContainerRequest(containerType, k8sv1.ResourceCPU); cpuReq != nil {
5✔
594
                res[k8sv1.ResourceCPU] = *cpuReq
×
595
        }
×
596
        if memReq := config.GetSupportContainerRequest(containerType, k8sv1.ResourceMemory); memReq != nil {
5✔
597
                res[k8sv1.ResourceMemory] = *memReq
×
598
        }
×
599
        return res
5✔
600
}
601

602
func hotplugContainerResourceRequirementsForVMI(config *virtconfig.ClusterConfig) k8sv1.ResourceRequirements {
21✔
603
        return k8sv1.ResourceRequirements{
21✔
604
                Limits:   hotplugContainerLimits(config),
21✔
605
                Requests: hotplugContainerRequests(config),
21✔
606
        }
21✔
607
}
21✔
608

609
func hotplugContainerLimits(config *virtconfig.ClusterConfig) k8sv1.ResourceList {
21✔
610
        cpuQuantity := resource.MustParse("100m")
21✔
611
        if cpu := config.GetSupportContainerLimit(v1.HotplugAttachment, k8sv1.ResourceCPU); cpu != nil {
26✔
612
                cpuQuantity = *cpu
5✔
613
        }
5✔
614
        memQuantity := resource.MustParse("80M")
21✔
615
        if mem := config.GetSupportContainerLimit(v1.HotplugAttachment, k8sv1.ResourceMemory); mem != nil {
26✔
616
                memQuantity = *mem
5✔
617
        }
5✔
618
        return k8sv1.ResourceList{
21✔
619
                k8sv1.ResourceCPU:    cpuQuantity,
21✔
620
                k8sv1.ResourceMemory: memQuantity,
21✔
621
        }
21✔
622
}
623

624
func hotplugContainerRequests(config *virtconfig.ClusterConfig) k8sv1.ResourceList {
21✔
625
        cpuQuantity := resource.MustParse("10m")
21✔
626
        if cpu := config.GetSupportContainerRequest(v1.HotplugAttachment, k8sv1.ResourceCPU); cpu != nil {
26✔
627
                cpuQuantity = *cpu
5✔
628
        }
5✔
629
        memQuantity := resource.MustParse("2M")
21✔
630
        if mem := config.GetSupportContainerRequest(v1.HotplugAttachment, k8sv1.ResourceMemory); mem != nil {
26✔
631
                memQuantity = *mem
5✔
632
        }
5✔
633
        return k8sv1.ResourceList{
21✔
634
                k8sv1.ResourceCPU:    cpuQuantity,
21✔
635
                k8sv1.ResourceMemory: memQuantity,
21✔
636
        }
21✔
637
}
638

639
func hotplugPodTolerations() []k8sv1.Toleration {
9✔
640
        return []k8sv1.Toleration{
9✔
641
                {
9✔
642
                        Key:      k8sv1.TaintNodeUnschedulable,
9✔
643
                        Operator: k8sv1.TolerationOpExists,
9✔
644
                        Effect:   k8sv1.TaintEffectNoSchedule,
9✔
645
                },
9✔
646
                {
9✔
647
                        Key:      k8sv1.TaintNodeNetworkUnavailable,
9✔
648
                        Operator: k8sv1.TolerationOpExists,
9✔
649
                        Effect:   k8sv1.TaintEffectNoSchedule,
9✔
650
                },
9✔
651
                {
9✔
652
                        Key:      k8sv1.TaintNodeDiskPressure,
9✔
653
                        Operator: k8sv1.TolerationOpExists,
9✔
654
                        Effect:   k8sv1.TaintEffectNoSchedule,
9✔
655
                },
9✔
656
                {
9✔
657
                        Key:      k8sv1.TaintNodeMemoryPressure,
9✔
658
                        Operator: k8sv1.TolerationOpExists,
9✔
659
                        Effect:   k8sv1.TaintEffectNoSchedule,
9✔
660
                },
9✔
661
                {
9✔
662
                        Key:      k8sv1.TaintNodePIDPressure,
9✔
663
                        Operator: k8sv1.TolerationOpExists,
9✔
664
                        Effect:   k8sv1.TaintEffectNoSchedule,
9✔
665
                },
9✔
666
        }
9✔
667
}
9✔
668

669
func vmExportContainerResourceRequirements(config *virtconfig.ClusterConfig) k8sv1.ResourceRequirements {
24✔
670
        return k8sv1.ResourceRequirements{
24✔
671
                Limits:   vmExportContainerLimits(config),
24✔
672
                Requests: vmExportContainerRequests(config),
24✔
673
        }
24✔
674
}
24✔
675

676
func vmExportContainerLimits(config *virtconfig.ClusterConfig) k8sv1.ResourceList {
24✔
677
        cpuQuantity := resource.MustParse("1")
24✔
678
        if cpu := config.GetSupportContainerLimit(v1.VMExport, k8sv1.ResourceCPU); cpu != nil {
24✔
679
                cpuQuantity = *cpu
×
680
        }
×
681
        memQuantity := resource.MustParse("1024Mi")
24✔
682
        if mem := config.GetSupportContainerLimit(v1.VMExport, k8sv1.ResourceMemory); mem != nil {
24✔
683
                memQuantity = *mem
×
684
        }
×
685
        return k8sv1.ResourceList{
24✔
686
                k8sv1.ResourceCPU:    cpuQuantity,
24✔
687
                k8sv1.ResourceMemory: memQuantity,
24✔
688
        }
24✔
689
}
690

691
func vmExportContainerRequests(config *virtconfig.ClusterConfig) k8sv1.ResourceList {
24✔
692
        cpuQuantity := resource.MustParse("100m")
24✔
693
        if cpu := config.GetSupportContainerRequest(v1.VMExport, k8sv1.ResourceCPU); cpu != nil {
24✔
694
                cpuQuantity = *cpu
×
695
        }
×
696
        memQuantity := resource.MustParse("200Mi")
24✔
697
        if mem := config.GetSupportContainerRequest(v1.VMExport, k8sv1.ResourceMemory); mem != nil {
24✔
698
                memQuantity = *mem
×
699
        }
×
700
        return k8sv1.ResourceList{
24✔
701
                k8sv1.ResourceCPU:    cpuQuantity,
24✔
702
                k8sv1.ResourceMemory: memQuantity,
24✔
703
        }
24✔
704
}
705

706
func getMemoryLimitsRatio(namespace string, namespaceStore cache.Store) float64 {
7✔
707
        if namespaceStore == nil {
7✔
708
                return DefaultMemoryLimitOverheadRatio
×
709
        }
×
710

711
        obj, exists, err := namespaceStore.GetByKey(namespace)
7✔
712
        if err != nil {
7✔
713
                log.Log.Warningf("Error retrieving namespace from informer. Using the default memory limits ratio. %s", err.Error())
×
714
                return DefaultMemoryLimitOverheadRatio
×
715
        } else if !exists {
10✔
716
                log.Log.Warningf("namespace %s does not exist. Using the default memory limits ratio.", namespace)
3✔
717
                return DefaultMemoryLimitOverheadRatio
3✔
718
        }
3✔
719

720
        ns, ok := obj.(*k8sv1.Namespace)
4✔
721
        if !ok {
4✔
722
                log.Log.Errorf("couldn't cast object to Namespace: %+v", obj)
×
723
                return DefaultMemoryLimitOverheadRatio
×
724
        }
×
725

726
        value, ok := ns.GetLabels()[v1.AutoMemoryLimitsRatioLabel]
4✔
727
        if !ok {
4✔
728
                return DefaultMemoryLimitOverheadRatio
×
729
        }
×
730

731
        limitRatioValue, err := strconv.ParseFloat(value, 64)
4✔
732
        if err != nil || limitRatioValue < 1.0 {
5✔
733
                log.Log.Warningf("%s is an invalid value for %s label in namespace %s. Using the default one: %f", value, v1.AutoMemoryLimitsRatioLabel, namespace, DefaultMemoryLimitOverheadRatio)
1✔
734
                return DefaultMemoryLimitOverheadRatio
1✔
735
        }
1✔
736

737
        return limitRatioValue
3✔
738
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc