Repository: vmware/govmomi (go)

Issue:
FindByIp fails for replayed data
Using govc, vcsim and real vcenter.

Now suppose I connect govc to a real vcenter and do a host.ip search for instance - 
`govc host.info -dc=xxxxx -host.ip=x.x.x.x`
it passes and returns a host.

But now I record the same vcenter and load it in vcsim (`object.save` and `vcsim -load`), and then I issue the same command, it fails giving error -
 

> `"https://<IP:port>/sdk": EOF`

 and there is a panic on line https://github.com/vmware/govmomi/blob/master/simulator/search_index.go#L247
(a separate testing code using FindByIp using govmomi client also fails similarly)

although `govc host.info  -dc=xxxxx `
passes (there is only one host in that DC).

Does anyone know what is going on?

Pull request description:
vcsim: set HostSystem IP in cluster AddHost_Task
If HostConnectSpec.HostName is an IP address, also use it to set
Config.Network.Vnic[0].Spec.Ip.IpAddress

Fixes #1981

Patch:
diff --git a/simulator/host_system.go b/simulator/host_system.go
index c0b2e986..28c4b3e6 100644
--- a/simulator/host_system.go
+++ b/simulator/host_system.go
@@ -17,6 +17,7 @@ limitations under the License.
 package simulator
 
 import (
+	"net"
 	"os"
 	"time"
 
@@ -63,6 +64,10 @@ func NewHostSystem(host mo.HostSystem) *HostSystem {
 	info := *esx.HostHardwareInfo
 	hs.Hardware = &info
 
+	cfg := new(types.HostConfigInfo)
+	deepCopy(hs.Config, cfg)
+	hs.Config = cfg
+
 	config := []struct {
 		ref **types.ManagedObjectReference
 		obj mo.Reference
@@ -88,6 +93,9 @@ func (h *HostSystem) configure(spec types.HostConnectSpec, connected bool) {
 	if connected {
 		h.Runtime.ConnectionState = types.HostSystemConnectionStateConnected
 	}
+	if net.ParseIP(spec.HostName) != nil {
+		h.Config.Network.Vnic[0].Spec.Ip.IpAddress = spec.HostName
+	}
 
 	h.Summary.Config.Name = spec.HostName
 	h.Name = h.Summary.Config.Name
diff --git a/simulator/object.go b/simulator/object.go
index d13ef1c1..8a1bed43 100644
--- a/simulator/object.go
+++ b/simulator/object.go
@@ -17,10 +17,13 @@ limitations under the License.
 package simulator
 
 import (
+	"bytes"
+
 	"github.com/google/uuid"
 	"github.com/vmware/govmomi/vim25/methods"
 	"github.com/vmware/govmomi/vim25/soap"
 	"github.com/vmware/govmomi/vim25/types"
+	"github.com/vmware/govmomi/vim25/xml"
 )
 
 func SetCustomValue(ctx *Context, req *types.SetCustomValue) soap.HasFault {
@@ -59,3 +62,18 @@ func newUUID(s string) string {
 func sha1UUID(s string) uuid.UUID {
 	return uuid.NewSHA1(uuid.NameSpaceOID, []byte(s))
 }
+
+// deepCopy uses xml encode/decode to copy src to dst
+func deepCopy(src, dst interface{}) {
+	b, err := xml.Marshal(src)
+	if err != nil {
+		panic(err)
+	}
+
+	dec := xml.NewDecoder(bytes.NewReader(b))
+	dec.TypeFunc = types.TypeFunc()
+	err = dec.Decode(dst)
+	if err != nil {
+		panic(err)
+	}
+}


Test patch:
diff --git a/govc/test/cluster.bats b/govc/test/cluster.bats
index 6c0ba60f..d15dc437 100755
--- a/govc/test/cluster.bats
+++ b/govc/test/cluster.bats
@@ -227,3 +227,21 @@ _EOF_
   run govc cluster.override.remove -vm DC0_C0_RP0_VM0
   assert_success
 }
+
+@test "cluster.add" {
+    vcsim_env
+    unset GOVC_HOST
+
+    ip=$(govc object.collect -o -json host/DC0_C0/DC0_C0_H0 | jq -r .Config.Network.Vnic[].Spec.Ip.IpAddress)
+    assert_equal 127.0.0.1 "$ip"
+
+    govc cluster.add -cluster DC0_C0 -hostname 10.0.0.42 -username user -password pass
+    assert_success
+
+    ip=$(govc object.collect -o -json host/DC0_C0/10.0.0.42 | jq -r .Config.Network.Vnic[].Spec.Ip.IpAddress)
+
+    assert_equal 10.0.0.42 "$ip"
+    govc host.info -json '*' | jq -r .HostSystems[].Config.Network.Vnic[].Spec.Ip
+    name=$(govc host.info -json -host.ip 10.0.0.42 | jq -r .HostSystems[].Name)
+    assert_equal 10.0.0.42 "$name"
+}
diff --git a/simulator/object_test.go b/simulator/object_test.go
index f51f1b48..5ae4bafd 100644
--- a/simulator/object_test.go
+++ b/simulator/object_test.go
@@ -22,6 +22,7 @@ import (
 
 	"github.com/vmware/govmomi"
 	"github.com/vmware/govmomi/object"
+	"github.com/vmware/govmomi/simulator/esx"
 	"github.com/vmware/govmomi/vim25/types"
 )
 
@@ -188,3 +189,10 @@ func TestObjectCustomFields(t *testing.T) {
 		t.Fatalf("len(vm.AvailableField) expected 0, got %d", len(vm.AvailableField))
 	}
 }
+
+func BenchmarkDeepCopy(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		config := new(types.HostConfigInfo)
+		deepCopy(esx.HostConfigInfo, config)
+	}
+}