github.com/vmware/govmomi@v0.51.0/internal/helpers.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package internal 6 7 import ( 8 "context" 9 "crypto/sha256" 10 "encoding/xml" 11 "fmt" 12 "io" 13 "net" 14 "net/http" 15 "net/url" 16 "os" 17 "path" 18 "slices" 19 "strings" 20 21 "github.com/google/uuid" 22 23 "github.com/vmware/govmomi/vim25" 24 "github.com/vmware/govmomi/vim25/mo" 25 "github.com/vmware/govmomi/vim25/soap" 26 "github.com/vmware/govmomi/vim25/types" 27 ) 28 29 const ( 30 vCenterHostGatewaySocket = "/var/run/envoy-hgw/hgw-pipe" 31 vCenterHostGatewaySocketEnv = "VCENTER_ENVOY_HOST_GATEWAY" 32 ) 33 34 // InventoryPath composed of entities by Name 35 func InventoryPath(entities []mo.ManagedEntity) string { 36 val := "/" 37 38 for _, entity := range entities { 39 // Skip root folder in building inventory path. 40 if entity.Parent == nil { 41 continue 42 } 43 val = path.Join(val, entity.Name) 44 } 45 46 return val 47 } 48 49 var vsanFS = []string{ 50 string(types.HostFileSystemVolumeFileSystemTypeVsan), 51 string(types.HostFileSystemVolumeFileSystemTypeVVOL), 52 } 53 54 func IsDatastoreVSAN(ds mo.Datastore) bool { 55 return slices.Contains(vsanFS, ds.Summary.Type) 56 } 57 58 func HostSystemManagementIPs(config []types.VirtualNicManagerNetConfig) []net.IP { 59 var ips []net.IP 60 61 for _, nc := range config { 62 if nc.NicType != string(types.HostVirtualNicManagerNicTypeManagement) { 63 continue 64 } 65 for ix := range nc.CandidateVnic { 66 for _, selectedVnicKey := range nc.SelectedVnic { 67 if nc.CandidateVnic[ix].Key != selectedVnicKey { 68 continue 69 } 70 ip := net.ParseIP(nc.CandidateVnic[ix].Spec.Ip.IpAddress) 71 if ip != nil { 72 ips = append(ips, ip) 73 } 74 } 75 } 76 } 77 78 return ips 79 } 80 81 // UsingEnvoySidecar determines if the given *vim25.Client is using vCenter's 82 // local Envoy sidecar (as opposed to using the HTTPS port.) 83 // Returns a boolean indicating whether to use the sidecar or not. 84 func UsingEnvoySidecar(c *vim25.Client) bool { 85 envoySidecarPort := os.Getenv("GOVMOMI_ENVOY_SIDECAR_PORT") 86 if envoySidecarPort == "" { 87 envoySidecarPort = "1080" 88 } 89 envoySidecarHost := os.Getenv("GOVMOMI_ENVOY_SIDECAR_HOST") 90 if envoySidecarHost == "" { 91 envoySidecarHost = "localhost" 92 } 93 return c.URL().Hostname() == envoySidecarHost && c.URL().Scheme == "http" && c.URL().Port() == envoySidecarPort 94 } 95 96 // ClientWithEnvoyHostGateway clones the provided soap.Client and returns a new 97 // one that uses a Unix socket to leverage vCenter's local Envoy host 98 // gateway. 99 // This should be used to construct clients that talk to ESX. 100 // This method returns a new *vim25.Client and does not modify the original input. 101 // This client disables HTTP keep alives and is intended for a single round 102 // trip. (eg. guest file transfer, datastore file transfer) 103 func ClientWithEnvoyHostGateway(vc *vim25.Client) *vim25.Client { 104 // Override the vim client with a new one that wraps a Unix socket transport. 105 // Using HTTP here so secure means nothing. 106 sc := soap.NewClient(vc.URL(), true) 107 // Clone the underlying HTTP transport, only replacing the dialer logic. 108 transport := sc.DefaultTransport().Clone() 109 hostGatewaySocketPath := os.Getenv(vCenterHostGatewaySocketEnv) 110 if hostGatewaySocketPath == "" { 111 hostGatewaySocketPath = vCenterHostGatewaySocket 112 } 113 transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) { 114 return net.Dial("unix", hostGatewaySocketPath) 115 } 116 // We use this client for a single request, so we don't require keepalives. 117 transport.DisableKeepAlives = true 118 sc.Client = http.Client{ 119 Transport: transport, 120 } 121 newVC := &vim25.Client{ 122 Client: sc, 123 } 124 return newVC 125 } 126 127 // HostGatewayTransferURL rewrites the provided URL to be suitable for use 128 // with the Envoy host gateway on vCenter. 129 // It returns a copy of the provided URL with the host, scheme rewritten as needed. 130 // Receivers of such URLs must typically also use ClientWithEnvoyHostGateway to 131 // use the appropriate http.Transport to be able to make use of the host 132 // gateway. 133 // nil input yields an uninitialized struct. 134 func HostGatewayTransferURL(u *url.URL, hostMoref types.ManagedObjectReference) *url.URL { 135 if u == nil { 136 return &url.URL{} 137 } 138 // Make a copy of the provided URL. 139 turl := *u 140 turl.Host = "localhost" 141 turl.Scheme = "http" 142 oldPath := turl.Path 143 turl.Path = fmt.Sprintf("/hgw/%s%s", hostMoref.Value, oldPath) 144 return &turl 145 } 146 147 func (arg ReflectManagedMethodExecuterSoapArgument) Value() []string { 148 if arg.Val == "" { 149 return nil 150 } 151 152 d := xml.NewDecoder(strings.NewReader(arg.Val)) 153 var val []string 154 155 for { 156 t, err := d.Token() 157 if err != nil { 158 if err == io.EOF { 159 break 160 } 161 panic(err) 162 } 163 if c, ok := t.(xml.CharData); ok { 164 val = append(val, string(c)) 165 } 166 } 167 168 return val 169 } 170 171 func EsxcliName(name string) string { 172 return strings.ReplaceAll(strings.Title(name), ".", "") 173 } 174 175 // OID returns a stable UUID based on input s 176 func OID(s string) uuid.UUID { 177 return uuid.NewHash(sha256.New(), uuid.NameSpaceOID, []byte(s), 5) 178 }