diff --git a/cmd/vfkit/main.go b/cmd/vfkit/main.go index ca526da4..e97c6afa 100644 --- a/cmd/vfkit/main.go +++ b/cmd/vfkit/main.go @@ -131,7 +131,7 @@ func runVFKit(vmConfig *config.VirtualMachine, opts *cmdline.Options) error { // Do not enable the rests server if user sets scheme to None if opts.RestfulURI != cmdline.DefaultRestfulURI { - restVM := restvf.NewVzVirtualMachine(vm, vzVMConfig) + restVM := restvf.NewVzVirtualMachine(vm, vzVMConfig, vmConfig) srv, err := rest.NewServer(restVM, restVM, opts.RestfulURI) if err != nil { return err diff --git a/pkg/config/json_test.go b/pkg/config/json_test.go index 9345ebb1..0fb751c3 100644 --- a/pkg/config/json_test.go +++ b/pkg/config/json_test.go @@ -56,7 +56,7 @@ var jsonTests = map[string]jsonTest{ require.NoError(t, err) return vm }, - expectedJSON: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"virtioblk","DevName":"virtio-blk","ImagePath":"/virtioblk1","ReadOnly":false,"DeviceIdentifier":""},{"kind":"virtioblk","DevName":"virtio-blk","ImagePath":"/virtioblk2","ReadOnly":false,"DeviceIdentifier":"virtio-blk2"}]}`, + expectedJSON: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"virtioblk","devName":"virtio-blk","imagePath":"/virtioblk1","readOnly":false,"deviceIdentifier":""},{"kind":"virtioblk","devName":"virtio-blk","imagePath":"/virtioblk2","readOnly":false,"deviceIdentifier":"virtio-blk2"}]}`, }, "TestAllVirtioDevices": { newVM: func(t *testing.T) *VirtualMachine { @@ -110,7 +110,7 @@ var jsonTests = map[string]jsonTest{ return vm }, - expectedJSON: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"virtioserial","LogFile":"/virtioserial","UsesStdio":false},{"kind":"virtioinput","inputType":"keyboard"},{"kind":"virtiogpu","usesGUI":false,"width":800,"height":600},{"kind":"virtionet","Nat":true,"MacAddress":"ABEiM0RV","Socket":null,"UnixSocketPath":""},{"kind":"virtiorng"},{"kind":"virtioblk","DevName":"virtio-blk","ImagePath":"/virtioblk","ReadOnly":false,"DeviceIdentifier":""},{"kind":"virtiosock","Port":1234,"SocketURL":"/virtiovsock","Listen":false},{"kind":"virtiofs","MountTag":"tag","SharedDir":"/virtiofs"},{"kind":"usbmassstorage","DevName":"usb-mass-storage","ImagePath":"/usbmassstorage","ReadOnly":false},{"kind":"rosetta","MountTag":"vz-rosetta","InstallRosetta":false}]}`, + expectedJSON: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"virtioserial","logFile":"/virtioserial","usesStdio":false},{"kind":"virtioinput","inputType":"keyboard"},{"kind":"virtiogpu","usesGUI":false,"width":800,"height":600},{"kind":"virtionet","nat":true,"macAddress":"ABEiM0RV","socket":null,"unixSocketPath":""},{"kind":"virtiorng"},{"kind":"virtioblk","devName":"virtio-blk","imagePath":"/virtioblk","readOnly":false,"deviceIdentifier":""},{"kind":"virtiosock","port":1234,"socketURL":"/virtiovsock","listen":false},{"kind":"virtiofs","mountTag":"tag","sharedDir":"/virtiofs"},{"kind":"usbmassstorage","devName":"usb-mass-storage","imagePath":"/usbmassstorage","readOnly":false},{"kind":"rosetta","mountTag":"vz-rosetta","installRosetta":false}]}`, }, } @@ -129,13 +129,13 @@ var invalidJSONTests = map[string]invalidJSONTest{ json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"}}`, }, "TestEmptyDeviceKind": { - json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"","DevName":"virtio-blk","ImagePath":"/virtioblk1","ReadOnly":false,"DeviceIdentifier":""}]}`, + json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"","devName":"virtio-blk","imagePath":"/virtioblk1","readOnly":false,"deviceIdentifier":""}]}`, }, "TestInvalidDeviceKind": { - json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"invalid","DevName":"virtio-blk","ImagePath":"/virtioblk1","ReadOnly":false,"DeviceIdentifier":""}]}`, + json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"kind":"invalid","devName":"virtio-blk","imagePath":"/virtioblk1","readOnly":false,"deviceIdentifier":""}]}`, }, "TestMissingDeviceKind": { - json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"DevName":"virtio-blk","ImagePath":"/virtioblk1","ReadOnly":false,"DeviceIdentifier":""}]}`, + json: `{"vcpus":3,"memoryBytes":4000000000,"bootloader":{"kind":"linuxBootloader","VmlinuzPath":"/vmlinuz","KernelCmdLine":"/initrd","InitrdPath":"console=hvc0"},"devices":[{"devName":"virtio-blk","imagePath":"/virtioblk1","readOnly":false,"deviceIdentifier":""}]}`, }, } diff --git a/pkg/config/virtio.go b/pkg/config/virtio.go index c8631e84..51668009 100644 --- a/pkg/config/virtio.go +++ b/pkg/config/virtio.go @@ -47,34 +47,34 @@ type VirtioGPU struct { type VirtioVsock struct { // Port is the virtio-vsock port used for this device, see `man vsock` for more // details. - Port uint + Port uint `json:"port"` // SocketURL is the path to a unix socket on the host to use for the virtio-vsock communication with the guest. - SocketURL string + SocketURL string `json:"socketURL"` // If true, vsock connections will have to be done from guest to host. If false, vsock connections will only be possible // from host to guest - Listen bool + Listen bool `json:"listen"` } // VirtioBlk configures a disk device. type VirtioBlk struct { StorageConfig - DeviceIdentifier string + DeviceIdentifier string `json:"deviceIdentifier"` } type DirectorySharingConfig struct { - MountTag string + MountTag string `json:"mountTag"` } // VirtioFs configures directory sharing between the guest and the host. type VirtioFs struct { DirectorySharingConfig - SharedDir string + SharedDir string `json:"sharedDir"` } // RosettaShare configures rosetta support in the guest to run Intel binaries on Apple CPUs type RosettaShare struct { DirectorySharingConfig - InstallRosetta bool + InstallRosetta bool `json:"installRosetta"` } // NVMExpressController configures a NVMe controller in the guest @@ -91,19 +91,19 @@ type VirtioRng struct { // VirtioNet configures the virtual machine networking. type VirtioNet struct { - Nat bool - MacAddress net.HardwareAddr + Nat bool `json:"nat"` + MacAddress net.HardwareAddr `json:"macAddress"` // file parameter is holding a connected datagram socket. // see https://github.com/Code-Hex/vz/blob/7f648b6fb9205d6f11792263d79876e3042c33ec/network.go#L113-L155 - Socket *os.File + Socket *os.File `json:"socket"` - UnixSocketPath string + UnixSocketPath string `json:"unixSocketPath"` } // VirtioSerial configures the virtual machine serial ports. type VirtioSerial struct { - LogFile string - UsesStdio bool + LogFile string `json:"logFile"` + UsesStdio bool `json:"usesStdio"` } // TODO: Add VirtioBalloon @@ -676,9 +676,9 @@ func USBMassStorageNew(imagePath string) (VMComponent, error) { // StorageConfig configures a disk device. type StorageConfig struct { - DevName string - ImagePath string - ReadOnly bool + DevName string `json:"devName"` + ImagePath string `json:"imagePath"` + ReadOnly bool `json:"readOnly"` } func (config *StorageConfig) ToCmdLine() ([]string, error) { diff --git a/pkg/rest/define/config.go b/pkg/rest/define/config.go index 62f85cf1..6a604fbd 100644 --- a/pkg/rest/define/config.go +++ b/pkg/rest/define/config.go @@ -1,11 +1,36 @@ package define +import ( + "github.com/crc-org/vfkit/pkg/config" +) + +type VirtioNetResponse struct { + Nat bool `json:"nat"` + MacAddress string `json:"macAddress"` + UnixSocketPath string `json:"unixSocketPath"` + Fd int `json:"fd"` +} + +type DevicesResponse struct { + Input []config.VirtioInput `json:"input"` + GPU []config.VirtioGPU `json:"gpu"` + Vsock []config.VirtioVsock `json:"vsock"` + Blk []config.VirtioBlk `json:"blk"` + FS []config.VirtioFs `json:"fs"` + Rosetta config.RosettaShare `json:"rosetta"` + NVMe []config.NVMExpressController `json:"nvme"` + Net []VirtioNetResponse `json:"net"` + Rng bool `json:"rng"` + Serial config.VirtioSerial `json:"serial"` + USBMassStorage []config.USBMassStorage `json:"usbMassStorage"` +} + // InspectResponse is used when responding to a request for // information about the virtual machine type InspectResponse struct { - CPUs uint `json:"cpus"` - Memory uint64 `json:"memory"` - // Devices []config.VirtioDevice `json:"devices"` + CPUs uint `json:"cpus"` + Memory uint64 `json:"memory"` + Devices DevicesResponse `json:"devices"` } // VMState can be used to describe the current state of a VM diff --git a/pkg/rest/vf/vm_config.go b/pkg/rest/vf/vm_config.go index 5f5cbbd5..5dcd2e71 100644 --- a/pkg/rest/vf/vm_config.go +++ b/pkg/rest/vf/vm_config.go @@ -2,30 +2,80 @@ package rest import ( "net/http" + "sync" "github.com/Code-Hex/vz/v3" + "github.com/crc-org/vfkit/pkg/config" "github.com/crc-org/vfkit/pkg/rest/define" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" ) type VzVirtualMachine struct { - VzVM *vz.VirtualMachine - config *vz.VirtualMachineConfiguration + VzVM *vz.VirtualMachine + config *vz.VirtualMachineConfiguration + vmConfig *config.VirtualMachine } -func NewVzVirtualMachine(vm *vz.VirtualMachine, config *vz.VirtualMachineConfiguration) *VzVirtualMachine { - return &VzVirtualMachine{config: config, VzVM: vm} +func NewVzVirtualMachine(vm *vz.VirtualMachine, config *vz.VirtualMachineConfiguration, vmConfig *config.VirtualMachine) *VzVirtualMachine { + return &VzVirtualMachine{config: config, VzVM: vm, vmConfig: vmConfig} +} + +var ( + once sync.Once + devicesResponse define.DevicesResponse +) + +func devicesToResp(devices []config.VirtioDevice) define.DevicesResponse { + once.Do(func() { + for _, dev := range devices { + switch d := dev.(type) { + case *config.USBMassStorage: + devicesResponse.USBMassStorage = append(devicesResponse.USBMassStorage, *d) + case *config.VirtioBlk: + devicesResponse.Blk = append(devicesResponse.Blk, *d) + case *config.RosettaShare: + devicesResponse.Rosetta = *d + case *config.NVMExpressController: + devicesResponse.NVMe = append(devicesResponse.NVMe, *d) + case *config.VirtioFs: + devicesResponse.FS = append(devicesResponse.FS, *d) + case *config.VirtioNet: + n := define.VirtioNetResponse{ + Nat: d.Nat, + MacAddress: d.MacAddress.String(), + UnixSocketPath: d.UnixSocketPath, + } + + if d.Socket != nil { + n.Fd = int(d.Socket.Fd()) + } + + devicesResponse.Net = append(devicesResponse.Net, n) + case *config.VirtioRng: + devicesResponse.Rng = true + case *config.VirtioSerial: + devicesResponse.Serial = *d + case *config.VirtioVsock: + devicesResponse.Vsock = append(devicesResponse.Vsock, *d) + case *config.VirtioInput: + devicesResponse.Input = append(devicesResponse.Input, *d) + case *config.VirtioGPU: + devicesResponse.GPU = append(devicesResponse.GPU, *d) + } + } + }) + + return devicesResponse } // Inspect returns information about the virtual machine like hw resources // and devices func (vm *VzVirtualMachine) Inspect(c *gin.Context) { ii := define.InspectResponse{ - // TODO complete me - CPUs: 1, - Memory: 2048, - //Devices: vm.Devices, + CPUs: vm.vmConfig.Vcpus, + Memory: vm.vmConfig.MemoryBytes, + Devices: devicesToResp(vm.vmConfig.Devices), } c.JSON(http.StatusOK, ii) }