github.com/openshift/installer@v1.4.17/pkg/asset/quota/openstack/openstack.go (about)

     1  package openstack
     2  
     3  import (
     4  	"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors"
     5  	"github.com/sirupsen/logrus"
     6  
     7  	machinev1alpha1 "github.com/openshift/api/machine/v1alpha1"
     8  	machineapi "github.com/openshift/api/machine/v1beta1"
     9  	"github.com/openshift/installer/pkg/asset/installconfig/openstack/validation"
    10  	"github.com/openshift/installer/pkg/quota"
    11  )
    12  
    13  // These numbers should reflect what is documented here:
    14  // https://github.com/openshift/installer/tree/master/docs/user/openstack
    15  // Number of ports, routers, subnets and routers here don't include the constraints needed
    16  // for each machine, which are calculated later
    17  var minNetworkConstraint = buildNetworkConstraint(4, 0, 0, 0, 2, 56)
    18  
    19  func buildNetworkConstraint(ports, routers, subnets, networks, securityGroups, securityGroupRules int64) []quota.Constraint {
    20  	return []quota.Constraint{
    21  		{Name: "Port", Count: ports},
    22  		{Name: "Router", Count: routers},
    23  		{Name: "Subnet", Count: subnets},
    24  		{Name: "Network", Count: networks},
    25  		{Name: "SecurityGroup", Count: securityGroups},
    26  		{Name: "SecurityGroupRule", Count: securityGroupRules},
    27  	}
    28  }
    29  
    30  func getNetworkConstraints() []quota.Constraint {
    31  	return minNetworkConstraint
    32  }
    33  
    34  // Constraints returns a list of quota constraints based on the InstallConfig.
    35  // These constraints can be used to check if there is enough quota for creating a cluster
    36  // for the install config.
    37  func Constraints(ci *validation.CloudInfo, controlPlanes []machineapi.Machine, computes []machineapi.MachineSet) []quota.Constraint {
    38  	var constraints []quota.Constraint
    39  
    40  	for i := 0; i < len(controlPlanes); i++ {
    41  		constraints = append(constraints, machineConstraints(ci, &controlPlanes[i])...)
    42  	}
    43  	constraints = append(constraints, instanceConstraint(int64(len(controlPlanes))))
    44  
    45  	for i := 0; i < len(computes); i++ {
    46  		constraints = append(constraints, machineSetConstraints(ci, &computes[i])...)
    47  	}
    48  	constraints = append(constraints, instanceConstraint(int64(len(computes))))
    49  	constraints = append(constraints, getNetworkConstraints()...)
    50  
    51  	// If the cluster is using pre-provisioned networks, then the quota constraints should be
    52  	// null because the installer doesn't need to create any resources.
    53  	if len(ci.ControlPlanePortSubnets) == 0 {
    54  		constraints = append(constraints, networkConstraint(1), routerConstraint(1), subnetConstraint(1))
    55  	}
    56  
    57  	return aggregate(constraints)
    58  }
    59  
    60  func getOpenstackProviderSpec(spec *machineapi.ProviderSpec) *machinev1alpha1.OpenstackProviderSpec {
    61  	if spec.Value == nil {
    62  		logrus.Warnf("Empty ProviderSpec")
    63  		return nil
    64  	}
    65  
    66  	return spec.Value.Object.(*machinev1alpha1.OpenstackProviderSpec)
    67  }
    68  
    69  func machineConstraints(ci *validation.CloudInfo, machine *machineapi.Machine) []quota.Constraint {
    70  	osps := getOpenstackProviderSpec(&machine.Spec.ProviderSpec)
    71  	if osps == nil {
    72  		logrus.Warnf("Skipping quota validation for Machine %s: Invalid ProviderSpec", machine.Name)
    73  		return nil
    74  	}
    75  
    76  	flavorInfo, ok := ci.Flavors[osps.Flavor]
    77  	if !ok {
    78  		// This will result in a separate validation failure
    79  		logrus.Warnf("Skipping quota validation for Machine %s: Flavor '%s' is not valid",
    80  			machine.Name, osps.Flavor)
    81  		return nil
    82  	}
    83  	flavor := flavorInfo.Flavor
    84  	return []quota.Constraint{machineFlavorCoresToQuota(&flavor), machineFlavorRAMToQuota(&flavor), portConstraint(int64(len(osps.Networks)))}
    85  }
    86  
    87  func machineSetConstraints(ci *validation.CloudInfo, ms *machineapi.MachineSet) []quota.Constraint {
    88  	osps := getOpenstackProviderSpec(&ms.Spec.Template.Spec.ProviderSpec)
    89  	if osps == nil {
    90  		logrus.Warnf("Skipping quota validation for MachineSet %s: Invalid ProviderSpec", ms.Name)
    91  		return nil
    92  	}
    93  
    94  	replicas := ms.Spec.Replicas
    95  	if replicas == nil {
    96  		// We defensively check for nil Replicas here, but this should have
    97  		// already been defaulted if omitted.
    98  
    99  		logrus.Warnf("Skipping quota validation for MachineSet %s due to unspecified replica count", ms.Name)
   100  		return nil
   101  	}
   102  
   103  	flavorInfo, ok := ci.Flavors[osps.Flavor]
   104  	if !ok {
   105  		// This will result in a separate validation failure
   106  		logrus.Warnf("Skipping quota validation for MachineSet %s: Flavor '%s' is not valid", ms.Name, osps.Flavor)
   107  		return nil
   108  	}
   109  	flavor := flavorInfo.Flavor
   110  
   111  	coresConstraint := machineFlavorCoresToQuota(&flavor)
   112  	coresConstraint.Count = coresConstraint.Count * int64(*replicas)
   113  	ramConstraint := machineFlavorRAMToQuota(&flavor)
   114  	ramConstraint.Count = ramConstraint.Count * int64(*replicas)
   115  	portConstraint := portConstraint((int64(len(osps.Networks)) * int64(*replicas)))
   116  
   117  	return []quota.Constraint{coresConstraint, ramConstraint, portConstraint}
   118  }
   119  
   120  func aggregate(quotas []quota.Constraint) []quota.Constraint {
   121  	counts := map[string]int64{}
   122  	for _, q := range quotas {
   123  		counts[q.Name] = counts[q.Name] + q.Count
   124  	}
   125  	aggregatedQuotas := make([]quota.Constraint, 0, len(counts))
   126  	for n, c := range counts {
   127  		aggregatedQuotas = append(aggregatedQuotas, quota.Constraint{Name: n, Count: c})
   128  	}
   129  	return aggregatedQuotas
   130  }
   131  
   132  func machineFlavorCoresToQuota(f *flavors.Flavor) quota.Constraint {
   133  	return generateConstraint("Cores", int64(f.VCPUs))
   134  }
   135  
   136  func machineFlavorRAMToQuota(f *flavors.Flavor) quota.Constraint {
   137  	return generateConstraint("RAM", int64(f.RAM))
   138  }
   139  
   140  func instanceConstraint(count int64) quota.Constraint {
   141  	return generateConstraint("Instances", count)
   142  }
   143  
   144  func portConstraint(count int64) quota.Constraint {
   145  	return generateConstraint("Port", count)
   146  }
   147  
   148  func routerConstraint(count int64) quota.Constraint {
   149  	return generateConstraint("Router", count)
   150  }
   151  
   152  func networkConstraint(count int64) quota.Constraint {
   153  	return generateConstraint("Network", count)
   154  }
   155  
   156  func subnetConstraint(count int64) quota.Constraint {
   157  	return generateConstraint("Subnet", count)
   158  }
   159  
   160  func generateConstraint(name string, count int64) quota.Constraint {
   161  	return quota.Constraint{
   162  		Name:  name,
   163  		Count: count,
   164  	}
   165  }