github.com/openshift/installer@v1.4.17/pkg/asset/installconfig/ovirt/validation.go (about) 1 package ovirt 2 3 import ( 4 "fmt" 5 6 "github.com/AlecAivazis/survey/v2" 7 ovirtsdk "github.com/ovirt/go-ovirt" 8 "github.com/pkg/errors" 9 "github.com/sirupsen/logrus" 10 "k8s.io/apimachinery/pkg/util/validation/field" 11 12 "github.com/openshift/installer/pkg/types" 13 "github.com/openshift/installer/pkg/types/ovirt" 14 "github.com/openshift/installer/pkg/types/ovirt/validation" 15 ) 16 17 // Validate executes ovirt specific validation 18 func Validate(ic *types.InstallConfig) error { 19 allErrs := field.ErrorList{} 20 ovirtPlatformPath := field.NewPath("platform", "ovirt") 21 22 if ic.Platform.Ovirt == nil { 23 return errors.New(field.Required( 24 ovirtPlatformPath, 25 "validation requires a Engine platform configuration").Error()) 26 } 27 28 allErrs = append( 29 allErrs, 30 validation.ValidatePlatform(ic.Platform.Ovirt, ovirtPlatformPath, ic)...) 31 32 con, err := NewConnection() 33 if err != nil { 34 return err 35 } 36 defer con.Close() 37 38 if err := validateVNICProfile(*ic.Ovirt, con); err != nil { 39 allErrs = append( 40 allErrs, 41 field.Invalid(ovirtPlatformPath.Child("vnicProfileID"), ic.Ovirt.VNICProfileID, err.Error())) 42 } 43 if ic.ControlPlane != nil && ic.ControlPlane.Platform.Ovirt != nil { 44 allErrs = append( 45 allErrs, 46 validateMachinePool(con, field.NewPath("controlPlane", "platform", "ovirt"), ic.ControlPlane.Platform.Ovirt, *ic.Ovirt)...) 47 } 48 for idx, compute := range ic.Compute { 49 fldPath := field.NewPath("compute").Index(idx) 50 if compute.Platform.Ovirt != nil { 51 allErrs = append( 52 allErrs, 53 validateMachinePool(con, fldPath.Child("platform", "ovirt"), compute.Platform.Ovirt, *ic.Ovirt)...) 54 } 55 } 56 57 return allErrs.ToAggregate() 58 } 59 60 func validateMachinePool(con *ovirtsdk.Connection, child *field.Path, pool *ovirt.MachinePool, platform ovirt.Platform) field.ErrorList { 61 allErrs := field.ErrorList{} 62 allErrs = append(allErrs, validateInstanceTypeID(con, child, pool)...) 63 allErrs = append(allErrs, validateMachineAffinityGroups(con, child, pool, platform)...) 64 65 return allErrs 66 } 67 68 // validateExistingAffinityGroup checks that there is no affinity group with the same name in the cluster 69 func validateExistingAffinityGroup(con *ovirtsdk.Connection, platform ovirt.Platform) error { 70 res, err := con.SystemService().ClustersService(). 71 ClusterService(platform.ClusterID).AffinityGroupsService().List().Send() 72 if err != nil { 73 return errors.Errorf("failed listing affinity groups for cluster %v", platform.ClusterID) 74 } 75 for _, ag := range res.MustGroups().Slice() { 76 for _, agNew := range platform.AffinityGroups { 77 if ag.MustName() == agNew.Name { 78 return errors.Errorf( 79 "affinity group %v already exist in cluster %v", agNew.Name, platform.ClusterID) 80 } 81 } 82 } 83 return nil 84 } 85 86 func validateClusterResources(con *ovirtsdk.Connection, ic *types.InstallConfig) error { 87 mAgReplicas := make(map[string]int) 88 for _, agn := range ic.ControlPlane.Platform.Ovirt.AffinityGroupsNames { 89 mAgReplicas[agn] = mAgReplicas[agn] + int(*ic.ControlPlane.Replicas) 90 } 91 for _, compute := range ic.Compute { 92 for _, agn := range compute.Platform.Ovirt.AffinityGroupsNames { 93 mAgReplicas[agn] = mAgReplicas[agn] + int(*compute.Replicas) 94 } 95 } 96 97 clusterName, err := GetClusterName(con, ic.Ovirt.ClusterID) 98 if err != nil { 99 return err 100 } 101 hosts, err := FindHostsInCluster(con, clusterName) 102 if err != nil { 103 return err 104 } 105 for _, ag := range ic.Ovirt.AffinityGroups { 106 if _, found := mAgReplicas[ag.Name]; found { 107 if len(hosts) < mAgReplicas[ag.Name] { 108 msg := fmt.Sprintf("Affinity Group %v cannot be fulfilled, oVirt cluster doesn't"+ 109 "have enough hosts: found %v hosts but %v replicas assigned to affinity group", 110 ag.Name, len(hosts), mAgReplicas[ag.Name]) 111 if ag.Enforcing { 112 return fmt.Errorf(msg, ag) 113 } 114 logrus.Warning(msg) 115 } 116 } 117 } 118 return nil 119 } 120 121 func validateInstanceTypeID(con *ovirtsdk.Connection, child *field.Path, machinePool *ovirt.MachinePool) field.ErrorList { 122 allErrs := field.ErrorList{} 123 if machinePool.InstanceTypeID != "" { 124 _, err := con.SystemService().InstanceTypesService().InstanceTypeService(machinePool.InstanceTypeID).Get().Send() 125 if err != nil { 126 allErrs = append(allErrs, field.NotFound(child.Child("instanceTypeID"), machinePool.InstanceTypeID)) 127 } 128 } 129 return allErrs 130 } 131 132 // validateMachineAffinityGroups checks that the affinity groups on the machine object exist in the oVirt cluster 133 // or created by the installer. 134 func validateMachineAffinityGroups(con *ovirtsdk.Connection, child *field.Path, machinePool *ovirt.MachinePool, platform ovirt.Platform) field.ErrorList { 135 allErrs := field.ErrorList{} 136 existingAG := make(map[string]int) 137 138 res, err := con.SystemService().ClustersService(). 139 ClusterService(platform.ClusterID).AffinityGroupsService().List().Send() 140 if err != nil { 141 return append( 142 allErrs, 143 field.InternalError( 144 child, 145 errors.Errorf("failed listing affinity groups for cluster %v", platform.ClusterID))) 146 } 147 for _, ag := range res.MustGroups().Slice() { 148 existingAG[ag.MustName()] = 0 149 } 150 // add affinity groups the installer creates 151 for _, ag := range platform.AffinityGroups { 152 existingAG[ag.Name] = 0 153 } 154 for _, ag := range machinePool.AffinityGroupsNames { 155 if _, ok := existingAG[ag]; !ok { 156 allErrs = append( 157 allErrs, 158 field.Invalid( 159 child.Child("affinityGroupsNames"), ag, 160 fmt.Sprintf( 161 "Affinity Group %v doesn't exist in oVirt cluster or created by the installer", ag))) 162 } 163 } 164 return allErrs 165 } 166 167 // authenticated takes an ovirt platform and validates 168 // its connection to the API by establishing 169 // the connection and authenticating successfully. 170 // The API connection is closed in the end and must leak 171 // or be reused in any way. 172 func authenticated(c *Config) survey.Validator { 173 return func(val interface{}) error { 174 connection, err := ovirtsdk.NewConnectionBuilder(). 175 URL(c.URL). 176 Username(c.Username). 177 Password(fmt.Sprint(val)). 178 CAFile(c.CAFile). 179 CACert([]byte(c.CABundle)). 180 Insecure(c.Insecure). 181 Build() 182 183 if err != nil { 184 return errors.Errorf("failed to construct connection to Engine platform %s", err) 185 } 186 187 defer connection.Close() 188 189 err = connection.Test() 190 if err != nil { 191 return errors.Errorf("failed to connect to the Engine platform %s", err) 192 } 193 return nil 194 } 195 } 196 197 // validate the provided vnic profile exists and belongs the the cluster network 198 func validateVNICProfile(platform ovirt.Platform, con *ovirtsdk.Connection) error { 199 if platform.VNICProfileID != "" { 200 profiles, err := FetchVNICProfileByClusterNetwork(con, platform.ClusterID, platform.NetworkName) 201 if err != nil { 202 return err 203 } 204 205 for _, p := range profiles { 206 if platform.VNICProfileID == p.MustId() { 207 return nil 208 } 209 } 210 211 return fmt.Errorf( 212 "vNic profile ID %s does not belong to cluster network %s", 213 platform.VNICProfileID, 214 platform.NetworkName) 215 } 216 return nil 217 } 218 219 // ValidateForProvisioning validates that the install config is valid for provisioning the cluster. 220 func ValidateForProvisioning(ic *types.InstallConfig) error { 221 con, err := NewConnection() 222 if err != nil { 223 return err 224 } 225 defer con.Close() 226 if err := validateClusterResources(con, ic); err != nil { 227 return err 228 } 229 if err := validateExistingAffinityGroup(con, *ic.Ovirt); err != nil { 230 return err 231 } 232 return nil 233 }