github.com/openshift/installer@v1.4.17/pkg/types/vsphere/conversion/installconfig.go (about)

     1  package conversion
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/url"
     8  	"path"
     9  	"time"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	"github.com/vmware/govmomi"
    13  	"github.com/vmware/govmomi/find"
    14  	"github.com/vmware/govmomi/vim25/soap"
    15  
    16  	"github.com/openshift/installer/pkg/types"
    17  	"github.com/openshift/installer/pkg/types/vsphere"
    18  )
    19  
    20  var localLogger = logrus.New()
    21  
    22  const (
    23  	// GeneratedFailureDomainName is a placeholder name when one wasn't provided.
    24  	GeneratedFailureDomainName string = "generated-failure-domain"
    25  	// GeneratedFailureDomainRegion is a placeholder region when one wasn't provided.
    26  	GeneratedFailureDomainRegion string = "generated-region"
    27  	// GeneratedFailureDomainZone is a placeholder zone when one wasn't provided.
    28  	GeneratedFailureDomainZone string = "generated-zone"
    29  )
    30  
    31  // GetFinder connects to vCenter via SOAP and returns the Finder object if the SOAP
    32  // connection is successful. If the connection fails it returns nil.
    33  // Errors are mostly ignored to support AI and agent installers.
    34  func GetFinder(server, username, password string) (*find.Finder, error) {
    35  	var finder *find.Finder
    36  
    37  	if server != "" && password != "" && username != "" {
    38  		ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    39  		defer cancel()
    40  		u, err := soap.ParseURL(server)
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		u.User = url.UserPassword(username, password)
    45  
    46  		client, err := govmomi.NewClient(ctx, u, false)
    47  		if err != nil {
    48  			// If bogus authentication is provided in the scenario of AI or assisted
    49  			// just provide warning message. If this is IPI or UPI validation will
    50  			// catch and halt on incorrect authentication.
    51  			localLogger.Warnf("unable to log into vCenter %s, %v", server, err)
    52  			return nil, nil
    53  		}
    54  		finder = find.NewFinder(client.Client, true)
    55  	}
    56  
    57  	return finder, nil
    58  }
    59  
    60  func findViaPathOrName(finder *find.Finder, objectPath, objectFindPath string) (string, error) {
    61  	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    62  	defer cancel()
    63  
    64  	elements, err := finder.ManagedObjectListChildren(ctx, objectFindPath)
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  
    69  	for _, e := range elements {
    70  		if e.Path == objectPath {
    71  			return objectPath, nil
    72  		}
    73  
    74  		if path.Base(e.Path) == path.Base(objectPath) {
    75  			return e.Path, nil
    76  		}
    77  	}
    78  	return "", errors.New("unable to find object")
    79  }
    80  
    81  // fixNoVCentersScenario this function creates the VCenters slice
    82  // with existing legacy vcenter authentication and configuration.
    83  func fixNoVCentersScenario(platform *vsphere.Platform) {
    84  	if len(platform.VCenters) == 0 {
    85  		createVCenters(platform)
    86  
    87  		// Scenario: 4.12 Zonal IPI
    88  		if len(platform.FailureDomains) > 0 {
    89  			for i := range platform.FailureDomains {
    90  				if platform.FailureDomains[i].Topology.Datacenter == "" {
    91  					platform.FailureDomains[i].Topology.Datacenter = platform.DeprecatedDatacenter
    92  				}
    93  				if platform.FailureDomains[i].Server == "" {
    94  					// Assumption: by the time it is possible to use multiple vcenters
    95  					// it will be past 4.15
    96  					// so this conversion can be removed.
    97  					platform.FailureDomains[i].Server = platform.VCenters[0].Server
    98  				}
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func fixTechPreviewZonalFailureDomainsScenario(platform *vsphere.Platform, finders map[string]*find.Finder) error {
   105  	if len(platform.FailureDomains) > 0 {
   106  		var err error
   107  
   108  		for i := range platform.FailureDomains {
   109  			computeCluster := platform.FailureDomains[i].Topology.ComputeCluster
   110  			datastore := platform.FailureDomains[i].Topology.Datastore
   111  			folder := platform.FailureDomains[i].Topology.Folder
   112  			datacenter := platform.FailureDomains[i].Topology.Datacenter
   113  			vCenter := platform.FailureDomains[i].Server
   114  			fdName := platform.FailureDomains[i].Name
   115  
   116  			finder, ok := finders[vCenter]
   117  			if !ok {
   118  				// This is when invalid config happens.  There is a check later in cycle that will print it out.  For now,
   119  				// lets just log warning and return.
   120  				localLogger.Warnf("unable to find finder for vCenter %v in order to do upconvert", vCenter)
   121  				return nil
   122  			}
   123  			platform.FailureDomains[i].Topology.ComputeCluster, err = SetObjectPath(finder, "host", computeCluster, datacenter)
   124  			if err != nil {
   125  				return fmt.Errorf("unable to SetObjectPath for compute cluster of failure domain %s: %w", fdName, err)
   126  			}
   127  
   128  			platform.FailureDomains[i].Topology.Datastore, err = SetObjectPath(finder, "datastore", datastore, datacenter)
   129  			if err != nil {
   130  				return fmt.Errorf("unable to SetObjectPath for datastore of failure domain %s: %w", fdName, err)
   131  			}
   132  
   133  			platform.FailureDomains[i].Topology.Folder, err = SetObjectPath(finder, "vm", folder, datacenter)
   134  			if err != nil {
   135  				return fmt.Errorf("unable to SetObjectPath for folder of failure domain %s: %w", fdName, err)
   136  			}
   137  		}
   138  	}
   139  	return nil
   140  }
   141  
   142  func fixLegacyPlatformScenario(platform *vsphere.Platform, finders map[string]*find.Finder) error {
   143  	if len(platform.FailureDomains) == 0 {
   144  		var err error
   145  		localLogger.Warn("vsphere topology fields are now deprecated; please use failureDomains")
   146  
   147  		platform.FailureDomains = make([]vsphere.FailureDomain, 1)
   148  		platform.FailureDomains[0].Name = GeneratedFailureDomainName
   149  		platform.FailureDomains[0].Server = platform.VCenters[0].Server
   150  		platform.FailureDomains[0].Region = GeneratedFailureDomainRegion
   151  		platform.FailureDomains[0].Zone = GeneratedFailureDomainZone
   152  
   153  		platform.FailureDomains[0].Topology.Datacenter = platform.DeprecatedDatacenter
   154  		platform.FailureDomains[0].Topology.ResourcePool = platform.DeprecatedResourcePool
   155  		platform.FailureDomains[0].Topology.Networks = make([]string, 1)
   156  		platform.FailureDomains[0].Topology.Networks[0] = platform.DeprecatedNetwork
   157  
   158  		finder, ok := finders[platform.FailureDomains[0].Server]
   159  		if !ok {
   160  			return fmt.Errorf("unable to find finder for vCenter %v", platform.FailureDomains[0].Server)
   161  		}
   162  		platform.FailureDomains[0].Topology.ComputeCluster, err = SetObjectPath(finder, "host", platform.DeprecatedCluster, platform.DeprecatedDatacenter)
   163  		if err != nil {
   164  			return err
   165  		}
   166  
   167  		platform.FailureDomains[0].Topology.Datastore, err = SetObjectPath(finder, "datastore", platform.DeprecatedDefaultDatastore, platform.DeprecatedDatacenter)
   168  		if err != nil {
   169  			return err
   170  		}
   171  
   172  		platform.FailureDomains[0].Topology.Folder, err = SetObjectPath(finder, "vm", platform.DeprecatedFolder, platform.DeprecatedDatacenter)
   173  		if err != nil {
   174  			return err
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  // ConvertInstallConfig modifies a given platform spec for the new requirements.
   181  func ConvertInstallConfig(config *types.InstallConfig) error {
   182  	var err error
   183  	platform := config.Platform.VSphere
   184  
   185  	fixNoVCentersScenario(platform)
   186  	finders := make(map[string]*find.Finder)
   187  	for _, vcenter := range platform.VCenters {
   188  		finder, err := GetFinder(vcenter.Server, vcenter.Username, vcenter.Password)
   189  		if err != nil {
   190  			return err
   191  		}
   192  		finders[vcenter.Server] = finder
   193  	}
   194  	err = fixTechPreviewZonalFailureDomainsScenario(platform, finders)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	err = fixLegacyPlatformScenario(platform, finders)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	return nil
   204  }
   205  
   206  // SetObjectPath based on the pathType will either determine the path for the type via
   207  // a simple join of the datacenter, pathType and objectPath if finder is nil
   208  // or via a connection to vCenter find of all child objects under the
   209  // datacenter and pathType.
   210  // pathType must only be "host", "vm", or "datastore".
   211  func SetObjectPath(finder *find.Finder, pathType, objectPath, datacenter string) (string, error) {
   212  	if objectPath != "" && !path.IsAbs(objectPath) {
   213  		var joinedObjectPath string
   214  		var joinedObjectFindPath string
   215  		var paramName string
   216  
   217  		switch pathType {
   218  		case "host":
   219  			paramName = "computeCluster"
   220  		case "vm":
   221  			paramName = "folder"
   222  		case "datastore":
   223  			paramName = "datastore"
   224  		default:
   225  			return "", errors.New("pathType can only be host, datastore or vm")
   226  		}
   227  
   228  		joinedObjectFindPath = path.Join("/", datacenter, pathType, "...")
   229  		joinedObjectPath = path.Join("/", datacenter, pathType, objectPath)
   230  
   231  		if finder == nil {
   232  			localLogger.Warnf("%s as a non-path is now deprecated; please use the joined form: %s", paramName, joinedObjectPath)
   233  			return joinedObjectPath, nil
   234  		}
   235  
   236  		newObjectPath, err := findViaPathOrName(finder, joinedObjectPath, joinedObjectFindPath)
   237  		if err != nil {
   238  			return "", err
   239  		}
   240  
   241  		if objectPath != newObjectPath {
   242  			localLogger.Debugf("%s path changed from %s to %s", paramName, objectPath, newObjectPath)
   243  		}
   244  		localLogger.Warnf("%s as a non-path is now deprecated; please use the discovered form: %s", paramName, newObjectPath)
   245  
   246  		return newObjectPath, nil
   247  	}
   248  
   249  	return objectPath, nil
   250  }
   251  
   252  func createVCenters(platform *vsphere.Platform) {
   253  	localLogger.Warn("vsphere authentication fields are now deprecated; please use vcenters")
   254  
   255  	platform.VCenters = make([]vsphere.VCenter, 1)
   256  	platform.VCenters[0].Server = platform.DeprecatedVCenter
   257  	platform.VCenters[0].Username = platform.DeprecatedUsername
   258  	platform.VCenters[0].Password = platform.DeprecatedPassword
   259  	platform.VCenters[0].Port = 443
   260  
   261  	if platform.DeprecatedDatacenter != "" {
   262  		platform.VCenters[0].Datacenters = append(platform.VCenters[0].Datacenters, platform.DeprecatedDatacenter)
   263  	}
   264  
   265  	// Scenario: Zonal IPI w/o vcenters defined
   266  	// Confirms the list of datacenters from FailureDomains are updated
   267  	// in vcenters[0].datacenters
   268  	for _, failureDomain := range platform.FailureDomains {
   269  		found := false
   270  		if failureDomain.Topology.Datacenter != "" {
   271  			for _, dc := range platform.VCenters[0].Datacenters {
   272  				if dc == failureDomain.Topology.Datacenter {
   273  					found = true
   274  				}
   275  			}
   276  
   277  			if !found {
   278  				platform.VCenters[0].Datacenters = append(platform.VCenters[0].Datacenters, failureDomain.Topology.Datacenter)
   279  			}
   280  		}
   281  	}
   282  }