Skip to content

Commit

Permalink
govc: add option to enable hidden properties in import.{spec,ova}
Browse files Browse the repository at this point in the history
vcsim: ResourcePool.ImportVApp improvements
- Run within an internal Task as real vCenter does
- Set VirtualMachineImportSpec.VAppConfig in CreateImportSpec
- Propagate VAppConfig to VirtualMachine (config.vAppConfig)
- Validate userConfigurable=false properties

Fixes vmware#3111
  • Loading branch information
dougm committed Oct 2, 2023
1 parent 2ff5e50 commit 3bcaf42
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 88 deletions.
3 changes: 3 additions & 0 deletions govc/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3153,6 +3153,7 @@ Usage: govc import.ova [OPTIONS] PATH_TO_OVA
Options:
-ds= Datastore [GOVC_DATASTORE]
-folder= Inventory folder [GOVC_FOLDER]
-hidden=false Enable hidden properties
-host= Host system [GOVC_HOST]
-m=false Verify checksum of uploaded files against manifest (.mf)
-name= Name to use for new entity
Expand All @@ -3168,6 +3169,7 @@ Usage: govc import.ovf [OPTIONS] PATH_TO_OVF
Options:
-ds= Datastore [GOVC_DATASTORE]
-folder= Inventory folder [GOVC_FOLDER]
-hidden=false Enable hidden properties
-host= Host system [GOVC_HOST]
-m=false Verify checksum of uploaded files against manifest (.mf)
-name= Name to use for new entity
Expand All @@ -3181,6 +3183,7 @@ Options:
Usage: govc import.spec [OPTIONS] PATH_TO_OVF_OR_OVA
Options:
-hidden=false Enable hidden properties
```

## import.vmdk
Expand Down
12 changes: 11 additions & 1 deletion govc/importx/ovf.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -17,6 +17,7 @@ limitations under the License.
package importx

import (
"bytes"
"context"
"errors"
"flag"
Expand Down Expand Up @@ -47,6 +48,7 @@ type ovfx struct {

Name string
VerifyManifest bool
Hidden bool

Client *vim25.Client
Datacenter *object.Datacenter
Expand Down Expand Up @@ -77,6 +79,7 @@ func (cmd *ovfx) Register(ctx context.Context, f *flag.FlagSet) {

f.StringVar(&cmd.Name, "name", "", "Name to use for new entity")
f.BoolVar(&cmd.VerifyManifest, "m", false, "Verify checksum of uploaded files against manifest (.mf)")
f.BoolVar(&cmd.Hidden, "hidden", false, "Enable hidden properties")
}

func (cmd *ovfx) Process(ctx context.Context) error {
Expand Down Expand Up @@ -243,6 +246,13 @@ func (cmd *ovfx) Import(fpath string) (*types.ManagedObjectReference, error) {
if e.VirtualSystem.Name != nil {
name = *e.VirtualSystem.Name
}

if cmd.Hidden {
// TODO: userConfigurable is optional and defaults to false, so we should *add* userConfigurable=true
// if not set for a Property. But, there'd be a bunch more work involved to preserve other data in doing
// a complete xml.Marshal of the .ovf
o = bytes.ReplaceAll(o, []byte(`userConfigurable="false"`), []byte(`userConfigurable="true"`))
}
}

// Override name from options if specified
Expand Down
22 changes: 10 additions & 12 deletions govc/importx/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand Down Expand Up @@ -59,6 +59,8 @@ type spec struct {
*ArchiveFlag
*flags.ClientFlag
*flags.OutputFlag

hidden bool
}

func init() {
Expand All @@ -73,6 +75,8 @@ func (cmd *spec) Register(ctx context.Context, f *flag.FlagSet) {

cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
cmd.OutputFlag.Register(ctx, f)

f.BoolVar(&cmd.hidden, "hidden", false, "Enable hidden properties")
}

func (cmd *spec) Process(ctx context.Context) error {
Expand Down Expand Up @@ -147,7 +151,10 @@ func (cmd *spec) Map(e *ovf.Envelope) (res []Property) {

for _, p := range e.VirtualSystem.Product {
for i, v := range p.Property {
if v.UserConfigurable == nil || !*v.UserConfigurable {
if v.UserConfigurable == nil {
continue
}
if !*v.UserConfigurable && !cmd.hidden {
continue
}

Expand All @@ -161,17 +168,8 @@ func (cmd *spec) Map(e *ovf.Envelope) (res []Property) {
d = strings.Title(d)
}

// From OVF spec, section 9.5.1:
// key-value-env = [class-value "."] key-value-prod ["." instance-value]
k := v.Key
if p.Class != nil {
k = fmt.Sprintf("%s.%s", *p.Class, k)
}
if p.Instance != nil {
k = fmt.Sprintf("%s.%s", k, *p.Instance)
}
np := Property{KeyValue: KeyValue{Key: p.Key(v), Value: d}}

np := Property{KeyValue: KeyValue{Key: k, Value: d}}
if cmd.Verbose() {
np.Spec = &p.Property[i]
}
Expand Down
29 changes: 29 additions & 0 deletions govc/test/import.bats
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,32 @@ load test_helper
assert_success # using raw MO id
grep "invalid NetworkMapping.Name" <<<"$output"
}

@test "import properties" {
vcsim_env

ovf=../../ovf/fixtures/properties.ovf

govc import.spec $ovf | grep -q -v nfs_mount

options=$(govc import.spec -hidden $ovf)

grep -q -v vm.name <<<"$options"
grep -q nfs_mount <<<"$options"

run govc import.ovf -name "$(new_id)" -options - "$ovf" <<<"$options"
assert_success # hidden options but no value changes

run govc import.ovf -options - "$ovf" <<<"${options//transfer/other}"
assert_failure # userConfigurable=false

id=$(new_id)
run govc import.ovf -name "$id" -hidden -options - "$ovf" <<<"${options//transfer/other}"
assert_success # userConfigurable=true

config=$(govc object.collect -o -json "vm/$id" | jq .config.vAppConfig)
name=$(jq -r .product[0].name <<<"$config")
version=$(jq -r .product[0].version <<<"$config")
assert_equal ttylinux "$name"
assert_equal 16.1 "$version"
}
23 changes: 21 additions & 2 deletions ovf/envelope.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Copyright (c) 2015-2023 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -16,6 +16,10 @@ limitations under the License.

package ovf

import (
"fmt"
)

type Envelope struct {
References []File `xml:"References>File"`

Expand Down Expand Up @@ -61,6 +65,7 @@ type Content struct {
type Section struct {
Required *bool `xml:"required,attr"`
Info string `xml:"Info"`
Category string `xml:"Category"`
}

type AnnotationSection struct {
Expand All @@ -85,6 +90,20 @@ type ProductSection struct {
Property []Property `xml:"Property"`
}

func (p ProductSection) Key(prop Property) string {
// From OVF spec, section 9.5.1:
// key-value-env = [class-value "."] key-value-prod ["." instance-value]

k := prop.Key
if p.Class != nil {
k = fmt.Sprintf("%s.%s", *p.Class, k)
}
if p.Instance != nil {
k = fmt.Sprintf("%s.%s", k, *p.Instance)
}
return k
}

type Property struct {
Key string `xml:"key,attr"`
Type string `xml:"type,attr"`
Expand Down
159 changes: 159 additions & 0 deletions ovf/fixtures/properties.ovf
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated by VMware VirtualCenter Server, User: VSPHERE.LOCAL\Administrator, UTC time: 2023-07-22T15:33:49.408233Z-->
<Envelope vmw:buildId="build-66536396" xmlns="http://schemas.dmtf.org/ovf/envelope/1" xmlns:cim="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<References>
</References>
<DiskSection>
<Info>Virtual disk information</Info>
</DiskSection>
<NetworkSection>
<Info>The list of logical networks</Info>
<Network ovf:name="VM Network">
<Description>The VM Network network</Description>
</Network>
</NetworkSection>
<VirtualSystem ovf:id="test-vm">
<Info>A virtual machine</Info>
<Name>test-vm</Name>
<OperatingSystemSection ovf:id="94" vmw:osType="ubuntu64Guest">
<Info>The kind of installed guest operating system</Info>
<Description>Ubuntu Linux (64-bit)</Description>
</OperatingSystemSection>
<VirtualHardwareSection>
<Info>Virtual hardware requirements</Info>
<System>
<vssd:ElementName>Virtual Hardware Family</vssd:ElementName>
<vssd:InstanceID>0</vssd:InstanceID>
<vssd:VirtualSystemIdentifier>test-vm</vssd:VirtualSystemIdentifier>
<vssd:VirtualSystemType>vmx-19</vssd:VirtualSystemType>
</System>
<Item>
<rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
<rasd:Description>Number of Virtual CPUs</rasd:Description>
<rasd:ElementName>2 virtual CPU(s)</rasd:ElementName>
<rasd:InstanceID>1</rasd:InstanceID>
<rasd:ResourceType>3</rasd:ResourceType>
<rasd:VirtualQuantity>2</rasd:VirtualQuantity>
</Item>
<Item>
<rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
<rasd:Description>Memory Size</rasd:Description>
<rasd:ElementName>2048MB of memory</rasd:ElementName>
<rasd:InstanceID>2</rasd:InstanceID>
<rasd:ResourceType>4</rasd:ResourceType>
<rasd:VirtualQuantity>2048</rasd:VirtualQuantity>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Description>SCSI Controller</rasd:Description>
<rasd:ElementName>SCSI controller 0</rasd:ElementName>
<rasd:InstanceID>3</rasd:InstanceID>
<rasd:ResourceSubType>VirtualSCSI</rasd:ResourceSubType>
<rasd:ResourceType>6</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="160"/>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</Item>
<Item>
<rasd:Address>1</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>IDE 1</rasd:ElementName>
<rasd:InstanceID>4</rasd:InstanceID>
<rasd:ResourceType>5</rasd:ResourceType>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</Item>
<Item>
<rasd:Address>0</rasd:Address>
<rasd:Description>IDE Controller</rasd:Description>
<rasd:ElementName>IDE 0</rasd:ElementName>
<rasd:InstanceID>5</rasd:InstanceID>
<rasd:ResourceType>5</rasd:ResourceType>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</Item>
<Item ovf:required="false">
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>Video card</rasd:ElementName>
<rasd:InstanceID>6</rasd:InstanceID>
<rasd:ResourceType>24</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="useAutoDetect" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="videoRamSizeInKB" vmw:value="4096"/>
<vmw:Config ovf:required="false" vmw:key="enable3DSupport" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="use3dRenderer" vmw:value="automatic"/>
<vmw:Config ovf:required="false" vmw:key="graphicsMemorySizeInKB" vmw:value="262144"/>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</Item>
<Item ovf:required="false">
<rasd:AutomaticAllocation>false</rasd:AutomaticAllocation>
<rasd:ElementName>VMCI device</rasd:ElementName>
<rasd:InstanceID>7</rasd:InstanceID>
<rasd:ResourceSubType>vmware.vmci</rasd:ResourceSubType>
<rasd:ResourceType>1</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="allowUnrestrictedCommunication" vmw:value="false"/>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</Item>
<Item>
<rasd:AddressOnParent>7</rasd:AddressOnParent>
<rasd:AutomaticAllocation>true</rasd:AutomaticAllocation>
<rasd:Connection>VM Network</rasd:Connection>
<rasd:Description>E1000 ethernet adapter on &quot;VM Network&quot;</rasd:Description>
<rasd:ElementName>Network adapter 1</rasd:ElementName>
<rasd:InstanceID>8</rasd:InstanceID>
<rasd:ResourceSubType>E1000</rasd:ResourceSubType>
<rasd:ResourceType>10</rasd:ResourceType>
<vmw:Config ovf:required="false" vmw:key="slotInfo.pciSlotNumber" vmw:value="32"/>
<vmw:Config ovf:required="false" vmw:key="wakeOnLanEnabled" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="connectable.allowGuestControl" vmw:value="true"/>
<vmw:Config ovf:required="false" vmw:key="uptCompatibilityEnabled" vmw:value="false"/>
<vmw:CoresPerSocket ovf:required="false">1</vmw:CoresPerSocket>
</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="memoryHotAddEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="firmware" vmw:value="bios"/>
<vmw:Config ovf:required="false" vmw:key="cpuAllocation.shares.shares" vmw:value="2000"/>
<vmw:Config ovf:required="false" vmw:key="cpuAllocation.shares.level" vmw:value="normal"/>
<vmw:Config ovf:required="false" vmw:key="simultaneousThreads" vmw:value="1"/>
<vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHost" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="tools.syncTimeWithHostAllowed" vmw:value="true"/>
<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.toolsUpgradePolicy" vmw:value="manual"/>
<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.suspendType" vmw:value="hard"/>
<vmw:Config ovf:required="false" vmw:key="nestedHVEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="vPMCEnabled" vmw:value="false"/>
<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="flags.vvtdEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="flags.vbsEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="bootOptions.efiSecureBootEnabled" vmw:value="false"/>
<vmw:Config ovf:required="false" vmw:key="powerOpInfo.standbyAction" vmw:value="checkpoint"/>
</VirtualHardwareSection>
<ProductSection ovf:class="ttylinux" ovf:instance="vm" ovf:required="false">
<Info>ttylinux info</Info>
<Product>ttylinux</Product>
<Vendor>Minimalinux</Vendor>
<Version>16.1</Version>
<Category>ttylinux Settings</Category>
<Property ovf:key="ntp-server" ovf:type="string" ovf:userConfigurable="true" ovf:qualifiers="MinLen(1),MaxLen(65535)">
<Label>NTP Server(s)</Label>
<Description>NTP Server(s) to use. Please specify space delimited list.</Description>
</Property>
<Property ovf:key="enable_ssh" ovf:type="boolean" ovf:userConfigurable="true" ovf:value="false">
<Label>Enable SSH root login</Label>
<Description>Check here to enable SSH login for root user. SSH login for root is disabled by default.</Description>
</Property>
<Property ovf:key="nfs_mount" ovf:type="string" ovf:userConfigurable="false" ovf:value="/transfer" ovf:qualifiers="MinLen(0),MaxLen(65535)">
<Label>NFS mount for transfer file location</Label>
<Description>Ex: 10.0.0.1:/transfer</Description>
</Property>
</ProductSection>
<ProductSection ovf:class="vm" ovf:required="false">
<Info>VM specific properties</Info>
<Property ovf:key="vmname" ovf:type="string" ovf:value="vcsim_test"/>
</ProductSection>
</VirtualSystem>
</Envelope>
Loading

0 comments on commit 3bcaf42

Please sign in to comment.