def testTwoResourcesSameType(): """Test making a match with two resources of the same types.""" claim = ResourceClaim.create(( ResourceSpec.create('ref0', 'typeA', ()), ResourceSpec.create('ref1', 'typeA', ()), )) resA = FakeResource('typeA', ()) resB = FakeResource('typeA', ()) resources = (resA, resB) resMap = {res.getId(): res for res in resources} match = pickResources(claim, resMap) assert match is not None
def testTwoResourcesUniqueCaps(): """Test making a match with two resources with unique capabilities.""" claim = ResourceClaim.create(( ResourceSpec.create('ref0', 'typeA', ('capY', )), ResourceSpec.create('ref1', 'typeA', ('capX', )), )) resX = FakeResource('typeA', ('capX', )) resY = FakeResource('typeA', ('capY', )) resources = (resX, resY) expected = {'ref0': resY, 'ref1': resX} check(claim, resources, expected)
def testTwoResourcesDiffTypes(): """Test making a match with two resources of different types.""" claim = ResourceClaim.create(( ResourceSpec.create('ref0', 'typeA', ()), ResourceSpec.create('ref1', 'typeB', ()), )) resA = FakeResource('typeA', ()) resB = FakeResource('typeB', ()) resources = (resA, resB) expected = {'ref0': resA, 'ref1': resB} check(claim, resources, expected)
def testAllCombinations(): """Test all possible claims on all possible resources, at small size. It is feasible to do this for low numbers of claims, capabilities and resources, but it becomes expotentially slower at larger sizes. """ numCaps = 3 numSpecs = 3 numResources = 3 caps = tuple('cap%d' % i for i in range(numCaps)) refs = tuple('ref%d' % i for i in range(numSpecs)) claims = tuple( ResourceClaim.create((ResourceSpec.create(refs[i], 'typeA', ( caps[j] for j in range(numCaps) if (claimBits >> (i * numCaps + j)) & 1)) for i in range(numSpecs))) for claimBits in range(1 << (numSpecs * numCaps))) resourceMaps = [] for resBits in range(1 << (numResources * numCaps)): resources = [ FakeResource('typeA', (caps[j] for j in range(numCaps) if (resBits >> (i * numCaps + j)) & 1)) for i in range(numResources) ] resourceMaps.append({res.getId(): res for res in resources}) for claim in claims: for resMap in resourceMaps: checkSolve(claim, resMap)
def _addResource(self, attributes: Dict[str, str]) -> ResourceSpec: if attributes['type'] == taskRunnerResourceTypeName: # COMPAT 2.16: Force reference name for TR. attributes['ref'] = taskRunnerResourceRefName spec = ResourceSpec(attributes) self.addResourceSpec(spec) return spec
def testOneResourceImpossible(): """Test making a impossible match with one resource.""" claim = ResourceClaim.create((ResourceSpec.create('ref0', 'typeA', ()), )) res = FakeResource('typeB', ()) resources = (res, ) check(claim, resources, None)
def testOverlappingCaps(): """Test making a match with overlapping capabilities.""" claim = ResourceClaim.create(( ResourceSpec.create('ref2', 'typeA', ('capY', )), ResourceSpec.create('ref0', 'typeA', ('capW', )), ResourceSpec.create('ref1', 'typeA', ('capX', )), ResourceSpec.create('ref3', 'typeA', ('capZ', )), )) resW = FakeResource('typeA', ('capW', )) resX = FakeResource('typeA', ('capW', 'capX')) resY = FakeResource('typeA', ('capW', 'capX', 'capY')) resZ = FakeResource('typeA', ('capW', 'capX', 'capY', 'capZ')) resources = (resW, resX, resY, resZ) expected = {'ref0': resW, 'ref1': resX, 'ref2': resY, 'ref3': resZ} check(claim, resources, expected)
def testOneResourcePossible(): """Test making a possible match with one resource.""" claim = ResourceClaim.create((ResourceSpec.create('ref0', 'typeA', ()), )) res = FakeResource('typeA', ()) resources = (res, ) expected = {'ref0': res} check(claim, resources, expected)
def testMissingType(): """Test a claim that can be honored for one type but not for another. """ claim = ResourceClaim.create((ResourceSpec.create('ref%d' % i, 'typeA', ('cap%d' % (i % 4), )) for i in range(10))) resources = [] for i in range(24): resources.append(FakeResource('typeA', ('cap%d' % (i % 4), ))) # Sanity check for our test data. resMap = {res.getId(): res for res in resources} match = pickResources(claim, resMap) assert match is not None claim = claim.merge( ResourceClaim.create((ResourceSpec.create('refB', 'typeB', ('cap0', )), ))) check(claim, resources, None)
def reserveResources(self, claim: ResourceClaim, reservedBy: str, whyNot: Optional[List[ReasonForWaiting]] ) -> Optional[Dict[str, Resource]]: resourceDB = self.__jobFactory.resourceDB if self.__resources: bound = {} toReserve = [] keepPerJob = [] for spec in claim: ref = spec.reference resType = spec.typeName info = self.__resources.get(ref) if info is not None: _, caps, resId = info specCaps = spec.capabilities assert caps >= specCaps if resId is not None: bound[ref] = resId else: keepPerJob.append(ref) if caps != specCaps: spec = ResourceSpec.create(ref, resType, caps) toReserve.append(spec) else: toReserve.append(spec) reservedPerJob = {} for ref, resId in bound.items(): resource = resourceDB.get(resId) if resource is not None: assert isinstance(resource, Resource), resId reservedPerJob[ref] = resource else: if whyNot is not None: # TODO: report multiple resources at the same time whyNot.append(ResourceMissingReason(resId)) return None reservedBy = 'J-' + self.getId() reserved = _reserveResources(resourceDB, ResourceClaim.create(toReserve), reservedBy, whyNot) if reserved is not None: for ref in keepPerJob: self.__resources[ref][2] = reserved[ref].getId() reserved.update(reservedPerJob) return reserved else: return _reserveResources(resourceDB, claim, reservedBy, whyNot)
def testUnequalValue(): """Test making a match with two resources with differing value. A resource without capabilities should be picked before resources with capabilities. """ claim = ResourceClaim.create((ResourceSpec.create('ref0', 'typeA', ()), )) resources = [] for _ in range(50): resources.append(FakeResource('typeA', ('capX', ))) resNoCaps = FakeResource('typeA', ()) resources[29] = resNoCaps expected = {'ref0': resNoCaps} check(claim, resources, expected)
def simulateRandom(maxCaps, maxSpecs, maxResources, runsPerConfig, numConfigs, verifyFunc, seed=None): if seed is None: seed = int(time.time()) print('Random seed: %d' % seed) rnd = Random(seed) # Pre-create capability and reference names. caps = tuple('cap%d' % i for i in range(maxCaps)) refs = tuple('ref%d' % i for i in range(maxSpecs)) for _ in range(numConfigs): numCaps = rnd.randint(maxCaps // 2, maxCaps) numResources = rnd.randint(maxResources // 2, maxResources) # Create resources. resMap = {} for _ in range(numResources): capBits = rnd.randrange(1 << numCaps) resource = FakeResource('typeA', applyBitmask(caps, capBits)) resMap[resource.getId()] = resource for _ in range(runsPerConfig): numSpecs = rnd.randint(maxSpecs // 2, maxSpecs) # Create specs. specs = [] for i in range(numSpecs): # Create a slight bias towards having fewer capabilities # required, so we test finding the lowest cost assignment # among multiple possible ones. capBits = rnd.randrange(1 << numCaps) capBits &= rnd.randrange(1 << numCaps) spec = ResourceSpec.create(refs[i], 'typeA', applyBitmask(caps, capBits)) specs.append(spec) claim = ResourceClaim.create(specs) verifyFunc(claim, resMap)
def testNoResources(): """Test making a match without resources.""" claim = ResourceClaim.create((ResourceSpec.create('ref0', 'typeA', ()), )) check(claim, (), None)
def addTaskRunnerSpec(self, capabilities: Iterable[str] = ()) -> None: self.addResourceSpec( ResourceSpec.create(taskRunnerResourceRefName, taskRunnerResourceTypeName, capabilities))
class CapabilitiesPanel(Panel): label = 'Capabilities' content = xhtml.br.join( (textInput(name='capabilities', size=80, style='width:100%'), 'Multiple capabilities should be separated by spaces.', 'Task definitions use capabilities to put additional ' 'requirements on the resources they need.')) class CommentPanel(Panel): label = 'Description' content = textInput(name='description', size=80, style='width:100%') initialResourceClaim = ResourceClaim.create( (ResourceSpec.create(taskRunnerResourceRefName, taskRunnerResourceTypeName, ()), )) class ResourceRequirementsArgsMixin: '''Adds resource requirement editing arguments to a page.''' ref = ListArg() type = ListArg() caps = ListArg() class _ResourceRequirementsArgs(ResourceRequirementsArgsMixin, PageArgs): """Helper class for type checking.""" def addResourceRequirementsToElement( element: TaskDefBase, args: ResourceRequirementsArgsMixin) -> None:
def addResourceRequirementsToElement( element: TaskDefBase, args: ResourceRequirementsArgsMixin) -> None: args = cast(_ResourceRequirementsArgs, args) for ref, resType, caps in zip(args.ref, args.type, args.caps): element.addResourceSpec(ResourceSpec.create(ref, resType, caps.split()))
def _addResources(record, resources): if resources is not None: for args in resources: spec = ResourceSpec.create(*args) record.addResourceSpec(spec)