Skip to content

Commit

Permalink
modified some API definitions for NetworkBlockDeviceStorageDeviceAtta…
Browse files Browse the repository at this point in the history
…chment
  • Loading branch information
Code-Hex committed Nov 2, 2024
1 parent 07aef59 commit 239cc8d
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 95 deletions.
13 changes: 4 additions & 9 deletions configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type VirtualMachineConfiguration struct {
cpuCount uint
memorySize uint64
*pointer

storageDeviceConfiguration []StorageDeviceConfiguration
}

// NewVirtualMachineConfiguration creates a new configuration.
Expand Down Expand Up @@ -172,20 +174,13 @@ func (v *VirtualMachineConfiguration) SetStorageDevicesVirtualMachineConfigurati
}
array := objc.ConvertToNSMutableArray(ptrs)
C.setStorageDevicesVZVirtualMachineConfiguration(objc.Ptr(v), objc.Ptr(array))
v.storageDeviceConfiguration = cs
}

// StorageDevices return the list of storage device configuration configured in this virtual machine configuration.
// Return an empty array if no storage device configuration is set.
func (v *VirtualMachineConfiguration) StorageDevices() []StorageDeviceConfiguration {
nsArray := objc.NewNSArray(
C.storageDevicesVZVirtualMachineConfiguration(objc.Ptr(v)),
)
ptrs := nsArray.ToPointerSlice()
storageDevices := make([]StorageDeviceConfiguration, len(ptrs))
for i, ptr := range ptrs {
storageDevices[i] = newVirtioBlockDeviceConfiguration(ptr)
}
return storageDevices
return v.storageDeviceConfiguration
}

// SetDirectorySharingDevicesVirtualMachineConfiguration sets list of directory sharing devices. Empty by default.
Expand Down
84 changes: 46 additions & 38 deletions example/macOS/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,19 @@ func runVM(ctx context.Context) error {
}()

// it start listening to the NBD server, if any
listenNetworkBlockDevice(config)
nbdAttachment := retrieveNetworkBlockDeviceStorageDeviceAttachment(config.StorageDevices())
if nbdAttachment != nil {
go func() {
for {
select {
case err := <-nbdAttachment.DidEncounterError():
log.Printf("NBD client has been encountered error: %v\n", err)
case <-nbdAttachment.Connected():
log.Println("NBD client connected with the server")
}
}
}()
}

// cleanup is this function is useful when finished graphic application.
cleanup := func() {
Expand All @@ -105,9 +117,7 @@ func runVM(ctx context.Context) error {
log.Println("finished cleanup")
}

runtime.LockOSThread()
vm.StartGraphicApplication(960, 600)
runtime.UnlockOSThread()

cleanup()

Expand Down Expand Up @@ -147,29 +157,30 @@ func computeMemorySize() uint64 {
}

func createBlockDeviceConfiguration(diskPath string) (*vz.VirtioBlockDeviceConfiguration, error) {
var attachment vz.StorageDeviceAttachment
var err error

if nbdURL == "" {
// create disk image with 64 GiB
if err := vz.CreateDiskImage(diskPath, 64*1024*1024*1024); err != nil {
if !os.IsExist(err) {
return nil, fmt.Errorf("failed to create disk image: %w", err)
}
// create disk image with 64 GiB
if err := vz.CreateDiskImage(diskPath, 64*1024*1024*1024); err != nil {
if !os.IsExist(err) {
return nil, fmt.Errorf("failed to create disk image: %w", err)
}
}

attachment, err = vz.NewDiskImageStorageDeviceAttachment(
diskPath,
false,
)
} else {
attachment, err = vz.NewNetworkBlockDeviceStorageDeviceAttachment(
nbdURL,
10*time.Second,
false,
vz.DiskSynchronizationModeFull,
)
attachment, err := vz.NewDiskImageStorageDeviceAttachment(
diskPath,
false,
)
if err != nil {
return nil, err
}
return vz.NewVirtioBlockDeviceConfiguration(attachment)
}

func createNetworkBlockDeviceConfiguration(nbdURL string) (*vz.VirtioBlockDeviceConfiguration, error) {
attachment, err := vz.NewNetworkBlockDeviceStorageDeviceAttachment(
nbdURL,
10*time.Second,
false,
vz.DiskSynchronizationModeFull,
)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -280,7 +291,15 @@ func setupVMConfiguration(platformConfig vz.PlatformConfiguration) (*vz.VirtualM
if err != nil {
return nil, fmt.Errorf("failed to create block device configuration: %w", err)
}
config.SetStorageDevicesVirtualMachineConfiguration([]vz.StorageDeviceConfiguration{blockDeviceConfig})
sdconfigs := []vz.StorageDeviceConfiguration{blockDeviceConfig}
if nbdURL != "" {
ndbConfig, err := createNetworkBlockDeviceConfiguration(nbdURL)
if err != nil {
return nil, fmt.Errorf("failed to create network block device configuration: %w", err)
}
sdconfigs = append(sdconfigs, ndbConfig)
}
config.SetStorageDevicesVirtualMachineConfiguration(sdconfigs)

networkDeviceConfig, err := createNetworkDeviceConfiguration()
if err != nil {
Expand Down Expand Up @@ -335,22 +354,11 @@ func setupVMConfiguration(platformConfig vz.PlatformConfiguration) (*vz.VirtualM
return config, nil
}

func listenNetworkBlockDevice(vm *vz.VirtualMachineConfiguration) error {
storages := vm.StorageDevices()
func retrieveNetworkBlockDeviceStorageDeviceAttachment(storages []vz.StorageDeviceConfiguration) *vz.NetworkBlockDeviceStorageDeviceAttachment {
for _, storage := range storages {
attachment := storage.Attachment()
if nbdAttachment, isNbdAttachment := attachment.(*vz.NetworkBlockDeviceStorageDeviceAttachment); isNbdAttachment {
nbdAttachmentStatusCh := make(chan vz.NetworkBlockDeviceStorageDeviceAttachmentStatus)
nbdAttachment.Listen(nbdAttachmentStatusCh)
go func() {
for status := range nbdAttachmentStatusCh {
if status.IsConnected() {
log.Println("Successfully connected to NBD server")
} else {
log.Printf("Disconnected from NBD server. Error %v\n", status.Error().Error())
}
}
}()
if nbdAttachment, ok := attachment.(*vz.NetworkBlockDeviceStorageDeviceAttachment); ok {
return nbdAttachment
}
}
return nil
Expand Down
81 changes: 38 additions & 43 deletions storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"time"
"unsafe"

infinity "github.com/Code-Hex/go-infinity-channel"
"github.com/Code-Hex/vz/v3/internal/objc"
)

Expand Down Expand Up @@ -210,12 +211,6 @@ func NewVirtioBlockDeviceConfiguration(attachment StorageDeviceAttachment) (*Vir
return config, nil
}

func newVirtioBlockDeviceConfiguration(ptr unsafe.Pointer) *VirtioBlockDeviceConfiguration {
return &VirtioBlockDeviceConfiguration{
pointer: objc.NewPointer(ptr),
}
}

// BlockDeviceIdentifier returns the device identifier is a string identifying the Virtio block device.
// Empty string by default.
//
Expand Down Expand Up @@ -425,17 +420,8 @@ type NetworkBlockDeviceStorageDeviceAttachment struct {

*baseStorageDeviceAttachment

attachmentStatusCh chan networkBlockDeviceStorageDeviceAttachmentStatusData
}

type NetworkBlockDeviceStorageDeviceAttachmentStatus interface {
IsConnected() bool
Error() error
}

type networkBlockDeviceStorageDeviceAttachmentStatusData struct {
connected bool
err error
didEncounterError *infinity.Channel[error]
connected *infinity.Channel[struct{}]
}

var _ StorageDeviceAttachment = (*NetworkBlockDeviceStorageDeviceAttachment)(nil)
Expand All @@ -459,14 +445,15 @@ func NewNetworkBlockDeviceStorageDeviceAttachment(url string, timeout time.Durat
return nil, err
}

ch := make(chan networkBlockDeviceStorageDeviceAttachmentStatusData)
didEncounterError := infinity.NewChannel[error]()
connected := infinity.NewChannel[struct{}]()

handle := cgo.NewHandle(func(err error) {
connected := true
if err != nil {
connected = false
didEncounterError.In() <- err
return
}
ch <- networkBlockDeviceStorageDeviceAttachmentStatusData{connected, err}
connected.In() <- struct{}{}
})

nserrPtr := newNSErrorAsNil()
Expand All @@ -484,7 +471,8 @@ func NewNetworkBlockDeviceStorageDeviceAttachment(url string, timeout time.Durat
C.uintptr_t(handle),
),
),
attachmentStatusCh: ch,
didEncounterError: didEncounterError,
connected: connected,
}
if err := newNSError(nserrPtr); err != nil {
return nil, err
Expand All @@ -495,41 +483,48 @@ func NewNetworkBlockDeviceStorageDeviceAttachment(url string, timeout time.Durat
return attachment, nil
}

// Returns the connected value. It is true if the NBD client successfully connected to the server. False if there was an error that prevented the connection
func (n *networkBlockDeviceStorageDeviceAttachmentStatusData) IsConnected() bool {
return n.connected
}

// Returns the error associated to the failed connection of the NBD client to the server
func (n *networkBlockDeviceStorageDeviceAttachmentStatusData) Error() error {
return n.err
// Connected receive the signal via channel when the NBD client successfully connects or reconnects with the server.
//
// The NBD connection with the server takes place when the VM is first started, and reconnection attempts take place when the connection
// times out or when the NBD client has encountered a recoverable error, such as an I/O error from the server.
//
// Note that the Virtualization framework may call this method multiple times during a VM’s life cycle. Reconnections are transparent to the guest.
func (n *NetworkBlockDeviceStorageDeviceAttachment) Connected() <-chan struct{} {
return n.connected.Out()
}

// It allows the caller to subscribe to the attachmentStatus channel to listen to changes of the network block device attachment
// This way it can be informed if the NBD client successfully connects/reconnects to the server or it encounter an error
func (n *NetworkBlockDeviceStorageDeviceAttachment) Listen(statusCh chan NetworkBlockDeviceStorageDeviceAttachmentStatus) {
go func() {
for {
status := <-n.attachmentStatusCh
statusCh <- &status
}
}()
// The DidEncounterError is triggered via the channel when the NBD client encounters an error that cannot be resolved on the client side.
// In this state, the client will continue attempting to reconnect, but recovery depends entirely on the server's availability.
// If the server resumes operation, the connection will recover automatically; however, until the server is restored, the client will continue to experience errors.
func (n *NetworkBlockDeviceStorageDeviceAttachment) DidEncounterError() <-chan error {
return n.didEncounterError.Out()
}

//export attachmentHandler
func attachmentHandler(cgoHandleUintptr C.uintptr_t, errorPtr unsafe.Pointer) {
// attachmentDidEncounterErrorHandler function is called when the NBD client encounters a nonrecoverable error.
// After the attachment object calls this method, the NBD client is in a nonfunctional state.
//
//export attachmentDidEncounterErrorHandler
func attachmentDidEncounterErrorHandler(cgoHandleUintptr C.uintptr_t, errorPtr unsafe.Pointer) {
cgoHandle := cgo.Handle(cgoHandleUintptr)
handler := cgoHandle.Value().(func(error))

err := newNSError(errorPtr)

go handler(err)
handler(err)
}

// attachmentWasConnectedHandler function is called when a connection to the server is first established as the VM starts,
// and during any reconnection attempts triggered by connection timeouts or recoverable errors encountered by the NBD client,
// such as server-side I/O errors.
//
// Note that the Virtualization framework may invoke this method multiple times throughout the VM’s lifecycle,
// ensuring reconnection processes remain seamless and transparent to the guest.
// For more details, see: https://developer.apple.com/documentation/virtualization/vznetworkblockdevicestoragedeviceattachmentdelegate/4168511-attachmentwasconnected?language=objc
//
//export attachmentWasConnectedHandler
func attachmentWasConnectedHandler(cgoHandleUintptr C.uintptr_t) {
cgoHandle := cgo.Handle(cgoHandleUintptr)
handler := cgoHandle.Value().(func(error))

go handler(nil)
handler(nil)
}
2 changes: 1 addition & 1 deletion virtualization_14.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#import <Virtualization/Virtualization.h>

/* exported from cgo */
void attachmentHandler(uintptr_t cgoHandle, void *err);
void attachmentDidEncounterErrorHandler(uintptr_t cgoHandle, void *err);
void attachmentWasConnectedHandler(uintptr_t cgoHandle);

/* macOS 14 API */
Expand Down
3 changes: 1 addition & 2 deletions virtualization_14.m
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,11 @@ - (instancetype)initWithHandle:(uintptr_t)cgoHandle

- (void)attachment:(VZNetworkBlockDeviceStorageDeviceAttachment *)attachment didEncounterError:(NSError *)error
{
attachmentHandler(_cgoHandle, error);
attachmentDidEncounterErrorHandler(_cgoHandle, error);
}

- (void)attachmentWasConnected:(VZNetworkBlockDeviceStorageDeviceAttachment *)attachment
{
attachmentWasConnectedHandler(_cgoHandle);
}
@end

4 changes: 2 additions & 2 deletions virtualization_15.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ bool isNestedVirtualizationSupported()
{
#ifdef INCLUDE_TARGET_OSX_15
if (@available(macOS 15, *)) {
return (bool) VZGenericPlatformConfiguration.isNestedVirtualizationSupported;
return (bool)VZGenericPlatformConfiguration.isNestedVirtualizationSupported;
}
#endif
RAISE_UNSUPPORTED_MACOS_EXCEPTION();
Expand All @@ -25,7 +25,7 @@ void setNestedVirtualizationEnabled(void *config, bool nestedVirtualizationEnabl
#ifdef INCLUDE_TARGET_OSX_15
if (@available(macOS 15, *)) {
VZGenericPlatformConfiguration *platformConfig = (VZGenericPlatformConfiguration *)config;
platformConfig.nestedVirtualizationEnabled = (BOOL) nestedVirtualizationEnabled;
platformConfig.nestedVirtualizationEnabled = (BOOL)nestedVirtualizationEnabled;
return;
}
#endif
Expand Down

0 comments on commit 239cc8d

Please sign in to comment.