func (vs *VSphere) CurrentNodeName(ctx context.Context, hostname string) (k8stypes.NodeName, error) {
	return convertToK8sType(vs.hostName), nil
}
CurrentNodeName gives the current node name
package vsphere
import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
	"gopkg.in/gcfg.v1"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vapi/rest"
	"github.com/vmware/govmomi/vapi/tags"
	"github.com/vmware/govmomi/vim25/mo"
	vmwaretypes "github.com/vmware/govmomi/vim25/types"
	v1 "k8s.io/api/core/v1"
	k8stypes "k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/tools/cache"
	cloudprovider "k8s.io/cloud-provider"
	nodehelpers "k8s.io/cloud-provider/node/helpers"
	volerr "k8s.io/cloud-provider/volume/errors"
	volumehelpers "k8s.io/cloud-provider/volume/helpers"
	"k8s.io/klog/v2"
	netutils "k8s.io/utils/net"
	"k8s.io/legacy-cloud-providers/vsphere/vclib"
	"k8s.io/legacy-cloud-providers/vsphere/vclib/diskmanagers"
)
const (
	ProviderName                  = "vsphere"
	VolDir                        = "kubevols"
	RoundTripperDefaultCount      = 3
	DummyVMPrefixName             = "vsphere-k8s"
	CleanUpDummyVMRoutineInterval = 5
)
var cleanUpRoutineInitialized = false
var datastoreFolderIDMap = make(map[string]map[string]string)
var cleanUpRoutineInitLock sync.Mutex
var cleanUpDummyVMLock sync.RWMutex
const (
	MissingUsernameErrMsg = "Username is missing"
	MissingPasswordErrMsg = "Password is missing"
	NoZoneTagInVCErrMsg   = "No zone tags found in vCenter"
)
var (
	ErrUsernameMissing = errors.New(MissingUsernameErrMsg)
	ErrPasswordMissing = errors.New(MissingPasswordErrMsg)
	ErrNoZoneTagInVC   = errors.New(NoZoneTagInVCErrMsg)
)
var _ cloudprovider.Interface = (*VSphere)(nil)
var _ cloudprovider.Instances = (*VSphere)(nil)
var _ cloudprovider.Zones = (*VSphere)(nil)
var _ cloudprovider.PVLabeler = (*VSphere)(nil)
type VSphere struct {
	cfg      *VSphereConfig
	hostName string
	vsphereInstanceMap map[string]*VSphereInstance
	vsphereVolumeMap   *VsphereVolumeMap
	nodeManager          *NodeManager
	vmUUID               string
	isSecretInfoProvided bool
	isSecretManaged      bool
}
type VSphereInstance struct {
	conn *vclib.VSphereConnection
	cfg  *VirtualCenterConfig
}
type VirtualCenterConfig struct {
	User string `gcfg:"user"`
	Password string `gcfg:"password" datapolicy:"password"`
	VCenterPort string `gcfg:"port"`
	Datacenters string `gcfg:"datacenters"`
	RoundTripperCount uint `gcfg:"soap-roundtrip-count"`
	Thumbprint string `gcfg:"thumbprint"`
}
type VSphereConfig struct {
	Global struct {
		User string `gcfg:"user"`
		Password string `gcfg:"password" datapolicy:"password"`
		VCenterIP string `gcfg:"server"`
		VCenterPort string `gcfg:"port"`
		InsecureFlag bool `gcfg:"insecure-flag"`
		CAFile string `gcfg:"ca-file"`
		Thumbprint string `gcfg:"thumbprint"`
		Datacenter string `gcfg:"datacenter"`
		Datacenters string `gcfg:"datacenters"`
		DefaultDatastore string `gcfg:"datastore"`
		WorkingDir string `gcfg:"working-dir"`
		RoundTripperCount uint `gcfg:"soap-roundtrip-count"`
		VMUUID string `gcfg:"vm-uuid"`
		VMName string `gcfg:"vm-name"`
		SecretName string `gcfg:"secret-name"`
		SecretNamespace string `gcfg:"secret-namespace"`
		SecretNotManaged bool `gcfg:"secret-not-managed"`
	}
	VirtualCenter map[string]*VirtualCenterConfig
	Network struct {
		PublicNetwork string `gcfg:"public-network"`
	}
	Disk struct {
		SCSIControllerType string `dcfg:"scsicontrollertype"`
	}
	Workspace struct {
		VCenterIP        string `gcfg:"server"`
		Datacenter       string `gcfg:"datacenter"`
		Folder           string `gcfg:"folder"`
		DefaultDatastore string `gcfg:"default-datastore"`
		ResourcePoolPath string `gcfg:"resourcepool-path"`
	}
	Labels struct {
		Zone   string `gcfg:"zone"`
		Region string `gcfg:"region"`
	}
}
type Volumes interface {
	AttachDisk(vmDiskPath string, storagePolicyName string, nodeName k8stypes.NodeName) (diskUUID string, err error)
	DetachDisk(volPath string, nodeName k8stypes.NodeName) error
	DiskIsAttached(volPath string, nodeName k8stypes.NodeName) (bool, string, error)
	DisksAreAttached(nodeVolumes map[k8stypes.NodeName][]string) (map[k8stypes.NodeName]map[string]bool, error)
	CreateVolume(volumeOptions *vclib.VolumeOptions) (volumePath string, err error)
	DeleteVolume(vmDiskPath string) error
}
func readConfig(config io.Reader) (VSphereConfig, error) {
	if config == nil {
		err := fmt.Errorf("no vSphere cloud provider config file given")
		return VSphereConfig{}, err
	}
	var cfg VSphereConfig
	err := gcfg.ReadInto(&cfg, config)
	return cfg, err
}
func init() {
	vclib.RegisterMetrics()
	cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
		if config == nil {
			return newWorkerNode()
		}
		cfg, err := readConfig(config)
		if err != nil {
			return nil, err
		}
		return newControllerNode(cfg)
	})
}
func (vs *VSphere) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) {
}
func (vs *VSphere) SetInformers(informerFactory informers.SharedInformerFactory) {
	if vs.cfg == nil {
		return
	}
	if vs.isSecretInfoProvided {
		secretCredentialManager := &SecretCredentialManager{
			SecretName:      vs.cfg.Global.SecretName,
			SecretNamespace: vs.cfg.Global.SecretNamespace,
			SecretLister:    informerFactory.Core().V1().Secrets().Lister(),
			Cache: &SecretCache{
				VirtualCenter: make(map[string]*Credential),
			},
		}
		if vs.isSecretManaged {
			klog.V(4).Infof("Setting up secret informers for vSphere Cloud Provider")
			secretInformer := informerFactory.Core().V1().Secrets().Informer()
			secretInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
				AddFunc:    vs.SecretAdded,
				UpdateFunc: vs.SecretUpdated,
			})
			klog.V(4).Infof("Secret informers in vSphere cloud provider initialized")
		}
		vs.nodeManager.UpdateCredentialManager(secretCredentialManager)
	}
	klog.V(4).Infof("Setting up node informers for vSphere Cloud Provider")
	nodeInformer := informerFactory.Core().V1().Nodes().Informer()
	nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    vs.NodeAdded,
		DeleteFunc: vs.NodeDeleted,
	})
	klog.V(4).Infof("Node informers in vSphere cloud provider initialized")
}
func newWorkerNode() (*VSphere, error) {
	var err error
	vs := VSphere{}
	vs.hostName, err = os.Hostname()
	if err != nil {
		klog.Errorf("Failed to get hostname. err: %+v", err)
		return nil, err
	}
	vs.vmUUID, err = GetVMUUID()
	if err != nil {
		klog.Errorf("Failed to get uuid. err: %+v", err)
		return nil, err
	}
	return &vs, nil
}
func populateVsphereInstanceMap(cfg *VSphereConfig) (map[string]*VSphereInstance, error) {
	vsphereInstanceMap := make(map[string]*VSphereInstance)
	isSecretInfoProvided := true
	if cfg.Global.SecretName == "" || cfg.Global.SecretNamespace == "" {
		klog.Warningf("SecretName and/or SecretNamespace is not provided. " +
			"VCP will use username and password from config file")
		isSecretInfoProvided = false
	}
	if isSecretInfoProvided {
		if cfg.Global.User != "" {
			klog.Warning("Global.User and Secret info provided. VCP will use secret to get credentials")
			cfg.Global.User = ""
		}
		if cfg.Global.Password != "" {
			klog.Warning("Global.Password and Secret info provided. VCP will use secret to get credentials")
			cfg.Global.Password = ""
		}
	}
	if cfg.VirtualCenter == nil || len(cfg.VirtualCenter) == 0 {
		klog.V(4).Infof("Config is not per virtual center and is in old format.")
		if !isSecretInfoProvided {
			if cfg.Global.User == "" {
				klog.Error("Global.User is empty!")
				return nil, ErrUsernameMissing
			}
			if cfg.Global.Password == "" {
				klog.Error("Global.Password is empty!")
				return nil, ErrPasswordMissing
			}
		}
		if cfg.Global.WorkingDir == "" {
			klog.Error("Global.WorkingDir is empty!")
			return nil, errors.New("Global.WorkingDir is empty!")
		}
		if cfg.Global.VCenterIP == "" {
			klog.Error("Global.VCenterIP is empty!")
			return nil, errors.New("Global.VCenterIP is empty!")
		}
		if cfg.Global.Datacenter == "" {
			klog.Error("Global.Datacenter is empty!")
			return nil, errors.New("Global.Datacenter is empty!")
		}
		cfg.Workspace.VCenterIP = cfg.Global.VCenterIP
		cfg.Workspace.Datacenter = cfg.Global.Datacenter
		cfg.Workspace.Folder = cfg.Global.WorkingDir
		cfg.Workspace.DefaultDatastore = cfg.Global.DefaultDatastore
		vcConfig := VirtualCenterConfig{
			User:              cfg.Global.User,
			Password:          cfg.Global.Password,
			VCenterPort:       cfg.Global.VCenterPort,
			Datacenters:       cfg.Global.Datacenter,
			RoundTripperCount: cfg.Global.RoundTripperCount,
			Thumbprint:        cfg.Global.Thumbprint,
		}
		vSphereConn := vclib.VSphereConnection{
			Username:          vcConfig.User,
			Password:          vcConfig.Password,
			Hostname:          cfg.Global.VCenterIP,
			Insecure:          cfg.Global.InsecureFlag,
			RoundTripperCount: vcConfig.RoundTripperCount,
			Port:              vcConfig.VCenterPort,
			CACert:            cfg.Global.CAFile,
			Thumbprint:        cfg.Global.Thumbprint,
		}
		vsphereIns := VSphereInstance{
			conn: &vSphereConn,
			cfg:  &vcConfig,
		}
		vsphereInstanceMap[cfg.Global.VCenterIP] = &vsphereIns
	} else {
		if cfg.Workspace.VCenterIP == "" || cfg.Workspace.Folder == "" || cfg.Workspace.Datacenter == "" {
			msg := fmt.Sprintf("All fields in workspace are mandatory."+
				" vsphere.conf does not have the workspace specified correctly. cfg.Workspace: %+v", cfg.Workspace)
			klog.Error(msg)
			return nil, errors.New(msg)
		}
		if len(cfg.VirtualCenter) > 1 {
			klog.Warning("Multi vCenter support is deprecated. vSphere CSI Driver does not support Kubernetes nodes spread across multiple vCenter servers. Please consider moving all Kubernetes nodes to single vCenter server")
		}
		for vcServer, vcConfig := range cfg.VirtualCenter {
			klog.V(4).Infof("Initializing vc server %s", vcServer)
			if vcServer == "" {
				klog.Error("vsphere.conf does not have the VirtualCenter IP address specified")
				return nil, errors.New("vsphere.conf does not have the VirtualCenter IP address specified")
			}
			if !isSecretInfoProvided {
				if vcConfig.User == "" {
					vcConfig.User = cfg.Global.User
					if vcConfig.User == "" {
						klog.Errorf("vcConfig.User is empty for vc %s!", vcServer)
						return nil, ErrUsernameMissing
					}
				}
				if vcConfig.Password == "" {
					vcConfig.Password = cfg.Global.Password
					if vcConfig.Password == "" {
						klog.Errorf("vcConfig.Password is empty for vc %s!", vcServer)
						return nil, ErrPasswordMissing
					}
				}
			} else {
				if vcConfig.User != "" {
					klog.Warningf("vcConfig.User for server %s and Secret info provided. VCP will use secret to get credentials", vcServer)
					vcConfig.User = ""
				}
				if vcConfig.Password != "" {
					klog.Warningf("vcConfig.Password for server %s and Secret info provided. VCP will use secret to get credentials", vcServer)
					vcConfig.Password = ""
				}
			}
			if vcConfig.VCenterPort == "" {
				vcConfig.VCenterPort = cfg.Global.VCenterPort
			}
			if vcConfig.Datacenters == "" {
				if cfg.Global.Datacenters != "" {
					vcConfig.Datacenters = cfg.Global.Datacenters
				} else {
					vcConfig.Datacenters = cfg.Global.Datacenter
				}
			}
			if vcConfig.RoundTripperCount == 0 {
				vcConfig.RoundTripperCount = cfg.Global.RoundTripperCount
			}
			vSphereConn := vclib.VSphereConnection{
				Username:          vcConfig.User,
				Password:          vcConfig.Password,
				Hostname:          vcServer,
				Insecure:          cfg.Global.InsecureFlag,
				RoundTripperCount: vcConfig.RoundTripperCount,
				Port:              vcConfig.VCenterPort,
				CACert:            cfg.Global.CAFile,
				Thumbprint:        vcConfig.Thumbprint,
			}
			vsphereIns := VSphereInstance{
				conn: &vSphereConn,
				cfg:  vcConfig,
			}
			vsphereInstanceMap[vcServer] = &vsphereIns
		}
	}
	return vsphereInstanceMap, nil
}
var getVMUUID = GetVMUUID
func newControllerNode(cfg VSphereConfig) (*VSphere, error) {
	vs, err := buildVSphereFromConfig(cfg)
	if err != nil {
		return nil, err
	}
	vs.hostName, err = os.Hostname()
	if err != nil {
		klog.Errorf("Failed to get hostname. err: %+v", err)
		return nil, err
	}
	if cfg.Global.VMUUID != "" {
		vs.vmUUID = cfg.Global.VMUUID
	} else {
		vs.vmUUID, err = getVMUUID()
		if err != nil {
			klog.Errorf("Failed to get uuid. err: %+v", err)
			return nil, err
		}
	}
	runtime.SetFinalizer(vs, logout)
	return vs, nil
}
func buildVSphereFromConfig(cfg VSphereConfig) (*VSphere, error) {
	isSecretInfoProvided := false
	if cfg.Global.SecretName != "" && cfg.Global.SecretNamespace != "" {
		isSecretInfoProvided = true
	}
	if cfg.Disk.SCSIControllerType == "" {
		cfg.Disk.SCSIControllerType = vclib.PVSCSIControllerType
	} else if !vclib.CheckControllerSupported(cfg.Disk.SCSIControllerType) {
		klog.Errorf("%v is not a supported SCSI Controller type. Please configure 'lsilogic-sas' OR 'pvscsi'", cfg.Disk.SCSIControllerType)
		return nil, errors.New("Controller type not supported. Please configure 'lsilogic-sas' OR 'pvscsi'")
	}
	if cfg.Global.WorkingDir != "" {
		cfg.Global.WorkingDir = path.Clean(cfg.Global.WorkingDir)
	}
	if cfg.Global.RoundTripperCount == 0 {
		cfg.Global.RoundTripperCount = RoundTripperDefaultCount
	}
	if cfg.Global.VCenterPort == "" {
		cfg.Global.VCenterPort = "443"
	}
	vsphereInstanceMap, err := populateVsphereInstanceMap(&cfg)
	if err != nil {
		return nil, err
	}
	vs := VSphere{
		vsphereInstanceMap: vsphereInstanceMap,
		nodeManager: &NodeManager{
			vsphereInstanceMap: vsphereInstanceMap,
			nodeInfoMap:        make(map[string]*NodeInfo),
			registeredNodes:    make(map[string]*v1.Node),
		},
		vsphereVolumeMap:     NewVsphereVolumeMap(),
		isSecretInfoProvided: isSecretInfoProvided,
		isSecretManaged:      !cfg.Global.SecretNotManaged,
		cfg:                  &cfg,
	}
	return &vs, nil
}
func logout(vs *VSphere) {
	for _, vsphereIns := range vs.vsphereInstanceMap {
		vsphereIns.conn.Logout(context.TODO())
	}
}
func (vs *VSphere) Instances() (cloudprovider.Instances, bool) {
	return vs, true
}
func (vs *VSphere) InstancesV2() (cloudprovider.InstancesV2, bool) {
	return nil, false
}
func getLocalIP() ([]v1.NodeAddress, error) {
	vmwareOUI := map[string]bool{
		"00:05:69": true,
		"00:0c:29": true,
		"00:1c:14": true,
		"00:50:56": true,
	}
	addrs := []v1.NodeAddress{}
	ifaces, err := net.Interfaces()
	if err != nil {
		klog.Errorf("net.Interfaces() failed for NodeAddresses - %v", err)
		return nil, err
	}
	for _, i := range ifaces {
		if i.Flags&net.FlagLoopback != 0 {
			continue
		}
		localAddrs, err := i.Addrs()
		if err != nil {
			klog.Warningf("Failed to extract addresses for NodeAddresses - %v", err)
		} else {
			for _, addr := range localAddrs {
				if ipnet, ok := addr.(*net.IPNet); ok {
					if !ipnet.IP.IsLinkLocalUnicast() {
						vmMACAddr := strings.ToLower(i.HardwareAddr.String())
						if len(vmMACAddr) < 17 {
							klog.V(4).Infof("Skipping invalid MAC address: %q", vmMACAddr)
							continue
						}
						if vmwareOUI[vmMACAddr[:8]] {
							nodehelpers.AddToNodeAddresses(&addrs,
								v1.NodeAddress{
									Type:    v1.NodeExternalIP,
									Address: ipnet.IP.String(),
								},
								v1.NodeAddress{
									Type:    v1.NodeInternalIP,
									Address: ipnet.IP.String(),
								},
							)
							klog.V(4).Infof("Detected local IP address as %q", ipnet.IP.String())
						} else {
							klog.V(4).Infof("Failed to patch IP for interface %q as MAC address %q does not belong to a VMware platform", i.Name, vmMACAddr)
						}
					}
				}
			}
		}
	}
	return addrs, nil
}
func (vs *VSphere) getVSphereInstance(nodeName k8stypes.NodeName) (*VSphereInstance, error) {
	vsphereIns, err := vs.nodeManager.GetVSphereInstance(nodeName)
	if err != nil {
		klog.Errorf("Cannot find node %q in cache. Node not found!!!", nodeName)
		return nil, err
	}
	return &vsphereIns, nil
}
func (vs *VSphere) getVSphereInstanceForServer(vcServer string, ctx context.Context) (*VSphereInstance, error) {
	vsphereIns, ok := vs.vsphereInstanceMap[vcServer]
	if !ok {
		klog.Errorf("cannot find vcServer %q in cache. VC not found!!!", vcServer)
		return nil, fmt.Errorf("cannot find node %q in vsphere configuration map", vcServer)
	}
	err := vs.nodeManager.vcConnect(ctx, vsphereIns)
	if err != nil {
		klog.Errorf("failed connecting to vcServer %q with error %+v", vcServer, err)
		return nil, err
	}
	return vsphereIns, nil
}
func (vs *VSphere) getVMFromNodeName(ctx context.Context, nodeName k8stypes.NodeName) (*vclib.VirtualMachine, error) {
	nodeInfo, err := vs.nodeManager.GetNodeInfo(nodeName)
	if err != nil {
		return nil, err
	}
	return nodeInfo.vm, nil
}
func (vs *VSphere) NodeAddresses(ctx context.Context, nodeName k8stypes.NodeName) ([]v1.NodeAddress, error) {
	if vs.hostName == convertToString(nodeName) {
		return vs.getNodeAddressesFromLocalIP()
	}
	return vs.getNodeAddressesFromVM(ctx, nodeName)
}
func (vs *VSphere) getNodeAddressesFromLocalIP() ([]v1.NodeAddress, error) {
	addrs, err := getLocalIP()
	if err != nil {
		return nil, err
	}
	nodehelpers.AddToNodeAddresses(&addrs, v1.NodeAddress{Type: v1.NodeHostName, Address: vs.hostName})
	return addrs, nil
}
func (vs *VSphere) getNodeAddressesFromVM(ctx context.Context, nodeName k8stypes.NodeName) ([]v1.NodeAddress, error) {
	if vs.cfg == nil {
		return nil, cloudprovider.InstanceNotFound
	}
	addrs := []v1.NodeAddress{}
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	vsi, err := vs.getVSphereInstance(nodeName)
	if err != nil {
		return nil, err
	}
	err = vs.nodeManager.vcConnect(ctx, vsi)
	if err != nil {
		return nil, err
	}
	vm, err := vs.getVMFromNodeName(ctx, nodeName)
	if err != nil {
		klog.Errorf("Failed to get VM object for node: %q. err: +%v", convertToString(nodeName), err)
		return nil, err
	}
	vmMoList, err := vm.Datacenter.GetVMMoList(ctx, []*vclib.VirtualMachine{vm}, []string{"guest.net"})
	if err != nil {
		klog.Errorf("Failed to get VM Managed object with property guest.net for node: %q. err: +%v", convertToString(nodeName), err)
		return nil, err
	}
	for _, v := range vmMoList[0].Guest.Net {
		if vs.cfg.Network.PublicNetwork == v.Network {
			for _, ip := range v.IpAddress {
				if !netutils.ParseIPSloppy(ip).IsLinkLocalUnicast() {
					nodehelpers.AddToNodeAddresses(&addrs,
						v1.NodeAddress{
							Type:    v1.NodeExternalIP,
							Address: ip,
						}, v1.NodeAddress{
							Type:    v1.NodeInternalIP,
							Address: ip,
						},
					)
				}
			}
		}
	}
	return addrs, nil
}
func (vs *VSphere) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]v1.NodeAddress, error) {
	return vs.NodeAddresses(ctx, convertToK8sType(providerID))
}
func (vs *VSphere) AddSSHKeyToAllInstances(ctx context.Context, user string, keyData []byte) error {
	return cloudprovider.NotImplemented
}