Skip to content

Commit

Permalink
api: Update OVF parser to conform to spec
Browse files Browse the repository at this point in the history
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 <akutz@vmware.com>
  • Loading branch information
akutz committed Dec 5, 2024
1 parent 46c15fb commit 42bf813
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 21 deletions.
46 changes: 30 additions & 16 deletions ovf/envelope.go
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 {
Expand Down
189 changes: 189 additions & 0 deletions ovf/fixtures/virtualsystemcollection.ovf
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Example 3 from https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.1.pdf
-->
<Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/2"
xmlns="http://schemas.dmtf.org/ovf/envelope/2"
xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
xmlns:epasd="http://schemas.dmtf.org/wbem/wscim/1/cim2608 schema/2/CIM_EthernetPortAllocationSettingData"
xmlns:sasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_StorageAllocationSettingData">
<!-- References to all external files -->
<References>
<File ovf:id="file1" ovf:href="vmdisk1.vmdk" ovf:size="2000000000" />
</References>
<!-- Describes meta-information for all virtual disks in the package -->
<DiskSection>
<Info>Describes the set of virtual disks</Info>
<Disk ovf:diskId="vmdisk1" ovf:fileRef="file1" ovf:capacity="4294967296"
ovf:format="http://www.examplecompany.com/interfaces/specifications/vmdk.html#sparse" />
</DiskSection>
<!-- Describes all networks used in the package -->
<NetworkSection>
<Info>List of logical networks used in the package</Info>
<Network ovf:name="VS Network">
<Description>The network that the VSs connect to</Description>
<!-- Network port profile for storage traffic -->

<NetworkPortProfileURI>http://www.dmtf.org/networkportprofiles/networkportprofile1.xml</NetworkPortProfileURI>
<!-- Network port profile for networking traffic -->

<NetworkPortProfileURI>http://www.dmtf.org/networkportprofiles/networkportprofile2.xml</NetworkPortProfileURI>
</Network>
</NetworkSection>
<VirtualSystemCollection ovf:id="vsc1">
<Info>Collection of 2 VSs</Info>
<VirtualSystem ovf:id="storage server">
<Info>Describes a virtual system</Info>
<Name>Virtual Appliance One</Name>
<ProductSection>
<Info>Describes product information for the appliance</Info>
<Product>The Great Appliance</Product>
<Vendor>Some Great Corporation</Vendor>
<Version>13.00</Version>
<FullVersion>13.00-b5</FullVersion>
<ProductUrl>http://www.somegreatcorporation.com/greatappliance</ProductUrl>
<VendorUrl>http://www.somegreatcorporation.com/</VendorUrl>
<Property ovf:key="adminemail" ovf:type="string">
<Description>Email address of administrator</Description>
</Property>
<Property ovf:key="app_ip" ovf:type="string" ovf:defaultValue="192.168.0.10">
<Description>The IP address of this appliance</Description>
</Property>
</ProductSection>
<AnnotationSection ovf:required="false">
<Info>A random annotation on this service. It can be ignored</Info>
<Annotation>Contact customer support if you have any problems</Annotation>
</AnnotationSection>
<EulaSection>
<Info>License information for the appliance</Info>
<License>Insert your favorite license here</License>
</EulaSection>
<VirtualHardwareSection>
<Info>Memory = 4 GB, CPU = 1 GHz, Disk = 100 GB, 1 Ethernet nic</Info>
<Item>
<rasd:AllocationUnits>Hertz*10^9</rasd:AllocationUnits>
<rasd:Description>Virtual CPU</rasd:Description>
<rasd:ElementName>1 GHz virtual CPU</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:Reservation>1</rasd:Reservation>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:AllocationUnits>byte*2^30</rasd:AllocationUnits>
<rasd:Description>Memory</rasd:Description>
<rasd:ElementName>1 GByte of memory</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<EthernetPortItem>
<epasd:Address>00-16-8B-DB-00-5E</epasd:Address>
<epasd:Connection>VS Network</epasd:Connection>
<epasd:Description>Virtual NIC</epasd:Description>

<epasd:ElementName>Ethernet Port</epasd:ElementName>

<epasd:InstanceID>3</epasd:InstanceID>

<epasd:NetworkPortProfileID>
http://www.dmtf.org/networkportprofiles/networkportprofile1.xml</epasd:NetworkPortProfileID>
<epasd:NetworkPortProfileIDType>2</epasd:NetworkPortProfileIDType>
<epasd:ResourceType>10</epasd:ResourceType>
<epasd:VirtualQuantityUnits>1</epasd:VirtualQuantityUnits>
</EthernetPortItem>
<StorageItem>
<sasd:AllocationUnits>byte*2^30</sasd:AllocationUnits>
<sasd:Description>Virtual Disk</sasd:Description>
<sasd:ElementName>100 GByte Virtual Disk</sasd:ElementName>
<sasd:InstanceID>4</sasd:InstanceID>
<sasd:Reservation>100</sasd:Reservation>
<sasd:ResourceType>31</sasd:ResourceType>
<sasd:VirtualQuantity>1</sasd:VirtualQuantity>
</StorageItem>
</VirtualHardwareSection>
<OperatingSystemSection ovf:id="58" ovf:required="false">
<Info>Guest Operating System</Info>
<Description>OS</Description>
</OperatingSystemSection>
</VirtualSystem>
<VirtualSystem ovf:id="web-server">
<Info>Describes a virtual system</Info>
<Name>Virtual Appliance Two</Name>
<ProductSection>
<Info>Describes product information for the appliance</Info>
<Product>The Great Appliance</Product>
<Vendor>Some Great Corporation</Vendor>
<Version>13.00</Version>
<FullVersion>13.00-b5</FullVersion>
<ProductUrl>http://www.somegreatcorporation.com/greatappliance</ProductUrl>
<VendorUrl>http://www.somegreatcorporation.com/</VendorUrl>
<Property ovf:key="adminemail" ovf:type="string">
<Description>Email address of administrator</Description>
</Property>
<Property ovf:key="app_ip" ovf:type="string" ovf:defaultValue="192.168.0.10">
<Description>The IP address of this appliance</Description>
</Property>
</ProductSection>
<AnnotationSection ovf:required="false">
<Info>A random annotation on this service. It can be ignored</Info>
<Annotation>Contact customer support if you have any problems</Annotation>
</AnnotationSection>
<EulaSection>
<Info>License information for the appliance</Info>
<License>Insert your favorite license here</License>
</EulaSection>
<VirtualHardwareSection>
<Info>Memory = 4 GB, CPU = 1 GHz, Disk = 100 GB, 1 Ethernet nic</Info>
<Item>
<rasd:AllocationUnits>Hertz*10^9</rasd:AllocationUnits>
<rasd:Description>Virtual CPU</rasd:Description>
<rasd:ElementName>1 GHz virtual CPU</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:Reservation>1</rasd:Reservation>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:AllocationUnits>byte*2^30</rasd:AllocationUnits>
<rasd:Description>Memory</rasd:Description>
<rasd:ElementName>1 GByte of memory</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
</Item>
<EthernetPortItem>
<epasd:Address>00-16-8B-DB-00-5F</epasd:Address>
<epasd:Connection>VS Network</epasd:Connection>
<epasd:Description>Virtual NIC</epasd:Description>

<epasd:ElementName>Ethernet Port</epasd:ElementName>
<!-- Virtual NIC for networking traffic -->
<epasd:InstanceID>3</epasd:InstanceID>

<epasd:NetworkPortProfileID>
http://www.dmtf.org/networkportprofiles/networkportprofile2.xml</epasd:NetworkPortProfileID>
<epasd:NetworkPortProfileIDType>2</epasd:NetworkPortProfileIDType>
<epasd:ResourceType>10</epasd:ResourceType>
<epasd:VirtualQuantityUnits>1</epasd:VirtualQuantityUnits>
</EthernetPortItem>
<StorageItem>
<sasd:AllocationUnits>byte*2^30</sasd:AllocationUnits>
<sasd:Description>Virtual Disk</sasd:Description>
<sasd:ElementName>100 GByte Virtual Disk</sasd:ElementName>
<sasd:InstanceID>4</sasd:InstanceID>
<sasd:Reservation>100</sasd:Reservation>
<sasd:ResourceType>31</sasd:ResourceType>
<sasd:VirtualQuantity>1</sasd:VirtualQuantity>
</StorageItem>
</VirtualHardwareSection>
<OperatingSystemSection ovf:id="58" ovf:required="false">
<Info>Guest Operating System</Info>
<Description>OS</Description>
</OperatingSystemSection>
</VirtualSystem>
</VirtualSystemCollection>
</Envelope>
4 changes: 1 addition & 3 deletions ovf/importer/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions ovf/ovf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"os"
"testing"
"text/tabwriter"

"github.com/stretchr/testify/assert"
)

func testEnvelope(t *testing.T, fn string) *Envelope {
Expand Down Expand Up @@ -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")
}
4 changes: 2 additions & 2 deletions simulator/ovf_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down

0 comments on commit 42bf813

Please sign in to comment.