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  }