Created
October 20, 2012 12:06
-
-
Save jbowes/3923129 to your computer and use it in GitHub Desktop.
Candlepin default rules auto-converted to CoffeeScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# * Default Candlepin rule set. | |
# | |
entitlement_name_space = -> | |
Entitlement | |
consumer_delete_name_space = -> | |
ConsumerDelete | |
pool_name_space = -> | |
Pool | |
criteria_name_space = -> | |
PoolCriteria | |
export_name_space = -> | |
Export | |
compliance_name_space = -> | |
Compliance | |
unbind_name_space = -> | |
Unbind | |
# Utility functions | |
contains = (a, obj) -> | |
i = 0 | |
while i < a.length | |
result = a[i] is obj | |
return true if result | |
i++ | |
false | |
containsAll = (a, b) -> | |
i = 0 | |
while i < b.length | |
return false unless contains(a, b[i]) | |
i++ | |
true | |
getRelevantProvidedProducts = (pool, products) -> | |
provided = [] | |
i = 0 | |
while i < products.length | |
product = products[i] | |
provided.push product if pool.provides(product.getId()) | |
i++ | |
provided | |
providesSameProducts = (products1, products2) -> | |
containsAll(products1, products2) and containsAll(products2, products1) | |
arrayToString = (a) -> | |
msg = "[" | |
for q of a | |
msg += q.getId() + " " | |
msg += "]" | |
msg | |
# XXX i don't know what this is really called | |
recursiveCombination = (a, n) -> | |
return [] if a.length is 0 | |
res = [] | |
for x of recursiveCombination(a.slice(1), n) | |
res.push x if x.length <= n | |
if x.length + 1 <= n | |
z = x.slice(0) | |
z.push a[0] | |
res.push z | |
res.push [a[0]] | |
res | |
# Check if the provided list of pools contains any duplicated products | |
# We don't need to worry about checking multi-entitle allowed products, | |
# as you can use as many of those as you want. | |
hasNoProductOverlap = (combination) -> | |
seen_product_ids = [] | |
for pool_class of combination | |
pool = pool_class[0] | |
products = pool.products | |
i = 0 | |
while i < products.length | |
product = products[i] | |
unless contains(seen_product_ids, product.id) | |
seen_product_ids.push product.id | |
else return false unless product.getAttribute("multi-entitlement") is "yes" | |
i++ | |
true | |
#Check to see if a pool provides any products that are already compliant | |
hasNoInstalledOverlap = (pool, compliance) -> | |
products = pool.products | |
i = 0 | |
while i < products.length | |
product = products[i] | |
log.debug "installed overlap: " + product.id | |
return false if product.getAttribute("multi-entitlement") isnt "yes" and compliance.getCompliantProducts().containsKey(product.id) | |
i++ | |
true | |
architectureMatches = (product, consumer) -> | |
# Non-system consumers without an architecture fact can pass this rule | |
# regardless what arch the product requires. | |
return true if not consumer.hasFact("uname.machine") and not consumer.getType().equals("system") | |
supportedArches = [] | |
archString = product.getAttribute("arch") | |
if archString? | |
supportedArches = archString.toUpperCase().split(prodAttrSeparator) | |
supportedArches = new java.util.HashSet(java.util.Arrays.asList(supportedArches)) | |
# If X86 is supported, add all variants to this list: | |
if supportedArches.contains("X86") | |
supportedArches.add "I386" | |
supportedArches.add "I586" | |
supportedArches.add "I686" | |
return false if not supportedArches.contains("ALL") and (not consumer.hasFact("uname.machine") or not supportedArches.contains(consumer.getFact("uname.machine").toUpperCase())) | |
true | |
# get the number of sockets that each entitlement from a pool covers. | |
# if sockets is set to 0 or is not set, it is considered to be unlimited. | |
get_pool_sockets = (pool) -> | |
if pool.getProductAttribute("sockets") | |
# this can be either a ReadOnlyPool or a Pool, so deal with attributes as appropriate. | |
attribute = pool.getProductAttribute("sockets") | |
if "getValue" of attribute | |
sockets = attribute.getValue() | |
else | |
sockets = attribute | |
if sockets is 0 | |
Infinity | |
else | |
parseInt sockets | |
else | |
Infinity | |
# assumptions: number of pools consumed from is not considered, so we might not be taking from the smallest amount. | |
# we only stack within the same pool_class. if you have stacks that provide different sets of products, | |
# you won't be able to stack from them | |
# | |
# iterate over a pool class, and determine the quantity of entitlements needed | |
# to satisfy any stacking on the pools in the class, for the given consumer | |
# | |
# If we find a pool that has no stacking requirements, just use that one | |
# (as we'll only need a quantity of one) | |
# otherwise, group the pools by stack id, then select the pools we wish to use | |
# based on which grouping will come closest to fully stacking. | |
# | |
# | |
findStackingPools = (pool_class, consumer, compliance) -> | |
consumer_sockets = 1 | |
consumer_sockets = consumer.getFact(SOCKET_FACT) if consumer.hasFact(SOCKET_FACT) | |
stackToEntitledSockets = {} | |
stackToPoolMap = {} | |
notStackable = [] | |
# data for existing partial stacks | |
# we need a map of product id to stack id | |
# (to see if there is an existing stack for a product | |
# we can build upon, or a conflicting stack) | |
productIdToStackId = {} | |
partialStacks = compliance.getPartialStacks() | |
# going to assume one stack per product on the system | |
for stack_id of compliance.getPartialStacks().keySet().toArray() | |
covered_sockets = 0 | |
for entitlement of partialStacks.get(stack_id).toArray() | |
covered_sockets += entitlement.getQuantity() * get_pool_sockets(entitlement.getPool()) | |
productIdToStackId[entitlement.getPool().getProductId()] = stack_id | |
for product of entitlement.getPool().getProvidedProducts().toArray() | |
productIdToStackId[product.getProductId()] = stack_id | |
# we can start entitling from the partial stack | |
stackToEntitledSockets[stack_id] = covered_sockets | |
for pool of pool_class | |
quantity = 0 | |
# ignore any pools that clash with installed compliant products | |
unless hasNoInstalledOverlap(pool, compliance) | |
log.debug "installed overlap found, skipping: " + pool.getId() | |
continue | |
if pool.getProductAttribute("multi-entitlement") and pool.getProductAttribute("stacking_id") | |
# make sure there isn't a conflicting pool already on the system | |
installed_stack_id = undefined | |
seen_stack_id = false | |
conflicting_stacks = false | |
for product of pool.getProducts() | |
if productIdToStackId.hasOwnProperty(product.id) | |
new_installed_stack_id = productIdToStackId[product.id] | |
unless new_installed_stack_id is installed_stack_id | |
# the first id will be different | |
unless seen_stack_id | |
installed_stack_id = new_installed_stack_id | |
seen_stack_id = true | |
else | |
conflicting_stacks = true | |
# this pool provides 2 or more products that already have entitlements on the system, | |
# with multiple stack ids | |
continue if conflicting_stacks | |
stack_id = pool.getProductAttribute("stacking_id") | |
# check if this pool matches the stack id of an existing partial stack | |
continue if seen_stack_id and installed_stack_id isnt stack_id | |
unless stackToPoolMap.hasOwnProperty(stack_id) | |
stackToPoolMap[stack_id] = new java.util.HashMap() | |
# we might already have the partial stack from compliance | |
stackToEntitledSockets[stack_id] = 0 unless stackToEntitledSockets.hasOwnProperty(stack_id) | |
# if this stack is already done, no need to add more to it. | |
continue if stackToEntitledSockets[stack_id] >= consumer_sockets | |
product_sockets = 0 | |
pool_sockets = get_pool_sockets(pool) | |
while stackToEntitledSockets[stack_id] + product_sockets < consumer_sockets | |
product_sockets += pool_sockets | |
quantity++ | |
# don't take more entitlements than are available! | |
quantity = pool.getMaxMembers() - pool.getCurrentMembers() if quantity > pool.getMaxMembers() - pool.getCurrentMembers() | |
stackToEntitledSockets[stack_id] += quantity * pool_sockets | |
stackToPoolMap[stack_id].put pool, quantity | |
else | |
# not stackable, just take one. | |
notStackable.push pool | |
found_pool = false | |
not_stacked_sockets = 0 | |
not_stacked_pool_map = new java.util.HashMap() | |
# We have a not stackable pool. | |
if notStackable.length > 0 | |
for pool of notStackable | |
covered_sockets = get_pool_sockets(pool) | |
if covered_sockets > not_stacked_sockets | |
found_pool = true | |
not_stacked_pool_map = new java.util.HashMap() | |
not_stacked_pool_map.put pool, 1 | |
not_stacked_sockets = covered_sockets | |
# if an unstacked pool can cover all our products, take that. | |
return not_stacked_pool_map if not_stacked_sockets >= consumer_sockets | |
# loop over our potential stacks, and just take the first stack that covers all sockets. | |
# else take the stack that covers the most sockets. | |
best_sockets = 0 | |
best_stack = undefined | |
for stack_id of stackToPoolMap | |
found_pool = true | |
if stackToEntitledSockets[stack_id] >= consumer_sockets | |
return stackToPoolMap[stack_id] | |
else if stackToEntitledSockets[stack_id] > best_sockets | |
best_stack = stack_id | |
best_sockets = stackToEntitledSockets[stack_id] | |
# All possible pools may have overlapped with existing products | |
# so return nothing! | |
return new java.util.HashMap() unless found_pool | |
# we can't fully cover the product. either select the best non stacker, or the best stacker. | |
if not_stacked_sockets >= best_sockets | |
not_stacked_pool_map | |
else | |
stackToPoolMap[best_stack] | |
# given 2 pools, select the best one. It is a assumed that the pools offer the | |
# same selection of products. | |
# returns true if pool1 is a better choice than pool2, else false | |
comparePools = (pool1, pool2) -> | |
# Prefer a virt_only pool over a regular pool, else fall through to the next rules. | |
# At this point virt_only pools will have already been filtered out by the pre rules | |
# for non virt machines. | |
if pool1.getAttribute("virt_only") is "true" and pool2.getAttribute("virt_only") isnt "true" | |
return true | |
else return false if pool2.getAttribute("virt_only") is "true" and pool1.getAttribute("virt_only") isnt "true" | |
# If both virt_only, prefer one with host_requires, otherwise keep looking | |
# for a reason to pick one or the other. We know that the host must match | |
# as pools are filtered before even being passed to select best pools. | |
if pool1.getAttribute("virt_only") is "true" and pool2.getAttribute("virt_only") is "true" | |
return true if pool1.getAttribute("requires_host")? and not pool2.getAttribute("requires_host")? | |
return false if pool2.getAttribute("requires_host")? and not pool1.getAttribute("requires_host")? | |
# If neither condition is true, no preference... | |
# If two pools are still considered equal, select the pool that expires first | |
true if pool2.getEndDate().after(pool1.getEndDate()) | |
isLevelExempt = (level, exemptList) -> | |
for exemptLevel of exemptList.toArray() | |
return true if exemptLevel.equalsIgnoreCase(level) | |
false | |
# defines mapping of product attributes to functions | |
# the format is: <function name>:<order number>:<attr1>:...:<attrn>, comma-separated ex.: | |
# func1:1:attr1:attr2:attr3, func2:2:attr3:attr4 | |
# It shouldn't be possible to get a host restricted pool in hosted, but just in | |
# case, make sure it won't be enforced if we do. | |
# Default to using the same product from the pool. | |
# Check if the sub-pool should be for a different product: | |
# Create a sub-pool for this user | |
# if we are exporting we need to deal with the bonus pools | |
# if the bonus pool is not unlimited, then the bonus pool quantity | |
# needs to be adjusted based on the virt limit | |
# if the bonus pool is unlimited, then the quantity needs to go to 0 | |
# when the physical pool is exhausted completely by export. | |
# A quantity of 0 will block future binds, whereas -1 does not. | |
#getting all pools matching the sub id. Filtering out the 'parent'. | |
#usually, we assume socket count to be 1 if it is undef. However, we need to know if it's | |
#undef here in order to know to skip the socket comparison (per acarter/jomara) | |
# If the product has no required consumer type, assume it is restricted to "system". | |
# "hypervisor"/"uebercert" type are essentially the same as "system". | |
# Manifest consumers should not be able to find to any derived pools. Because | |
# they are exempt from all pre-rules, to keep these derived pools out of the list | |
# they can bind to we must use pre_global, which is used for manifest consumers. | |
# FIXME | |
# for auto sub stacking, we need to be able to pull across multiple | |
# pools eventually, so this would need to go away in that case | |
# Greedy selection for now, in order | |
# XXX need to watch out for multientitle products - how so? | |
# An array of arrays of pools. each array is a grouping of pools that provide the | |
# same subset of products which are applicable to the requested products. | |
# further, each array is sorted, from best to worst. (pool fitness is determined | |
# arbitrarily by rules herein. | |
# "pools" is a list of all the owner's pools which are compatible for the system: | |
# Builds out the pools_by_class by iterating each pool, checking which products it provides (that | |
# are relevant to this request), then filtering out other pools which provide the *exact* same products | |
# by selecting the preferred pool based on other criteria. | |
# If the SLA of the consumer does not match that of the pool | |
# we do not consider the pool unless the level is exempt | |
# XXX wasteful, should be a hash or something. | |
# Tracks if we found another pool previously looked at which had the exact same provided products: | |
# Check current pool against previous best to see if it's better: | |
# figure out where to insert this pool in its sorted class | |
# now insert the pool into the middle of the array | |
# If we did not find a duplicate pool providing the same products, | |
# Select the best pool combo. We prefer: | |
# -The combo that provides the most products | |
# -The combo that uses the fewest entitlements | |
# number of provided products is less than our best selection. keep our current selection. | |
# we do it after the unique provided.length check because that value is the best we can do | |
# create 'best' stacking combo here | |
# use that best combo for the following comparison | |
# XXX we'll have to do something here to ensure no product overlap after selecting the actual pool/pools from the combo | |
# now verify that after selecting our actual pools from the pool combo, | |
# we still have a better choice here | |
# We may not have selected pools for all products; that's ok. | |
# | |
# * Return Hibernate criteria we can apply to the pool query when listing pools that | |
# * are relevant for a consumer. | |
# | |
# FIXME: alot of this could be cleaned up with some | |
# class/method var's instead of full paths, etc | |
# Don't load virt_only pools if this consumer isn't a guest: | |
# not a guest | |
# same criteria but for PoolProduct attributes | |
# not sure if this should be two seperate criteria, or if it's | |
# worth it to combine in some clever fashion | |
# we are a virt guest | |
# add criteria for filtering out pools that are not for this guest | |
# need a default value in case there is no registered host | |
# Note: looking for pools that are not for this guest | |
# we do want everything else | |
# no virt.uuid, we can't try to filter | |
# | |
# * Creates all appropriate pools for a subscription. | |
# | |
# note: the product attributes are getting copied above, but the following will make | |
# virt_only a pool attribute. That makes the pool explicitly virt_only to subscription | |
# manager and any other downstream comsumer. | |
# Check if we need to create a virt-only pool for this subscription: | |
# Make sure the virt pool does not have a virt_limit, | |
# otherwise this will recurse infinitely | |
# | |
# * Updates the existing pools for a subscription. | |
# | |
# Expected quantity is normally the subscription's quantity, but for | |
# virt only pools we expect it to be sub quantity * virt_limit: | |
# | |
# * WARNING: when updating pools, we have the added complication of having to | |
# * watch out for pools that candlepin creates internally. (i.e. virt bonus | |
# * pools in hosted (created when sub is first detected), and host restricted | |
# * virt pools when on-site. (created when a host binds) | |
# | |
# Assuming there mere be a virt limit attribute set on the sub product, | |
# this is true for all pools with pool_derived. (for now...) | |
# this will only happen if the rules set it to be 0. | |
# don't modify | |
# pretty much all the rest. | |
# this is how we determined the quantity | |
# we need to see if a parent pool exists and has been exported. Adjust is number exported | |
# from a parent pool. If no parent pool, adjust = 0 [a scenario of virtual pool only] | |
#TODO: Should we check whether pool is overflowing here? | |
is_stacked = (ent) -> | |
ent.getPool().hasProductAttribute "stacking_id" | |
### | |
Check the given list of entitlements to see if a stack ID is compliant for | |
a consumer's socket count. | |
### | |
stack_is_compliant = (consumer, stack_id, ents, log) -> | |
log.debug "Checking stack compliance for: " + stack_id | |
consumer_sockets = 1 | |
consumer_sockets = parseInt(consumer.getFact(SOCKET_FACT)) if consumer.hasFact(SOCKET_FACT) | |
log.debug "Consumer sockets: " + consumer_sockets | |
covered_sockets = 0 | |
for ent of ents.toArray() | |
if is_stacked(ent) | |
currentStackId = ent.getPool().getProductAttribute("stacking_id").getValue() | |
if currentStackId.equals(stack_id) | |
covered_sockets += get_pool_sockets(ent.getPool()) * ent.getQuantity() | |
log.debug "Ent " + ent.getId() + " took covered sockets to: " + covered_sockets | |
covered_sockets >= consumer_sockets | |
# | |
# * Check an entitlement to see if it provides sufficent CPU sockets a consumer. | |
# | |
ent_is_compliant = (consumer, ent, log) -> | |
log.debug "Checking entitlement compliance: " + ent.getId() | |
consumer_sockets = 1 | |
consumer_sockets = parseInt(consumer.getFact(SOCKET_FACT)) if consumer.hasFact(SOCKET_FACT) | |
covered_sockets = get_pool_sockets(ent.getPool()) | |
covered_sockets >= consumer_sockets | |
### | |
Returns an array of product IDs the entitlement provides which are relevant | |
(installed) on the given consumer. | |
### | |
find_relevant_pids = (entitlement, consumer) -> | |
provided_pids = [] | |
return provided_pids unless consumer.getInstalledProducts()? | |
for installed_prod of consumer.getInstalledProducts().toArray() | |
installed_pid = installed_prod.getProductId() | |
if entitlement.getPool().provides(installed_pid) is true | |
log.debug "pool provides: " + installed_pid | |
provided_pids.push installed_pid | |
provided_pids | |
### | |
Checks compliance status for a consumer on a given date. | |
### | |
getComplianceStatusOnDate = (consumer, entitlements, ondate, log) -> | |
status = new org.candlepin.policy.js.compliance.ComplianceStatus(ondate) | |
# Track the stack IDs we've already checked to save some time: | |
compliant_stack_ids = new java.util.HashSet() | |
non_compliant_stack_ids = new java.util.HashSet() | |
log.debug "Checking compliance status for consumer: " + consumer.getUuid() | |
for e of entitlements.toArray() | |
log.debug " checking entitlement: " + e.getId() | |
relevant_pids = find_relevant_pids(e, consumer) | |
log.debug " relevant products: " + relevant_pids | |
partially_stacked = false | |
ent_is_stacked = is_stacked(e) | |
# If the pool is stacked, check that the stack requirements are met: | |
if ent_is_stacked | |
stack_id = e.getPool().getProductAttribute("stacking_id").getValue() | |
log.debug " pool has stack ID: " + stack_id | |
# Shortcuts for stacks we've already checked: | |
if non_compliant_stack_ids.contains(stack_id) > 0 | |
log.debug " stack already found to be non-compliant" | |
partially_stacked = true | |
status.addPartialStack stack_id, e | |
else if compliant_stack_ids.contains(stack_id) > 0 | |
log.debug " stack already found to be compliant" | |
# Otherwise check the stack and add appropriately: | |
else unless stack_is_compliant(consumer, stack_id, entitlements, log) | |
log.debug " stack is non-compliant" | |
partially_stacked = true | |
status.addPartialStack stack_id, e | |
non_compliant_stack_ids.add stack_id | |
else | |
log.debug " stack is compliant" | |
compliant_stack_ids.add stack_id | |
for relevant_pid of relevant_pids | |
if partially_stacked | |
log.debug " partially compliant: " + relevant_pid | |
status.addPartiallyCompliantProduct relevant_pid, e | |
else if not ent_is_compliant(consumer, e, log) and not ent_is_stacked | |
log.debug " partially compliant (non-stacked): " + relevant_pid | |
status.addPartiallyCompliantProduct relevant_pid, e | |
else | |
log.debug " fully compliant: " + relevant_pid | |
status.addCompliantProduct relevant_pid, e | |
# Run through each partially compliant product, if we also found a | |
# regular entitlement which provides that product, then it should not be | |
# considered partially compliant as well. We do however still leavecomplianceRules.getStatus(consumer, next); the *stack* | |
# in partial stacks list, as this should be repaired. (it could offer other | |
# products) | |
for partial_prod of status.getPartiallyCompliantProducts().keySet().toArray() | |
status.getPartiallyCompliantProducts().remove partial_prod if status.getCompliantProducts().keySet().contains(partial_prod) | |
# Run through the consumer's installed products and see if there are any we | |
# didn't find an entitlement for along the way: | |
if consumer.getInstalledProducts()? | |
for installed_prod of consumer.getInstalledProducts().toArray() | |
installed_pid = installed_prod.getProductId() | |
# Not compliant if we didn't find any entitlements for this product: | |
status.addNonCompliantProduct installed_pid if not status.getCompliantProducts().containsKey(installed_pid) and not status.getPartiallyCompliantProducts().containsKey(installed_pid) | |
status | |
### | |
Determine the compliant until date for a consumer based on the specifed start date | |
and entitlements. | |
### | |
determineCompliantUntilDate = (consumer, startDate, complianceHelper, log) -> | |
initialEntitlements = complianceHelper.getEntitlementsOnDate(consumer, startDate) | |
# Get all end dates from current entitlements sorted ascending. | |
dates = complianceHelper.getSortedEndDatesFromEntitlements(initialEntitlements).toArray() | |
for dateToCheck of dates | |
next = new Date(dateToCheck.getTime()) | |
jsStartDate = new Date(startDate.getTime()) | |
# Ignore past dates. | |
continue if next < jsStartDate | |
# Need to check if we are still compliant after the end date, | |
# so we add one second. | |
next.setSeconds next.getSeconds() + 1 | |
entitlementsOnDate = complianceHelper.getEntitlementsOnDate(consumer, next) | |
status = getComplianceStatusOnDate(consumer, entitlementsOnDate, next, log) | |
return next unless status.isCompliant() | |
null | |
SOCKET_FACT = "cpu.cpu_socket(s)" | |
Entitlement = | |
attribute_mappings: -> | |
"architecture:1:arch," + "sockets:1:sockets," + "requires_consumer_type:1:requires_consumer_type," + "user_license:1:user_license," + "virt_only:1:virt_only," + "virt_limit:1:virt_limit," + "requires_host:1:requires_host" | |
pre_virt_only: -> | |
virt_pool = "true".equalsIgnoreCase(attributes.get("virt_only")) | |
guest = false | |
guest = "true".equalsIgnoreCase(consumer.getFact("virt.is_guest")) if consumer.hasFact("virt.is_guest") | |
pre.addError "rulefailed.virt.only" if virt_pool and not guest | |
pre_requires_host: -> | |
return unless standalone | |
unless consumer.hasFact("virt.uuid") | |
pre.addError "rulefailed.virt.only" | |
return | |
hostConsumer = pre.getHostConsumer(consumer.getFact("virt.uuid")) | |
pre.addError "virt.guest.host.does.not.match.pool.owner" if not hostConsumer? or not hostConsumer.getUuid().equals(attributes.get("requires_host")) | |
post_user_license: -> | |
unless consumer.isManifest() | |
productId = pool.getProductId() | |
productId = attributes.get("user_license_product") if attributes.containsKey("user_license_product") | |
post.createUserRestrictedPool productId, pool, attributes.get("user_license") | |
pre_requires_consumer_type: -> | |
pre.addError "rulefailed.consumer.type.mismatch" if not attributes.get("requires_consumer_type").equals(consumer.getType()) and not consumer.getType().equals("uebercert") | |
pre_virt_limit: -> | |
post_virt_limit: -> | |
if standalone | |
productId = pool.getProductId() | |
virt_limit = attributes.get("virt_limit") | |
if "unlimited".equals(virt_limit) | |
post.createHostRestrictedPool productId, pool, "unlimited" | |
else | |
virt_quantity = parseInt(virt_limit) * entitlement.getQuantity() | |
post.createHostRestrictedPool productId, pool, virt_quantity.toString() if virt_quantity > 0 | |
else | |
if consumer.isManifest() | |
virt_limit = attributes.get("virt_limit") | |
unless "unlimited".equals(virt_limit) | |
virt_quantity = parseInt(virt_limit) * entitlement.getQuantity() | |
if virt_quantity > 0 | |
pools = post.lookupBySubscriptionId(pool.getSubscriptionId()) | |
idex = 0 | |
while idex < pools.size() | |
derivedPool = pools.get(idex) | |
derivedPool = post.updatePoolQuantity(derivedPool, -1 * virt_quantity) if derivedPool.getAttributeValue("pool_derived") | |
idex++ | |
else | |
if pool.getQuantity() is pool.getExported() | |
pools = post.lookupBySubscriptionId(pool.getSubscriptionId()) | |
idex = 0 | |
while idex < pools.size() | |
derivedPool = pools.get(idex) | |
derivedPool = post.setPoolQuantity(derivedPool, 0) if derivedPool.getAttributeValue("pool_derived") | |
idex++ | |
pre_architecture: -> | |
pre.addWarning "rulewarning.architecture.mismatch" unless architectureMatches(product, consumer) | |
post_architecture: -> | |
pre_sockets: -> | |
pre.addWarning "rulewarning.unsupported.number.of.sockets" if (parseInt(product.getAttribute("sockets")) > 0) and (parseInt(product.getAttribute("sockets")) < parseInt(consumer.getFact(SOCKET_FACT))) if consumer.hasFact(SOCKET_FACT) and not product.hasAttribute("stacking_id") | |
post_sockets: -> | |
pre_global: -> | |
unless consumer.isManifest() | |
pre.addError "rulefailed.consumer.already.has.product" if consumer.hasEntitlement(pool.getId()) and product.getAttribute("multi-entitlement") isnt "yes" | |
pre.addError "rulefailed.pool.does.not.support.multi-entitlement" if pre.getQuantity() > 1 and product.getAttribute("multi-entitlement") isnt "yes" | |
pre.addError "rulefailed.consumer.type.mismatch" if not consumer.getType().equals("system") and not consumer.getType().equals("hypervisor") and not consumer.getType().equals("uebercert") unless product.hasAttribute("requires_consumer_type") | |
pre.addError "pool.not.available.to.user, pool= '" + pool.getRestrictedToUsername() + "', actual username='" + consumer.getUsername() + "'" if pool.getRestrictedToUsername()? and not pool.getRestrictedToUsername().equals(consumer.getUsername()) | |
else | |
pre.addError "pool.not.available.to.manifest.consumers" if pool.getAttributes().containsKey("pool_derived") | |
pre.checkQuantity pool | |
post_global: -> | |
select_pool_global: -> | |
pools_by_class = [] | |
if log.isDebugEnabled() | |
log.debug "Selecting best pools from: " + pools.length | |
for pool of pools | |
log.debug " " + pool.getId() | |
consumerSLA = consumer.getServiceLevel() | |
log.debug "Filtering pools by SLA: " + consumerSLA if consumerSLA and not consumerSLA.equals("") | |
i = 0 | |
while i < pools.length | |
pool = pools[i] | |
poolSLA = pool.getProductAttribute("support_level") | |
poolSLAExempt = isLevelExempt(pool.getProductAttribute("support_level"), exemptList) | |
if not poolSLAExempt and consumerSLA and not consumerSLA.equals("") and not consumerSLA.equalsIgnoreCase(poolSLA) | |
log.debug "Skipping pool " + pool.getId() + " since SLA does not match that of the consumer." | |
continue | |
log.debug "Checking pool for best unique provides combination: " + pool.getId() | |
log.debug " top level product: " + (pool.getTopLevelProduct().getId()) | |
if architectureMatches(pool.getTopLevelProduct(), consumer) | |
provided_products = getRelevantProvidedProducts(pool, products) | |
log.debug " relevant provided products: " | |
for pp of provided_products | |
log.debug " " + pp.getId() | |
duplicate_found = false | |
for pool_class of pools_by_class | |
best_pool = pool_class[0] | |
best_provided_products = getRelevantProvidedProducts(best_pool, products) | |
if providesSameProducts(provided_products, best_provided_products) | |
duplicate_found = true | |
log.debug " provides same product combo as: " + pool.getId() | |
i = 0 | |
while i < pool_class.length | |
break if comparePools(pool, best_pool) | |
i++ | |
pool_class.splice i, 0, pool | |
break | |
unless duplicate_found | |
pool_class = [] | |
pool_class.push pool | |
pools_by_class.push pool_class | |
i++ | |
candidate_combos = recursiveCombination(pools_by_class, products.length) | |
log.debug "Selecting " + products.length + " products from " + pools_by_class.length + " pools in " + candidate_combos.length + " possible combinations" | |
selected_pools = new java.util.HashMap() | |
best_provided_count = 0 | |
best_entitlements_count = 0 | |
for pool_combo of candidate_combos | |
provided_count = 0 | |
unique_provided = [] | |
for pool_class of pool_combo | |
pool = pool_class[0] | |
provided_products = getRelevantProvidedProducts(pool, products) | |
for provided_product of provided_products | |
log.debug "\t\tprovided_product " + provided_product.getId() | |
unique_provided.push provided_product unless contains(unique_provided, provided_product) | |
for product of unique_provided | |
log.debug "unique_provided " + product.getId() + " " + product.getName() | |
continue if unique_provided.length < best_provided_count | |
if unique_provided.length > best_provided_count or pool_combo.length < best_entitlements_count | |
if hasNoProductOverlap(pool_combo) | |
new_selection = new java.util.HashMap() | |
total_entitlements = 0 | |
for pool_class of pool_combo | |
poolMap = findStackingPools(pool_class, consumer, compliance) | |
new_selection.putAll poolMap | |
quantity = 0 | |
for value of poolMap.values() | |
quantity += value | |
total_entitlements += quantity | |
if new_selection.size() > 0 | |
selected_pools = new_selection | |
best_provided_count = unique_provided.length | |
best_entitlements_count = total_entitlements | |
for pool of selected_pools.keySet() | |
log.debug "selected_pool2 " + pool | |
selected_pools | |
ConsumerDelete = global: -> | |
helper.deleteUserRestrictedPools consumer.getUsername() if consumer.getType() is "person" | |
PoolCriteria = poolCriteria: -> | |
criteriaFilters = new java.util.LinkedList() | |
unless "true".equalsIgnoreCase(consumer.getFact("virt.is_guest")) | |
noVirtOnlyPoolAttr = org.hibernate.criterion.DetachedCriteria.forClass(org.candlepin.model.PoolAttribute, "pool_attr").add(org.hibernate.criterion.Restrictions.eq("name", "virt_only")).add(org.hibernate.criterion.Restrictions.eq("value", "true")).add(org.hibernate.criterion.Property.forName("this.id").eqProperty("pool_attr.pool.id")).setProjection(org.hibernate.criterion.Projections.property("pool_attr.id")) | |
criteriaFilters.add org.hibernate.criterion.Subqueries.notExists(noVirtOnlyPoolAttr) | |
noVirtOnlyProductAttr = org.hibernate.criterion.DetachedCriteria.forClass(org.candlepin.model.ProductPoolAttribute, "prod_attr").add(org.hibernate.criterion.Restrictions.eq("name", "virt_only")).add(org.hibernate.criterion.Restrictions.eq("value", "true")).add(org.hibernate.criterion.Property.forName("this.id").eqProperty("prod_attr.pool.id")).setProjection(org.hibernate.criterion.Projections.property("prod_attr.id")) | |
criteriaFilters.add org.hibernate.criterion.Subqueries.notExists(noVirtOnlyProductAttr) | |
else | |
if consumer.hasFact("virt.uuid") | |
hostUuid = "" | |
hostUuid = hostConsumer.getUuid() if hostConsumer? | |
noRequiresHost = org.hibernate.criterion.DetachedCriteria.forClass(org.candlepin.model.PoolAttribute, "attr").add(org.hibernate.criterion.Restrictions.eq("name", "requires_host")).add(org.hibernate.criterion.Restrictions.ne("value", hostUuid)).add(org.hibernate.criterion.Property.forName("this.id").eqProperty("attr.pool.id")).setProjection(org.hibernate.criterion.Projections.property("attr.id")) | |
criteriaFilters.add org.hibernate.criterion.Subqueries.notExists(noRequiresHost) | |
criteriaFilters | |
Pool = | |
createPools: -> | |
log.info "creating pool: " + sub.getId() | |
pools = new java.util.LinkedList() | |
quantity = sub.getQuantity() * sub.getProduct().getMultiplier() | |
providedProducts = new java.util.HashSet() | |
newPool = new org.candlepin.model.Pool(sub.getOwner(), sub.getProduct().getId(), sub.getProduct().getName(), providedProducts, quantity, sub.getStartDate(), sub.getEndDate(), sub.getContractNumber(), sub.getAccountNumber()) | |
if sub.getProvidedProducts()? | |
for p of sub.getProvidedProducts().toArray() | |
providedProduct = new org.candlepin.model.ProvidedProduct(p.getId(), p.getName()) | |
providedProduct.setPool newPool | |
providedProducts.add providedProduct | |
helper.copyProductAttributesOntoPool sub, newPool | |
newPool.setSubscriptionId sub.getId() | |
newPool.setSubscriptionSubKey "master" | |
virtAtt = sub.getProduct().getAttribute("virt_only") | |
newPool.addAttribute new org.candlepin.model.PoolAttribute("virt_only", virtAtt.getValue()) if virtAtt? and virtAtt.getValue()? and not virtAtt.getValue().equals("") | |
pools.add newPool | |
if attributes.containsKey("virt_limit") and not standalone | |
virt_limit = attributes.get("virt_limit") | |
virt_attributes = new java.util.HashMap() | |
virt_attributes.put "virt_only", "true" | |
virt_attributes.put "pool_derived", "true" | |
virt_attributes.put "virt_limit", "0" | |
if "unlimited".equals(virt_limit) | |
derivedPool = helper.createPool(sub, sub.getProduct().getId(), "unlimited", virt_attributes) | |
derivedPool.setSubscriptionSubKey "derived" | |
pools.add derivedPool | |
else | |
virt_limit_quantity = parseInt(virt_limit) | |
if virt_limit_quantity > 0 | |
virt_quantity = quantity * virt_limit_quantity | |
log.debug "creating virt only pool" | |
derivedPool = helper.createPool(sub, sub.getProduct().getId(), virt_quantity.toString(), virt_attributes) | |
derivedPool.setSubscriptionSubKey "derived" | |
pools.add derivedPool | |
pools | |
updatePools: -> | |
poolsUpdated = new java.util.LinkedList() | |
for existingPool of pools.toArray() | |
log.info "Updating pool: " + existingPool.getId() | |
datesChanged = (not sub.getStartDate().equals(existingPool.getStartDate())) or (not sub.getEndDate().equals(existingPool.getEndDate())) | |
expectedQuantity = sub.getQuantity() * sub.getProduct().getMultiplier() | |
if existingPool.hasAttribute("pool_derived") and existingPool.attributeEquals("virt_only", "true") and existingPool.hasProductAttribute("virt_limit") | |
virt_limit = attributes.get("virt_limit") | |
if "unlimited".equals(virt_limit) | |
if existingPool.getQuantity() is 0 | |
expectedQuantity = 0 | |
else | |
expectedQuantity = -1 | |
else | |
if standalone | |
expectedQuantity = existingPool.getSourceEntitlement().getQuantity() * parseInt(virt_limit) | |
else | |
adjust = 0 | |
idex = 0 | |
while idex < pools.size() | |
derivedPool = pools.get(idex) | |
adjust = derivedPool.getExported() unless derivedPool.getAttributeValue("pool_derived") | |
idex++ | |
expectedQuantity = (expectedQuantity - adjust) * parseInt(virt_limit) | |
quantityChanged = (expectedQuantity isnt existingPool.getQuantity()) | |
productsChanged = helper.checkForChangedProducts(existingPool, sub) | |
productAttributesChanged = helper.copyProductAttributesOntoPool(sub, existingPool) | |
log.info "Updated product attributes from subscription." if productAttributesChanged | |
unless quantityChanged or datesChanged or productsChanged or productAttributesChanged | |
log.info " No updates required." | |
continue | |
if quantityChanged | |
log.info " Quantity changed to: " + expectedQuantity | |
existingPool.setQuantity expectedQuantity | |
if datesChanged | |
log.info " Subscription dates changed." | |
existingPool.setStartDate sub.getStartDate() | |
existingPool.setEndDate sub.getEndDate() | |
if productsChanged | |
log.info " Subscription products changed." | |
existingPool.setProductName sub.getProduct().getName() | |
existingPool.setProductId sub.getProduct().getId() | |
existingPool.getProvidedProducts().clear() | |
if sub.getProvidedProducts()? | |
for p of sub.getProvidedProducts().toArray() | |
providedProduct = new org.candlepin.model.ProvidedProduct(p.getId(), p.getName()) | |
existingPool.addProvidedProduct providedProduct | |
poolsUpdated.add new org.candlepin.policy.js.pool.PoolUpdate(existingPool, datesChanged, quantityChanged, productsChanged) | |
poolsUpdated | |
Export = can_export_entitlement: -> | |
pool_derived = attributes.containsKey("pool_derived") | |
not consumer.isManifest() or not pool_derived | |
Compliance = | |
get_status: -> | |
status = getComplianceStatusOnDate(consumer, entitlements, ondate, log) | |
compliantUntil = ondate | |
if status.isCompliant() | |
if entitlements.isEmpty() | |
compliantUntil = null | |
else | |
compliantUntil = determineCompliantUntilDate(consumer, ondate, helper, log) | |
status.setCompliantUntil compliantUntil | |
status | |
is_stack_compliant: -> | |
stack_is_compliant consumer, stack_id, entitlements, log | |
is_ent_compliant: -> | |
ent_is_compliant consumer, ent, log | |
Unbind = | |
# defines mapping of product attributes to functions | |
# the format is: <function name>:<order number>:<attr1>:...:<attrn>, comma-separated ex.: | |
# func1:1:attr1:attr2:attr3, func2:2:attr3:attr4 | |
attribute_mappings: -> | |
"virt_limit:1:virt_limit" | |
pre_virt_limit: -> | |
post_virt_limit: -> | |
if not standalone and consumer.isManifest() | |
virt_limit = attributes.get("virt_limit") | |
unless "unlimited".equals(virt_limit) | |
# As we have unbound an entitlement from a physical pool that was previously | |
# exported, we need to add back the reduced bonus pool quantity. | |
virt_quantity = parseInt(virt_limit) * entitlement.getQuantity() | |
if virt_quantity > 0 | |
pools = post.lookupBySubscriptionId(pool.getSubscriptionId()) | |
idex = 0 | |
while idex < pools.size() | |
derivedPool = pools.get(idex) | |
post.updatePoolQuantity derivedPool, virt_quantity if derivedPool.getAttributeValue("pool_derived") | |
idex++ | |
else | |
# As we have unbound an entitlement from a physical pool that was previously | |
# exported, we need to set the unlimited bonus pool quantity to -1. | |
pools = post.lookupBySubscriptionId(pool.getSubscriptionId()) | |
idex = 0 | |
while idex < pools.size() | |
derivedPool = pools.get(idex) | |
post.setPoolQuantity derivedPool, -1 if derivedPool.getQuantity() is 0 if derivedPool.getAttributeValue("pool_derived") | |
idex++ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment