Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VM.GetEnvironment() to retrieve OVF Environment #528

Merged
merged 12 commits into from
Mar 10, 2023
1 change: 1 addition & 0 deletions .changes/v2.20.0/528-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added method `VM.GetEnvironment` to retrieve OVF Environment [GH-528]
14 changes: 14 additions & 0 deletions govcd/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,20 @@ func (vm *VM) GetProductSectionList() (*types.ProductSectionList, error) {
return getProductSectionList(vm.client, vm.VM.HREF)
}

// GetEnvironment returns the OVF Environment. It's only available for poweredOn VM
func (vm *VM) GetEnvironment() (*types.OvfEnvironment, error) {
vmStatus, err := vm.GetStatus()
if err != nil {
return nil, fmt.Errorf("unable to get OVF environment: %s", err)
}

if vmStatus != "POWERED_ON" {
return nil, fmt.Errorf("OVF environment is only available when VM is powered on")
}

return vm.VM.Environment, nil
}

// GetGuestCustomizationSection retrieves guest customization section for a VM. It allows to read VM guest customization properties.
func (vm *VM) GetGuestCustomizationSection() (*types.GuestCustomizationSection, error) {
if vm == nil || vm.VM.HREF == "" {
Expand Down
67 changes: 67 additions & 0 deletions govcd/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2281,3 +2281,70 @@ func createNsxtVAppAndVm(vcd *TestVCD, check *C) (*VApp, *VM) {

return vapp, vm
}

func (vcd *TestVCD) Test_GetOvfEnvironment(check *C) {
if vcd.skipVappTests {
check.Skip("Skipping test because vapp was not successfully created at setup")
}
vapp := vcd.findFirstVapp()
existingVm, vmName := vcd.findFirstVm(vapp)
if vmName == "" {
check.Skip("skipping test because no VM is found")
}
vm, err := vcd.client.Client.GetVMByHref(existingVm.HREF)
check.Assert(err, IsNil)

vmStatus, err := vm.GetStatus()
check.Assert(err, IsNil)
if vmStatus != "POWERED_ON" {
task, err := vm.PowerOn()
check.Assert(err, IsNil)
err = task.WaitTaskCompletion()
check.Assert(err, IsNil)
check.Assert(task.Task.Status, Equals, "success")
}

// Read ovfenv when VM is started
ovfenv, err := vm.GetEnvironment()
check.Assert(err, IsNil)
check.Assert(ovfenv, NotNil)

// Provides information from the virtualization platform like VM moref
check.Assert(strings.Contains(ovfenv.VCenterId, "vm-"), Equals, true)

// Check virtualization platform Vendor
check.Assert(ovfenv.PlatformSection, NotNil)
check.Assert(ovfenv.PlatformSection.Vendor, Equals, "VMware, Inc.")

// Check guest operating system level configuration for hostname
check.Assert(ovfenv.PropertySection, NotNil)
for _, p := range ovfenv.PropertySection.Properties {
if p.Key == "vCloud_computerName" {
check.Assert(p.Value, Not(Equals), "")
}
}
check.Assert(ovfenv.EthernetAdapterSection, NotNil)
for _, p := range ovfenv.EthernetAdapterSection.Adapters {
check.Assert(p.Mac, Not(Equals), "")
}

// PowerOff
task, err := vm.PowerOff()
check.Assert(err, IsNil)
err = task.WaitTaskCompletion()
check.Assert(err, IsNil)
check.Assert(task.Task.Status, Equals, "success")

ovfenv, err = vm.GetEnvironment()
check.Assert(strings.Contains(err.Error(), "OVF environment is only available when VM is powered on"), Equals, true)
check.Assert(ovfenv, IsNil)

// Leave things as they were
if vmStatus != "POWERED_OFF" {
task, err := vm.PowerOn()
check.Assert(err, IsNil)
err = task.WaitTaskCompletion()
check.Assert(err, IsNil)
check.Assert(task.Task.Status, Equals, "success")
}
}
51 changes: 49 additions & 2 deletions types/v56/vm_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ type Vm struct {

Snapshots *SnapshotSection `xml:"SnapshotSection,omitempty"`

// TODO: OVF Sections to be implemented
// Environment OVF_Environment `xml:"Environment,omitempty"
// The OVF environment defines how the guest software and the virtualization platform interact.
Environment *OvfEnvironment `xml:"Environment,omitempty"`

VmSpecSection *VmSpecSection `xml:"VmSpecSection,omitempty"`

Expand Down Expand Up @@ -161,3 +161,50 @@ type SourcedVmTemplateParams struct {
VmTemplateInstantiationParams *InstantiationParams `xml:"VmTemplateInstantiationParams,omitempty"` // Same as InstantiationParams used for VMs within a vApp
StorageProfile *Reference `xml:"StorageProfile,omitempty"` // A reference to a storage profile to be used for the VM. The specified storage profile must exist in the organization vDC that contains the composed vApp. If not specified, the default storage profile for the vDC is used.
}

// The OVF environment enables the guest software to access information about the virtualization platform, such as
// the user-specified values for the properties defined in the OVF descriptor.
type OvfEnvironment struct {
XMLName xml.Name `xml:"Environment"`
Ve string `xml:"ve,attr,omitempty"` // Xml namespace
Id string `xml:"id,attr,omitempty"` // Identification of VM from OVF Descriptor. Describes this virtual system.
VCenterId string `xml:"vCenterId,attr,omitempty"` // VM moref in the vCenter
PlatformSection *PlatformSection `xml:"PlatformSection,omitempty"` // Describes the virtualization platform
PropertySection *PropertySection `xml:"PropertySection,omitempty"` // Property elements with key/value pairs
EthernetAdapterSection *EthernetAdapterSection `xml:"EthernetAdapterSection,omitempty"` // Contains adapters info and virtual networks attached
}

// Provides information from the virtualization platform
type PlatformSection struct {
XMLName xml.Name `xml:"PlatformSection"`
Kind string `xml:"Kind,omitempty"` // Hypervisor kind is typically VMware ESXi
Version string `xml:"Version,omitempty"` // Hypervisor version
Vendor string `xml:"Vendor,omitempty"` // VMware, Inc.
Locale string `xml:"Locale,omitempty"` // Hypervisor locale
}

// Contains a list of key/value pairs corresponding to properties defined in the OVF descriptor
// Operating system level configuration, such as host names, IP address, subnets, gateways, etc.
// Application-level configuration such as DNS name of active directory server, databases and
// other external services.
type PropertySection struct {
XMLName xml.Name `xml:"PropertySection"`
Properties []*OvfProperty `xml:"Property,omitempty"`
}

type OvfProperty struct {
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}

// Contains adapters info and virtual networks attached
type EthernetAdapterSection struct {
XMLName xml.Name `xml:"EthernetAdapterSection"`
Adapters []*Adapter `xml:"Adapter,omitempty"`
}

type Adapter struct {
Mac string `xml:"mac,attr"`
Network string `xml:"network,attr"`
UnitNumber string `xml:"unitNumber,attr"`
}