-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathhypervisor.go
More file actions
331 lines (266 loc) · 12.3 KB
/
Copy pathhypervisor.go
File metadata and controls
331 lines (266 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// Package hypervisor provides an abstraction layer for virtual machine managers.
// This allows the instances package to work with different hypervisors
// (e.g., Cloud Hypervisor, QEMU) through a common interface.
package hypervisor
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/kernel/hypeman/lib/paths"
)
// Common errors
var (
// ErrHypervisorNotRunning is returned when trying to connect to a hypervisor
// that is not currently running or cannot be reconnected to.
ErrHypervisorNotRunning = errors.New("hypervisor is not running")
// ErrNotSupported is returned when an operation is not supported by the hypervisor.
ErrNotSupported = errors.New("operation not supported by this hypervisor")
)
// Type identifies the hypervisor implementation
type Type string
const (
// TypeCloudHypervisor is the Cloud Hypervisor VMM
TypeCloudHypervisor Type = "cloud-hypervisor"
// TypeFirecracker is the Firecracker VMM
TypeFirecracker Type = "firecracker"
// TypeQEMU is the QEMU VMM
TypeQEMU Type = "qemu"
// TypeVZ is the Virtualization.framework VMM (macOS only)
TypeVZ Type = "vz"
)
// socketNames maps hypervisor types to their socket filenames.
// Registered by each hypervisor package's init() function.
var socketNames = make(map[Type]string)
// vsockSocketNames maps hypervisor types to their vsock socket filenames.
// Registered by hypervisor packages when they use socket-based vsock routing.
var vsockSocketNames = make(map[Type]string)
// capabilitiesByType maps hypervisor types to their static capabilities.
// Registered by each hypervisor package's init() function.
var capabilitiesByType = make(map[Type]Capabilities)
// RegisterSocketName registers the socket filename for a hypervisor type.
// Called by each hypervisor implementation's init() function.
func RegisterSocketName(t Type, name string) {
socketNames[t] = name
}
// SocketNameForType returns the socket filename for a hypervisor type.
// Falls back to type + ".sock" if not registered.
func SocketNameForType(t Type) string {
if name, ok := socketNames[t]; ok {
return name
}
return string(t) + ".sock"
}
// RegisterVsockSocketName registers the vsock socket filename for a hypervisor type.
func RegisterVsockSocketName(t Type, name string) {
vsockSocketNames[t] = name
}
// VsockSocketNameForType returns the vsock socket filename for a hypervisor type.
// Falls back to "vsock.sock" when a hypervisor doesn't require a custom name.
func VsockSocketNameForType(t Type) string {
if name, ok := vsockSocketNames[t]; ok {
return name
}
return "vsock.sock"
}
// RegisterCapabilities registers static capabilities for a hypervisor type.
func RegisterCapabilities(t Type, caps Capabilities) {
capabilitiesByType[t] = caps
}
// CapabilitiesForType returns static capabilities for a hypervisor type.
func CapabilitiesForType(t Type) (Capabilities, bool) {
caps, ok := capabilitiesByType[t]
return caps, ok
}
// VMStarter handles the full VM startup sequence.
// Each hypervisor implements its own startup flow:
// - Cloud Hypervisor: starts process, configures via HTTP API, boots via HTTP API
// - QEMU: converts config to command-line args, starts process (VM runs immediately)
type VMStarter interface {
// SocketName returns the socket filename for this hypervisor.
// Uses short names to stay within Unix socket path length limits (SUN_LEN ~108 bytes).
SocketName() string
// GetBinaryPath returns the path to the hypervisor binary, extracting if needed.
GetBinaryPath(p *paths.Paths, version string) (string, error)
// GetVersion returns the version of the hypervisor binary.
// For embedded binaries (Cloud Hypervisor), returns the latest supported version.
// For system binaries (QEMU), queries the installed binary for its version.
GetVersion(p *paths.Paths) (string, error)
// StartVM launches the hypervisor process and boots the VM.
// Returns the process ID and a Hypervisor client for subsequent operations.
StartVM(ctx context.Context, p *paths.Paths, version string, socketPath string, config VMConfig) (pid int, hv Hypervisor, err error)
// RestoreVM starts the hypervisor and restores VM state from a snapshot.
// Each hypervisor implements its own restore flow:
// - Cloud Hypervisor: starts process, calls Restore API
// - QEMU: would start with -incoming or -loadvm flags (not yet implemented)
// Returns the process ID and a Hypervisor client. The VM is in paused state after restore.
RestoreVM(ctx context.Context, p *paths.Paths, version string, socketPath string, snapshotPath string, opts RestoreOptions) (pid int, hv Hypervisor, err error)
// PrepareFork allows hypervisors to prepare forked instance state.
// For snapshot-based forks, implementations can rewrite snapshot config with
// fork identity (paths, vsock, network). Hypervisors that don't support fork
// should return ErrNotSupported.
PrepareFork(ctx context.Context, req ForkPrepareRequest) (ForkPrepareResult, error)
}
type SnapshotMemoryBackend string
const (
SnapshotMemoryBackendFile SnapshotMemoryBackend = "file"
SnapshotMemoryBackendUFFD SnapshotMemoryBackend = "uffd"
)
type RestoreOptions struct {
SnapshotMemoryBackend SnapshotMemoryBackend
SnapshotMemoryBackingPath string
SnapshotMemoryCacheKey string
SnapshotMemorySessionID string
}
type SnapshotOptions struct {
DeferredMemoryBackingPath string
}
// ForkNetworkConfig contains network identity fields for fork preparation.
type ForkNetworkConfig struct {
TAPDevice string
IP string
MAC string
Netmask string
}
// ForkPrepareRequest contains hypervisor-specific fork preparation inputs.
type ForkPrepareRequest struct {
// SnapshotConfigPath is optional. When empty, implementations should only
// validate fork support and return without snapshot rewrites.
SnapshotConfigPath string
SourceDataDir string
TargetDataDir string
VsockCID int64
VsockSocket string
SerialLogPath string
Network *ForkNetworkConfig
}
// ForkPrepareResult describes which optional fork rewrites were actually applied.
type ForkPrepareResult struct {
// VsockCIDUpdated indicates whether snapshot state was updated to use
// ForkPrepareRequest.VsockCID.
VsockCIDUpdated bool
// RequiresSnapshotSourceAlias indicates the restored fork still depends on
// temporarily aliasing the source data directory during snapshot load.
RequiresSnapshotSourceAlias bool
}
// Hypervisor defines the interface for VM control operations.
// A Hypervisor client is returned by VMStarter.StartVM after the VM is running.
type Hypervisor interface {
// DeleteVM sends a graceful shutdown signal to the guest.
DeleteVM(ctx context.Context) error
// Shutdown stops the VMM process gracefully.
Shutdown(ctx context.Context) error
// GetVMInfo returns current VM state information.
GetVMInfo(ctx context.Context) (*VMInfo, error)
// Pause suspends VM execution.
// Check Capabilities().SupportsPause before calling.
Pause(ctx context.Context) error
// Resume continues VM execution after pause.
// Check Capabilities().SupportsPause before calling.
Resume(ctx context.Context) error
// Snapshot creates a VM snapshot at the given path.
// Check Capabilities().SupportsSnapshot before calling.
Snapshot(ctx context.Context, destPath string, opts SnapshotOptions) error
// ResizeMemory changes the VM's memory allocation.
// Check Capabilities().SupportsHotplugMemory before calling.
ResizeMemory(ctx context.Context, bytes int64) error
// ResizeMemoryAndWait changes the VM's memory allocation and waits for it to stabilize.
// This polls until the actual memory size matches the target or stabilizes.
// Check Capabilities().SupportsHotplugMemory before calling.
ResizeMemoryAndWait(ctx context.Context, bytes int64, timeout time.Duration) error
// SetTargetGuestMemoryBytes adjusts the runtime balloon target so the guest
// sees the requested amount of RAM.
// Check Capabilities().SupportsBalloonControl before calling.
SetTargetGuestMemoryBytes(ctx context.Context, bytes int64) error
// GetTargetGuestMemoryBytes returns the current guest-visible RAM target after
// runtime ballooning is applied.
// Check Capabilities().SupportsBalloonControl before calling.
GetTargetGuestMemoryBytes(ctx context.Context) (int64, error)
// Capabilities returns what features this hypervisor supports.
Capabilities() Capabilities
}
// Capabilities indicates which optional features a hypervisor supports.
// Callers should check these before calling optional methods.
type Capabilities struct {
// SupportsSnapshot indicates if Snapshot/Restore are available
SupportsSnapshot bool
// SupportsHotplugMemory indicates if ResizeMemory is available
SupportsHotplugMemory bool
// SupportsBalloonControl indicates if runtime balloon target changes are available.
SupportsBalloonControl bool
// SupportsPause indicates if Pause/Resume are available
SupportsPause bool
// SupportsVsock indicates if vsock communication is available
SupportsVsock bool
// SupportsGPUPassthrough indicates if PCI device passthrough is available
SupportsGPUPassthrough bool
// SupportsDiskIOLimit indicates if disk I/O rate limiting is available
SupportsDiskIOLimit bool
// SupportsGracefulVMMShutdown indicates the hypervisor exposes an API to
// ask the VMM process itself to exit cleanly.
SupportsGracefulVMMShutdown bool
// SupportsSnapshotBaseReuse indicates snapshots can safely reuse a retained
// on-disk base across restore/standby cycles.
SupportsSnapshotBaseReuse bool
// SupportsConcurrentForkPrepare indicates stopped/standby forks can prepare
// separate target snapshots concurrently from the same source.
SupportsConcurrentForkPrepare bool
// SupportsDiskResize indicates if live disk resizing (/vm.resize-disk) is available.
// Cloud Hypervisor v50.0+ only.
SupportsDiskResize bool
// UsesDetachableSnapshotMemoryPager indicates restores can be backed by an
// external snapshot-memory pager that a running VM can later be detached
// from (populate remaining pages, then release the session).
UsesDetachableSnapshotMemoryPager bool
}
// VsockDialer provides vsock connectivity to a guest VM.
// Each hypervisor implements its own connection method:
// - Cloud Hypervisor: Unix socket file + text handshake protocol
// - QEMU: Kernel AF_VSOCK with CID-based addressing
type VsockDialer interface {
// DialVsock connects to the guest on the specified port.
// Returns a net.Conn that can be used for bidirectional communication.
DialVsock(ctx context.Context, port int) (net.Conn, error)
// Key returns a unique identifier for this dialer, used for connection pooling.
Key() string
}
// VsockDialerFactory creates VsockDialer instances for a hypervisor type.
type VsockDialerFactory func(vsockSocket string, vsockCID int64) VsockDialer
// vsockDialerFactories maps hypervisor types to their dialer factories.
// Registered by each hypervisor package's init() function.
var vsockDialerFactories = make(map[Type]VsockDialerFactory)
// RegisterVsockDialerFactory registers a VsockDialer factory for a hypervisor type.
// Called by each hypervisor implementation's init() function.
func RegisterVsockDialerFactory(t Type, factory VsockDialerFactory) {
vsockDialerFactories[t] = factory
}
// NewVsockDialer creates a VsockDialer for the given hypervisor type.
// Returns an error if the hypervisor type doesn't have a registered factory.
func NewVsockDialer(hvType Type, vsockSocket string, vsockCID int64) (VsockDialer, error) {
factory, ok := vsockDialerFactories[hvType]
if !ok {
return nil, fmt.Errorf("no vsock dialer registered for hypervisor type: %s", hvType)
}
return factory(vsockSocket, vsockCID), nil
}
// ClientFactory creates Hypervisor client instances for a hypervisor type.
type ClientFactory func(socketPath string) (Hypervisor, error)
// clientFactories maps hypervisor types to their client factories.
var clientFactories = make(map[Type]ClientFactory)
// RegisterClientFactory registers a Hypervisor client factory.
func RegisterClientFactory(t Type, factory ClientFactory) {
clientFactories[t] = factory
}
// NewClient creates a Hypervisor client for the given type and socket.
func NewClient(hvType Type, socketPath string) (Hypervisor, error) {
factory, ok := clientFactories[hvType]
if !ok {
return nil, fmt.Errorf("no client factory registered for hypervisor type: %s", hvType)
}
client, err := factory(socketPath)
if err != nil {
return nil, err
}
return WrapHypervisor(hvType, client), nil
}