github.com/uchennaokeke444/nomad@v0.11.8/nomad/structs/devices.go (about)

     1  package structs
     2  
     3  // DeviceAccounter is used to account for device usage on a node. It can detect
     4  // when a node is oversubscribed and can be used for deciding what devices are
     5  // free
     6  type DeviceAccounter struct {
     7  	// Devices maps a device group to its device accounter instance
     8  	Devices map[DeviceIdTuple]*DeviceAccounterInstance
     9  }
    10  
    11  // DeviceAccounterInstance wraps a device and adds tracking to the instances of
    12  // the device to determine if they are free or not.
    13  type DeviceAccounterInstance struct {
    14  	// Device is the device being wrapped
    15  	Device *NodeDeviceResource
    16  
    17  	// Instances is a mapping of the device IDs to their usage.
    18  	// Only a value of 0 indicates that the instance is unused.
    19  	Instances map[string]int
    20  }
    21  
    22  // NewDeviceAccounter returns a new device accounter. The node is used to
    23  // populate the set of available devices based on what healthy device instances
    24  // exist on the node.
    25  func NewDeviceAccounter(n *Node) *DeviceAccounter {
    26  	numDevices := 0
    27  	var devices []*NodeDeviceResource
    28  
    29  	// COMPAT(0.11): Remove in 0.11
    30  	if n.NodeResources != nil {
    31  		numDevices = len(n.NodeResources.Devices)
    32  		devices = n.NodeResources.Devices
    33  	}
    34  
    35  	d := &DeviceAccounter{
    36  		Devices: make(map[DeviceIdTuple]*DeviceAccounterInstance, numDevices),
    37  	}
    38  
    39  	for _, dev := range devices {
    40  		id := *dev.ID()
    41  		d.Devices[id] = &DeviceAccounterInstance{
    42  			Device:    dev,
    43  			Instances: make(map[string]int, len(dev.Instances)),
    44  		}
    45  		for _, instance := range dev.Instances {
    46  			// Skip unhealthy devices as they aren't allocatable
    47  			if !instance.Healthy {
    48  				continue
    49  			}
    50  
    51  			d.Devices[id].Instances[instance.ID] = 0
    52  		}
    53  	}
    54  
    55  	return d
    56  }
    57  
    58  // AddAllocs takes a set of allocations and internally marks which devices are
    59  // used. If a device is used more than once by the set of passed allocations,
    60  // the collision will be returned as true.
    61  func (d *DeviceAccounter) AddAllocs(allocs []*Allocation) (collision bool) {
    62  	for _, a := range allocs {
    63  		// Filter any terminal allocation
    64  		if a.TerminalStatus() {
    65  			continue
    66  		}
    67  
    68  		// COMPAT(0.11): Remove in 0.11
    69  		// If the alloc doesn't have the new style resources, it can't have
    70  		// devices
    71  		if a.AllocatedResources == nil {
    72  			continue
    73  		}
    74  
    75  		// Go through each task  resource
    76  		for _, tr := range a.AllocatedResources.Tasks {
    77  
    78  			// Go through each assigned device group
    79  			for _, device := range tr.Devices {
    80  				devID := device.ID()
    81  
    82  				// Go through each assigned device
    83  				for _, instanceID := range device.DeviceIDs {
    84  
    85  					// Mark that we are using the device. It may not be in the
    86  					// map if the device is no longer being fingerprinted, is
    87  					// unhealthy, etc.
    88  					if devInst, ok := d.Devices[*devID]; ok {
    89  						if i, ok := devInst.Instances[instanceID]; ok {
    90  							// Mark that the device is in use
    91  							devInst.Instances[instanceID]++
    92  
    93  							if i != 0 {
    94  								collision = true
    95  							}
    96  						}
    97  					}
    98  				}
    99  			}
   100  		}
   101  	}
   102  
   103  	return
   104  }
   105  
   106  // AddReserved marks the device instances in the passed device reservation as
   107  // used and returns if there is a collision.
   108  func (d *DeviceAccounter) AddReserved(res *AllocatedDeviceResource) (collision bool) {
   109  	// Lookup the device.
   110  	devInst, ok := d.Devices[*res.ID()]
   111  	if !ok {
   112  		return false
   113  	}
   114  
   115  	// For each reserved instance, mark it as used
   116  	for _, id := range res.DeviceIDs {
   117  		cur, ok := devInst.Instances[id]
   118  		if !ok {
   119  			continue
   120  		}
   121  
   122  		// It has already been used, so mark that there is a collision
   123  		if cur != 0 {
   124  			collision = true
   125  		}
   126  
   127  		devInst.Instances[id]++
   128  	}
   129  
   130  	return
   131  }
   132  
   133  // FreeCount returns the number of free device instances
   134  func (i *DeviceAccounterInstance) FreeCount() int {
   135  	count := 0
   136  	for _, c := range i.Instances {
   137  		if c == 0 {
   138  			count++
   139  		}
   140  	}
   141  	return count
   142  }