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

kubevirt / kubevirt / a06e060b-38a1-4876-9d76-dfb0d337e982

29 May 2026 09:27AM UTC coverage: 71.554% (-0.001%) from 71.555%
a06e060b-38a1-4876-9d76-dfb0d337e982

push

prow

web-flow
Merge pull request #17550 from machadovilaca/CNV-82521-implement-get-monitoring-data-handler-in-virt-launcher-server

Add GetVMStats unified gRPC RPC for monitoring data collection

111 of 176 new or added lines in 7 files covered. (63.07%)

13 existing lines in 3 files now uncovered.

78252 of 109361 relevant lines covered (71.55%)

446.03 hits per line

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

64.97
/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
        "k8s.io/utils/ptr"
42

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

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

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

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

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

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

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

121
type VirtLauncherClient struct {
122
        v1client cmdv1.CmdClient
123
        conn     *grpc.ClientConn
124
}
125

126
const (
127
        shortTimeout    time.Duration = 5 * time.Second
128
        longTimeout     time.Duration = 20 * time.Second
129
        extendedTimeout time.Duration = 60 * time.Second
130
)
131

132
func SetBaseDir(dir string) {
13✔
133
        baseDir = dir
13✔
134
}
13✔
135

136
func SetPodsBaseDir(baseDir string) {
187✔
137
        podsBaseDir = baseDir
187✔
138
}
187✔
139

140
func SocketsDirectory() string {
1✔
141
        return filepath.Join(baseDir, "sockets")
1✔
142
}
1✔
143

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

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

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

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

177
func SocketDirectoryOnHost(podUID string) string {
210✔
178
        return filepath.Clean(fmt.Sprintf("/%s/%s/volumes/kubernetes.io~empty-dir/sockets", podsBaseDir, podUID))
210✔
179
}
210✔
180

181
func SocketFilePathOnHost(podUID string) string {
200✔
182
        return filepath.Clean(fmt.Sprintf("%s/%s", SocketDirectoryOnHost(podUID), StandardLauncherSocketFileName))
200✔
183
}
200✔
184

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

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

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

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

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

235
        return "", fmt.Errorf("No command socket found for vmi %s", vmi.UID)
3✔
236
}
237

238
// Finds exactly one socket on a host based on the NODE_NAME env. Returns error otherwise.
239
func FindSocket(vmi *v1.VirtualMachineInstance) (string, error) {
9✔
240
        host, _ := os.LookupEnv("NODE_NAME")
9✔
241
        return findSocketOnHost(vmi, host)
9✔
242
}
9✔
243

244
// Do not use this low level function unless you know what you are doing.
245
// Particularly testing is challenging as you need to instance a fully functional GRPC server.
246
//
247
// It is also not wise to have unbound number of clients to the launcher, or duplicate implementation of caching or  future recognition of source/target client(in case of same node migration).
248
func NewClient(socketPath string) (LauncherClient, error) {
53✔
249
        // dial socket
53✔
250
        conn, err := grpcutil.DialSocket(socketPath)
53✔
251
        if err != nil {
54✔
252
                log.Log.Reason(err).Infof("failed to dial cmd socket: %s", socketPath)
1✔
253
                return nil, err
1✔
254
        }
1✔
255

256
        // create info client and find cmd version to use
257
        infoClient := info.NewCmdInfoClient(conn)
52✔
258
        return newClientWithInfoClient(infoClient, conn)
52✔
259
}
260

261
func newClientWithInfoClient(infoClient info.CmdInfoClient, conn *grpc.ClientConn) (LauncherClient, error) {
53✔
262
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
53✔
263
        defer cancel()
53✔
264
        info, err := infoClient.Info(ctx, &info.CmdInfoRequest{})
53✔
265
        if err != nil {
53✔
266
                return nil, fmt.Errorf("could not check cmd server version: %v", err)
×
267
        }
×
268
        version, err := com.GetHighestCompatibleVersion(info.SupportedCmdVersions, supportedCmdVersions)
53✔
269
        if err != nil {
54✔
270
                return nil, err
1✔
271
        }
1✔
272

273
        // create cmd client
274
        switch version {
52✔
275
        case 1:
52✔
276
                client := cmdv1.NewCmdClient(conn)
52✔
277
                return newV1Client(client, conn), nil
52✔
278
        default:
×
279
                return nil, fmt.Errorf("cmd client version %v not implemented yet", version)
×
280
        }
281
}
282

283
func newV1Client(client cmdv1.CmdClient, conn *grpc.ClientConn) LauncherClient {
60✔
284
        return &VirtLauncherClient{
60✔
285
                v1client: client,
60✔
286
                conn:     conn,
60✔
287
        }
60✔
288
}
60✔
289

290
func (c *VirtLauncherClient) Close() {
53✔
291
        c.conn.Close()
53✔
292
}
53✔
293

294
func (c *VirtLauncherClient) genericSendVMICmd(cmdName string,
295
        cmdFunc func(ctx context.Context, request *cmdv1.VMIRequest, opts ...grpc.CallOption) (*cmdv1.Response, error),
296
        vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
11✔
297

11✔
298
        vmiJson, err := json.Marshal(vmi)
11✔
299
        if err != nil {
11✔
300
                return err
×
301
        }
×
302

303
        request := &cmdv1.VMIRequest{
11✔
304
                Vmi: &cmdv1.VMI{
11✔
305
                        VmiJson: vmiJson,
11✔
306
                },
11✔
307
                Options: options,
11✔
308
        }
11✔
309

11✔
310
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
11✔
311
        defer cancel()
11✔
312
        response, err := cmdFunc(ctx, request)
11✔
313

11✔
314
        err = handleError(err, cmdName, response)
11✔
315
        return err
11✔
316
}
317

318
func (c *VirtLauncherClient) SyncVirtualMachine(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
1✔
319
        return c.genericSendVMICmd("SyncVMI", c.v1client.SyncVirtualMachine, vmi, options)
1✔
320
}
1✔
321

322
func (c *VirtLauncherClient) PauseVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
323
        return c.genericSendVMICmd("Pause", c.v1client.PauseVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
324
}
1✔
325

326
func (c *VirtLauncherClient) UnpauseVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
327
        return c.genericSendVMICmd("Unpause", c.v1client.UnpauseVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
328
}
1✔
329

330
func (c *VirtLauncherClient) FreezeVirtualMachine(vmi *v1.VirtualMachineInstance, unfreezeTimeoutSeconds int32) error {
1✔
331
        vmiJson, err := json.Marshal(vmi)
1✔
332
        if err != nil {
1✔
333
                return err
×
334
        }
×
335

336
        request := &cmdv1.FreezeRequest{
1✔
337
                Vmi: &cmdv1.VMI{
1✔
338
                        VmiJson: vmiJson,
1✔
339
                },
1✔
340
                UnfreezeTimeoutSeconds: unfreezeTimeoutSeconds,
1✔
341
        }
1✔
342

1✔
343
        // Use extended timeout as Windows VSS can take up to 60 seconds
1✔
344
        ctx, cancel := context.WithTimeout(context.Background(), extendedTimeout)
1✔
345
        defer cancel()
1✔
346
        response, err := c.v1client.FreezeVirtualMachine(ctx, request)
1✔
347

1✔
348
        err = handleError(err, "Freeze", response)
1✔
349
        return err
1✔
350
}
351

352
func (c *VirtLauncherClient) UnfreezeVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
353
        return c.genericSendVMICmd("Unfreeze", c.v1client.UnfreezeVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
354
}
1✔
355

356
func (c *VirtLauncherClient) VirtualMachineMemoryDump(vmi *v1.VirtualMachineInstance, dumpPath string) error {
1✔
357
        vmiJson, err := json.Marshal(vmi)
1✔
358
        if err != nil {
1✔
359
                return err
×
360
        }
×
361

362
        request := &cmdv1.MemoryDumpRequest{
1✔
363
                Vmi: &cmdv1.VMI{
1✔
364
                        VmiJson: vmiJson,
1✔
365
                },
1✔
366
                DumpPath: dumpPath,
1✔
367
        }
1✔
368

1✔
369
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
1✔
370
        defer cancel()
1✔
371
        response, err := c.v1client.VirtualMachineMemoryDump(ctx, request)
1✔
372
        err = handleError(err, "Memorydump", response)
1✔
373
        return err
1✔
374
}
375

376
func (c *VirtLauncherClient) SoftRebootVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
377
        return c.genericSendVMICmd("SoftReboot", c.v1client.SoftRebootVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
378
}
1✔
379

380
func (c *VirtLauncherClient) ResetVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
381
        return c.genericSendVMICmd("Reset", c.v1client.ResetVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
382
}
1✔
383

384
func (c *VirtLauncherClient) ShutdownVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
385
        return c.genericSendVMICmd("Shutdown", c.v1client.ShutdownVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
386
}
1✔
387

388
func (c *VirtLauncherClient) KillVirtualMachine(vmi *v1.VirtualMachineInstance) error {
1✔
389
        return c.genericSendVMICmd("Kill", c.v1client.KillVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
1✔
390
}
1✔
391

392
func (c *VirtLauncherClient) DeleteDomain(vmi *v1.VirtualMachineInstance) error {
×
393
        return c.genericSendVMICmd("Delete", c.v1client.DeleteVirtualMachine, vmi, &cmdv1.VirtualMachineOptions{})
×
394
}
×
395

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

×
398
        vmiJson, err := json.Marshal(vmi)
×
399
        if err != nil {
×
400
                return err
×
401
        }
×
402

403
        optionsJson, err := json.Marshal(options)
×
404
        if err != nil {
×
405
                return err
×
406
        }
×
407

408
        request := &cmdv1.MigrationRequest{
×
409
                Vmi: &cmdv1.VMI{
×
410
                        VmiJson: vmiJson,
×
411
                },
×
412
                Options: optionsJson,
×
413
        }
×
414

×
415
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
×
416
        defer cancel()
×
417
        response, err := c.v1client.MigrateVirtualMachine(ctx, request)
×
418

×
419
        err = handleError(err, "Migrate", response)
×
420
        return err
×
421

422
}
423

424
func (c *VirtLauncherClient) CancelVirtualMachineMigration(vmi *v1.VirtualMachineInstance) error {
×
425
        return c.genericSendVMICmd("CancelMigration", c.v1client.CancelVirtualMachineMigration, vmi, &cmdv1.VirtualMachineOptions{})
×
426
}
×
427

428
func (c *VirtLauncherClient) SyncMigrationTarget(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
×
429
        return c.genericSendVMICmd("SyncMigrationTarget", c.v1client.SyncMigrationTarget, vmi, options)
×
430
}
×
431

432
func (c *VirtLauncherClient) SyncVirtualMachineCPUs(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
×
433
        return c.genericSendVMICmd("SyncVirtualMachineCPUs", c.v1client.SyncVirtualMachineCPUs, vmi, options)
×
434
}
×
435

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

440
func (c *VirtLauncherClient) FinalizeVirtualMachineMigration(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
2✔
441
        return c.genericSendVMICmd("FinalizeVirtualMachineMigration", c.v1client.FinalizeVirtualMachineMigration, vmi, options)
2✔
442
}
2✔
443

444
func (c *VirtLauncherClient) HotplugHostDevices(vmi *v1.VirtualMachineInstance) error {
×
445
        return c.genericSendVMICmd("HotplugHostDevices", c.v1client.HotplugHostDevices, vmi, &cmdv1.VirtualMachineOptions{})
×
446
}
×
447

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

9✔
450
        domain := &api.Domain{}
9✔
451
        exists := false
9✔
452

9✔
453
        request := &cmdv1.EmptyRequest{}
9✔
454
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
9✔
455
        defer cancel()
9✔
456

9✔
457
        domainResponse, err := c.v1client.GetDomain(ctx, request)
9✔
458
        var response *cmdv1.Response
9✔
459
        if domainResponse != nil {
18✔
460
                response = domainResponse.Response
9✔
461
        }
9✔
462

463
        if err = handleError(err, "GetDomain", response); err != nil || domainResponse == nil {
9✔
464
                return domain, exists, err
×
465
        }
×
466

467
        if domainResponse.Domain != "" {
17✔
468
                if err := json.Unmarshal([]byte(domainResponse.Domain), domain); err != nil {
8✔
469
                        log.Log.Reason(err).Error("error unmarshalling domain")
×
470
                        return domain, exists, err
×
471
                }
×
472
                exists = true
8✔
473
        }
474
        return domain, exists, nil
9✔
475
}
476

477
func (c *VirtLauncherClient) GetDomainDirtyRateStats() (dirtyRateMbps int64, err error) {
×
478
        request := &cmdv1.EmptyRequest{}
×
479
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
×
480
        defer cancel()
×
481

×
482
        domainDirtyRateStatsResponse, err := c.v1client.GetDomainDirtyRateStats(ctx, request)
×
483
        var response *cmdv1.Response
×
484
        if domainDirtyRateStatsResponse != nil {
×
485
                response = domainDirtyRateStatsResponse.Response
×
486
        }
×
487

488
        if err = handleError(err, "GetDomainDirtyRateStats", response); err != nil || domainDirtyRateStatsResponse == nil {
×
489
                return -1, err
×
490
        }
×
491

492
        return domainDirtyRateStatsResponse.DirtyRateMbs, nil
×
493
}
494

NEW
495
func (c *VirtLauncherClient) GetVMStats(request *cmdv1.VMStatsRequest) (*stats.VMStats, error) {
×
NEW
496
        result := &stats.VMStats{}
×
NEW
497
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
×
NEW
498
        defer cancel()
×
NEW
499

×
NEW
500
        vmstatsResponse, err := c.v1client.GetVMStats(ctx, request)
×
NEW
501
        var response *cmdv1.Response
×
NEW
502
        if vmstatsResponse != nil {
×
NEW
503
                response = vmstatsResponse.Response
×
NEW
504
        }
×
505

NEW
506
        if err := handleError(err, "GetVMStats", response); err != nil || vmstatsResponse == nil {
×
NEW
507
                return result, err
×
NEW
508
        }
×
509

NEW
510
        if vmstatsResponse.GetDomainStats() != nil && vmstatsResponse.GetDomainStats().GetDomainStats() != "" {
×
NEW
511
                if err := json.Unmarshal([]byte(vmstatsResponse.DomainStats.DomainStats), &result.DomainStats); err != nil {
×
NEW
512
                        return nil, err
×
NEW
513
                }
×
514
        }
515

NEW
516
        if vmstatsResponse.GetDirtyRateStats() != nil {
×
NEW
517
                result.DirtyRateMbps = ptr.To(vmstatsResponse.GetDirtyRateStats().GetDirtyRateMbs())
×
NEW
518
        }
×
519

NEW
520
        result.GuestAgentVersion = vmstatsResponse.GetGuestAgentVersion().GetMessage()
×
NEW
521
        result.GuestGetLoad = vmstatsResponse.GetGuestGetLoad().GetMessage()
×
NEW
522
        result.GuestGetCpuStats = vmstatsResponse.GetGuestGetCpuStats().GetMessage()
×
NEW
523
        result.GuestGetDiskStats = vmstatsResponse.GetGuestGetDiskStats().GetMessage()
×
NEW
524
        result.GuestGetTime = vmstatsResponse.GetGuestGetTime().GetMessage()
×
NEW
525
        result.GuestGetVcpus = vmstatsResponse.GetGuestGetVcpus().GetMessage()
×
NEW
526
        result.GuestGetMemoryBlockInfo = vmstatsResponse.GetGuestGetMemoryBlockInfo().GetMessage()
×
NEW
527
        result.GuestGetUsers = vmstatsResponse.GetGuestGetUsers().GetMessage()
×
NEW
528
        result.GuestGetOsInfo = vmstatsResponse.GetGuestGetOsInfo().GetMessage()
×
NEW
529
        result.GuestGetDisks = vmstatsResponse.GetGuestGetDisks().GetMessage()
×
NEW
530
        result.GuestGetHostName = vmstatsResponse.GetGuestGetHostName().GetMessage()
×
NEW
531
        result.GuestGetTimezone = vmstatsResponse.GetGuestGetTimezone().GetMessage()
×
NEW
532
        result.GuestNetworkGetRoute = vmstatsResponse.GetGuestNetworkGetRoute().GetMessage()
×
NEW
533
        result.GuestNetworkGetInterfaces = vmstatsResponse.GetGuestNetworkGetInterfaces().GetMessage()
×
NEW
534
        result.GuestGetMemoryBlocks = vmstatsResponse.GetGuestGetMemoryBlocks().GetMessage()
×
NEW
535

×
NEW
536
        return result, err
×
537
}
538

539
func (c *VirtLauncherClient) GetQemuVersion() (string, error) {
1✔
540
        request := &cmdv1.EmptyRequest{}
1✔
541
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
542
        defer cancel()
1✔
543

1✔
544
        versionResponse, err := c.v1client.GetQemuVersion(ctx, request)
1✔
545
        var response *cmdv1.Response
1✔
546
        if versionResponse != nil {
2✔
547
                response = versionResponse.Response
1✔
548
        }
1✔
549
        if err = handleError(err, "GetQemuVersion", response); err != nil {
1✔
550
                return "", err
×
551
        }
×
552

553
        if versionResponse != nil && versionResponse.Version != "" {
2✔
554
                return versionResponse.Version, nil
1✔
555
        }
1✔
556

557
        log.Log.Reason(err).Error("error getting the qemu version")
×
558
        return "", errors.New("error getting the qemu version")
×
559
}
560

561
func (c *VirtLauncherClient) GetDomainStats() (*stats.DomainStats, bool, error) {
1✔
562
        stats := &stats.DomainStats{}
1✔
563
        exists := false
1✔
564

1✔
565
        request := &cmdv1.EmptyRequest{}
1✔
566
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
567
        defer cancel()
1✔
568

1✔
569
        domainStatsResponse, err := c.v1client.GetDomainStats(ctx, request)
1✔
570
        var response *cmdv1.Response
1✔
571
        if domainStatsResponse != nil {
2✔
572
                response = domainStatsResponse.Response
1✔
573
        }
1✔
574

575
        if err = handleError(err, "GetDomainStats", response); err != nil || domainStatsResponse == nil {
1✔
576
                return stats, exists, err
×
577
        }
×
578

579
        if domainStatsResponse.DomainStats != "" {
2✔
580
                if err := json.Unmarshal([]byte(domainStatsResponse.DomainStats), stats); err != nil {
1✔
581
                        log.Log.Reason(err).Error("error unmarshalling domain")
×
582
                        return stats, exists, err
×
583
                }
×
584
                exists = true
1✔
585
        }
586
        return stats, exists, nil
1✔
587
}
588

589
func (c *VirtLauncherClient) Ping() error {
2✔
590
        request := &cmdv1.EmptyRequest{}
2✔
591
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
2✔
592
        defer cancel()
2✔
593
        response, err := c.v1client.Ping(ctx, request)
2✔
594

2✔
595
        err = handleError(err, "Ping", response)
2✔
596
        return err
2✔
597
}
2✔
598

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

×
603
        request := &cmdv1.EmptyRequest{}
×
604
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
×
605
        defer cancel()
×
606

×
607
        gaRespose, err := c.v1client.GetGuestInfo(ctx, request)
×
608
        var response *cmdv1.Response
×
609
        if gaRespose != nil {
×
610
                response = gaRespose.Response
×
611
        }
×
612

613
        if err = handleError(err, "GetGuestInfo", response); err != nil || gaRespose == nil {
×
614
                return guestInfo, err
×
615
        }
×
616

617
        if gaRespose.GuestInfoResponse != "" {
×
618
                if err := json.Unmarshal([]byte(gaRespose.GetGuestInfoResponse()), guestInfo); err != nil {
×
619
                        log.Log.Reason(err).Error("error unmarshalling guest agent response")
×
620
                        return guestInfo, err
×
621
                }
×
622
        }
623
        return guestInfo, nil
×
624
}
625

626
// GetUsers returns the list of the active users on the guest machine
627
func (c *VirtLauncherClient) GetUsers() (v1.VirtualMachineInstanceGuestOSUserList, error) {
1✔
628
        var userList []v1.VirtualMachineInstanceGuestOSUser
1✔
629

1✔
630
        request := &cmdv1.EmptyRequest{}
1✔
631
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
632
        defer cancel()
1✔
633

1✔
634
        uResponse, err := c.v1client.GetUsers(ctx, request)
1✔
635
        var response *cmdv1.Response
1✔
636
        if uResponse != nil {
2✔
637
                response = uResponse.Response
1✔
638
        }
1✔
639

640
        if err = handleError(err, "GetUsers", response); err != nil || uResponse == nil {
1✔
641
                return v1.VirtualMachineInstanceGuestOSUserList{}, err
×
642
        }
×
643

644
        if uResponse.GetGuestUserListResponse() != "" {
2✔
645
                if err := json.Unmarshal([]byte(uResponse.GetGuestUserListResponse()), &userList); err != nil {
1✔
646
                        log.Log.Reason(err).Error("error unmarshalling guest user list response")
×
647
                        return v1.VirtualMachineInstanceGuestOSUserList{}, err
×
648
                }
×
649
        }
650

651
        guestUserList := v1.VirtualMachineInstanceGuestOSUserList{
1✔
652
                Items: userList,
1✔
653
        }
1✔
654

1✔
655
        return guestUserList, nil
1✔
656
}
657

658
// GetFilesystems returns the list of active filesystems on the guest machine
659
func (c *VirtLauncherClient) GetFilesystems() (v1.VirtualMachineInstanceFileSystemList, error) {
1✔
660
        var fsList []v1.VirtualMachineInstanceFileSystem
1✔
661

1✔
662
        request := &cmdv1.EmptyRequest{}
1✔
663
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
664
        defer cancel()
1✔
665

1✔
666
        fsResponse, err := c.v1client.GetFilesystems(ctx, request)
1✔
667
        var response *cmdv1.Response
1✔
668
        if fsResponse != nil {
2✔
669
                response = fsResponse.Response
1✔
670
        }
1✔
671

672
        if err = handleError(err, "GetFilesystems", response); err != nil || fsResponse == nil {
1✔
673
                return v1.VirtualMachineInstanceFileSystemList{}, err
×
674
        }
×
675

676
        if fsResponse.GetGuestFilesystemsResponse() != "" {
2✔
677
                if err := json.Unmarshal([]byte(fsResponse.GetGuestFilesystemsResponse()), &fsList); err != nil {
1✔
678
                        log.Log.Reason(err).Error("error unmarshalling guest filesystem list response")
×
679
                        return v1.VirtualMachineInstanceFileSystemList{}, err
×
680
                }
×
681
        }
682

683
        filesystemList := v1.VirtualMachineInstanceFileSystemList{
1✔
684
                Items: fsList,
1✔
685
        }
1✔
686

1✔
687
        return filesystemList, nil
1✔
688
}
689

690
// Exec the command with args on the guest and return the resulting status code, stdOut and error
691
func (c *VirtLauncherClient) Exec(domainName, command string, args []string, timeoutSeconds int32) (int, string, error) {
4✔
692
        request := &cmdv1.ExecRequest{
4✔
693
                DomainName:     domainName,
4✔
694
                Command:        command,
4✔
695
                Args:           args,
4✔
696
                TimeoutSeconds: timeoutSeconds,
4✔
697
        }
4✔
698
        exitCode := -1
4✔
699
        stdOut := ""
4✔
700

4✔
701
        ctx, cancel := context.WithTimeout(
4✔
702
                context.Background(),
4✔
703
                // we give the context a bit more time as the timeout should kick
4✔
704
                // on the actual execution
4✔
705
                time.Duration(timeoutSeconds)*time.Second+shortTimeout,
4✔
706
        )
4✔
707
        defer cancel()
4✔
708

4✔
709
        resp, err := c.v1client.Exec(ctx, request)
4✔
710
        if resp == nil {
6✔
711
                return exitCode, stdOut, err
2✔
712
        }
2✔
713

714
        exitCode = int(resp.ExitCode)
2✔
715
        stdOut = resp.StdOut
2✔
716

2✔
717
        return exitCode, stdOut, err
2✔
718
}
719

720
func (c *VirtLauncherClient) GuestPing(domainName string, timeoutSeconds int32) error {
3✔
721
        request := &cmdv1.GuestPingRequest{
3✔
722
                DomainName:     domainName,
3✔
723
                TimeoutSeconds: timeoutSeconds,
3✔
724
        }
3✔
725
        ctx, cancel := context.WithTimeout(
3✔
726
                context.Background(),
3✔
727
                // we give the context a bit more time as the timeout should kick
3✔
728
                // on the actual execution
3✔
729
                time.Duration(timeoutSeconds)*time.Second+shortTimeout,
3✔
730
        )
3✔
731
        defer cancel()
3✔
732

3✔
733
        _, err := c.v1client.GuestPing(ctx, request)
3✔
734
        return err
3✔
735
}
3✔
736

737
func (c *VirtLauncherClient) GetScreenshot(vmi *v1.VirtualMachineInstance) (*cmdv1.ScreenshotResponse, error) {
×
738
        vmiJson, err := json.Marshal(vmi)
×
739
        if err != nil {
×
740
                return nil, err
×
741
        }
×
742

743
        request := &cmdv1.VMIRequest{
×
744
                Vmi: &cmdv1.VMI{
×
745
                        VmiJson: vmiJson,
×
746
                },
×
747
        }
×
748

×
749
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
×
750
        defer cancel()
×
751

×
752
        return c.v1client.GetScreenshot(ctx, request)
×
753
}
754

755
func (c *VirtLauncherClient) GetSEVInfo() (*v1.SEVPlatformInfo, error) {
1✔
756
        request := &cmdv1.EmptyRequest{}
1✔
757
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
758
        defer cancel()
1✔
759

1✔
760
        sevInfoResponse, err := c.v1client.GetSEVInfo(ctx, request)
1✔
761
        if err = handleError(err, "GetSEVInfo", sevInfoResponse.GetResponse()); err != nil {
1✔
762
                return nil, err
×
763
        }
×
764

765
        sevPlatformInfo := &v1.SEVPlatformInfo{}
1✔
766
        if err := json.Unmarshal(sevInfoResponse.GetSevInfo(), sevPlatformInfo); err != nil {
1✔
767
                log.Log.Reason(err).Error("error unmarshalling SEV info response")
×
768
                return nil, err
×
769
        }
×
770

771
        return sevPlatformInfo, nil
1✔
772
}
773

774
func (c *VirtLauncherClient) GetLaunchMeasurement(vmi *v1.VirtualMachineInstance) (*v1.SEVMeasurementInfo, error) {
1✔
775
        vmiJson, err := json.Marshal(vmi)
1✔
776
        if err != nil {
1✔
777
                return nil, err
×
778
        }
×
779

780
        request := &cmdv1.VMIRequest{
1✔
781
                Vmi: &cmdv1.VMI{
1✔
782
                        VmiJson: vmiJson,
1✔
783
                },
1✔
784
        }
1✔
785

1✔
786
        ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
1✔
787
        defer cancel()
1✔
788

1✔
789
        launchMeasurementRespose, err := c.v1client.GetLaunchMeasurement(ctx, request)
1✔
790
        if err = handleError(err, "GetLaunchMeasurement", launchMeasurementRespose.GetResponse()); err != nil {
1✔
791
                return nil, err
×
792
        }
×
793

794
        sevMeasurementInfo := &v1.SEVMeasurementInfo{}
1✔
795
        if err := json.Unmarshal(launchMeasurementRespose.GetLaunchMeasurement(), sevMeasurementInfo); err != nil {
1✔
796
                log.Log.Reason(err).Error("error unmarshalling launch measurement response")
×
797
                return nil, err
×
798
        }
×
799

800
        return sevMeasurementInfo, nil
1✔
801
}
802

803
func (c *VirtLauncherClient) InjectLaunchSecret(vmi *v1.VirtualMachineInstance, sevSecretOptions *v1.SEVSecretOptions) error {
1✔
804
        vmiJson, err := json.Marshal(vmi)
1✔
805
        if err != nil {
1✔
806
                return err
×
807
        }
×
808

809
        optionsJson, err := json.Marshal(sevSecretOptions)
1✔
810
        if err != nil {
1✔
811
                return err
×
812
        }
×
813

814
        request := &cmdv1.InjectLaunchSecretRequest{
1✔
815
                Vmi: &cmdv1.VMI{
1✔
816
                        VmiJson: vmiJson,
1✔
817
                },
1✔
818
                Options: optionsJson,
1✔
819
        }
1✔
820

1✔
821
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
1✔
822
        defer cancel()
1✔
823

1✔
824
        response, err := c.v1client.InjectLaunchSecret(ctx, request)
1✔
825

1✔
826
        return handleError(err, "InjectLaunchSecret", response)
1✔
827
}
828

829
func (c *VirtLauncherClient) SyncVirtualMachineMemory(vmi *v1.VirtualMachineInstance, options *cmdv1.VirtualMachineOptions) error {
1✔
830
        return c.genericSendVMICmd("SyncVirtualMachineMemory", c.v1client.SyncVirtualMachineMemory, vmi, options)
1✔
831
}
1✔
832

833
func (c *VirtLauncherClient) VirtualMachineBackup(vmi *v1.VirtualMachineInstance, options *backupv1.BackupOptions) error {
×
834
        vmiJson, err := json.Marshal(vmi)
×
835
        if err != nil {
×
836
                return err
×
837
        }
×
838

839
        optionsJson, err := json.Marshal(options)
×
840
        if err != nil {
×
841
                return err
×
842
        }
×
843

844
        request := &cmdv1.BackupRequest{
×
845
                Vmi: &cmdv1.VMI{
×
846
                        VmiJson: vmiJson,
×
847
                },
×
848
                Options: optionsJson,
×
849
        }
×
850

×
851
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
×
852
        defer cancel()
×
853
        response, err := c.v1client.BackupVirtualMachine(ctx, request)
×
854

×
855
        err = handleError(err, "Backup", response)
×
856
        return err
×
857
}
858

859
func (c *VirtLauncherClient) RedefineCheckpoint(vmi *v1.VirtualMachineInstance, checkpoint *backupv1.BackupCheckpoint) (checkpointInvalid bool, err error) {
4✔
860
        vmiJson, err := json.Marshal(vmi)
4✔
861
        if err != nil {
4✔
862
                return false, err
×
863
        }
×
864

865
        checkpointJson, err := json.Marshal(checkpoint)
4✔
866
        if err != nil {
4✔
867
                return false, err
×
868
        }
×
869

870
        request := &cmdv1.RedefineCheckpointRequest{
4✔
871
                Vmi: &cmdv1.VMI{
4✔
872
                        VmiJson: vmiJson,
4✔
873
                },
4✔
874
                Checkpoint: checkpointJson,
4✔
875
        }
4✔
876

4✔
877
        ctx, cancel := context.WithTimeout(context.Background(), longTimeout)
4✔
878
        defer cancel()
4✔
879
        response, err := c.v1client.RedefineCheckpoint(ctx, request)
4✔
880
        if err != nil {
4✔
881
                return false, fmt.Errorf("RedefineCheckpoint call failed: %v", err)
×
882
        }
×
883

884
        if response.Response != nil && !response.Response.Success {
7✔
885
                return response.CheckpointInvalid, fmt.Errorf("RedefineCheckpoint failed: %s", response.Response.Message)
3✔
886
        }
3✔
887

888
        return false, nil
1✔
889
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc