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  }