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