basic hypverv machine implementation

with libhvee, we are able to do the basics of podman machine management
on hyperv.  The basic functions like init, rm, stop, and start are all
functional.  Start and stop will periodically throw a benign error
processing the hyperv message being returned from the action.  The error
is described in the todo's below.

notable items:

* no podman commands will work (like ps, images, etc)
* the machine must be initialized with --image-path and fed a custom image.
* disk size is set to 100GB statically.
* the vm joins the default hyperv network which is TCP/IP network based.
* podman machine ssh does not work
* podman machine set does not work
* you can grab the ip address from hyperv and fake a machine connection
  with `podman system connection`.
* when booting, use the hyperv console to know the boot is complete.

TODOs:
* podman machine ssh
* podman machine set
* podman machine rm needs force bool
* disk size in NewMachine is set to 100GB
* podman start needs to wait until fully booted
* establish a boot complete signal from guest
* implement gvproxy like user networking
* fix benign failures in stop/start -> Error: error 2147749890 (FormatMessage failed with: The system cannot find message text for message number 0x%1 in the message file for %2.)

[NO NEW TESTS NEEDED]

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2023-02-25 15:02:16 -06:00
parent f1bcd0d781
commit 0dac214f56
106 changed files with 9552 additions and 33 deletions

View File

@@ -0,0 +1,207 @@
//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"time"
"github.com/containers/libhvee/pkg/wmiext"
)
type SystemSettings struct {
S__PATH string
InstanceID string
Caption string // = "Virtual Machine Settings"
Description string
ElementName string
VirtualSystemIdentifier string
VirtualSystemType string
Notes []string
CreationTime time.Time
ConfigurationID string
ConfigurationDataRoot string
ConfigurationFile string
SnapshotDataRoot string
SuspendDataRoot string
SwapFileDataRoot string
LogDataRoot string
AutomaticStartupAction uint16 // non-zero
AutomaticStartupActionDelay time.Duration
AutomaticStartupActionSequenceNumber uint16
AutomaticShutdownAction uint16 // non-zero
AutomaticRecoveryAction uint16 // non-zero
RecoveryFile string
BIOSGUID string
BIOSSerialNumber string
BaseBoardSerialNumber string
ChassisSerialNumber string
Architecture string
ChassisAssetTag string
BIOSNumLock bool
BootOrder []uint16
Parent string
UserSnapshotType uint16 // non-zero
IsSaved bool
AdditionalRecoveryInformation string
AllowFullSCSICommandSet bool
DebugChannelId uint32
DebugPortEnabled uint16
DebugPort uint32
Version string
IncrementalBackupEnabled bool
VirtualNumaEnabled bool
AllowReducedFcRedundancy bool // = False
VirtualSystemSubType string
BootSourceOrder []string
PauseAfterBootFailure bool
NetworkBootPreferredProtocol uint16 // non-zero
GuestControlledCacheTypes bool
AutomaticSnapshotsEnabled bool
IsAutomaticSnapshot bool
GuestStateFile string
GuestStateDataRoot string
LockOnDisconnect bool
ParentPackage string
AutomaticCriticalErrorActionTimeout time.Duration
AutomaticCriticalErrorAction uint16
ConsoleMode uint16
SecureBootEnabled bool
SecureBootTemplateId string
LowMmioGapSize uint64
HighMmioGapSize uint64
EnhancedSessionTransportType uint16
}
func DefaultSystemSettings() *SystemSettings {
return &SystemSettings{
// setup all non-zero settings
AutomaticStartupAction: 2, // no auto-start
AutomaticShutdownAction: 4, // shutdown
AutomaticRecoveryAction: 3, // restart
UserSnapshotType: 2, // no snapshotting
NetworkBootPreferredProtocol: 4096, // ipv4 for pxe
VirtualSystemSubType: "Microsoft:Hyper-V:SubType:2",
}
}
func (s *SystemSettings) Path() string {
return s.S__PATH
}
func (s *SystemSettings) AddScsiController() (*ScsiControllerSettings, error) {
const scsiControllerType = "Microsoft:Hyper-V:Synthetic SCSI Controller"
controller := &ScsiControllerSettings{}
if err := s.createSystemResourceInternal(controller, scsiControllerType, nil); err != nil {
return nil, err
}
controller.systemSettings = s
return controller, nil
}
func (s *SystemSettings) createSystemResourceInternal(settings interface{}, resourceType string, cb func()) error {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
if err = populateDefaults(resourceType, settings); err != nil {
return err
}
if cb != nil {
cb()
}
resourceStr, err := createResourceSettingGeneric(settings, resourceType)
if err != nil {
return err
}
path, err := addResource(service, s.Path(), resourceStr)
if err != nil {
return err
}
err = service.GetObjectAsObject(path, settings)
return err
}
func (s *SystemSettings) AddSyntheticEthernetPort(beforeAdd func(*SyntheticEthernetPortSettings)) (*SyntheticEthernetPortSettings, error) {
const networkAdapterType = SyntheticEthernetPortResourceType
port := &SyntheticEthernetPortSettings{}
var cb func()
if beforeAdd != nil {
cb = func() {
beforeAdd(port)
}
}
if err := s.createSystemResourceInternal(port, networkAdapterType, cb); err != nil {
return nil, err
}
port.systemSettings = s
return port, nil
}
func addResource(service *wmiext.Service, systemSettingPath string, resourceSettings string) (string, error) {
vsms, err := service.GetSingletonInstance(VirtualSystemManagementService)
if err != nil {
return "", err
}
defer vsms.Close()
var res int32
var resultingSettings []string
var job *wmiext.Instance
err = vsms.BeginInvoke("AddResourceSettings").
In("AffectedConfiguration", systemSettingPath).
In("ResourceSettings", []string{resourceSettings}).
Execute().
Out("Job", &job).
Out("ResultingResourceSettings", &resultingSettings).
Out("ReturnValue", &res).End()
if err != nil {
return "", fmt.Errorf("AddResourceSettings failed: %w", err)
}
err = waitVMResult(res, service, job)
if len(resultingSettings) > 0 {
return resultingSettings[0], err
}
return "", err
}
func (s *SystemSettings) GetVM() (*VirtualMachine, error) {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return nil, err
}
defer service.Close()
inst, err := service.FindFirstRelatedInstance(s.Path(), "Msvm_ComputerSystem")
if err != nil {
return nil, err
}
defer inst.Close()
vm := &VirtualMachine{}
if err = inst.GetAll(vm); err != nil {
return nil, err
}
return vm, nil
}