Repository: vmware/govmomi (go)

Issue:
[BUG] The govc import.ova command is failing to upload the OVA file.
**Describe the bug**

The govc import.ova command is failing to upload the OVA file.

**Manual Export vs govc Export:**

When we manually upload a VM template to an OVA using govc export.ovf, the network configuration shows differences compared to a manual export.

Consider that i was using vCenter to export vm
**Manual export values in network in .ovf as follows**
  <NetworkSection>
    <Info>The list of logical networks</Info>
    <Network ovf:name="VLAN-2533">
      <Description>The VLAN-2533 network</Description>
    </Network>
  </NetworkSection>


**govc export values in network in .ovf file as follows**
  <NetworkSection>
    <Info>The list of logical networks</Info>
    <Network ovf:name="dvportgroup-2325">
      <Description>The dvportgroup-2325 network</Description>
    </Network>
  </NetworkSection>

due to this when i try uploading the ova file into the vCenter i was getting below error 

**ERROR**
govc: Line 100: Invalid value 'dvportgroup-2325' for element 'Connection'.


**Expected behavior**
govc import should work as like how we do from GUI manually 
**Affected version**
failed to upload a ova using govc import.ova 

**Screenshots/Debug Output**
attached 
![Image](https://github.com/user-attachments/assets/b6329b29-cbce-4e89-9926-14d35bd1604a)

**Additional context**

![Image](https://github.com/user-attachments/assets/dc420e84-fb8f-462d-998f-28464be29c2d)


Pull request description:
govc: Add import.ova -net flag
Network can already be set via -options, but this simplifies the common case.

Added a hint to specify network (See also: #1561), example when not specified:
```console
% govc import.ovf foo.ovf
govc: Host did not have any virtual network defined. (specify Network with '-net' or '-options')
```

Fixes #3679


Patch:
diff --git a/cli/importx/ovf.go b/cli/importx/ovf.go
index 71525998..7a7aa7f9 100644
--- a/cli/importx/ovf.go
+++ b/cli/importx/ovf.go
@@ -8,11 +8,14 @@ import (
 	"context"
 	"errors"
 	"flag"
+	"fmt"
 
 	"github.com/vmware/govmomi/cli"
 	"github.com/vmware/govmomi/cli/flags"
+	"github.com/vmware/govmomi/fault"
 	"github.com/vmware/govmomi/object"
 	"github.com/vmware/govmomi/ovf/importer"
+	"github.com/vmware/govmomi/vim25/types"
 )
 
 type ovfx struct {
@@ -27,6 +30,7 @@ type ovfx struct {
 	Importer importer.Importer
 
 	lease bool
+	net   string // No need for *flags.NetworkFlag here
 }
 
 func init() {
@@ -52,6 +56,7 @@ func (cmd *ovfx) Register(ctx context.Context, f *flag.FlagSet) {
 	f.BoolVar(&cmd.Importer.VerifyManifest, "m", false, "Verify checksum of uploaded files against manifest (.mf)")
 	f.BoolVar(&cmd.Importer.Hidden, "hidden", false, "Enable hidden properties")
 	f.BoolVar(&cmd.lease, "lease", false, "Output NFC Lease only")
+	f.StringVar(&cmd.net, "net", "", "Network")
 }
 
 func (cmd *ovfx) Process(ctx context.Context) error {
@@ -91,10 +96,32 @@ func (cmd *ovfx) Run(ctx context.Context, f *flag.FlagSet) error {
 
 	cmd.Importer.Archive = archive
 
-	return cmd.Import(ctx, fpath)
+	if err = cmd.Import(ctx, fpath); err != nil {
+		if fault.Is(err, &types.OvfNoHostNic{}) {
+			hint := "specify Network with '-net' or '-options'"
+			return fmt.Errorf("%s (%s)", err.Error(), hint)
+		}
+		return err
+	}
+	return nil
 }
 
 func (cmd *ovfx) Import(ctx context.Context, fpath string) error {
+	if cmd.net != "" {
+		if len(cmd.Options.NetworkMapping) == 0 {
+			env, err := importer.Spec(fpath, cmd.Importer.Archive, false, false)
+			if err != nil {
+				return err
+			}
+
+			cmd.Options.NetworkMapping = env.NetworkMapping
+		}
+
+		for i := range cmd.Options.NetworkMapping {
+			cmd.Options.NetworkMapping[i].Network = cmd.net
+		}
+	}
+
 	if cmd.lease {
 		_, lease, err := cmd.Importer.ImportVApp(ctx, fpath, cmd.Options)
 		if err != nil {
diff --git a/govc/USAGE.md b/govc/USAGE.md
index c84c0b7d..b6826ea6 100644
--- a/govc/USAGE.md
+++ b/govc/USAGE.md
@@ -3772,6 +3772,7 @@ Options:
   -lease=false           Output NFC Lease only
   -m=false               Verify checksum of uploaded files against manifest (.mf)
   -name=                 Name to use for new entity
+  -net=                  Network
   -options=              Options spec file path for VM deployment
   -pool=                 Resource pool [GOVC_RESOURCE_POOL]
 ```
@@ -3789,6 +3790,7 @@ Options:
   -lease=false           Output NFC Lease only
   -m=false               Verify checksum of uploaded files against manifest (.mf)
   -name=                 Name to use for new entity
+  -net=                  Network
   -options=              Options spec file path for VM deployment
   -pool=                 Resource pool [GOVC_RESOURCE_POOL]
 ```
diff --git a/ovf/importer/importer.go b/ovf/importer/importer.go
index b9a4908b..2cf3554c 100644
--- a/ovf/importer/importer.go
+++ b/ovf/importer/importer.go
@@ -17,6 +17,7 @@ import (
 	"github.com/vmware/govmomi/nfc"
 	"github.com/vmware/govmomi/object"
 	"github.com/vmware/govmomi/ovf"
+	"github.com/vmware/govmomi/task"
 	"github.com/vmware/govmomi/vapi/library"
 	"github.com/vmware/govmomi/vim25"
 	"github.com/vmware/govmomi/vim25/progress"
@@ -118,7 +119,7 @@ func (imp *Importer) ImportVApp(ctx context.Context, fpath string, opts Options)
 		return nil, nil, err
 	}
 	if spec.Error != nil {
-		return nil, nil, errors.New(spec.Error[0].LocalizedMessage)
+		return nil, nil, &task.Error{LocalizedMethodFault: &spec.Error[0]}
 	}
 	if spec.Warning != nil {
 		for _, w := range spec.Warning {


Test patch:
diff --git a/govc/test/import.bats b/govc/test/import.bats
index 2ea00e5f..43257718 100755
--- a/govc/test/import.bats
+++ b/govc/test/import.bats
@@ -216,6 +216,12 @@ load test_helper
   run govc import.ovf -options - "$ovf" <<<"$options"
   assert_success # using raw MO id
   grep "invalid NetworkMapping.Name" <<<"$output"
+
+  run govc import.ovf -name netflag -net enoent "$ovf"
+  assert_failure
+
+  run govc import.ovf -name netflag -net "VM Network" "$ovf"
+  assert_success
 }
 
 @test "import invalid disk provisioning" {