github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/maas/constraints.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package maas 5 6 import ( 7 "net/url" 8 "strings" 9 10 "github.com/juju/collections/set" 11 "github.com/juju/gomaasapi/v2" 12 13 "github.com/juju/juju/core/arch" 14 "github.com/juju/juju/core/constraints" 15 "github.com/juju/juju/environs/context" 16 ) 17 18 var unsupportedConstraints = []string{ 19 constraints.CpuPower, 20 constraints.InstanceType, 21 constraints.VirtType, 22 constraints.AllocatePublicIP, 23 } 24 25 // ConstraintsValidator is defined on the Environs interface. 26 func (env *maasEnviron) ConstraintsValidator(ctx context.ProviderCallContext) (constraints.Validator, error) { 27 validator := constraints.NewValidator() 28 validator.RegisterUnsupported(unsupportedConstraints) 29 supportedArches, err := env.getSupportedArchitectures(ctx) 30 if err != nil { 31 return nil, err 32 } 33 34 // Only consume supported juju architectures for this release. This will 35 // also remove any duplicate architectures. 36 maasArches := set.NewStrings(supportedArches...) 37 supported := set.NewStrings(arch.AllSupportedArches...).Intersection(maasArches) 38 39 validator.RegisterVocabulary(constraints.Arch, supported.SortedValues()) 40 41 return validator, nil 42 } 43 44 // convertConstraints converts the given constraints into a 45 // gomaasapi.AllocateMachineArgs for passing to MAAS. 46 func convertConstraints(cons constraints.Value) gomaasapi.AllocateMachineArgs { 47 params := gomaasapi.AllocateMachineArgs{} 48 if cons.Arch != nil { 49 params.Architecture = *cons.Arch 50 } 51 if cons.CpuCores != nil { 52 params.MinCPUCount = int(*cons.CpuCores) 53 } 54 if cons.Mem != nil { 55 params.MinMemory = int(*cons.Mem) 56 } 57 if cons.Tags != nil { 58 positives, negatives := parseDelimitedValues(*cons.Tags) 59 if len(positives) > 0 { 60 params.Tags = positives 61 } 62 if len(negatives) > 0 { 63 params.NotTags = negatives 64 } 65 } 66 if cons.CpuPower != nil { 67 logger.Warningf("ignoring unsupported constraint 'cpu-power'") 68 } 69 return params 70 } 71 72 // convertTagsToParams converts a list of positive/negative tags from 73 // constraints into two comma-delimited lists of values, which can then be 74 // passed to MAAS using the "tags" and "not_tags" arguments to acquire. If 75 // either list of tags is empty, the respective argument is not added to params. 76 func convertTagsToParams(params url.Values, tags *[]string) { 77 if tags == nil || len(*tags) == 0 { 78 return 79 } 80 positives, negatives := parseDelimitedValues(*tags) 81 if len(positives) > 0 { 82 params.Add("tags", strings.Join(positives, ",")) 83 } 84 if len(negatives) > 0 { 85 params.Add("not_tags", strings.Join(negatives, ",")) 86 } 87 } 88 89 // convertSpacesFromConstraints extracts spaces from constraints and converts 90 // them to two lists of positive and negative spaces. 91 func convertSpacesFromConstraints(spaces *[]string) ([]string, []string) { 92 if spaces == nil || len(*spaces) == 0 { 93 return nil, nil 94 } 95 return parseDelimitedValues(*spaces) 96 } 97 98 // parseDelimitedValues parses a slice of raw values coming from constraints 99 // (Tags or Spaces). The result is split into two slices - positives and 100 // negatives (prefixed with "^"). Empty values are ignored. 101 func parseDelimitedValues(rawValues []string) (positives, negatives []string) { 102 for _, value := range rawValues { 103 if value == "" || value == "^" { 104 // Neither of these cases should happen in practise, as constraints 105 // are validated before setting them and empty names for spaces or 106 // tags are not allowed. 107 continue 108 } 109 if strings.HasPrefix(value, "^") { 110 negatives = append(negatives, strings.TrimPrefix(value, "^")) 111 } else { 112 positives = append(positives, value) 113 } 114 } 115 return positives, negatives 116 } 117 118 func addInterfaces(params *gomaasapi.AllocateMachineArgs, positiveSpaceIDs, negativeSpaceIDs set.Strings) { 119 if len(positiveSpaceIDs) > 0 { 120 for _, providerSpaceID := range positiveSpaceIDs.SortedValues() { 121 // NOTE(achilleasa): use the provider ID as the label for the 122 // iface. Using the space name might seem to be more 123 // user-friendly but space names can change after the machine 124 // gets provisioned so they should not be used for labeling things. 125 params.Interfaces = append( 126 params.Interfaces, 127 gomaasapi.InterfaceSpec{ 128 Label: providerSpaceID, 129 Space: providerSpaceID, 130 }, 131 ) 132 } 133 } 134 135 if len(negativeSpaceIDs) != 0 { 136 params.NotSpace = negativeSpaceIDs.SortedValues() 137 } 138 } 139 140 // addStorage adds volume information onto a gomaasapi.AllocateMachineArgs 141 // object suitable to pass to MAAS 2 when acquiring a node. 142 func addStorage(params *gomaasapi.AllocateMachineArgs, volumes []volumeInfo) { 143 if len(volumes) == 0 { 144 return 145 } 146 var volParams []gomaasapi.StorageSpec 147 for _, v := range volumes { 148 volSpec := gomaasapi.StorageSpec{ 149 Label: v.name, 150 Size: int(v.sizeInGB), 151 Tags: v.tags, 152 } 153 volParams = append(volParams, volSpec) 154 } 155 params.Storage = volParams 156 }