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 }