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  }