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 }