Skip to content

Commit

Permalink
Add OVF file upload capability
Browse files Browse the repository at this point in the history
Signed-off-by: Guo, Larry (NSB - CN/Qingdao) <larry.guo@nokia-sbell.com>
  • Loading branch information
Guo, Larry (NSB - CN/Qingdao) committed Jul 27, 2020
1 parent 74c8a0f commit a525920
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 10 deletions.
1 change: 1 addition & 0 deletions govcd/api_vcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ type TestConfig struct {
OvaPath string `yaml:"ovaPath,omitempty"`
OvaChunkedPath string `yaml:"ovaChunkedPath,omitempty"`
OvaMultiVmPath string `yaml:"ovaMultiVmPath,omitempty"`
OvfPath string `yaml:"ovfPath,omitempty"`
} `yaml:"ova"`
Media struct {
MediaPath string `yaml:"mediaPath,omitempty"`
Expand Down
43 changes: 34 additions & 9 deletions govcd/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"path"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/vmware/go-vcloud-director/v2/types/v56"
Expand Down Expand Up @@ -129,24 +130,48 @@ func (cat *Catalog) UploadOvf(ovaFileName, itemName, description string, uploadP
}
}

filesAbsPaths, tmpDir, err := util.Unpack(ovaFileName)
isOvf := false
fileContentType, err := util.GetFileContentType(ovaFileName)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
return UploadTask{}, err
}

ovfFilePath, err := getOvfPath(filesAbsPaths)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
if strings.Contains(fileContentType, "text/xml") {
isOvf = true
}
ovfFilePath := ovaFileName
tmpDir := path.Dir(ovaFileName)
filesAbsPaths := []string{ovfFilePath}
if !isOvf {
filesAbsPaths, tmpDir, err = util.Unpack(ovaFileName)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
}
ovfFilePath, err = getOvfPath(filesAbsPaths)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
}
}

ovfFileDesc, err := getOvf(ovfFilePath)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
}

err = validateOvaContent(filesAbsPaths, &ovfFileDesc, tmpDir)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
if !isOvf {
err = validateOvaContent(filesAbsPaths, &ovfFileDesc, tmpDir)
if err != nil {
return UploadTask{}, fmt.Errorf("%s. Unpacked files for checking are accessible in: "+tmpDir, err)
}
} else {
dir := path.Dir(ovfFilePath)
for _, fileItem := range ovfFileDesc.File {
dependFile := path.Join(dir, fileItem.HREF)
dependFile, err := validateAndFixFilePath(dependFile)
if err != nil {
return UploadTask{}, err
}
filesAbsPaths = append(filesAbsPaths, dependFile)
}
}

catalogItemUploadURL, err := findCatalogItemUploadLink(cat, "application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml")
Expand Down
9 changes: 9 additions & 0 deletions govcd/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,15 @@ func (vcd *TestVCD) Test_UploadOvf_cleaned_extracted_files(check *C) {

}

// Tests System function UploadOvf by creating catalog and
// checking if provided standard ovf file uploaded.
func (vcd *TestVCD) Test_UploadOvfFile(check *C) {
fmt.Printf("Running: %s\n", check.TestName())

skipWhenOvaPathMissing(vcd, check)
checkUploadOvf(vcd, check, vcd.config.OVA.OvfPath, vcd.config.VCD.Catalog.Name, TestUploadOvf+"7")
}

func countFolders() int {
files, err := ioutil.ReadDir(os.TempDir())
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion govcd/sample_govcd_test_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
},
"ova": {
"ovaPath": "../test-resources/test_vapp_template.ova",
"ovaChunkedPath": "../test-resources/template_with_custom_chunk_size.ova"
"ovaChunkedPath": "../test-resources/template_with_custom_chunk_size.ova",
"ovfPath": "../test-resources/test_vapp_template_ovf/descriptor.ovf"
},
"media": {
"mediaPath": "../test-resources/test.iso",
Expand Down
3 changes: 3 additions & 0 deletions govcd/sample_govcd_test_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ ova:
#
# The ova with multi VMs for tests.
ovaMultiVmPath: ../test-resources/vapp_with_3_vms.ova
#
# The ovf for uploading catalog item for tests.
ovfPath: ../test-resources/test_vapp_template_ovf/descriptor.ovf
media:
# The iso for uploading media item for tests.
# Default paths are simple iso provided by project
Expand Down
171 changes: 171 additions & 0 deletions test-resources/test_vapp_template_ovf/descriptor.ovf
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?xml version='1.0' encoding='utf-8'?>
<ovf:Envelope xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:vcloud="http://www.vmware.com/vcloud/v1.5" 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:vmw="http://www.vmware.com/schema/ovf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.dmtf.org/ovf/envelope/1 http://schemas.dmtf.org/ovf/envelope/1/dsp8023_1.1.0.xsd http://www.vmware.com/vcloud/v1.5 http://bos1-vcd-sp-static-198-193.eng.vmware.com/api/v1.5/schema/master.xsd http://www.vmware.com/schema/ovf http://www.vmware.com/schema/ovf http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.22.0/CIM_ResourceAllocationSettingData.xsd http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.22.0/CIM_VirtualSystemSettingData.xsd">
<ovf:References>
<ovf:File ovf:href="vm-20b41fa7-3212-4faf-9295-045d0bc49783-disk-0.vmdk" ovf:id="file-b4652207-7d14-4d4b-bf42-c6911d947d64-2000" ovf:size="68096"/>
</ovf:References>
<ovf:DiskSection>
<ovf:Info>Virtual disk information</ovf:Info>
<ovf:Disk ovf:capacity="40" ovf:capacityAllocationUnits="byte * 2^20" ovf:diskId="vmdisk-b4652207-7d14-4d4b-bf42-c6911d947d64-2000" ovf:fileRef="file-b4652207-7d14-4d4b-bf42-c6911d947d64-2000" ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"/>
</ovf:DiskSection>
<ovf:NetworkSection>
<ovf:Info>The list of logical networks</ovf:Info>
<ovf:Network ovf:name="none">
<ovf:Description>This is a special place-holder used for disconnected network interfaces.</ovf:Description>
</ovf:Network>
</ovf:NetworkSection>
<vcloud:CustomizationSection goldMaster="false" ovf:required="false">
<ovf:Info>VApp template customization section</ovf:Info>
<vcloud:CustomizeOnInstantiate>true</vcloud:CustomizeOnInstantiate>
</vcloud:CustomizationSection>
<vcloud:NetworkConfigSection ovf:required="false">
<ovf:Info>The configuration parameters for logical networks</ovf:Info>
<vcloud:NetworkConfig networkName="none">
<vcloud:Description>This is a special place-holder used for disconnected network interfaces.</vcloud:Description>
<vcloud:Configuration>
<vcloud:IpScopes>
<vcloud:IpScope>
<vcloud:IsInherited>false</vcloud:IsInherited>
<vcloud:Gateway>196.254.254.254</vcloud:Gateway>
<vcloud:Netmask>255.255.0.0</vcloud:Netmask>
<vcloud:Dns1>196.254.254.254</vcloud:Dns1>
</vcloud:IpScope>
</vcloud:IpScopes>
<vcloud:FenceMode>isolated</vcloud:FenceMode>
</vcloud:Configuration>
<vcloud:IsDeployed>false</vcloud:IsDeployed>
</vcloud:NetworkConfig>
</vcloud:NetworkConfigSection>
<ovf:VirtualSystemCollection ovf:id="test_vapp_template">
<ovf:Info>A collection of virtual machines</ovf:Info>
<ovf:Name>test_vapp_template</ovf:Name>
<ovf:StartupSection>
<ovf:Info>VApp startup section</ovf:Info>
<ovf:Item ovf:id="testvm1" ovf:order="0" ovf:startAction="powerOn" ovf:startDelay="0" ovf:stopAction="powerOff" ovf:stopDelay="0"/>
</ovf:StartupSection>
<ovf:VirtualSystem ovf:id="testvm1">
<ovf:Info>A virtual machine</ovf:Info>
<ovf:Name>testvm1</ovf:Name>
<ovf:OperatingSystemSection ovf:id="1" vmw:osType="windows9Server64Guest">
<ovf:Info>Specifies the operating system installed</ovf:Info>
<ovf:Description>Microsoft Windows Server 2016 (64-bit)</ovf:Description>
</ovf:OperatingSystemSection>
<ovf:VirtualHardwareSection ovf:transport="">
<ovf:Info>Virtual hardware requirements</ovf:Info>
<ovf:System>
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
<vssd:InstanceID>0</vssd:InstanceID>
<vssd:VirtualSystemIdentifier>testvm1</vssd:VirtualSystemIdentifier>
<vssd:VirtualSystemType>vmx-11</vssd:VirtualSystemType>
</ovf:System>
<ovf:Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:Connection vcloud:ipAddressingMode="NONE" vcloud:primaryNetworkConnection="true">none</rasd:Connection>
<rasd:Description>E1000s ethernet adapter on "none"</rasd:Description>
<rasd:ElementName>Network adapter 0</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:ResourceSubType>E1000E</rasd:ResourceSubType>
<rasd:ResourceType>10</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>
</ovf:Item>
<ovf:Item>
<rasd:Address>0</rasd:Address>
<rasd:Description>SCSI Controller</rasd:Description>
<rasd:ElementName>SCSI Controller 0</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceSubType>lsilogicsas</rasd:ResourceSubType>
<rasd:ResourceType>6</rasd:ResourceType>
</ovf:Item>
<ovf:Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:Description>Hard disk</rasd:Description>
<rasd:ElementName>Hard disk 1</rasd:ElementName>
<rasd:HostResource>ovf:/disk/vmdisk-b4652207-7d14-4d4b-bf42-c6911d947d64-2000</rasd:HostResource>
<rasd:InstanceID>2000</rasd:InstanceID>
<rasd:Parent>2</rasd:Parent>
<rasd:ResourceType>17</rasd:ResourceType>
<rasd:VirtualQuantity>41943040</rasd:VirtualQuantity>
<rasd:VirtualQuantityUnits>byte</rasd:VirtualQuantityUnits>
<vmw:Config ovf:required="false" vmw:key="backing.writeThrough" vmw:value="false"/>
</ovf:Item>
<ovf:Item>
<rasd:Address>0</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>IDE Controller 0</rasd:ElementName>
<rasd:InstanceID>3</rasd:InstanceID>
<rasd:ResourceType>5</rasd:ResourceType>
</ovf:Item>
<ovf:Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:Description>Floppy Drive</rasd:Description>
<rasd:ElementName>Floppy Drive 1</rasd:ElementName>
<rasd:HostResource/>
<rasd:InstanceID>8000</rasd:InstanceID>
<rasd:ResourceType>14</rasd:ResourceType>
</ovf:Item>
<ovf:Item>
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
<rasd:Description>Number of Virtual CPUs</rasd:Description>
<rasd:ElementName>1 virtual CPU(s)</rasd:ElementName>
<rasd:InstanceID>4</rasd:InstanceID>
<rasd:Reservation>0</rasd:Reservation>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>1</rasd:VirtualQuantity>
<rasd:Weight>1000</rasd:Weight>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</ovf:Item>
<ovf:Item>
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
<rasd:Description>Memory Size</rasd:Description>
<rasd:ElementName>4 MB of memory</rasd:ElementName>
<rasd:InstanceID>5</rasd:InstanceID>
<rasd:Reservation>0</rasd:Reservation>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>4</rasd:VirtualQuantity>
<rasd:Weight>40</rasd:Weight>
</ovf:Item>
<ovf:Item>
<rasd:AddressOnParent>0</rasd:AddressOnParent>
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:Description>CD/DVD Drive</rasd:Description>
<rasd:ElementName>CD/DVD Drive 1</rasd:ElementName>
<rasd:HostResource/>
<rasd:InstanceID>3000</rasd:InstanceID>
<rasd:Parent>3</rasd:Parent>
<rasd:ResourceType>15</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="backing.exclusive" vmw:value="false"/>
</ovf:Item>
<vmw:Config ovf:required="false" vmw:key="cpuHotAddEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="cpuHotRemoveEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios"/>
<vmw:Config ovf:required="false" vmw:key="virtualICH7MPresent" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="virtualSMCPresent" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="memoryHotAddEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.powerOffType" vmw:value="soft"/>
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.resetType" vmw:value="soft"/>
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.standbyAction" vmw:value="checkpoint"/>
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.suspendType" vmw:value="hard"/>
<vmw:Config ovf:required="false" vmw:key="tools.afterPowerOn" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="tools.afterResume" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="tools.beforeGuestShutdown" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="tools.beforeGuestStandby" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="tools.toolsUpgradePolicy" vmw:value="manual"/>
</ovf:VirtualHardwareSection>
<vcloud:GuestCustomizationSection ovf:required="false">
<ovf:Info>Specifies Guest OS Customization Settings</ovf:Info>
<vcloud:Enabled>false</vcloud:Enabled>
<vcloud:ChangeSid>true</vcloud:ChangeSid>
<vcloud:VirtualMachineId>b4652207-7d14-4d4b-bf42-c6911d947d64</vcloud:VirtualMachineId>
<vcloud:JoinDomainEnabled>false</vcloud:JoinDomainEnabled>
<vcloud:UseOrgSettings>false</vcloud:UseOrgSettings>
<vcloud:AdminPasswordEnabled>true</vcloud:AdminPasswordEnabled>
<vcloud:AdminPasswordAuto>true</vcloud:AdminPasswordAuto>
<vcloud:ResetPasswordRequired>false</vcloud:ResetPasswordRequired>
<vcloud:ComputerName>testvm1</vcloud:ComputerName>
</vcloud:GuestCustomizationSection>
</ovf:VirtualSystem>
</ovf:VirtualSystemCollection>
</ovf:Envelope>
Binary file not shown.
23 changes: 23 additions & 0 deletions util/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -129,3 +130,25 @@ func sanitizedName(filename string) string {
filename = strings.Replace(filename, "../../", "../", -1)
return strings.Replace(filename, "..\\", "", -1)
}

// GetFileContentType returns the real file type
func GetFileContentType(file string) (string, error) { // Open File
f, err := os.Open(file)
if err != nil {
return "", err
}
defer f.Close()
// Only the first 512 bytes are used to sniff the content type.
buffer := make([]byte, 512)

_, err = f.Read(buffer)
if err != nil {
return "", err
}

// Use the net/http package's handy DectectContentType function. Always returns a valid
// content-type by returning "application/octet-stream" if no others seemed to match.
contentType := http.DetectContentType(buffer)

return contentType, nil
}

0 comments on commit a525920

Please sign in to comment.