Files
Brent Baude 60d9f9b807 Update to libhvee 0.5.0
Signed-off-by: Brent Baude <bbaude@redhat.com>
2023-11-17 13:34:14 -06:00

214 lines
5.1 KiB
Go

//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"github.com/containers/libhvee/pkg/wmiext"
)
const (
HyperVNamespace = "root\\virtualization\\v2"
VirtualSystemManagementService = "Msvm_VirtualSystemManagementService"
MsvmComputerSystem = "Msvm_ComputerSystem"
)
// https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-computersystem
type VirtualMachineManager struct {
}
func NewVirtualMachineManager() *VirtualMachineManager {
return &VirtualMachineManager{}
}
func NewLocalHyperVService() (*wmiext.Service, error) {
service, err := wmiext.NewLocalService(HyperVNamespace)
if err != nil {
return nil, translateCommonHyperVWmiError(err)
}
return service, nil
}
func (vmm *VirtualMachineManager) GetAll() ([]*VirtualMachine, error) {
// Fetch through settings to avoid locale sensitive properties
const wql = "Select * From Msvm_VirtualSystemSettingData Where VirtualSystemType = 'Microsoft:Hyper-V:System:Realized'"
var service *wmiext.Service
var err error
if service, err = NewLocalHyperVService(); err != nil {
return []*VirtualMachine{}, err
}
defer service.Close()
var enum *wmiext.Enum
if enum, err = service.ExecQuery(wql); err != nil {
return nil, err
}
defer enum.Close()
var vms []*VirtualMachine
for {
settings, err := enum.Next()
if err != nil {
return vms, err
}
// Finished iterating
if settings == nil {
break
}
vm, err := vmm.findVMFromSettings(service, settings)
settings.Close()
if err != nil {
return vms, err
}
vms = append(vms, vm)
}
return vms, nil
}
func (vmm *VirtualMachineManager) Exists(name string) (bool, error) {
vms, err := vmm.GetAll()
if err != nil {
return false, err
}
for _, i := range vms {
// TODO should case be honored or ignored?
if i.Name == name {
return true, nil
}
}
return false, nil
}
func (vmm *VirtualMachineManager) GetMachine(name string) (*VirtualMachine, error) {
const wql = "Select * From Msvm_VirtualSystemSettingData Where VirtualSystemType = 'Microsoft:Hyper-V:System:Realized' And ElementName='%s'"
vm := &VirtualMachine{}
var service *wmiext.Service
var err error
if service, err = NewLocalHyperVService(); err != nil {
return vm, err
}
defer service.Close()
var enum *wmiext.Enum
if enum, err = service.ExecQuery(fmt.Sprintf(wql, name)); err != nil {
return nil, err
}
defer enum.Close()
settings, err := service.FindFirstInstance(fmt.Sprintf(wql, name))
if err != nil {
return vm, fmt.Errorf("could not find virtual machine %q: %w", name, err)
}
defer settings.Close()
return vmm.findVMFromSettings(service, settings)
}
func (vmm *VirtualMachineManager) findVMFromSettings(service *wmiext.Service, settings *wmiext.Instance) (*VirtualMachine, error) {
path, err := settings.Path()
if err != nil {
return nil, err
}
vm := &VirtualMachine{vmm: vmm}
err = service.FindFirstRelatedObject(path, MsvmComputerSystem, vm)
return vm, err
}
func (*VirtualMachineManager) CreateVhdxFile(path string, maxSize uint64) error {
var service *wmiext.Service
var err error
if service, err = NewLocalHyperVService(); err != nil {
return err
}
defer service.Close()
settings := &VirtualHardDiskSettings{}
settings.Format = 3
settings.MaxInternalSize = maxSize
settings.Type = 3
settings.Path = path
instance, err := service.CreateInstance("Msvm_VirtualHardDiskSettingData", settings)
if err != nil {
return err
}
defer instance.Close()
settingsStr := instance.GetCimText()
imms, err := service.GetSingletonInstance("Msvm_ImageManagementService")
if err != nil {
return err
}
defer imms.Close()
var job *wmiext.Instance
var ret int32
err = imms.BeginInvoke("CreateVirtualHardDisk").
In("VirtualDiskSettingData", settingsStr).
Execute().
Out("Job", &job).
Out("ReturnValue", &ret).
End()
if err != nil {
return fmt.Errorf("failed to create vhdx: %w", err)
}
return waitVMResult(ret, service, job, "failed to create vhdx", nil)
}
// GetSummaryInformation returns the live VM summary information for all virtual machines.
// The requestedFields parameter controls which fields of summary information are populated.
// SummaryRequestCommon and SummaryRequestNearAll provide predefined combinations for this
// parameter.
func (vmm *VirtualMachineManager) GetSummaryInformation(requestedFields SummaryRequestSet) ([]SummaryInformation, error) {
return vmm.getSummaryInformation("", requestedFields)
}
func (vmm *VirtualMachineManager) getSummaryInformation(settingsPath string, requestedFields SummaryRequestSet) ([]SummaryInformation, error) {
var service *wmiext.Service
var err error
if service, err = NewLocalHyperVService(); err != nil {
return nil, err
}
defer service.Close()
vsms, err := service.GetSingletonInstance(VirtualSystemManagementService)
if err != nil {
return nil, err
}
defer vsms.Close()
var summary []SummaryInformation
inv := vsms.BeginInvoke("GetSummaryInformation").
In("RequestedInformation", []uint(requestedFields))
if len(settingsPath) > 0 {
inv.In("SettingData", []string{settingsPath})
}
err = inv.Execute().
Out("SummaryInformation", &summary).
End()
if err != nil {
return nil, err
}
return summary, nil
}