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 }