Skip to content

Instantly share code, notes, and snippets.

@jbowes
Created October 20, 2012 12:06
Show Gist options
  • Save jbowes/3923129 to your computer and use it in GitHub Desktop.
Save jbowes/3923129 to your computer and use it in GitHub Desktop.
Candlepin default rules auto-converted to CoffeeScript
#
# * 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