From 516034215f28e2b930553d5a694a03058cba3758 Mon Sep 17 00:00:00 2001 From: Jake Correnti Date: Thu, 22 Jun 2023 17:42:08 -0400 Subject: [PATCH] Re-organize hypervisor implementations Ensures that for each hypervisor implementation, their `config.go` file deals with implementing the `VirtProvider` interface while the `machine.go` file is for implementing the `VM` interface. Moves the `Virtualization` type into a common file and created wrappers for the individual hypervisors. Allows for shared functions that are exactly the same while providing the flexibility to create hypervisor-specific implementations of the functions. [NO NEW TESTS NEEDED] Signed-off-by: Jake Correnti --- cmd/podman/machine/platform.go | 2 +- cmd/podman/machine/platform_darwin.go | 4 +- cmd/podman/machine/platform_windows.go | 4 +- pkg/machine/applehv/config.go | 36 +-- pkg/machine/applehv/machine.go | 8 - pkg/machine/config.go | 52 +++- pkg/machine/e2e/machine_test.go | 2 +- pkg/machine/hyperv/config.go | 36 +-- pkg/machine/hyperv/machine.go | 8 - pkg/machine/qemu/config.go | 347 +++++++++++++++++++++---- pkg/machine/qemu/machine.go | 342 +++--------------------- pkg/machine/wsl/config.go | 213 +++++++++++++++ pkg/machine/wsl/machine.go | 216 --------------- 13 files changed, 627 insertions(+), 643 deletions(-) create mode 100644 pkg/machine/wsl/config.go diff --git a/cmd/podman/machine/platform.go b/cmd/podman/machine/platform.go index 0b08af843a..71b978e0bf 100644 --- a/cmd/podman/machine/platform.go +++ b/cmd/podman/machine/platform.go @@ -29,7 +29,7 @@ func GetSystemProvider() (machine.VirtProvider, error) { logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String()) switch resolvedVMType { case machine.QemuVirt: - return qemu.GetVirtualizationProvider(), nil + return qemu.VirtualizationProvider(), nil default: return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String()) } diff --git a/cmd/podman/machine/platform_darwin.go b/cmd/podman/machine/platform_darwin.go index 726cd8d12b..c3b6b9cd22 100644 --- a/cmd/podman/machine/platform_darwin.go +++ b/cmd/podman/machine/platform_darwin.go @@ -30,9 +30,9 @@ func GetSystemProvider() (machine.VirtProvider, error) { logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String()) switch resolvedVMType { case machine.QemuVirt: - return qemu.GetVirtualizationProvider(), nil + return qemu.VirtualizationProvider(), nil case machine.AppleHvVirt: - return applehv.GetVirtualizationProvider(), nil + return applehv.VirtualizationProvider(), nil default: return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String()) } diff --git a/cmd/podman/machine/platform_windows.go b/cmd/podman/machine/platform_windows.go index 88a5a3531a..82b34e3c33 100644 --- a/cmd/podman/machine/platform_windows.go +++ b/cmd/podman/machine/platform_windows.go @@ -28,9 +28,9 @@ func GetSystemProvider() (machine.VirtProvider, error) { logrus.Debugf("Using Podman machine with `%s` virtualization provider", resolvedVMType.String()) switch resolvedVMType { case machine.WSLVirt: - return wsl.GetWSLProvider(), nil + return wsl.VirtualizationProvider(), nil case machine.HyperVVirt: - return hyperv.GetVirtualizationProvider(), nil + return hyperv.VirtualizationProvider(), nil default: return nil, fmt.Errorf("unsupported virtualization provider: `%s`", resolvedVMType.String()) } diff --git a/pkg/machine/applehv/config.go b/pkg/machine/applehv/config.go index bba953b048..e153713a1a 100644 --- a/pkg/machine/applehv/config.go +++ b/pkg/machine/applehv/config.go @@ -17,10 +17,8 @@ const ( defaultVFKitEndpoint = "http://localhost:8081" ) -type Virtualization struct { - artifact machine.Artifact - compression machine.ImageCompression - format machine.ImageFormat +type AppleHVVirtualization struct { + machine.Virtualization } type MMHardwareConfig struct { @@ -30,11 +28,13 @@ type MMHardwareConfig struct { Memory int32 } -func (v Virtualization) Artifact() machine.Artifact { - return machine.Metal +func VirtualizationProvider() machine.VirtProvider { + return &AppleHVVirtualization{ + machine.NewVirtualization(machine.Metal, machine.Xz, machine.Raw), + } } -func (v Virtualization) CheckExclusiveActiveVM() (bool, string, error) { +func (v AppleHVVirtualization) CheckExclusiveActiveVM() (bool, string, error) { fsVms, err := getVMInfos() if err != nil { return false, "", err @@ -48,15 +48,7 @@ func (v Virtualization) CheckExclusiveActiveVM() (bool, string, error) { return false, "", nil } -func (v Virtualization) Compression() machine.ImageCompression { - return v.compression -} - -func (v Virtualization) Format() machine.ImageFormat { - return v.format -} - -func (v Virtualization) IsValidVMName(name string) (bool, error) { +func (v AppleHVVirtualization) IsValidVMName(name string) (bool, error) { mm := MacMachine{Name: name} configDir, err := machine.GetConfDir(machine.AppleHvVirt) if err != nil { @@ -68,7 +60,7 @@ func (v Virtualization) IsValidVMName(name string) (bool, error) { return true, nil } -func (v Virtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) { +func (v AppleHVVirtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) { var ( response []*machine.ListResponse ) @@ -108,12 +100,12 @@ func (v Virtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, return response, nil } -func (v Virtualization) LoadVMByName(name string) (machine.VM, error) { +func (v AppleHVVirtualization) LoadVMByName(name string) (machine.VM, error) { m := MacMachine{Name: name} return m.loadFromFile() } -func (v Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { +func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { m := MacMachine{Name: opts.Name} configDir, err := machine.GetConfDir(machine.AppleHvVirt) @@ -149,16 +141,16 @@ func (v Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) return m.loadFromFile() } -func (v Virtualization) RemoveAndCleanMachines() error { +func (v AppleHVVirtualization) RemoveAndCleanMachines() error { // This can be implemented when host networking is completed. return machine.ErrNotImplemented } -func (v Virtualization) VMType() machine.VMType { +func (v AppleHVVirtualization) VMType() machine.VMType { return vmtype } -func (v Virtualization) loadFromLocalJson() ([]*MacMachine, error) { +func (v AppleHVVirtualization) loadFromLocalJson() ([]*MacMachine, error) { var ( jsonFiles []string mms []*MacMachine diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go index 095ad65213..22f305124e 100644 --- a/pkg/machine/applehv/machine.go +++ b/pkg/machine/applehv/machine.go @@ -27,14 +27,6 @@ var ( vmtype = machine.AppleHvVirt ) -func GetVirtualizationProvider() machine.VirtProvider { - return &Virtualization{ - artifact: machine.None, - compression: machine.Xz, - format: machine.Raw, - } -} - // VfkitHelper describes the use of vfkit: cmdline and endpoint type VfkitHelper struct { Bootloader string diff --git a/pkg/machine/config.go b/pkg/machine/config.go index f59288590e..8b915321a7 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -48,19 +48,6 @@ const ( DefaultMachineName string = "podman-machine-default" ) -type VirtProvider interface { - Artifact() Artifact - CheckExclusiveActiveVM() (bool, string, error) - Compression() ImageCompression - Format() ImageFormat - IsValidVMName(name string) (bool, error) - List(opts ListOptions) ([]*ListResponse, error) - LoadVMByName(name string) (VM, error) - NewMachine(opts InitOptions) (VM, error) - RemoveAndCleanMachines() error - VMType() VMType -} - type RemoteConnectionType string var ( @@ -428,3 +415,42 @@ func ParseVMType(input string, emptyFallback VMType) (VMType, error) { return QemuVirt, fmt.Errorf("unknown VMType `%s`", input) } } + +type VirtProvider interface { + Artifact() Artifact + CheckExclusiveActiveVM() (bool, string, error) + Compression() ImageCompression + Format() ImageFormat + IsValidVMName(name string) (bool, error) + List(opts ListOptions) ([]*ListResponse, error) + LoadVMByName(name string) (VM, error) + NewMachine(opts InitOptions) (VM, error) + RemoveAndCleanMachines() error + VMType() VMType +} + +type Virtualization struct { + artifact Artifact + compression ImageCompression + format ImageFormat +} + +func (p *Virtualization) Artifact() Artifact { + return p.artifact +} + +func (p *Virtualization) Compression() ImageCompression { + return p.compression +} + +func (p *Virtualization) Format() ImageFormat { + return p.format +} + +func NewVirtualization(artifact Artifact, compression ImageCompression, format ImageFormat) Virtualization { + return Virtualization{ + artifact, + compression, + format, + } +} diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go index 76f5dfe049..2d28db146f 100644 --- a/pkg/machine/e2e/machine_test.go +++ b/pkg/machine/e2e/machine_test.go @@ -44,7 +44,7 @@ func TestMachine(t *testing.T) { } var _ = BeforeSuite(func() { - qemuVP := qemu.GetVirtualizationProvider() + qemuVP := qemu.VirtualizationProvider() fcd, err := machine.GetFCOSDownload(qemuVP, defaultStream) if err != nil { Fail("unable to get virtual machine image") diff --git a/pkg/machine/hyperv/config.go b/pkg/machine/hyperv/config.go index 8d334a4deb..b55a2f8396 100644 --- a/pkg/machine/hyperv/config.go +++ b/pkg/machine/hyperv/config.go @@ -17,17 +17,17 @@ import ( "github.com/sirupsen/logrus" ) -type Virtualization struct { - artifact machine.Artifact - compression machine.ImageCompression - format machine.ImageFormat +type HyperVVirtualization struct { + machine.Virtualization } -func (v Virtualization) Artifact() machine.Artifact { - return machine.None +func VirtualizationProvider() machine.VirtProvider { + return &HyperVVirtualization{ + machine.NewVirtualization(machine.HyperV, machine.Zip, machine.Vhdx), + } } -func (v Virtualization) CheckExclusiveActiveVM() (bool, string, error) { +func (v HyperVVirtualization) CheckExclusiveActiveVM() (bool, string, error) { vmm := hypervctl.NewVirtualMachineManager() // Use of GetAll is OK here because we do not want to use the same name // as something already *actually* configured in hyperv @@ -43,15 +43,7 @@ func (v Virtualization) CheckExclusiveActiveVM() (bool, string, error) { return false, "", nil } -func (v Virtualization) Compression() machine.ImageCompression { - return v.compression -} - -func (v Virtualization) Format() machine.ImageFormat { - return v.format -} - -func (v Virtualization) IsValidVMName(name string) (bool, error) { +func (v HyperVVirtualization) IsValidVMName(name string) (bool, error) { // We check both the local filesystem and hyperv for the valid name mm := HyperVMachine{Name: name} configDir, err := machine.GetConfDir(v.VMType()) @@ -69,7 +61,7 @@ func (v Virtualization) IsValidVMName(name string) (bool, error) { return true, nil } -func (v Virtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) { +func (v HyperVVirtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) { mms, err := v.loadFromLocalJson() if err != nil { return nil, err @@ -103,12 +95,12 @@ func (v Virtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, return response, err } -func (v Virtualization) LoadVMByName(name string) (machine.VM, error) { +func (v HyperVVirtualization) LoadVMByName(name string) (machine.VM, error) { m := &HyperVMachine{Name: name} return m.loadFromFile() } -func (v Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { +func (v HyperVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { m := HyperVMachine{Name: opts.Name} if len(opts.ImagePath) < 1 { return nil, errors.New("must define --image-path for hyperv support") @@ -180,7 +172,7 @@ func (v Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) return v.LoadVMByName(opts.Name) } -func (v Virtualization) RemoveAndCleanMachines() error { +func (v HyperVVirtualization) RemoveAndCleanMachines() error { // Error handling used here is following what qemu did var ( prevErr error @@ -238,11 +230,11 @@ func (v Virtualization) RemoveAndCleanMachines() error { return prevErr } -func (v Virtualization) VMType() machine.VMType { +func (v HyperVVirtualization) VMType() machine.VMType { return vmtype } -func (v Virtualization) loadFromLocalJson() ([]*HyperVMachine, error) { +func (v HyperVVirtualization) loadFromLocalJson() ([]*HyperVMachine, error) { var ( jsonFiles []string mms []*HyperVMachine diff --git a/pkg/machine/hyperv/machine.go b/pkg/machine/hyperv/machine.go index d0473663e4..d93febe3aa 100644 --- a/pkg/machine/hyperv/machine.go +++ b/pkg/machine/hyperv/machine.go @@ -30,14 +30,6 @@ var ( vmtype = machine.HyperVVirt ) -func GetVirtualizationProvider() machine.VirtProvider { - return &Virtualization{ - artifact: machine.HyperV, - compression: machine.Zip, - format: machine.Vhdx, - } -} - const ( // Some of this will need to change when we are closer to having // working code. diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 730dc4f038..b061d7635a 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -1,15 +1,312 @@ package qemu import ( + "encoding/json" + "fmt" + "io/fs" + "os" + "path/filepath" + "strconv" + "strings" "time" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v4/pkg/machine" + "github.com/containers/podman/v4/utils" + "github.com/docker/go-units" + "github.com/sirupsen/logrus" ) -type Virtualization struct { - artifact machine.Artifact - compression machine.ImageCompression - format machine.ImageFormat +type QEMUVirtualization struct { + machine.Virtualization +} + +// NewMachine initializes an instance of a virtual machine based on the qemu +// virtualization. +func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return nil, err + } + vm := new(MachineVM) + if len(opts.Name) > 0 { + vm.Name = opts.Name + } + ignitionFile, err := machine.NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil) + if err != nil { + return nil, err + } + vm.IgnitionFile = *ignitionFile + imagePath, err := machine.NewMachineFile(opts.ImagePath, nil) + if err != nil { + return nil, err + } + vm.ImagePath = *imagePath + vm.RemoteUsername = opts.Username + + // Add a random port for ssh + port, err := utils.GetRandomPort() + if err != nil { + return nil, err + } + vm.Port = port + + vm.CPUs = opts.CPUS + vm.Memory = opts.Memory + vm.DiskSize = opts.DiskSize + + vm.Created = time.Now() + + // Find the qemu executable + cfg, err := config.Default() + if err != nil { + return nil, err + } + execPath, err := cfg.FindHelperBinary(QemuCommand, true) + if err != nil { + return nil, err + } + if err := vm.setPIDSocket(); err != nil { + return nil, err + } + cmd := []string{execPath} + // Add memory + cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) + // Add cpus + cmd = append(cmd, []string{"-smp", strconv.Itoa(int(vm.CPUs))}...) + // Add ignition file + cmd = append(cmd, []string{"-fw_cfg", "name=opt/com.coreos/config,file=" + vm.IgnitionFile.GetPath()}...) + // Add qmp socket + monitor, err := NewQMPMonitor("unix", vm.Name, defaultQMPTimeout) + if err != nil { + return nil, err + } + vm.QMPMonitor = monitor + cmd = append(cmd, []string{"-qmp", monitor.Network + ":" + monitor.Address.GetPath() + ",server=on,wait=off"}...) + + // Add network + // Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is + // why we can only run one vm at a time right now + cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...) + if err := vm.setReadySocket(); err != nil { + return nil, err + } + + // Add serial port for readiness + cmd = append(cmd, []string{ + "-device", "virtio-serial", + // qemu needs to establish the long name; other connections can use the symlink'd + // Note both id and chardev start with an extra "a" because qemu requires that it + // starts with a letter but users can also use numbers + "-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=a" + vm.Name + "_ready", + "-device", "virtserialport,chardev=a" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0", + "-pidfile", vm.VMPidFilePath.GetPath()}...) + vm.CmdLine = cmd + return vm, nil +} + +// LoadVMByName reads a json file that describes a known qemu vm +// and returns a vm instance +func (p *QEMUVirtualization) LoadVMByName(name string) (machine.VM, error) { + vm := &MachineVM{Name: name} + vm.HostUser = machine.HostUser{UID: -1} // posix reserves -1, so use it to signify undefined + if err := vm.update(); err != nil { + return nil, err + } + + return vm, nil +} + +// List lists all vm's that use qemu virtualization +func (p *QEMUVirtualization) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { + return getVMInfos() +} + +func getVMInfos() ([]*machine.ListResponse, error) { + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return nil, err + } + + var listed []*machine.ListResponse + + if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { + vm := new(MachineVM) + if strings.HasSuffix(d.Name(), ".json") { + fullPath := filepath.Join(vmConfigDir, d.Name()) + b, err := os.ReadFile(fullPath) + if err != nil { + return err + } + err = json.Unmarshal(b, vm) + if err != nil { + // Checking if the file did not unmarshal because it is using + // the deprecated config file format. + migrateErr := migrateVM(fullPath, b, vm) + if migrateErr != nil { + return migrateErr + } + } + listEntry := new(machine.ListResponse) + + listEntry.Name = vm.Name + listEntry.Stream = vm.ImageStream + listEntry.VMType = "qemu" + listEntry.CPUs = vm.CPUs + listEntry.Memory = vm.Memory * units.MiB + listEntry.DiskSize = vm.DiskSize * units.GiB + listEntry.Port = vm.Port + listEntry.RemoteUsername = vm.RemoteUsername + listEntry.IdentityPath = vm.IdentityPath + listEntry.CreatedAt = vm.Created + listEntry.Starting = vm.Starting + listEntry.UserModeNetworking = true // always true + + if listEntry.CreatedAt.IsZero() { + listEntry.CreatedAt = time.Now() + vm.Created = time.Now() + if err := vm.writeConfig(); err != nil { + return err + } + } + + state, err := vm.State(false) + if err != nil { + return err + } + listEntry.Running = state == machine.Running + + if !vm.LastUp.IsZero() { // this means we have already written a time to the config + listEntry.LastUp = vm.LastUp + } else { // else we just created the machine AKA last up = created time + listEntry.LastUp = vm.Created + vm.LastUp = listEntry.LastUp + if err := vm.writeConfig(); err != nil { + return err + } + } + + listed = append(listed, listEntry) + } + return nil + }); err != nil { + return nil, err + } + return listed, err +} + +func (p *QEMUVirtualization) IsValidVMName(name string) (bool, error) { + infos, err := getVMInfos() + if err != nil { + return false, err + } + for _, vm := range infos { + if vm.Name == name { + return true, nil + } + } + return false, nil +} + +// CheckExclusiveActiveVM checks if there is a VM already running +// that does not allow other VMs to be running +func (p *QEMUVirtualization) CheckExclusiveActiveVM() (bool, string, error) { + vms, err := getVMInfos() + if err != nil { + return false, "", fmt.Errorf("checking VM active: %w", err) + } + for _, vm := range vms { + if vm.Running || vm.Starting { + return true, vm.Name, nil + } + } + return false, "", nil +} + +// RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine +func (p *QEMUVirtualization) RemoveAndCleanMachines() error { + var ( + vm machine.VM + listResponse []*machine.ListResponse + opts machine.ListOptions + destroyOptions machine.RemoveOptions + ) + destroyOptions.Force = true + var prevErr error + + listResponse, err := p.List(opts) + if err != nil { + return err + } + + for _, mach := range listResponse { + vm, err = p.LoadVMByName(mach.Name) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + _, remove, err := vm.Remove(mach.Name, destroyOptions) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } else { + if err := remove(); err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + } + } + + // Clean leftover files in data dir + dataDir, err := machine.DataDirPrefix() + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } else { + err := machine.GuardedRemoveAll(dataDir) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + } + + // Clean leftover files in conf dir + confDir, err := machine.ConfDirPrefix() + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } else { + err := machine.GuardedRemoveAll(confDir) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + } + return prevErr +} + +func (p *QEMUVirtualization) VMType() machine.VMType { + return vmtype +} + +func VirtualizationProvider() machine.VirtProvider { + return &QEMUVirtualization{ + machine.NewVirtualization(machine.Qemu, machine.Xz, machine.Qcow), + } } // Deprecated: MachineVMV1 is being deprecated in favor a more flexible and informative @@ -47,39 +344,6 @@ type MachineVMV1 struct { UID int } -type MachineVM struct { - // ConfigPath is the path to the configuration file - ConfigPath machine.VMFile - // The command line representation of the qemu command - CmdLine []string - // HostUser contains info about host user - machine.HostUser - // ImageConfig describes the bootable image - machine.ImageConfig - // Mounts is the list of remote filesystems to mount - Mounts []machine.Mount - // Name of VM - Name string - // PidFilePath is the where the Proxy PID file lives - PidFilePath machine.VMFile - // VMPidFilePath is the where the VM PID file lives - VMPidFilePath machine.VMFile - // QMPMonitor is the qemu monitor object for sending commands - QMPMonitor Monitor - // ReadySocket tells host when vm is booted - ReadySocket machine.VMFile - // ResourceConfig is physical attrs of the VM - machine.ResourceConfig - // SSHConfig for accessing the remote vm - machine.SSHConfig - // Starting tells us whether the machine is running or if we have just dialed it to start it - Starting bool - // Created contains the original created time instead of querying the file mod time - Created time.Time - // LastUp contains the last recorded uptime - LastUp time.Time -} - type Monitorv1 struct { // Address portion of the qmp monitor (/tmp/tmp.sock) Address string @@ -89,15 +353,6 @@ type Monitorv1 struct { Timeout time.Duration } -type Monitor struct { - // Address portion of the qmp monitor (/tmp/tmp.sock) - Address machine.VMFile - // Network portion of the qmp monitor (unix) - Network string - // Timeout in seconds for qmp monitor transactions - Timeout time.Duration -} - var ( // defaultQMPTimeout is the timeout duration for the // qmp monitor interactions. diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 390d23f078..e2d9c91e79 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -28,10 +28,8 @@ import ( "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" - "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/ioutils" "github.com/digitalocean/go-qemu/qmp" - "github.com/docker/go-units" "github.com/sirupsen/logrus" ) @@ -41,14 +39,6 @@ var ( vmtype = machine.QemuVirt ) -func GetVirtualizationProvider() machine.VirtProvider { - return &Virtualization{ - artifact: machine.Qemu, - compression: machine.Xz, - format: machine.Qcow, - } -} - const ( VolumeTypeVirtfs = "virtfs" MountType9p = "9p" @@ -57,88 +47,46 @@ const ( apiUpTimeout = 20 * time.Second ) -// NewMachine initializes an instance of a virtual machine based on the qemu -// virtualization. -func (p *Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { - vmConfigDir, err := machine.GetConfDir(vmtype) - if err != nil { - return nil, err - } - vm := new(MachineVM) - if len(opts.Name) > 0 { - vm.Name = opts.Name - } - ignitionFile, err := machine.NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil) - if err != nil { - return nil, err - } - vm.IgnitionFile = *ignitionFile - imagePath, err := machine.NewMachineFile(opts.ImagePath, nil) - if err != nil { - return nil, err - } - vm.ImagePath = *imagePath - vm.RemoteUsername = opts.Username - - // Add a random port for ssh - port, err := utils.GetRandomPort() - if err != nil { - return nil, err - } - vm.Port = port - - vm.CPUs = opts.CPUS - vm.Memory = opts.Memory - vm.DiskSize = opts.DiskSize - - vm.Created = time.Now() - - // Find the qemu executable - cfg, err := config.Default() - if err != nil { - return nil, err - } - execPath, err := cfg.FindHelperBinary(QemuCommand, true) - if err != nil { - return nil, err - } - if err := vm.setPIDSocket(); err != nil { - return nil, err - } - cmd := []string{execPath} - // Add memory - cmd = append(cmd, []string{"-m", strconv.Itoa(int(vm.Memory))}...) - // Add cpus - cmd = append(cmd, []string{"-smp", strconv.Itoa(int(vm.CPUs))}...) - // Add ignition file - cmd = append(cmd, []string{"-fw_cfg", "name=opt/com.coreos/config,file=" + vm.IgnitionFile.GetPath()}...) - // Add qmp socket - monitor, err := NewQMPMonitor("unix", vm.Name, defaultQMPTimeout) - if err != nil { - return nil, err - } - vm.QMPMonitor = monitor - cmd = append(cmd, []string{"-qmp", monitor.Network + ":" + monitor.Address.GetPath() + ",server=on,wait=off"}...) - - // Add network - // Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is - // why we can only run one vm at a time right now - cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...) - if err := vm.setReadySocket(); err != nil { - return nil, err - } +type MachineVM struct { + // ConfigPath is the path to the configuration file + ConfigPath machine.VMFile + // The command line representation of the qemu command + CmdLine []string + // HostUser contains info about host user + machine.HostUser + // ImageConfig describes the bootable image + machine.ImageConfig + // Mounts is the list of remote filesystems to mount + Mounts []machine.Mount + // Name of VM + Name string + // PidFilePath is the where the Proxy PID file lives + PidFilePath machine.VMFile + // VMPidFilePath is the where the VM PID file lives + VMPidFilePath machine.VMFile + // QMPMonitor is the qemu monitor object for sending commands + QMPMonitor Monitor + // ReadySocket tells host when vm is booted + ReadySocket machine.VMFile + // ResourceConfig is physical attrs of the VM + machine.ResourceConfig + // SSHConfig for accessing the remote vm + machine.SSHConfig + // Starting tells us whether the machine is running or if we have just dialed it to start it + Starting bool + // Created contains the original created time instead of querying the file mod time + Created time.Time + // LastUp contains the last recorded uptime + LastUp time.Time +} - // Add serial port for readiness - cmd = append(cmd, []string{ - "-device", "virtio-serial", - // qemu needs to establish the long name; other connections can use the symlink'd - // Note both id and chardev start with an extra "a" because qemu requires that it - // starts with a letter but users can also use numbers - "-chardev", "socket,path=" + vm.ReadySocket.Path + ",server=on,wait=off,id=a" + vm.Name + "_ready", - "-device", "virtserialport,chardev=a" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0", - "-pidfile", vm.VMPidFilePath.GetPath()}...) - vm.CmdLine = cmd - return vm, nil +type Monitor struct { + // Address portion of the qmp monitor (/tmp/tmp.sock) + Address machine.VMFile + // Network portion of the qmp monitor (unix) + Network string + // Timeout in seconds for qmp monitor transactions + Timeout time.Duration } // migrateVM takes the old configuration structure and migrates it @@ -224,18 +172,6 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error { return os.Remove(configPath + ".orig") } -// LoadVMByName reads a json file that describes a known qemu vm -// and returns a vm instance -func (p *Virtualization) LoadVMByName(name string) (machine.VM, error) { - vm := &MachineVM{Name: name} - vm.HostUser = machine.HostUser{UID: -1} // posix reserves -1, so use it to signify undefined - if err := vm.update(); err != nil { - return nil, err - } - - return vm, nil -} - // Init writes the json configuration file to the filesystem for // other verbs (start, stop) func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { @@ -250,7 +186,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { case machine.Testing.String(), machine.Next.String(), machine.Stable.String(), "": // Get image as usual v.ImageStream = opts.ImagePath - vp := GetVirtualizationProvider() + vp := VirtualizationProvider() dd, err := machine.NewFcosDownloader(vmtype, v.Name, machine.FCOSStreamFromString(opts.ImagePath), vp) if err != nil { @@ -1118,124 +1054,6 @@ func getDiskSize(path string) (uint64, error) { return tmpInfo.VirtualSize, nil } -// List lists all vm's that use qemu virtualization -func (p *Virtualization) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { - return getVMInfos() -} - -func getVMInfos() ([]*machine.ListResponse, error) { - vmConfigDir, err := machine.GetConfDir(vmtype) - if err != nil { - return nil, err - } - - var listed []*machine.ListResponse - - if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { - vm := new(MachineVM) - if strings.HasSuffix(d.Name(), ".json") { - fullPath := filepath.Join(vmConfigDir, d.Name()) - b, err := os.ReadFile(fullPath) - if err != nil { - return err - } - err = json.Unmarshal(b, vm) - if err != nil { - // Checking if the file did not unmarshal because it is using - // the deprecated config file format. - migrateErr := migrateVM(fullPath, b, vm) - if migrateErr != nil { - return migrateErr - } - } - listEntry := new(machine.ListResponse) - - listEntry.Name = vm.Name - listEntry.Stream = vm.ImageStream - listEntry.VMType = "qemu" - listEntry.CPUs = vm.CPUs - listEntry.Memory = vm.Memory * units.MiB - listEntry.DiskSize = vm.DiskSize * units.GiB - listEntry.Port = vm.Port - listEntry.RemoteUsername = vm.RemoteUsername - listEntry.IdentityPath = vm.IdentityPath - listEntry.CreatedAt = vm.Created - listEntry.Starting = vm.Starting - listEntry.UserModeNetworking = true // always true - - if listEntry.CreatedAt.IsZero() { - listEntry.CreatedAt = time.Now() - vm.Created = time.Now() - if err := vm.writeConfig(); err != nil { - return err - } - } - - state, err := vm.State(false) - if err != nil { - return err - } - listEntry.Running = state == machine.Running - - if !vm.LastUp.IsZero() { // this means we have already written a time to the config - listEntry.LastUp = vm.LastUp - } else { // else we just created the machine AKA last up = created time - listEntry.LastUp = vm.Created - vm.LastUp = listEntry.LastUp - if err := vm.writeConfig(); err != nil { - return err - } - } - - listed = append(listed, listEntry) - } - return nil - }); err != nil { - return nil, err - } - return listed, err -} - -func (p *Virtualization) IsValidVMName(name string) (bool, error) { - infos, err := getVMInfos() - if err != nil { - return false, err - } - for _, vm := range infos { - if vm.Name == name { - return true, nil - } - } - return false, nil -} - -// CheckExclusiveActiveVM checks if there is a VM already running -// that does not allow other VMs to be running -func (p *Virtualization) CheckExclusiveActiveVM() (bool, string, error) { - vms, err := getVMInfos() - if err != nil { - return false, "", fmt.Errorf("checking VM active: %w", err) - } - for _, vm := range vms { - if vm.Running || vm.Starting { - return true, vm.Name, nil - } - } - return false, "", nil -} - -func (p *Virtualization) Artifact() machine.Artifact { - return p.artifact -} - -func (p *Virtualization) Compression() machine.ImageCompression { - return p.compression -} - -func (p *Virtualization) Format() machine.ImageFormat { - return p.format -} - // startHostNetworking runs a binary on the host system that allows users // to set up port forwarding to the podman virtual machine func (v *MachineVM) startHostNetworking() (string, machine.APIForwardingState, error) { @@ -1695,86 +1513,6 @@ func (v *MachineVM) editCmdLine(flag string, value string) { } } -// RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine -func (p *Virtualization) RemoveAndCleanMachines() error { - var ( - vm machine.VM - listResponse []*machine.ListResponse - opts machine.ListOptions - destroyOptions machine.RemoveOptions - ) - destroyOptions.Force = true - var prevErr error - - listResponse, err := p.List(opts) - if err != nil { - return err - } - - for _, mach := range listResponse { - vm, err = p.LoadVMByName(mach.Name) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - _, remove, err := vm.Remove(mach.Name, destroyOptions) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } else { - if err := remove(); err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - } - } - - // Clean leftover files in data dir - dataDir, err := machine.DataDirPrefix() - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } else { - err := machine.GuardedRemoveAll(dataDir) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - } - - // Clean leftover files in conf dir - confDir, err := machine.ConfDirPrefix() - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } else { - err := machine.GuardedRemoveAll(confDir) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - } - return prevErr -} - -func (p *Virtualization) VMType() machine.VMType { - return vmtype -} - func isRootful() bool { // Rootless is not relevant on Windows. In the future rootless.IsRootless // could be switched to return true on Windows, and other codepaths migrated diff --git a/pkg/machine/wsl/config.go b/pkg/machine/wsl/config.go new file mode 100644 index 0000000000..4f02a852d6 --- /dev/null +++ b/pkg/machine/wsl/config.go @@ -0,0 +1,213 @@ +//go:build windows +// +build windows + +package wsl + +import ( + "io/fs" + "path/filepath" + "strings" + "time" + + "github.com/containers/podman/v4/pkg/machine" + "github.com/containers/podman/v4/utils" + "github.com/sirupsen/logrus" +) + +type WSLVirtualization struct { + machine.Virtualization +} + +func VirtualizationProvider() machine.VirtProvider { + return &WSLVirtualization{ + machine.NewVirtualization(machine.None, machine.Xz, machine.Tar), + } +} + +// NewMachine initializes an instance of a wsl machine +func (p *WSLVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { + vm := new(MachineVM) + if len(opts.Name) > 0 { + vm.Name = opts.Name + } + configPath, err := getConfigPath(opts.Name) + if err != nil { + return vm, err + } + + vm.ConfigPath = configPath + vm.ImagePath = opts.ImagePath + vm.RemoteUsername = opts.Username + vm.Created = time.Now() + vm.LastUp = vm.Created + + // Default is false + if opts.UserModeNetworking != nil { + vm.UserModeNetworking = *opts.UserModeNetworking + } + + // Add a random port for ssh + port, err := utils.GetRandomPort() + if err != nil { + return nil, err + } + vm.Port = port + + return vm, nil +} + +// LoadByName reads a json file that describes a known qemu vm +// and returns a vm instance +func (p *WSLVirtualization) LoadVMByName(name string) (machine.VM, error) { + configPath, err := getConfigPath(name) + if err != nil { + return nil, err + } + + vm, err := readAndMigrate(configPath, name) + return vm, err +} + +// List lists all vm's that use qemu virtualization +func (p *WSLVirtualization) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { + return GetVMInfos() +} + +func GetVMInfos() ([]*machine.ListResponse, error) { + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return nil, err + } + + var listed []*machine.ListResponse + + if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { + if strings.HasSuffix(d.Name(), ".json") { + path := filepath.Join(vmConfigDir, d.Name()) + vm, err := readAndMigrate(path, strings.TrimSuffix(d.Name(), ".json")) + if err != nil { + return err + } + listEntry := new(machine.ListResponse) + + listEntry.Name = vm.Name + listEntry.Stream = vm.ImageStream + listEntry.VMType = "wsl" + listEntry.CPUs, _ = getCPUs(vm) + listEntry.Memory, _ = getMem(vm) + listEntry.DiskSize = getDiskSize(vm) + listEntry.RemoteUsername = vm.RemoteUsername + listEntry.Port = vm.Port + listEntry.IdentityPath = vm.IdentityPath + listEntry.Starting = false + listEntry.UserModeNetworking = vm.UserModeNetworking + + running := vm.isRunning() + listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running) + listEntry.Running = running + + listed = append(listed, listEntry) + } + return nil + }); err != nil { + return nil, err + } + return listed, err +} + +func (p *WSLVirtualization) IsValidVMName(name string) (bool, error) { + infos, err := GetVMInfos() + if err != nil { + return false, err + } + for _, vm := range infos { + if vm.Name == name { + return true, nil + } + } + return false, nil +} + +func (p *WSLVirtualization) CheckExclusiveActiveVM() (bool, string, error) { + return false, "", nil +} + +// RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine +func (p *WSLVirtualization) RemoveAndCleanMachines() error { + var ( + vm machine.VM + listResponse []*machine.ListResponse + opts machine.ListOptions + destroyOptions machine.RemoveOptions + ) + destroyOptions.Force = true + var prevErr error + + listResponse, err := p.List(opts) + if err != nil { + return err + } + + for _, mach := range listResponse { + vm, err = p.LoadVMByName(mach.Name) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + _, remove, err := vm.Remove(mach.Name, destroyOptions) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } else { + if err := remove(); err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + } + } + + // Clean leftover files in data dir + dataDir, err := machine.DataDirPrefix() + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } else { + err := machine.GuardedRemoveAll(dataDir) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + } + + // Clean leftover files in conf dir + confDir, err := machine.ConfDirPrefix() + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } else { + err := machine.GuardedRemoveAll(confDir) + if err != nil { + if prevErr != nil { + logrus.Error(prevErr) + } + prevErr = err + } + } + return prevErr +} + +func (p *WSLVirtualization) VMType() machine.VMType { + return vmtype +} diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index fbad357043..1de1d2df95 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/fs" "net/url" "os" "os/exec" @@ -22,7 +21,6 @@ import ( "github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine/wsl/wutil" "github.com/containers/podman/v4/pkg/util" - "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/ioutils" "github.com/sirupsen/logrus" @@ -219,24 +217,6 @@ const ( rootlessSock = "/run/user/1000/podman/podman.sock" ) -type Virtualization struct { - artifact machine.Artifact - compression machine.ImageCompression - format machine.ImageFormat -} - -func (p *Virtualization) Artifact() machine.Artifact { - return p.artifact -} - -func (p *Virtualization) Compression() machine.ImageCompression { - return p.compression -} - -func (p *Virtualization) Format() machine.ImageFormat { - return p.format -} - type MachineVM struct { // ConfigPath is the path to the configuration file ConfigPath string @@ -268,46 +248,6 @@ func (e *ExitCodeError) Error() string { return fmt.Sprintf("Process failed with exit code: %d", e.code) } -func GetWSLProvider() machine.VirtProvider { - return &Virtualization{ - artifact: machine.None, - compression: machine.Xz, - format: machine.Tar, - } -} - -// NewMachine initializes an instance of a wsl machine -func (p *Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { - vm := new(MachineVM) - if len(opts.Name) > 0 { - vm.Name = opts.Name - } - configPath, err := getConfigPath(opts.Name) - if err != nil { - return vm, err - } - - vm.ConfigPath = configPath - vm.ImagePath = opts.ImagePath - vm.RemoteUsername = opts.Username - vm.Created = time.Now() - vm.LastUp = vm.Created - - // Default is false - if opts.UserModeNetworking != nil { - vm.UserModeNetworking = *opts.UserModeNetworking - } - - // Add a random port for ssh - port, err := utils.GetRandomPort() - if err != nil { - return nil, err - } - vm.Port = port - - return vm, nil -} - func getConfigPath(name string) (string, error) { return getConfigPathExt(name, "json") } @@ -321,18 +261,6 @@ func getConfigPathExt(name string, extension string) (string, error) { return filepath.Join(vmConfigDir, fmt.Sprintf("%s.%s", name, extension)), nil } -// LoadByName reads a json file that describes a known qemu vm -// and returns a vm instance -func (p *Virtualization) LoadVMByName(name string) (machine.VM, error) { - configPath, err := getConfigPath(name) - if err != nil { - return nil, err - } - - vm, err := readAndMigrate(configPath, name) - return vm, err -} - // readAndMigrate returns the content of the VM's // configuration file in json func readAndMigrate(configPath string, name string) (*MachineVM, error) { @@ -1512,53 +1440,6 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { return cmd.Run() } -// List lists all vm's that use qemu virtualization -func (p *Virtualization) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { - return GetVMInfos() -} - -func GetVMInfos() ([]*machine.ListResponse, error) { - vmConfigDir, err := machine.GetConfDir(vmtype) - if err != nil { - return nil, err - } - - var listed []*machine.ListResponse - - if err = filepath.WalkDir(vmConfigDir, func(path string, d fs.DirEntry, err error) error { - if strings.HasSuffix(d.Name(), ".json") { - path := filepath.Join(vmConfigDir, d.Name()) - vm, err := readAndMigrate(path, strings.TrimSuffix(d.Name(), ".json")) - if err != nil { - return err - } - listEntry := new(machine.ListResponse) - - listEntry.Name = vm.Name - listEntry.Stream = vm.ImageStream - listEntry.VMType = "wsl" - listEntry.CPUs, _ = getCPUs(vm) - listEntry.Memory, _ = getMem(vm) - listEntry.DiskSize = getDiskSize(vm) - listEntry.RemoteUsername = vm.RemoteUsername - listEntry.Port = vm.Port - listEntry.IdentityPath = vm.IdentityPath - listEntry.Starting = false - listEntry.UserModeNetworking = vm.UserModeNetworking - - running := vm.isRunning() - listEntry.CreatedAt, listEntry.LastUp, _ = vm.updateTimeStamps(running) - listEntry.Running = running - - listed = append(listed, listEntry) - } - return nil - }); err != nil { - return nil, err - } - return listed, err -} - func (vm *MachineVM) updateTimeStamps(updateLast bool) (time.Time, time.Time, error) { var err error if updateLast { @@ -1643,23 +1524,6 @@ func getMem(vm *MachineVM) (uint64, error) { return total - available, err } -func (p *Virtualization) IsValidVMName(name string) (bool, error) { - infos, err := GetVMInfos() - if err != nil { - return false, err - } - for _, vm := range infos { - if vm.Name == name { - return true, nil - } - } - return false, nil -} - -func (p *Virtualization) CheckExclusiveActiveVM() (bool, string, error) { - return false, "", nil -} - func (v *MachineVM) setRootful(rootful bool) error { changeCon, err := machine.AnyConnectionDefault(v.Name, v.Name+"-root") if err != nil { @@ -1716,83 +1580,3 @@ func (v *MachineVM) getResources() (resources machine.ResourceConfig) { resources.DiskSize = getDiskSize(v) return } - -// RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine -func (p *Virtualization) RemoveAndCleanMachines() error { - var ( - vm machine.VM - listResponse []*machine.ListResponse - opts machine.ListOptions - destroyOptions machine.RemoveOptions - ) - destroyOptions.Force = true - var prevErr error - - listResponse, err := p.List(opts) - if err != nil { - return err - } - - for _, mach := range listResponse { - vm, err = p.LoadVMByName(mach.Name) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - _, remove, err := vm.Remove(mach.Name, destroyOptions) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } else { - if err := remove(); err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - } - } - - // Clean leftover files in data dir - dataDir, err := machine.DataDirPrefix() - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } else { - err := machine.GuardedRemoveAll(dataDir) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - } - - // Clean leftover files in conf dir - confDir, err := machine.ConfDirPrefix() - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } else { - err := machine.GuardedRemoveAll(confDir) - if err != nil { - if prevErr != nil { - logrus.Error(prevErr) - } - prevErr = err - } - } - return prevErr -} - -func (p *Virtualization) VMType() machine.VMType { - return vmtype -}