From 42bf813058a4ca5ecf217c27e18225cabe9de9ec Mon Sep 17 00:00:00 2001 From: akutz Date: Wed, 4 Dec 2024 08:32:24 -0600 Subject: [PATCH] api: Update OVF parser to conform to spec This patch updates the OVF parser to conform to the DMTF spec for OVF content. This includes: * support for VirtualSystemCollection * a single OperatingSystem section in VirtualSystem * removing the roll-up sections at the root of the Envelope that belong only under a VirtualSystem or VirtualSystemCollection BREAKING: Users of the `ovf` package will need to update their sources to conform to the changes from this patch. It should be a fairly simple change. Signed-off-by: akutz --- ovf/envelope.go | 46 ++++-- ovf/fixtures/virtualsystemcollection.ovf | 189 +++++++++++++++++++++++ ovf/importer/spec.go | 4 +- ovf/ovf_test.go | 12 ++ simulator/ovf_manager.go | 4 +- 5 files changed, 234 insertions(+), 21 deletions(-) create mode 100644 ovf/fixtures/virtualsystemcollection.ovf diff --git a/ovf/envelope.go b/ovf/envelope.go index aa77aab2d..3b965bf88 100644 --- a/ovf/envelope.go +++ b/ovf/envelope.go @@ -1,5 +1,5 @@ /* -Copyright (c) 2015-2023 VMware, Inc. All Rights Reserved. +Copyright (c) 2015-2024 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,32 +20,46 @@ import ( "fmt" ) +// Envelope is defined according to +// https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.1.pdf. +// +// Section 9 describes the parent/child relationships. +// +// A VirtualSystem may have zero or more VirtualHardware sections. type Envelope struct { References []File `xml:"References>File"` // Package level meta-data - Annotation *AnnotationSection `xml:"AnnotationSection"` - Product *ProductSection `xml:"ProductSection"` - Network *NetworkSection `xml:"NetworkSection"` - Disk *DiskSection `xml:"DiskSection"` - OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"` - Eula *EulaSection `xml:"EulaSection"` - VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"` - ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"` - DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"` + Disk *DiskSection `xml:"DiskSection,omitempty"` + Network *NetworkSection `xml:"NetworkSection,omitempty"` + DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection,omitempty"` // Content: A VirtualSystem or a VirtualSystemCollection - VirtualSystem *VirtualSystem `xml:"VirtualSystem"` + VirtualSystem *VirtualSystem `xml:"VirtualSystem,omitempty"` + VirtualSystemCollection *VirtualSystemCollection `xml:"VirtualSystemCollection,omitempty"` } type VirtualSystem struct { Content - Annotation []AnnotationSection `xml:"AnnotationSection"` - Product []ProductSection `xml:"ProductSection"` - OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"` - Eula []EulaSection `xml:"EulaSection"` - VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"` + Annotation *AnnotationSection `xml:"AnnotationSection,omitempty"` + Product []ProductSection `xml:"ProductSection,omitempty"` + Eula []EulaSection `xml:"EulaSection,omitempty"` + OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection,omitempty"` + VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection,omitempty"` +} + +type VirtualSystemCollection struct { + Content + + // Collection level meta-data + ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection,omitempty"` + Annotation *AnnotationSection `xml:"AnnotationSection,omitempty"` + Product []ProductSection `xml:"ProductSection,omitempty"` + Eula []EulaSection `xml:"EulaSection,omitempty"` + + // Content: One or more VirtualSystems + VirtualSystem []VirtualSystem `xml:"VirtualSystem,omitempty"` } type File struct { diff --git a/ovf/fixtures/virtualsystemcollection.ovf b/ovf/fixtures/virtualsystemcollection.ovf new file mode 100644 index 000000000..a1368fb44 --- /dev/null +++ b/ovf/fixtures/virtualsystemcollection.ovf @@ -0,0 +1,189 @@ + + + + + + + + + + Describes the set of virtual disks + + + + + List of logical networks used in the package + + The network that the VSs connect to + + + http://www.dmtf.org/networkportprofiles/networkportprofile1.xml + + + http://www.dmtf.org/networkportprofiles/networkportprofile2.xml + + + + Collection of 2 VSs + + Describes a virtual system + Virtual Appliance One + + Describes product information for the appliance + The Great Appliance + Some Great Corporation + 13.00 + 13.00-b5 + http://www.somegreatcorporation.com/greatappliance + http://www.somegreatcorporation.com/ + + Email address of administrator + + + The IP address of this appliance + + + + A random annotation on this service. It can be ignored + Contact customer support if you have any problems + + + License information for the appliance + Insert your favorite license here + + + Memory = 4 GB, CPU = 1 GHz, Disk = 100 GB, 1 Ethernet nic + + Hertz*10^9 + Virtual CPU + 1 GHz virtual CPU + 1 + 1 + 3 + 1 + + + byte*2^30 + Memory + 1 GByte of memory + 2 + 4 + 1 + + + 00-16-8B-DB-00-5E + VS Network + Virtual NIC + + Ethernet Port + + 3 + + + http://www.dmtf.org/networkportprofiles/networkportprofile1.xml + 2 + 10 + 1 + + + byte*2^30 + Virtual Disk + 100 GByte Virtual Disk + 4 + 100 + 31 + 1 + + + + Guest Operating System + OS + + + + Describes a virtual system + Virtual Appliance Two + + Describes product information for the appliance + The Great Appliance + Some Great Corporation + 13.00 + 13.00-b5 + http://www.somegreatcorporation.com/greatappliance + http://www.somegreatcorporation.com/ + + Email address of administrator + + + The IP address of this appliance + + + + A random annotation on this service. It can be ignored + Contact customer support if you have any problems + + + License information for the appliance + Insert your favorite license here + + + Memory = 4 GB, CPU = 1 GHz, Disk = 100 GB, 1 Ethernet nic + + Hertz*10^9 + Virtual CPU + 1 GHz virtual CPU + 1 + 1 + 3 + 1 + + + byte*2^30 + Memory + 1 GByte of memory + 2 + 4 + 1 + + + 00-16-8B-DB-00-5F + VS Network + Virtual NIC + + Ethernet Port + + 3 + + + http://www.dmtf.org/networkportprofiles/networkportprofile2.xml + 2 + 10 + 1 + + + byte*2^30 + Virtual Disk + 100 GByte Virtual Disk + 4 + 100 + 31 + 1 + + + + Guest Operating System + OS + + + + \ No newline at end of file diff --git a/ovf/importer/spec.go b/ovf/importer/spec.go index 5afd00b89..637815350 100644 --- a/ovf/importer/spec.go +++ b/ovf/importer/spec.go @@ -114,9 +114,7 @@ func Spec(fpath string, a Archive, hidden, verbose bool) (*Options, error) { } if e.VirtualSystem != nil && e.VirtualSystem.Annotation != nil { - for _, a := range e.VirtualSystem.Annotation { - o.Annotation += a.Annotation - } + o.Annotation = e.VirtualSystem.Annotation.Annotation } if e.Network != nil { diff --git a/ovf/ovf_test.go b/ovf/ovf_test.go index f75e2fad9..fe343b28d 100644 --- a/ovf/ovf_test.go +++ b/ovf/ovf_test.go @@ -22,6 +22,8 @@ import ( "os" "testing" "text/tabwriter" + + "github.com/stretchr/testify/assert" ) func testEnvelope(t *testing.T, fn string) *Envelope { @@ -95,3 +97,13 @@ func TestDeploymentOptions(t *testing.T) { tw.Flush() t.Log(b.String()) } + +func TestVirtualSystemCollection(t *testing.T) { + e := testEnvelope(t, "fixtures/virtualsystemcollection.ovf") + + assert.Nil(t, e.VirtualSystem) + assert.NotNil(t, e.VirtualSystemCollection) + assert.Len(t, e.VirtualSystemCollection.VirtualSystem, 2) + assert.Equal(t, e.VirtualSystemCollection.VirtualSystem[0].ID, "storage server") + assert.Equal(t, e.VirtualSystemCollection.VirtualSystem[1].ID, "web-server") +} diff --git a/simulator/ovf_manager.go b/simulator/ovf_manager.go index 1ce6d860e..bc4db22f6 100644 --- a/simulator/ovf_manager.go +++ b/simulator/ovf_manager.go @@ -185,8 +185,8 @@ func (m *OvfManager) CreateImportSpec(ctx *Context, req *types.CreateImportSpec) } } - if os := env.VirtualSystem.OperatingSystem; len(os) != 0 { - if id := os[0].OSType; id != nil { + if os := env.VirtualSystem.OperatingSystem; os != nil { + if id := os.OSType; id != nil { spec.ConfigSpec.GuestId = *id } }