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

kubevirt / kubevirt / 55c8662e-3cac-41c2-9b0b-b09f98b851a1

09 Dec 2025 08:21AM UTC coverage: 70.666% (-0.01%) from 70.68%
55c8662e-3cac-41c2-9b0b-b09f98b851a1

push

prow

web-flow
Merge pull request #16081 from ShellyKa13/vmbackup

VMBackup: introduce new VM backup API

1189 of 1731 new or added lines in 35 files covered. (68.69%)

12 existing lines in 4 files now uncovered.

71582 of 101296 relevant lines covered (70.67%)

416.77 hits per line

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

68.75
/pkg/virt-handler/cmd-client/client.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 The KubeVirt Authors.
17
 *
18
 */
19

20
package cmdclient
21

22
//go:generate mockgen -source $GOFILE -package=$GOPACKAGE -destination=generated_mock_$GOFILE
23

24
/*
25
 ATTENTION: Rerun code generators when interface signatures are modified.
26
*/
27

28
import (
29
        "context"
30
        "errors"
31
        "fmt"
32
        "os"
33
        "path/filepath"
34
        "strings"
35
        "time"
36

37
        "google.golang.org/grpc"
38

39
        "k8s.io/apimachinery/pkg/api/resource"
40
        "k8s.io/apimachinery/pkg/util/json"
41

42
        backupv1 "kubevirt.io/api/backup/v1alpha1"
43
        v1 "kubevirt.io/api/core/v1"
44
        "kubevirt.io/client-go/log"
45

46
        "golang.org/x/sys/unix"
47

48
        com "kubevirt.io/kubevirt/pkg/handler-launcher-com"
49
        "kubevirt.io/kubevirt/pkg/handler-launcher-com/cmd/info"
50
        cmdv1 "kubevirt.io/kubevirt/pkg/handler-launcher-com/cmd/v1"
51
        "kubevirt.io/kubevirt/pkg/safepath"
52
        grpcutil "kubevirt.io/kubevirt/pkg/util/net/grpc"
53
        "kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/api"
54
        "kubevirt.io/kubevirt/pkg/virt-launcher/virtwrap/stats"
55
)
56

57
var (
58
        // add older version when supported
59
        // don't use the variable in pkg/handler-launcher-com/cmd/v1/version.go in order to detect version mismatches early
60
        supportedCmdVersions = []uint32{1}
61
        baseDir              = "/var/run/kubevirt"
62
        podsBaseDir          = "/pods"
63
)
64

65
const StandardLauncherSocketFileName = "launcher-sock"
66
const StandardInitLauncherSocketFileName = "launcher-init-sock"
67
const StandardLauncherUnresponsiveFileName = "launcher-unresponsive"
68

69
type MigrationOptions struct {
70
        Bandwidth                resource.Quantity
71
        ProgressTimeout          int64
72
        CompletionTimeoutPerGiB  int64
73
        UnsafeMigration          bool
74
        AllowAutoConverge        bool
75
        AllowPostCopy            bool
76
        ParallelMigrationThreads *uint
77
        AllowWorkloadDisruption  bool
78
}
79

80
type LauncherClient interface {
81
        SyncVirtualMachine(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error
82
        PauseVirtualMachine(vmi *v1.VirtualMachineInstance) error
83
        UnpauseVirtualMachine(vmi *v1.VirtualMachineInstance) error
84
        FreezeVirtualMachine(vmi *v1.VirtualMachineInstance, unfreezeTimeoutSeconds int32) error
85
        UnfreezeVirtualMachine(vmi *v1.VirtualMachineInstance) error
86
        SyncMigrationTarget(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error
87
        ResetVirtualMachine(vmi *v1.VirtualMachineInstance) error
88
        SoftRebootVirtualMachine(vmi *v1.VirtualMachineInstance) error
89
        SignalTargetPodCleanup(vmi *v1.VirtualMachineInstance) error
90
        ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error
91
        KillVirtualMachine(vmi *v1.VirtualMachineInstance) error
92
        MigrateVirtualMachine(vmi *v1.VirtualMachineInstance, options *MigrationOptions) error
93
        CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error
94
        FinalizeVirtualMachineMigration(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error
95
        HotplugHostDevices(vmi *v1.VirtualMachineInstance) error
96
        DeleteDomain(vmi *v1.VirtualMachineInstance) error
97
        GetDomain() (*api.Domain, bool, error)
98
        GetDomainStats() (*stats.DomainStats, bool, error)
99
        GetGuestInfo() (*v1.VirtualMachineInstanceGuestAgentInfo, error)
100
        GetUsers() (v1.VirtualMachineInstanceGuestOSUserList, error)
101
        GetFilesystems() (v1.VirtualMachineInstanceFileSystemList, error)
102
        Exec(string, string, []string, int32) (int, string, error)
103
        Ping() error
104
        GuestPing(string, int32) error
105
        Close()
106
        VirtualMachineMemoryDump(vmi *v1.VirtualMachineInstance, dumpPath string) error
107
        GetQemuVersion() (string, error)
108
        SyncVirtualMachineCPUs(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error
109
        GetSEVInfo() (*v1.SEVPlatformInfo, error)
110
        GetLaunchMeasurement(*v1.VirtualMachineInstance) (*v1.SEVMeasurementInfo, error)
111
        InjectLaunchSecret(*v1.VirtualMachineInstance, *v1.SEVSecretOptions) error
112
        SyncVirtualMachineMemory(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error
113
        GetDomainDirtyRateStats() (dirtyRateMbps int64, err error)
114
        GetScreenshot(*v1.VirtualMachineInstance) (*cmdv1.ScreenshotResponse, error)
115
        VirtualMachineBackup(vmi *v1.VirtualMachineInstance, options *backupv1.BackupOptions) error
116
}
117

118
type VirtLauncherClient struct {
119
        v1client cmdv1.CmdClient
120
        conn     *grpc.ClientConn
121
}
122

123
const (
124
        shortTimeout time.Duration = 5 * time.Second
125
        longTimeout  time.Duration = 20 * time.Second
126
)
127

128
func SetBaseDir(dir string) {
12✔
129
        baseDir = dir
12✔
130
}
12✔
131

132
func SetPodsBaseDir(baseDir string) {
168✔
133
        podsBaseDir = baseDir
168✔
134
}
168✔
135

136
func SocketsDirectory() string {
1✔
137
        return filepath.Join(baseDir, "sockets")
1✔
138
}
1✔
139

140
func IsSocketUnresponsive(socket string) bool {
4✔
141
        dir, err := safepath.NewPathNoFollow(filepath.Dir(socket))
4✔
142
        fileNotExists := errors.Is(err, unix.ENOENT)
4✔
143
        if err != nil {
4✔
144
                return fileNotExists
×
145
        }
×
146

147
        _, err = safepath.JoinNoFollow(dir, StandardLauncherUnresponsiveFileName)
4✔
148
        unresponsive := !errors.Is(err, unix.ENOENT)
4✔
149
        // if the unresponsive socket monitor marked this socket
4✔
150
        // as being unresponsive, return true
4✔
151
        if unresponsive {
5✔
152
                return true
1✔
153
        }
1✔
154

155
        _, err = safepath.JoinNoFollow(dir, filepath.Base(socket))
3✔
156
        fileNotExists = errors.Is(err, unix.ENOENT)
3✔
157
        // if the socket file doesn't exist, it's definitely unresponsive as well
3✔
158
        return fileNotExists
3✔
159
}
160

161
func MarkSocketUnresponsive(socket string) error {
4✔
162
        dir, err := safepath.NewPathNoFollow(filepath.Dir(socket))
4✔
163
        if err != nil {
4✔
164
                return err
×
165
        }
×
166
        err = safepath.TouchAtNoFollow(dir, StandardLauncherUnresponsiveFileName, 0666)
4✔
167
        if errors.Is(err, unix.EEXIST) {
5✔
168
                return nil
1✔
169
        }
1✔
170
        return err
3✔
171
}
172

173
func SocketDirectoryOnHost(podUID string) string {
183✔
174
        return filepath.Clean(fmt.Sprintf("/%s/%s/volumes/kubernetes.io~empty-dir/sockets", podsBaseDir, podUID))
183✔
175
}
183✔
176

177
func SocketFilePathOnHost(podUID string) string {
181✔
178
        return filepath.Clean(fmt.Sprintf("%s/%s", SocketDirectoryOnHost(podUID), StandardLauncherSocketFileName))
181✔
179
}
181✔
180

181
// gets the cmd socket for a VMI
182
func FindPodDirOnHost(vmi *v1.VirtualMachineInstance, socketDirFunc func(string) string) (string, error) {
2✔
183

2✔
184
        var socketDirsForErrorReporting []string
2✔
185
        // It is possible for multiple pods to be active on a single VMI
2✔
186
        // during migrations. This loop will discover the active pod on
2✔
187
        // this particular local node if it exists. A active pod not
2✔
188
        // running on this node will not have a kubelet pods directory,
2✔
189
        // so it will not be found.
2✔
190
        for podUID := range vmi.Status.ActivePods {
5✔
191
                socketPodDir := socketDirFunc(string(podUID))
3✔
192
                socketDirsForErrorReporting = append(socketDirsForErrorReporting, socketPodDir)
3✔
193
                _, err := safepath.NewPathNoFollow(socketPodDir)
3✔
194
                if err == nil {
4✔
195
                        return socketPodDir, nil
1✔
196
                }
1✔
197
        }
198

199
        return "", fmt.Errorf("No pod dir found for vmi %s in paths [%s]", vmi.UID, strings.Join(socketDirsForErrorReporting, ","))
1✔
200
}
201

202
// Finds exactly one socket on a host based on the hostname.
203
// A empty hostname is wildcard.
204
// Returns error otherwise.
205
func FindSocketOnHost(vmi *v1.VirtualMachineInstance, host string) (string, error) {
9✔
206
        socketsFound := 0
9✔
207
        foundSocket := ""
9✔
208
        // It is possible for multiple pods to be active on a single VMI
9✔
209
        // during migrations. This loop will discover the active pod on
9✔
210
        // this particular local node if it exists. A active pod not
9✔
211
        // running on this node will not have a kubelet pods directory,
9✔
212
        // so it will not be found.
9✔
213
        for podUID, phost := range vmi.Status.ActivePods {
17✔
214
                if host != "" && host != phost {
8✔
215
                        continue
×
216
                }
217
                socket := SocketFilePathOnHost(string(podUID))
8✔
218
                _, err := safepath.NewPathNoFollow(socket)
8✔
219
                if err == nil {
14✔
220
                        foundSocket = socket
6✔
221
                        socketsFound++
6✔
222
                }
6✔
223
        }
224

225
        if socketsFound == 1 {
15✔
226
                return foundSocket, nil
6✔
227
        } else if socketsFound > 1 {
9✔
228
                return "", fmt.Errorf("Found multiple sockets for vmi %s/%s. waiting for only one to exist", vmi.Namespace, vmi.Name)
×
229
        }
×
230

231
        return "", fmt.Errorf("No command socket found for vmi %s", vmi.UID)
3✔
232
}
233

234
// Finds exactly one socket on a host based on the NODE_NAME env. Returns error otherwise.
235
func FindSocket(vmi *v1.VirtualMachineInstance) (string, error) {
9✔
236
        host, _ := os.LookupEnv("NODE_NAME")
9✔
237
        return FindSocketOnHost(vmi, host)
9✔
238
}
9✔
239
func NewClient(socketPath string) (LauncherClient, error) {
46✔
240
        // dial socket
46✔
241
        conn, err := grpcutil.DialSocket(socketPath)
46✔
242
        if err != nil {
47✔
243
                log.Log.Reason(err).Infof("failed to dial cmd socket: %s", socketPath)
1✔
244
                return nil, err
1✔
245
        }
1✔
246

247
        // create info client and find cmd version to use
248
        infoClient := info.NewCmdInfoClient(conn)
45✔
249
        return NewClientWithInfoClient(infoClient, conn)
45✔
250
}
251

252
func NewClientWithInfoClient(infoClient info.CmdInfoClient, conn *grpc.ClientConn) (LauncherClient, error) {
46✔
253
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
46✔
254
        defer cancel()
46✔
255
        info, err := infoClient.Info(ctx, &info.CmdInfoRequest{})
46✔
256
        if err != nil {
46✔
257
                return nil, fmt.Errorf("could not check cmd server version: %v", err)
×
258
        }
×
259
        version, err := com.GetHighestCompatibleVersion(info.SupportedCmdVersions, supportedCmdVersions)
46✔
260
        if err != nil {
47✔
261
                return nil, err
1✔
262
        }
1✔
263

264
        // create cmd client
265
        switch version {
45✔
266
        case 1:
45✔
267
                client := cmdv1.NewCmdClient(conn)
45✔
268
                return newV1Client(client, conn), nil
45✔
269
        default:
×
270
                return nil, fmt.Errorf("cmd client version %v not implemented yet", version)
×
271
        }
272
}
273

274
func newV1Client(client cmdv1.CmdClient, conn *grpc.ClientConn) LauncherClient {
53✔
275
        return &VirtLauncherClient{
53✔
276
                v1client: client,
53✔
277
                conn:     conn,
53✔
278
        }
53✔
279
}
53✔
280

281
func (c *VirtLauncherClient) Close() {
46✔
282
        c.conn.Close()
46✔
283
}
46✔
284

285
func (c *VirtLauncherClient) genericSendVMICmd(cmdName string,
286
        cmdFunc func(ctx context.Context, request *cmdv1.VMIRequest, opts ...grpc.CallOption) (*cmdv1.Response, error),
287
        vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
11✔
288

11✔
289
        vmiJson, err := json.Marshal(vmi)
11✔
290
        if err != nil {
11✔
291
                return err
×
292
        }
×
293

294
        request := &cmdv1.VMIRequest{
11✔
295
                Vmi: &cmdv1.VMI{
11✔
296
                        VmiJson: vmiJson,
11✔
297
                },
11✔
298
                Options: options,
11✔
299
        }
11✔
300

11✔
301
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
11✔
302
        defer cancel()
11✔
303
        response, err := cmdFunc(ctx, request)
11✔
304

11✔
305
        err = handleError(err, cmdName, response)
11✔
306
        return err
11✔
307
}
308

309
func (c *VirtLauncherClient) SyncVirtualMachine(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
1✔
310
        return c.genericSendVMICmd("SyncVMI", c.v1client.SyncVirtualMachine, vmi, options)
1✔
311
}
1✔
312

313
func (c *VirtLauncherClient) PauseVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
314
        return c.genericSendVMICmd("Pause", c.v1client.PauseVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
315
}
1✔
316

317
func (c *VirtLauncherClient) UnpauseVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
318
        return c.genericSendVMICmd("Unpause", c.v1client.UnpauseVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
319
}
1✔
320

321
func (c *VirtLauncherClient) FreezeVirtualMachine(vmi *v1.VirtualMachineInstance, unfreezeTimeoutSeconds int32) error {
1✔
322
        vmiJson, err := json.Marshal(vmi)
1✔
323
        if err != nil {
1✔
324
                return err
×
325
        }
×
326

327
        request := &cmdv1.FreezeRequest{
1✔
328
                Vmi: &cmdv1.VMI{
1✔
329
                        VmiJson: vmiJson,
1✔
330
                },
1✔
331
                UnfreezeTimeoutSeconds: unfreezeTimeoutSeconds,
1✔
332
        }
1✔
333

1✔
334
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
1✔
335
        defer cancel()
1✔
336
        response, err := c.v1client.FreezeVirtualMachine(ctx, request)
1✔
337

1✔
338
        err = handleError(err, "Freeze", response)
1✔
339
        return err
1✔
340
}
341

342
func (c *VirtLauncherClient) UnfreezeVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
343
        return c.genericSendVMICmd("Unfreeze", c.v1client.UnfreezeVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
344
}
1✔
345

346
func (c *VirtLauncherClient) VirtualMachineMemoryDump(vmi *v1.VirtualMachineInstance, dumpPath string) error {
1✔
347
        vmiJson, err := json.Marshal(vmi)
1✔
348
        if err != nil {
1✔
349
                return err
×
350
        }
×
351

352
        request := &cmdv1.MemoryDumpRequest{
1✔
353
                Vmi: &cmdv1.VMI{
1✔
354
                        VmiJson: vmiJson,
1✔
355
                },
1✔
356
                DumpPath: dumpPath,
1✔
357
        }
1✔
358

1✔
359
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
1✔
360
        defer cancel()
1✔
361
        response, err := c.v1client.VirtualMachineMemoryDump(ctx, request)
1✔
362
        err = handleError(err, "Memorydump", response)
1✔
363
        return err
1✔
364
}
365

366
func (c *VirtLauncherClient) SoftRebootVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
367
        return c.genericSendVMICmd("SoftReboot", c.v1client.SoftRebootVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
368
}
1✔
369

370
func (c *VirtLauncherClient) ResetVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
371
        return c.genericSendVMICmd("Reset", c.v1client.ResetVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
372
}
1✔
373

374
func (c *VirtLauncherClient) ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
375
        return c.genericSendVMICmd("Shutdown", c.v1client.ShutdownVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
376
}
1✔
377

378
func (c *VirtLauncherClient) KillVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
379
        return c.genericSendVMICmd("Kill", c.v1client.KillVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
380
}
1✔
381

382
func (c *VirtLauncherClient) DeleteDomain(vmi *v1.VirtualMachineInstance) error {
×
383
        return c.genericSendVMICmd("Delete", c.v1client.DeleteVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
×
384
}
×
385

386
func (c *VirtLauncherClient) MigrateVirtualMachine(vmi *v1.VirtualMachineInstance, options *MigrationOptions) error {
×
387

×
388
        vmiJson, err := json.Marshal(vmi)
×
389
        if err != nil {
×
390
                return err
×
391
        }
×
392

393
        optionsJson, err := json.Marshal(options)
×
394
        if err != nil {
×
395
                return err
×
396
        }
×
397

398
        request := &cmdv1.MigrationRequest{
×
399
                Vmi: &cmdv1.VMI{
×
400
                        VmiJson: vmiJson,
×
401
                },
×
402
                Options: optionsJson,
×
403
        }
×
404

×
405
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
×
406
        defer cancel()
×
407
        response, err := c.v1client.MigrateVirtualMachine(ctx, request)
×
408

×
409
        err = handleError(err, "Migrate", response)
×
410
        return err
×
411

412
}
413

414
func (c *VirtLauncherClient) CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error {
×
415
        return c.genericSendVMICmd("CancelMigration", c.v1client.CancelVirtualMachineMigration, vmi, &cmdv1.VirtualMachineOptions{})
×
416
}
×
417

418
func (c *VirtLauncherClient) SyncMigrationTarget(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
×
419
        return c.genericSendVMICmd("SyncMigrationTarget", c.v1client.SyncMigrationTarget, vmi, options)
×
420
}
×
421

422
func (c *VirtLauncherClient) SyncVirtualMachineCPUs(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
×
423
        return c.genericSendVMICmd("SyncVirtualMachineCPUs", c.v1client.SyncVirtualMachineCPUs, vmi, options)
×
424
}
×
425

426
func (c *VirtLauncherClient) SignalTargetPodCleanup(vmi *v1.VirtualMachineInstance) error {
×
427
        return c.genericSendVMICmd("SignalTargetPodCleanup", c.v1client.SignalTargetPodCleanup, vmi, &cmdv1.VirtualMachineOptions{})
×
428
}
×
429

430
func (c *VirtLauncherClient) FinalizeVirtualMachineMigration(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
2✔
431
        return c.genericSendVMICmd("FinalizeVirtualMachineMigration", c.v1client.FinalizeVirtualMachineMigration, vmi, options)
2✔
432
}
2✔
433

434
func (c *VirtLauncherClient) HotplugHostDevices(vmi *v1.VirtualMachineInstance) error {
×
435
        return c.genericSendVMICmd("HotplugHostDevices", c.v1client.HotplugHostDevices, vmi, &cmdv1.VirtualMachineOptions{})
×
436
}
×
437

438
func (c *VirtLauncherClient) GetDomain() (*api.Domain, bool, error) {
9✔
439

9✔
440
        domain := &api.Domain{}
9✔
441
        exists := false
9✔
442

9✔
443
        request := &cmdv1.EmptyRequest{}
9✔
444
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
9✔
445
        defer cancel()
9✔
446

9✔
447
        domainResponse, err := c.v1client.GetDomain(ctx, request)
9✔
448
        var response *cmdv1.Response
9✔
449
        if domainResponse != nil {
18✔
450
                response = domainResponse.Response
9✔
451
        }
9✔
452

453
        if err = handleError(err, "GetDomain", response); err != nil || domainResponse == nil {
9✔
454
                return domain, exists, err
×
455
        }
×
456

457
        if domainResponse.Domain != "" {
17✔
458
                if err := json.Unmarshal([]byte(domainResponse.Domain), domain); err != nil {
8✔
459
                        log.Log.Reason(err).Error("error unmarshalling domain")
×
460
                        return domain, exists, err
×
461
                }
×
462
                exists = true
8✔
463
        }
464
        return domain, exists, nil
9✔
465
}
466

467
func (c *VirtLauncherClient) GetDomainDirtyRateStats() (dirtyRateMbps int64, err error) {
×
468
        request := &cmdv1.EmptyRequest{}
×
469
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
×
470
        defer cancel()
×
471

×
472
        domainDirtyRateStatsResponse, err := c.v1client.GetDomainDirtyRateStats(ctx, request)
×
473
        var response *cmdv1.Response
×
474
        if domainDirtyRateStatsResponse != nil {
×
475
                response = domainDirtyRateStatsResponse.Response
×
476
        }
×
477

478
        if err = handleError(err, "GetDomainDirtyRateStats", response); err != nil || domainDirtyRateStatsResponse == nil {
×
479
                return -1, err
×
480
        }
×
481

482
        return domainDirtyRateStatsResponse.DirtyRateMbs, nil
×
483
}
484

485
func (c *VirtLauncherClient) GetQemuVersion() (string, error) {
1✔
486
        request := &cmdv1.EmptyRequest{}
1✔
487
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
488
        defer cancel()
1✔
489

1✔
490
        versionResponse, err := c.v1client.GetQemuVersion(ctx, request)
1✔
491
        var response *cmdv1.Response
1✔
492
        if versionResponse != nil {
2✔
493
                response = versionResponse.Response
1✔
494
        }
1✔
495
        if err = handleError(err, "GetQemuVersion", response); err != nil {
1✔
496
                return "", err
×
497
        }
×
498

499
        if versionResponse != nil && versionResponse.Version != "" {
2✔
500
                return versionResponse.Version, nil
1✔
501
        }
1✔
502

503
        log.Log.Reason(err).Error("error getting the qemu version")
×
504
        return "", errors.New("error getting the qemu version")
×
505
}
506

507
func (c *VirtLauncherClient) GetDomainStats() (*stats.DomainStats, bool, error) {
1✔
508
        stats := &stats.DomainStats{}
1✔
509
        exists := false
1✔
510

1✔
511
        request := &cmdv1.EmptyRequest{}
1✔
512
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
513
        defer cancel()
1✔
514

1✔
515
        domainStatsResponse, err := c.v1client.GetDomainStats(ctx, request)
1✔
516
        var response *cmdv1.Response
1✔
517
        if domainStatsResponse != nil {
2✔
518
                response = domainStatsResponse.Response
1✔
519
        }
1✔
520

521
        if err = handleError(err, "GetDomainStats", response); err != nil || domainStatsResponse == nil {
1✔
522
                return stats, exists, err
×
523
        }
×
524

525
        if domainStatsResponse.DomainStats != "" {
2✔
526
                if err := json.Unmarshal([]byte(domainStatsResponse.DomainStats), stats); err != nil {
1✔
527
                        log.Log.Reason(err).Error("error unmarshalling domain")
×
528
                        return stats, exists, err
×
529
                }
×
530
                exists = true
1✔
531
        }
532
        return stats, exists, nil
1✔
533
}
534

535
func (c *VirtLauncherClient) Ping() error {
2✔
536
        request := &cmdv1.EmptyRequest{}
2✔
537
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
2✔
538
        defer cancel()
2✔
539
        response, err := c.v1client.Ping(ctx, request)
2✔
540

2✔
541
        err = handleError(err, "Ping", response)
2✔
542
        return err
2✔
543
}
2✔
544

545
// GetGuestInfo is a counterpart for virt-launcher call to gather guest agent data
546
func (c *VirtLauncherClient) GetGuestInfo() (*v1.VirtualMachineInstanceGuestAgentInfo, error) {
×
547
        guestInfo := &v1.VirtualMachineInstanceGuestAgentInfo{}
×
548

×
549
        request := &cmdv1.EmptyRequest{}
×
550
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
×
551
        defer cancel()
×
552

×
553
        gaRespose, err := c.v1client.GetGuestInfo(ctx, request)
×
554
        var response *cmdv1.Response
×
555
        if gaRespose != nil {
×
556
                response = gaRespose.Response
×
557
        }
×
558

559
        if err = handleError(err, "GetGuestInfo", response); err != nil || gaRespose == nil {
×
560
                return guestInfo, err
×
561
        }
×
562

563
        if gaRespose.GuestInfoResponse != "" {
×
564
                if err := json.Unmarshal([]byte(gaRespose.GetGuestInfoResponse()), guestInfo); err != nil {
×
565
                        log.Log.Reason(err).Error("error unmarshalling guest agent response")
×
566
                        return guestInfo, err
×
567
                }
×
568
        }
569
        return guestInfo, nil
×
570
}
571

572
// GetUsers returns the list of the active users on the guest machine
573
func (c *VirtLauncherClient) GetUsers() (v1.VirtualMachineInstanceGuestOSUserList, error) {
1✔
574
        var userList []v1.VirtualMachineInstanceGuestOSUser
1✔
575

1✔
576
        request := &cmdv1.EmptyRequest{}
1✔
577
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
578
        defer cancel()
1✔
579

1✔
580
        uResponse, err := c.v1client.GetUsers(ctx, request)
1✔
581
        var response *cmdv1.Response
1✔
582
        if uResponse != nil {
2✔
583
                response = uResponse.Response
1✔
584
        }
1✔
585

586
        if err = handleError(err, "GetUsers", response); err != nil || uResponse == nil {
1✔
587
                return v1.VirtualMachineInstanceGuestOSUserList{}, err
×
588
        }
×
589

590
        if uResponse.GetGuestUserListResponse() != "" {
2✔
591
                if err := json.Unmarshal([]byte(uResponse.GetGuestUserListResponse()), &userList); err != nil {
1✔
592
                        log.Log.Reason(err).Error("error unmarshalling guest user list response")
×
593
                        return v1.VirtualMachineInstanceGuestOSUserList{}, err
×
594
                }
×
595
        }
596

597
        guestUserList := v1.VirtualMachineInstanceGuestOSUserList{
1✔
598
                Items: userList,
1✔
599
        }
1✔
600

1✔
601
        return guestUserList, nil
1✔
602
}
603

604
// GetFilesystems returns the list of active filesystems on the guest machine
605
func (c *VirtLauncherClient) GetFilesystems() (v1.VirtualMachineInstanceFileSystemList, error) {
1✔
606
        var fsList []v1.VirtualMachineInstanceFileSystem
1✔
607

1✔
608
        request := &cmdv1.EmptyRequest{}
1✔
609
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
610
        defer cancel()
1✔
611

1✔
612
        fsResponse, err := c.v1client.GetFilesystems(ctx, request)
1✔
613
        var response *cmdv1.Response
1✔
614
        if fsResponse != nil {
2✔
615
                response = fsResponse.Response
1✔
616
        }
1✔
617

618
        if err = handleError(err, "GetFilesystems", response); err != nil || fsResponse == nil {
1✔
619
                return v1.VirtualMachineInstanceFileSystemList{}, err
×
620
        }
×
621

622
        if fsResponse.GetGuestFilesystemsResponse() != "" {
2✔
623
                if err := json.Unmarshal([]byte(fsResponse.GetGuestFilesystemsResponse()), &fsList); err != nil {
1✔
624
                        log.Log.Reason(err).Error("error unmarshalling guest filesystem list response")
×
625
                        return v1.VirtualMachineInstanceFileSystemList{}, err
×
626
                }
×
627
        }
628

629
        filesystemList := v1.VirtualMachineInstanceFileSystemList{
1✔
630
                Items: fsList,
1✔
631
        }
1✔
632

1✔
633
        return filesystemList, nil
1✔
634
}
635

636
// Exec the command with args on the guest and return the resulting status code, stdOut and error
637
func (c *VirtLauncherClient) Exec(domainName, command string, args []string, timeoutSeconds int32) (int, string, error) {
4✔
638
        request := &cmdv1.ExecRequest{
4✔
639
                DomainName:     domainName,
4✔
640
                Command:        command,
4✔
641
                Args:           args,
4✔
642
                TimeoutSeconds: timeoutSeconds,
4✔
643
        }
4✔
644
        exitCode := -1
4✔
645
        stdOut := ""
4✔
646

4✔
647
        ctx, cancel := context.WithTimeout(
4✔
648
                context.Background(),
4✔
649
                // we give the context a bit more time as the timeout should kick
4✔
650
                // on the actual execution
4✔
651
                time.Duration(timeoutSeconds)*time.Second+shortTimeout,
4✔
652
        )
4✔
653
        defer cancel()
4✔
654

4✔
655
        resp, err := c.v1client.Exec(ctx, request)
4✔
656
        if resp == nil {
6✔
657
                return exitCode, stdOut, err
2✔
658
        }
2✔
659

660
        exitCode = int(resp.ExitCode)
2✔
661
        stdOut = resp.StdOut
2✔
662

2✔
663
        return exitCode, stdOut, err
2✔
664
}
665

666
func (c *VirtLauncherClient) GuestPing(domainName string, timeoutSeconds int32) error {
3✔
667
        request := &cmdv1.GuestPingRequest{
3✔
668
                DomainName:     domainName,
3✔
669
                TimeoutSeconds: timeoutSeconds,
3✔
670
        }
3✔
671
        ctx, cancel := context.WithTimeout(
3✔
672
                context.Background(),
3✔
673
                // we give the context a bit more time as the timeout should kick
3✔
674
                // on the actual execution
3✔
675
                time.Duration(timeoutSeconds)*time.Second+shortTimeout,
3✔
676
        )
3✔
677
        defer cancel()
3✔
678

3✔
679
        _, err := c.v1client.GuestPing(ctx, request)
3✔
680
        return err
3✔
681
}
3✔
682

683
func (c *VirtLauncherClient) GetScreenshot(vmi *v1.VirtualMachineInstance) (*cmdv1.ScreenshotResponse, error) {
×
684
        vmiJson, err := json.Marshal(vmi)
×
685
        if err != nil {
×
686
                return nil, err
×
687
        }
×
688

689
        request := &cmdv1.VMIRequest{
×
690
                Vmi: &cmdv1.VMI{
×
691
                        VmiJson: vmiJson,
×
692
                },
×
693
        }
×
694

×
695
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
×
696
        defer cancel()
×
697

×
698
        return c.v1client.GetScreenshot(ctx, request)
×
699
}
700

701
func (c *VirtLauncherClient) GetSEVInfo() (*v1.SEVPlatformInfo, error) {
1✔
702
        request := &cmdv1.EmptyRequest{}
1✔
703
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
704
        defer cancel()
1✔
705

1✔
706
        sevInfoResponse, err := c.v1client.GetSEVInfo(ctx, request)
1✔
707
        if err = handleError(err, "GetSEVInfo", sevInfoResponse.GetResponse()); err != nil {
1✔
708
                return nil, err
×
709
        }
×
710

711
        sevPlatformInfo := &v1.SEVPlatformInfo{}
1✔
712
        if err := json.Unmarshal(sevInfoResponse.GetSevInfo(), sevPlatformInfo); err != nil {
1✔
713
                log.Log.Reason(err).Error("error unmarshalling SEV info response")
×
714
                return nil, err
×
715
        }
×
716

717
        return sevPlatformInfo, nil
1✔
718
}
719

720
func (c *VirtLauncherClient) GetLaunchMeasurement(vmi *v1.VirtualMachineInstance) (*v1.SEVMeasurementInfo, error) {
1✔
721
        vmiJson, err := json.Marshal(vmi)
1✔
722
        if err != nil {
1✔
723
                return nil, err
×
724
        }
×
725

726
        request := &cmdv1.VMIRequest{
1✔
727
                Vmi: &cmdv1.VMI{
1✔
728
                        VmiJson: vmiJson,
1✔
729
                },
1✔
730
        }
1✔
731

1✔
732
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
733
        defer cancel()
1✔
734

1✔
735
        launchMeasurementRespose, err := c.v1client.GetLaunchMeasurement(ctx, request)
1✔
736
        if err = handleError(err, "GetLaunchMeasurement", launchMeasurementRespose.GetResponse()); err != nil {
1✔
737
                return nil, err
×
738
        }
×
739

740
        sevMeasurementInfo := &v1.SEVMeasurementInfo{}
1✔
741
        if err := json.Unmarshal(launchMeasurementRespose.GetLaunchMeasurement(), sevMeasurementInfo); err != nil {
1✔
742
                log.Log.Reason(err).Error("error unmarshalling launch measurement response")
×
743
                return nil, err
×
744
        }
×
745

746
        return sevMeasurementInfo, nil
1✔
747
}
748

749
func (c *VirtLauncherClient) InjectLaunchSecret(vmi *v1.VirtualMachineInstance, sevSecretOptions *v1.SEVSecretOptions) error {
1✔
750
        vmiJson, err := json.Marshal(vmi)
1✔
751
        if err != nil {
1✔
752
                return err
×
753
        }
×
754

755
        optionsJson, err := json.Marshal(sevSecretOptions)
1✔
756
        if err != nil {
1✔
757
                return err
×
758
        }
×
759

760
        request := &cmdv1.InjectLaunchSecretRequest{
1✔
761
                Vmi: &cmdv1.VMI{
1✔
762
                        VmiJson: vmiJson,
1✔
763
                },
1✔
764
                Options: optionsJson,
1✔
765
        }
1✔
766

1✔
767
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
1✔
768
        defer cancel()
1✔
769

1✔
770
        response, err := c.v1client.InjectLaunchSecret(ctx, request)
1✔
771

1✔
772
        return handleError(err, "InjectLaunchSecret", response)
1✔
773
}
774

775
func (c *VirtLauncherClient) SyncVirtualMachineMemory(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
1✔
776
        return c.genericSendVMICmd("SyncVirtualMachineMemory", c.v1client.SyncVirtualMachineMemory, vmi, options)
1✔
777
}
1✔
778

NEW
779
func (c *VirtLauncherClient) VirtualMachineBackup(vmi *v1.VirtualMachineInstance, options *backupv1.BackupOptions) error {
×
NEW
780
        vmiJson, err := json.Marshal(vmi)
×
NEW
781
        if err != nil {
×
NEW
782
                return err
×
NEW
783
        }
×
784

NEW
785
        optionsJson, err := json.Marshal(options)
×
NEW
786
        if err != nil {
×
NEW
787
                return err
×
NEW
788
        }
×
789

NEW
790
        request := &cmdv1.BackupRequest{
×
NEW
791
                Vmi: &cmdv1.VMI{
×
NEW
792
                        VmiJson: vmiJson,
×
NEW
793
                },
×
NEW
794
                Options: optionsJson,
×
NEW
795
        }
×
NEW
796

×
NEW
797
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
×
NEW
798
        defer cancel()
×
NEW
799
        response, err := c.v1client.BackupVirtualMachine(ctx, request)
×
NEW
800

×
NEW
801
        err = handleError(err, "Backup", response)
×
NEW
802
        return err
×
803
}
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