github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/core/devices/constraints.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package devices 5 6 import ( 7 "strconv" 8 "strings" 9 10 "github.com/juju/errors" 11 ) 12 13 var deviceParseErr = errors.Errorf("cannot parse device constraints string, supported format is [<count>,]<device-class>|<vendor/type>[,<key>=<value>;...]") 14 15 // DeviceType defines a device type. 16 type DeviceType string 17 18 // Constraints describes a set of device constraints. 19 type Constraints struct { 20 21 // Type is the device type or device-class. 22 // currently supported types are 23 // - gpu 24 // - nvidia.com/gpu 25 // - amd.com/gpu 26 Type DeviceType `bson:"type"` 27 28 // Count is the number of devices that the user has asked for - count min and max are the 29 // number of devices the charm requires. 30 Count int64 `bson:"count"` 31 32 // Attributes is a collection of key value pairs device related (node affinity labels/tags etc.). 33 Attributes map[string]string `bson:"attributes"` 34 } 35 36 // ParseConstraints parses the specified string and creates a 37 // Constraints structure. 38 // 39 // The acceptable format for device constraints is a comma separated 40 // sequence of: COUNT, TYPE, and ATTRIBUTES with format like 41 // 42 // <device-name>=[<count>,]<device-class>|<vendor/type>[,<attributes>] 43 // 44 // where 45 // 46 // COUNT is the number of devices that the user has asked for - count min and max are the 47 // number of devices the charm requires. If unspecified, COUNT defaults to 1. 48 func ParseConstraints(s string) (Constraints, error) { 49 var cons Constraints 50 51 fields := strings.Split(s, ",") 52 fieldsLen := len(fields) 53 if fieldsLen < 1 || fieldsLen > 3 { 54 return cons, deviceParseErr 55 } 56 if fieldsLen == 1 { 57 cons.Count = 1 58 cons.Type = DeviceType(fields[0]) 59 } else { 60 count, err := parseCount(fields[0]) 61 if err != nil { 62 return Constraints{}, err 63 } 64 cons.Count = count 65 cons.Type = DeviceType(fields[1]) 66 67 if fieldsLen == 3 { 68 attr, err := parseAttributes(fields[2]) 69 if err != nil { 70 return Constraints{}, err 71 } 72 cons.Attributes = attr 73 } 74 } 75 return cons, nil 76 } 77 78 func parseAttributes(s string) (map[string]string, error) { 79 parseAttribute := func(s string) ([]string, error) { 80 kv := strings.Split(s, "=") 81 if len(kv) != 2 { 82 return nil, errors.Errorf("device attribute key/value pair has bad format: %q", s) 83 } 84 return kv, nil 85 } 86 attr := map[string]string{} 87 for _, attrStr := range strings.Split(s, ";") { 88 kv, err := parseAttribute(attrStr) 89 if err != nil { 90 return nil, err 91 } 92 attr[kv[0]] = kv[1] 93 } 94 return attr, nil 95 } 96 97 func parseCount(s string) (int64, error) { 98 errMsg := errors.Errorf("count must be greater than zero, got %q", s) 99 i, err := strconv.ParseInt(s, 10, 64) 100 if err != nil { 101 return 0, errMsg 102 } 103 if i > 0 { 104 return i, nil 105 } 106 return 0, errMsg 107 }