github.com/hashicorp/nomad/api@v0.0.0-20240306165712-3193ac204f65/resources.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package api
     5  
     6  import (
     7  	"strconv"
     8  )
     9  
    10  // Resources encapsulates the required resources of
    11  // a given task or task group.
    12  type Resources struct {
    13  	CPU         *int               `hcl:"cpu,optional"`
    14  	Cores       *int               `hcl:"cores,optional"`
    15  	MemoryMB    *int               `mapstructure:"memory" hcl:"memory,optional"`
    16  	MemoryMaxMB *int               `mapstructure:"memory_max" hcl:"memory_max,optional"`
    17  	DiskMB      *int               `mapstructure:"disk" hcl:"disk,optional"`
    18  	Networks    []*NetworkResource `hcl:"network,block"`
    19  	Devices     []*RequestedDevice `hcl:"device,block"`
    20  	NUMA        *NUMAResource      `hcl:"numa,block"`
    21  
    22  	// COMPAT(0.10)
    23  	// XXX Deprecated. Please do not use. The field will be removed in Nomad
    24  	// 0.10 and is only being kept to allow any references to be removed before
    25  	// then.
    26  	IOPS *int `hcl:"iops,optional"`
    27  }
    28  
    29  // Canonicalize will supply missing values in the cases
    30  // where they are not provided.
    31  func (r *Resources) Canonicalize() {
    32  	defaultResources := DefaultResources()
    33  	if r.Cores == nil {
    34  		r.Cores = defaultResources.Cores
    35  
    36  		// only set cpu to the default value if it and cores is not defined
    37  		if r.CPU == nil {
    38  			r.CPU = defaultResources.CPU
    39  		}
    40  	}
    41  
    42  	// CPU will be set to the default if cores is nil above.
    43  	// If cpu is nil here then cores has been set and cpu should be 0
    44  	if r.CPU == nil {
    45  		r.CPU = pointerOf(0)
    46  	}
    47  
    48  	if r.MemoryMB == nil {
    49  		r.MemoryMB = defaultResources.MemoryMB
    50  	}
    51  	for _, d := range r.Devices {
    52  		d.Canonicalize()
    53  	}
    54  
    55  	r.NUMA.Canonicalize()
    56  }
    57  
    58  // DefaultResources is a small resources object that contains the
    59  // default resources requests that we will provide to an object.
    60  // ---  THIS FUNCTION IS REPLICATED IN nomad/structs/structs.go
    61  // and should be kept in sync.
    62  func DefaultResources() *Resources {
    63  	return &Resources{
    64  		CPU:      pointerOf(100),
    65  		Cores:    pointerOf(0),
    66  		MemoryMB: pointerOf(300),
    67  	}
    68  }
    69  
    70  // MinResources is a small resources object that contains the
    71  // absolute minimum resources that we will provide to an object.
    72  // This should not be confused with the defaults which are
    73  // provided in DefaultResources() ---  THIS LOGIC IS REPLICATED
    74  // IN nomad/structs/structs.go and should be kept in sync.
    75  func MinResources() *Resources {
    76  	return &Resources{
    77  		CPU:      pointerOf(1),
    78  		Cores:    pointerOf(0),
    79  		MemoryMB: pointerOf(10),
    80  	}
    81  }
    82  
    83  // Merge merges this resource with another resource.
    84  func (r *Resources) Merge(other *Resources) {
    85  	if other == nil {
    86  		return
    87  	}
    88  	if other.CPU != nil {
    89  		r.CPU = other.CPU
    90  	}
    91  	if other.MemoryMB != nil {
    92  		r.MemoryMB = other.MemoryMB
    93  	}
    94  	if other.DiskMB != nil {
    95  		r.DiskMB = other.DiskMB
    96  	}
    97  	if len(other.Networks) != 0 {
    98  		r.Networks = other.Networks
    99  	}
   100  	if len(other.Devices) != 0 {
   101  		r.Devices = other.Devices
   102  	}
   103  	if other.NUMA != nil {
   104  		r.NUMA = other.NUMA.Copy()
   105  	}
   106  }
   107  
   108  // NUMAResource contains the NUMA affinity request for scheduling purposes.
   109  //
   110  // Applies only to Nomad Enterprise.
   111  type NUMAResource struct {
   112  	// Affinity must be one of "none", "prefer", "require".
   113  	Affinity string `hcl:"affinity,optional"`
   114  }
   115  
   116  func (n *NUMAResource) Copy() *NUMAResource {
   117  	if n == nil {
   118  		return nil
   119  	}
   120  	return &NUMAResource{
   121  		Affinity: n.Affinity,
   122  	}
   123  }
   124  
   125  func (n *NUMAResource) Canonicalize() {
   126  	if n == nil {
   127  		return
   128  	}
   129  	if n.Affinity == "" {
   130  		n.Affinity = "none"
   131  	}
   132  }
   133  
   134  type Port struct {
   135  	Label       string `hcl:",label"`
   136  	Value       int    `hcl:"static,optional"`
   137  	To          int    `hcl:"to,optional"`
   138  	HostNetwork string `hcl:"host_network,optional"`
   139  }
   140  
   141  type DNSConfig struct {
   142  	Servers  []string `mapstructure:"servers" hcl:"servers,optional"`
   143  	Searches []string `mapstructure:"searches" hcl:"searches,optional"`
   144  	Options  []string `mapstructure:"options" hcl:"options,optional"`
   145  }
   146  
   147  // NetworkResource is used to describe required network
   148  // resources of a given task.
   149  type NetworkResource struct {
   150  	Mode          string     `hcl:"mode,optional"`
   151  	Device        string     `hcl:"device,optional"`
   152  	CIDR          string     `hcl:"cidr,optional"`
   153  	IP            string     `hcl:"ip,optional"`
   154  	DNS           *DNSConfig `hcl:"dns,block"`
   155  	ReservedPorts []Port     `hcl:"reserved_ports,block"`
   156  	DynamicPorts  []Port     `hcl:"port,block"`
   157  	Hostname      string     `hcl:"hostname,optional"`
   158  
   159  	// COMPAT(0.13)
   160  	// XXX Deprecated. Please do not use. The field will be removed in Nomad
   161  	// 0.13 and is only being kept to allow any references to be removed before
   162  	// then.
   163  	MBits *int `hcl:"mbits,optional"`
   164  }
   165  
   166  // COMPAT(0.13)
   167  // XXX Deprecated. Please do not use. The method will be removed in Nomad
   168  // 0.13 and is only being kept to allow any references to be removed before
   169  // then.
   170  func (n *NetworkResource) Megabits() int {
   171  	if n == nil || n.MBits == nil {
   172  		return 0
   173  	}
   174  	return *n.MBits
   175  }
   176  
   177  func (n *NetworkResource) Canonicalize() {
   178  	// COMPAT(0.13)
   179  	// Noop to maintain backwards compatibility
   180  }
   181  
   182  func (n *NetworkResource) HasPorts() bool {
   183  	if n == nil {
   184  		return false
   185  	}
   186  
   187  	return len(n.ReservedPorts)+len(n.DynamicPorts) > 0
   188  }
   189  
   190  // NodeDeviceResource captures a set of devices sharing a common
   191  // vendor/type/device_name tuple.
   192  type NodeDeviceResource struct {
   193  
   194  	// Vendor specifies the vendor of device
   195  	Vendor string
   196  
   197  	// Type specifies the type of the device
   198  	Type string
   199  
   200  	// Name specifies the specific model of the device
   201  	Name string
   202  
   203  	// Instances are list of the devices matching the vendor/type/name
   204  	Instances []*NodeDevice
   205  
   206  	Attributes map[string]*Attribute
   207  }
   208  
   209  func (r NodeDeviceResource) ID() string {
   210  	return r.Vendor + "/" + r.Type + "/" + r.Name
   211  }
   212  
   213  // NodeDevice is an instance of a particular device.
   214  type NodeDevice struct {
   215  	// ID is the ID of the device.
   216  	ID string
   217  
   218  	// Healthy captures whether the device is healthy.
   219  	Healthy bool
   220  
   221  	// HealthDescription is used to provide a human readable description of why
   222  	// the device may be unhealthy.
   223  	HealthDescription string
   224  
   225  	// Locality stores HW locality information for the node to optionally be
   226  	// used when making placement decisions.
   227  	Locality *NodeDeviceLocality
   228  }
   229  
   230  // Attribute is used to describe the value of an attribute, optionally
   231  // specifying units
   232  type Attribute struct {
   233  	// Float is the float value for the attribute
   234  	FloatVal *float64 `json:"Float,omitempty"`
   235  
   236  	// Int is the int value for the attribute
   237  	IntVal *int64 `json:"Int,omitempty"`
   238  
   239  	// String is the string value for the attribute
   240  	StringVal *string `json:"String,omitempty"`
   241  
   242  	// Bool is the bool value for the attribute
   243  	BoolVal *bool `json:"Bool,omitempty"`
   244  
   245  	// Unit is the optional unit for the set int or float value
   246  	Unit string
   247  }
   248  
   249  func (a Attribute) String() string {
   250  	switch {
   251  	case a.FloatVal != nil:
   252  		str := formatFloat(*a.FloatVal, 3)
   253  		if a.Unit != "" {
   254  			str += " " + a.Unit
   255  		}
   256  		return str
   257  	case a.IntVal != nil:
   258  		str := strconv.FormatInt(*a.IntVal, 10)
   259  		if a.Unit != "" {
   260  			str += " " + a.Unit
   261  		}
   262  		return str
   263  	case a.StringVal != nil:
   264  		return *a.StringVal
   265  	case a.BoolVal != nil:
   266  		return strconv.FormatBool(*a.BoolVal)
   267  	default:
   268  		return "<unknown>"
   269  	}
   270  }
   271  
   272  // NodeDeviceLocality stores information about the devices hardware locality on
   273  // the node.
   274  type NodeDeviceLocality struct {
   275  	// PciBusID is the PCI Bus ID for the device.
   276  	PciBusID string
   277  }
   278  
   279  // RequestedDevice is used to request a device for a task.
   280  type RequestedDevice struct {
   281  	// Name is the request name. The possible values are as follows:
   282  	// * <type>: A single value only specifies the type of request.
   283  	// * <vendor>/<type>: A single slash delimiter assumes the vendor and type of device is specified.
   284  	// * <vendor>/<type>/<name>: Two slash delimiters assume vendor, type and specific model are specified.
   285  	//
   286  	// Examples are as follows:
   287  	// * "gpu"
   288  	// * "nvidia/gpu"
   289  	// * "nvidia/gpu/GTX2080Ti"
   290  	Name string `hcl:",label"`
   291  
   292  	// Count is the number of requested devices
   293  	Count *uint64 `hcl:"count,optional"`
   294  
   295  	// Constraints are a set of constraints to apply when selecting the device
   296  	// to use.
   297  	Constraints []*Constraint `hcl:"constraint,block"`
   298  
   299  	// Affinities are a set of affinites to apply when selecting the device
   300  	// to use.
   301  	Affinities []*Affinity `hcl:"affinity,block"`
   302  }
   303  
   304  func (d *RequestedDevice) Canonicalize() {
   305  	if d.Count == nil {
   306  		d.Count = pointerOf(uint64(1))
   307  	}
   308  
   309  	for _, a := range d.Affinities {
   310  		a.Canonicalize()
   311  	}
   312  }