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

kubevirt / kubevirt / 3035bd11-0cf8-4bda-be62-3903b24a5790

16 Apr 2025 02:55PM UTC coverage: 71.624% (+0.01%) from 71.612%
3035bd11-0cf8-4bda-be62-3903b24a5790

push

prow

web-flow
Merge pull request #14463 from brianmcarey/dequarantine-test-3007

e2e,compute: Remove from quarantine the test for force restart with termination grace period

62354 of 87057 relevant lines covered (71.62%)

0.8 hits per line

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

84.91
/pkg/virt-handler/device-manager/device_controller.go
1
/*
2
 * This file is part of the KubeVirt project
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
 * Copyright 2018 Red Hat, Inc.
17
 *
18
 */
19

20
package device_manager
21

22
import (
23
        "context"
24
        "math"
25
        "os"
26
        "strings"
27
        "sync"
28
        "time"
29

30
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31
        k8scli "k8s.io/client-go/kubernetes/typed/core/v1"
32

33
        "kubevirt.io/client-go/log"
34

35
        "kubevirt.io/kubevirt/pkg/storage/reservation"
36
        virtconfig "kubevirt.io/kubevirt/pkg/virt-config"
37
        "kubevirt.io/kubevirt/pkg/virt-handler/selinux"
38
)
39

40
var defaultBackoffTime = []time.Duration{1 * time.Second, 2 * time.Second, 5 * time.Second, 10 * time.Second}
41

42
type controlledDevice struct {
43
        devicePlugin Device
44
        started      bool
45
        stopChan     chan struct{}
46
        backoff      []time.Duration
47
}
48

49
func (c *controlledDevice) Start() {
1✔
50
        if c.started {
1✔
51
                return
×
52
        }
×
53

54
        stop := make(chan struct{})
1✔
55

1✔
56
        logger := log.DefaultLogger()
1✔
57
        dev := c.devicePlugin
1✔
58
        deviceName := dev.GetDeviceName()
1✔
59
        logger.Infof("Starting a device plugin for device: %s", deviceName)
1✔
60
        retries := 0
1✔
61

1✔
62
        backoff := c.backoff
1✔
63
        if backoff == nil {
1✔
64
                backoff = defaultBackoffTime
×
65
        }
×
66

67
        go func() {
2✔
68
                for {
2✔
69
                        err := dev.Start(stop)
1✔
70
                        if err != nil {
2✔
71
                                logger.Reason(err).Errorf("Error starting %s device plugin", deviceName)
1✔
72
                                retries = int(math.Min(float64(retries+1), float64(len(backoff)-1)))
1✔
73
                        } else {
2✔
74
                                retries = 0
1✔
75
                        }
1✔
76

77
                        select {
1✔
78
                        case <-stop:
1✔
79
                                // Ok we don't want to re-register
1✔
80
                                return
1✔
81
                        case <-time.After(backoff[retries]):
1✔
82
                                // Wait a little and re-register
1✔
83
                                continue
1✔
84
                        }
85
                }
86
        }()
87

88
        c.stopChan = stop
1✔
89
        c.started = true
1✔
90
}
91

92
func (c *controlledDevice) Stop() {
1✔
93
        if !c.started {
1✔
94
                return
×
95
        }
×
96
        close(c.stopChan)
1✔
97

1✔
98
        c.stopChan = nil
1✔
99
        c.started = false
1✔
100
}
101

102
func (c *controlledDevice) GetName() string {
×
103
        return c.devicePlugin.GetDeviceName()
×
104
}
×
105

106
func PermanentHostDevicePlugins(maxDevices int, permissions string) []Device {
1✔
107
        var permanentDevicePluginPaths = map[string]string{
1✔
108
                "kvm":       "/dev/kvm",
1✔
109
                "tun":       "/dev/net/tun",
1✔
110
                "vhost-net": "/dev/vhost-net",
1✔
111
        }
1✔
112

1✔
113
        ret := make([]Device, 0, len(permanentDevicePluginPaths))
1✔
114
        for name, path := range permanentDevicePluginPaths {
2✔
115
                ret = append(ret, NewGenericDevicePlugin(name, path, maxDevices, permissions, name != "kvm"))
1✔
116
        }
1✔
117
        return ret
1✔
118
}
119

120
type DeviceControllerInterface interface {
121
        Initialized() bool
122
        RefreshMediatedDeviceTypes()
123
}
124

125
type DeviceController struct {
126
        permanentPlugins    map[string]Device
127
        startedPlugins      map[string]controlledDevice
128
        startedPluginsMutex sync.Mutex
129
        host                string
130
        maxDevices          int
131
        permissions         string
132
        backoff             []time.Duration
133
        virtConfig          *virtconfig.ClusterConfig
134
        stop                chan struct{}
135
        mdevTypesManager    *MDEVTypesManager
136
        clientset           k8scli.CoreV1Interface
137
}
138

139
func NewDeviceController(
140
        host string,
141
        maxDevices int,
142
        permissions string,
143
        permanentPlugins []Device,
144
        clusterConfig *virtconfig.ClusterConfig,
145
        clientset k8scli.CoreV1Interface,
146
) *DeviceController {
1✔
147
        permanentPluginsMap := make(map[string]Device, len(permanentPlugins))
1✔
148
        for i := range permanentPlugins {
2✔
149
                permanentPluginsMap[permanentPlugins[i].GetDeviceName()] = permanentPlugins[i]
1✔
150
        }
1✔
151

152
        controller := &DeviceController{
1✔
153
                permanentPlugins: permanentPluginsMap,
1✔
154
                startedPlugins:   map[string]controlledDevice{},
1✔
155
                host:             host,
1✔
156
                maxDevices:       maxDevices,
1✔
157
                permissions:      permissions,
1✔
158
                backoff:          defaultBackoffTime,
1✔
159
                virtConfig:       clusterConfig,
1✔
160
                mdevTypesManager: NewMDEVTypesManager(),
1✔
161
                clientset:        clientset,
1✔
162
        }
1✔
163

1✔
164
        return controller
1✔
165
}
166

167
func (c *DeviceController) NodeHasDevice(devicePath string) bool {
1✔
168
        _, err := os.Stat(devicePath)
1✔
169
        // Since this is a boolean question, any error means "no"
1✔
170
        return err == nil
1✔
171
}
1✔
172

173
// updatePermittedHostDevicePlugins returns a slice of device plugins for permitted devices which are present on the node
174
func (c *DeviceController) updatePermittedHostDevicePlugins() []Device {
1✔
175
        var permittedDevices []Device
1✔
176

1✔
177
        var featureGatedDevices = []struct {
1✔
178
                Name      string
1✔
179
                Path      string
1✔
180
                IsAllowed func() bool
1✔
181
        }{
1✔
182
                {"sev", "/dev/sev", c.virtConfig.WorkloadEncryptionSEVEnabled},
1✔
183
                {"vhost-vsock", "/dev/vhost-vsock", c.virtConfig.VSOCKEnabled},
1✔
184
        }
1✔
185
        for _, dev := range featureGatedDevices {
2✔
186
                if dev.IsAllowed() {
1✔
187
                        permittedDevices = append(
×
188
                                permittedDevices,
×
189
                                NewGenericDevicePlugin(dev.Name, dev.Path, c.maxDevices, c.permissions, true),
×
190
                        )
×
191
                }
×
192
        }
193

194
        if c.virtConfig.PersistentReservationEnabled() {
1✔
195
                d, err := NewSocketDevicePlugin(reservation.GetPrResourceName(), reservation.GetPrHelperSocketDir(), reservation.GetPrHelperSocket(), c.maxDevices, selinux.SELinuxExecutor{}, NewPermissionManager())
×
196
                if err != nil {
×
197
                        log.Log.Reason(err).Errorf("failed to configure the desired mdev types, failed to get node details")
×
198
                } else {
×
199
                        permittedDevices = append(permittedDevices, d)
×
200
                }
×
201
        }
202

203
        hostDevs := c.virtConfig.GetPermittedHostDevices()
1✔
204
        if hostDevs == nil {
2✔
205
                return permittedDevices
1✔
206
        }
1✔
207

208
        if len(hostDevs.PciHostDevices) != 0 {
2✔
209
                supportedPCIDeviceMap := make(map[string]string)
1✔
210
                for _, pciDev := range hostDevs.PciHostDevices {
2✔
211
                        log.Log.V(4).Infof("Permitted PCI device in the cluster, ID: %s, resourceName: %s, externalProvider: %t",
1✔
212
                                strings.ToLower(pciDev.PCIVendorSelector),
1✔
213
                                pciDev.ResourceName,
1✔
214
                                pciDev.ExternalResourceProvider)
1✔
215
                        // do not add a device plugin for this resource if it's being provided via an external device plugin
1✔
216
                        if !pciDev.ExternalResourceProvider {
2✔
217
                                supportedPCIDeviceMap[strings.ToLower(pciDev.PCIVendorSelector)] = pciDev.ResourceName
1✔
218
                        }
1✔
219
                }
220
                for pciResourceName, pciDevices := range discoverPermittedHostPCIDevices(supportedPCIDeviceMap) {
2✔
221
                        log.Log.V(4).Infof("Discovered PCIs %d devices on the node for the resource: %s", len(pciDevices), pciResourceName)
1✔
222
                        // add a device plugin only for new devices
1✔
223
                        permittedDevices = append(permittedDevices, NewPCIDevicePlugin(pciDevices, pciResourceName))
1✔
224
                }
1✔
225
        }
226
        if len(hostDevs.MediatedDevices) != 0 {
2✔
227
                supportedMdevsMap := make(map[string]string)
1✔
228
                for _, supportedMdev := range hostDevs.MediatedDevices {
2✔
229
                        log.Log.V(4).Infof("Permitted mediated device in the cluster, ID: %s, resourceName: %s",
1✔
230
                                supportedMdev.MDEVNameSelector,
1✔
231
                                supportedMdev.ResourceName)
1✔
232
                        // do not add a device plugin for this resource if it's being provided via an external device plugin
1✔
233
                        if !supportedMdev.ExternalResourceProvider {
2✔
234
                                selector := removeSelectorSpaces(supportedMdev.MDEVNameSelector)
1✔
235
                                supportedMdevsMap[selector] = supportedMdev.ResourceName
1✔
236
                        }
1✔
237
                }
238
                for mdevTypeName, mdevUUIDs := range discoverPermittedHostMediatedDevices(supportedMdevsMap) {
2✔
239
                        mdevResourceName := supportedMdevsMap[mdevTypeName]
1✔
240
                        log.Log.V(4).Infof("Discovered mediated device on the node, type: %s, resourceName: %s", mdevTypeName, mdevResourceName)
1✔
241

1✔
242
                        permittedDevices = append(permittedDevices, NewMediatedDevicePlugin(mdevUUIDs, mdevResourceName))
1✔
243
                }
1✔
244
        }
245

246
        for resourceName, pluginDevices := range discoverAllowedUSBDevices(hostDevs.USB) {
1✔
247
                permittedDevices = append(permittedDevices, NewUSBDevicePlugin(resourceName, pluginDevices))
×
248
        }
×
249

250
        return permittedDevices
1✔
251
}
252

253
func removeSelectorSpaces(selectorName string) string {
1✔
254
        // The name usually contain spaces which should be replaced with _
1✔
255
        // Such as GRID T4-1Q
1✔
256
        typeNameStr := strings.Replace(string(selectorName), " ", "_", -1)
1✔
257
        typeNameStr = strings.TrimSpace(typeNameStr)
1✔
258
        return typeNameStr
1✔
259

1✔
260
}
1✔
261

262
func (c *DeviceController) splitPermittedDevices(devices []Device) (map[string]Device, map[string]struct{}) {
1✔
263
        devicePluginsToRun := make(map[string]Device)
1✔
264
        devicePluginsToStop := make(map[string]struct{})
1✔
265

1✔
266
        // generate a map of currently started device plugins
1✔
267
        for resourceName := range c.startedPlugins {
2✔
268
                _, isPermanent := c.permanentPlugins[resourceName]
1✔
269
                if !isPermanent {
2✔
270
                        devicePluginsToStop[resourceName] = struct{}{}
1✔
271
                }
1✔
272
        }
273

274
        for _, device := range devices {
2✔
275
                if _, isRunning := c.startedPlugins[device.GetDeviceName()]; !isRunning {
2✔
276
                        devicePluginsToRun[device.GetDeviceName()] = device
1✔
277
                } else {
1✔
278
                        delete(devicePluginsToStop, device.GetDeviceName())
×
279
                }
×
280
        }
281

282
        return devicePluginsToRun, devicePluginsToStop
1✔
283
}
284

285
func (c *DeviceController) RefreshMediatedDeviceTypes() {
×
286
        go func() {
×
287
                if c.refreshMediatedDeviceTypes() {
×
288
                        c.refreshPermittedDevices()
×
289
                }
×
290
        }()
291
}
292

293
func (c *DeviceController) getExternallyProvidedMdevs() map[string]struct{} {
1✔
294
        externalMdevResourcesMap := make(map[string]struct{})
1✔
295
        if hostDevs := c.virtConfig.GetPermittedHostDevices(); hostDevs != nil {
1✔
296
                for _, supportedMdev := range hostDevs.MediatedDevices {
×
297
                        if supportedMdev.ExternalResourceProvider {
×
298
                                selector := removeSelectorSpaces(supportedMdev.MDEVNameSelector)
×
299
                                externalMdevResourcesMap[selector] = struct{}{}
×
300
                        }
×
301
                }
302
        }
303
        return externalMdevResourcesMap
1✔
304
}
305

306
func (c *DeviceController) refreshMediatedDeviceTypes() bool {
1✔
307
        // the handling of mediated device is disabled
1✔
308
        if c.virtConfig.MediatedDevicesHandlingDisabled() {
1✔
309
                return false
×
310
        }
×
311

312
        requiresDevicePluginsUpdate := false
1✔
313
        node, err := c.clientset.Nodes().Get(context.Background(), c.host, metav1.GetOptions{})
1✔
314
        if err != nil {
2✔
315
                log.Log.Reason(err).Errorf("failed to configure the desired mdev types, failed to get node details")
1✔
316
                return requiresDevicePluginsUpdate
1✔
317
        }
1✔
318
        externallyProvidedMdevMap := c.getExternallyProvidedMdevs()
1✔
319

1✔
320
        nodeDesiredMdevTypesList := c.virtConfig.GetDesiredMDEVTypes(node)
1✔
321
        requiresDevicePluginsUpdate, err = c.mdevTypesManager.updateMDEVTypesConfiguration(nodeDesiredMdevTypesList, externallyProvidedMdevMap)
1✔
322
        if err != nil {
1✔
323
                log.Log.Reason(err).Errorf("failed to configure the desired mdev types: %s", strings.Join(nodeDesiredMdevTypesList, ", "))
×
324
        }
×
325
        return requiresDevicePluginsUpdate
1✔
326
}
327

328
func (c *DeviceController) refreshPermittedDevices() {
1✔
329
        logger := log.DefaultLogger()
1✔
330
        var debugDevAdded []string
1✔
331
        var debugDevRemoved []string
1✔
332

1✔
333
        // This function can be called multiple times in parallel, either because of multiple
1✔
334
        //   informer callbacks for the same event, or because the configmap was quickly updated
1✔
335
        //   multiple times in a row. To avoid starting/stopping device plugins multiple times,
1✔
336
        //   we need to protect c.startedPlugins, which we read from in
1✔
337
        //   c.updatePermittedHostDevicePlugins() and write to below.
1✔
338
        c.startedPluginsMutex.Lock()
1✔
339
        defer c.startedPluginsMutex.Unlock()
1✔
340

1✔
341
        enabledDevicePlugins, disabledDevicePlugins := c.splitPermittedDevices(
1✔
342
                c.updatePermittedHostDevicePlugins(),
1✔
343
        )
1✔
344

1✔
345
        // start device plugin for newly permitted devices
1✔
346
        for resourceName, dev := range enabledDevicePlugins {
1✔
347
                c.startDevice(resourceName, dev)
×
348
                debugDevAdded = append(debugDevAdded, resourceName)
×
349
        }
×
350
        // remove device plugin for now forbidden devices
351
        for resourceName := range disabledDevicePlugins {
2✔
352
                c.stopDevice(resourceName)
1✔
353
                debugDevRemoved = append(debugDevRemoved, resourceName)
1✔
354
        }
1✔
355

356
        logger.Info("refreshed device plugins for permitted/forbidden host devices")
1✔
357
        logger.Infof("enabled device-plugins for: %v", debugDevAdded)
1✔
358
        logger.Infof("disabled device-plugins for: %v", debugDevRemoved)
1✔
359
}
360

361
func (c *DeviceController) startDevice(resourceName string, dev Device) {
1✔
362
        c.stopDevice(resourceName)
1✔
363
        controlledDev := controlledDevice{
1✔
364
                devicePlugin: dev,
1✔
365
                backoff:      c.backoff,
1✔
366
        }
1✔
367
        controlledDev.Start()
1✔
368
        c.startedPlugins[resourceName] = controlledDev
1✔
369
}
1✔
370

371
func (c *DeviceController) stopDevice(resourceName string) {
1✔
372
        dev, exists := c.startedPlugins[resourceName]
1✔
373
        if exists {
2✔
374
                dev.Stop()
1✔
375
                delete(c.startedPlugins, resourceName)
1✔
376
        }
1✔
377
}
378

379
func (c *DeviceController) Run(stop chan struct{}) error {
1✔
380
        logger := log.DefaultLogger()
1✔
381

1✔
382
        // start the permanent DevicePlugins
1✔
383
        func() {
2✔
384
                c.startedPluginsMutex.Lock()
1✔
385
                defer c.startedPluginsMutex.Unlock()
1✔
386
                for name, dev := range c.permanentPlugins {
2✔
387
                        c.startDevice(name, dev)
1✔
388
                }
1✔
389
        }()
390

391
        refreshMediatedDeviceTypesFn := func() {
2✔
392
                c.refreshMediatedDeviceTypes()
1✔
393
        }
1✔
394
        c.virtConfig.SetConfigModifiedCallback(refreshMediatedDeviceTypesFn)
1✔
395
        c.virtConfig.SetConfigModifiedCallback(c.refreshPermittedDevices)
1✔
396
        c.refreshPermittedDevices()
1✔
397

1✔
398
        // keep running until stop
1✔
399
        <-stop
1✔
400

1✔
401
        // stop all device plugins
1✔
402
        func() {
2✔
403
                c.startedPluginsMutex.Lock()
1✔
404
                defer c.startedPluginsMutex.Unlock()
1✔
405
                for name := range c.startedPlugins {
2✔
406
                        c.stopDevice(name)
1✔
407
                }
1✔
408
        }()
409
        logger.Info("Shutting down device plugin controller")
1✔
410
        return nil
1✔
411
}
412

413
func (c *DeviceController) Initialized() bool {
1✔
414
        c.startedPluginsMutex.Lock()
1✔
415
        defer c.startedPluginsMutex.Unlock()
1✔
416
        for _, dev := range c.startedPlugins {
2✔
417
                if !dev.devicePlugin.GetInitialized() {
1✔
418
                        return false
×
419
                }
×
420
        }
421

422
        return true
1✔
423
}
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