def unmanagedQuotaUsed(self): ''' Sums up the quota used by servers unmanaged by nodepool. :return: Calculated quota in use by unmanaged servers ''' flavors = self.listFlavorsById() used_quota = QuotaInformation() node_ids = set([n.id for n in self._zk.nodeIterator()]) for server in self.listNodes(): meta = server.get('metadata', {}) nodepool_provider_name = meta.get('nodepool_provider_name') if (nodepool_provider_name and nodepool_provider_name == self.provider.name): # This provider (regardless of the launcher) owns this # server so it must not be accounted for unmanaged # quota; unless it has leaked. nodepool_node_id = meta.get('nodepool_node_id') # FIXME(tobiash): Add a test case for this if nodepool_node_id and nodepool_node_id in node_ids: # It has not leaked. continue flavor = flavors.get(server.flavor.id) used_quota.add(QuotaInformation.construct_from_flavor(flavor)) return used_quota
def unmanagedQuotaUsed(self): ''' Sums up the quota used by servers unmanaged by nodepool. :return: Calculated quota in use by unmanaged servers ''' flavors = self.listFlavorsById() used_quota = QuotaInformation() node_ids = set([n.id for n in self._zk.nodeIterator()]) for server in self.listNodes(): meta = server.get('metadata', {}) nodepool_provider_name = meta.get('nodepool_provider_name') if (nodepool_provider_name and nodepool_provider_name == self.provider.name): # This provider (regardless of the launcher) owns this # server so it must not be accounted for unmanaged # quota; unless it has leaked. nodepool_node_id = meta.get('nodepool_node_id') # FIXME(tobiash): Add a test case for this if nodepool_node_id and nodepool_node_id in node_ids: # It has not leaked. continue flavor = flavors.get(server.flavor.id) used_quota.add(QuotaInformation.construct_from_flavor(flavor)) return used_quota
def estimatedNodepoolQuotaUsed(self, zk, pool=None): ''' Sums up the quota used (or planned) currently by nodepool. If pool is given it is filtered by the pool. :param zk: the object to access zookeeper :param pool: If given, filtered by the pool. :return: Calculated quota in use by nodepool ''' used_quota = QuotaInformation() for node in zk.nodeIterator(): if node.provider == self.provider.name: if pool and not node.pool == pool.name: continue provider_pool = self.provider.pools.get(node.pool) if not provider_pool: self.log.warning("Cannot find provider pool for node %s" % node) # This node is in a funny state we log it for debugging # but move on and don't account it as we can't properly # calculate its cost without pool info. continue node_resources = self.quotaNeededByNodeType( node.type[0], provider_pool) used_quota.add(node_resources) return used_quota
def quotaNeededByNodeType(self, ntype, pool): provider_label = pool.labels[ntype] flavor = self.findFlavor(provider_label.flavor_name, provider_label.min_ram) return QuotaInformation.construct_from_flavor(flavor)
def quotaNeededByNodeType(self, ntype, pool): provider_label = pool.labels[ntype] flavor = self.findFlavor(provider_label.flavor_name, provider_label.min_ram) return QuotaInformation.construct_from_flavor(flavor)
def hasRemainingQuota(self, ntype): ''' Apply max_servers check, ignoring other quotas. :returns: True if we have room, False otherwise. ''' needed_quota = QuotaInformation(cores=1, instances=1, ram=1, default=1) n_running = self.manager.countNodes(self.pool.name) pool_quota = QuotaInformation( cores=math.inf, instances=self.pool.max_servers - n_running, ram=math.inf, default=math.inf) pool_quota.subtract(needed_quota) self.log.debug("hasRemainingQuota({},{}) = {}".format( self.pool, ntype, pool_quota)) return pool_quota.non_negative()
def hasRemainingQuota(self, ntype): needed_quota = self.manager.quotaNeededByNodeType(ntype, self.pool) # Calculate remaining quota which is calculated as: # quota = <total nodepool quota> - <used quota> - <quota for node> cloud_quota = self.manager.estimatedNodepoolQuota() cloud_quota.subtract(self.manager.estimatedNodepoolQuotaUsed(self.zk)) cloud_quota.subtract(needed_quota) self.log.debug("Predicted remaining tenant quota: %s", cloud_quota) if not cloud_quota.non_negative(): return False # Now calculate pool specific quota. Values indicating no quota default # to math.inf representing infinity that can be calculated with. pool_quota = QuotaInformation(cores=self.pool.max_cores, instances=self.pool.max_servers, ram=self.pool.max_ram, default=math.inf) pool_quota.subtract( self.manager.estimatedNodepoolQuotaUsed(self.zk, self.pool)) pool_quota.subtract(needed_quota) self.log.debug("Predicted remaining pool quota: %s", pool_quota) return pool_quota.non_negative()
def hasProviderQuota(self, node_types): ''' Apply max_servers check to a whole request :returns: True if we have room, False otherwise. ''' needed_quota = QuotaInformation( cores=1, instances=len(node_types), ram=1, default=1) pool_quota = QuotaInformation( cores=math.inf, instances=self.pool.max_servers, ram=math.inf, default=math.inf) pool_quota.subtract(needed_quota) self.log.debug("hasProviderQuota({},{}) = {}".format( self.pool, node_types, pool_quota)) return pool_quota.non_negative()
def hasRemainingQuota(self, ntype): needed_quota = self.manager.quotaNeededByNodeType(ntype, self.pool) if not self.pool.ignore_provider_quota: # Calculate remaining quota which is calculated as: # quota = <total nodepool quota> - <used quota> - <quota for node> cloud_quota = self.manager.estimatedNodepoolQuota() cloud_quota.subtract( self.manager.estimatedNodepoolQuotaUsed()) cloud_quota.subtract(needed_quota) self.log.debug("Predicted remaining provider quota: %s", cloud_quota) if not cloud_quota.non_negative(): return False # Now calculate pool specific quota. Values indicating no quota default # to math.inf representing infinity that can be calculated with. pool_quota = QuotaInformation(cores=self.pool.max_cores, instances=self.pool.max_servers, ram=self.pool.max_ram, default=math.inf) pool_quota.subtract( self.manager.estimatedNodepoolQuotaUsed(self.pool)) self.log.debug("Current pool quota: %s" % pool_quota) pool_quota.subtract(needed_quota) self.log.debug("Predicted remaining pool quota: %s", pool_quota) return pool_quota.non_negative()
def unmanagedQuotaUsed(self): ''' Sums up the quota used by servers unmanaged by nodepool. :return: Calculated quota in use by unmanaged servers ''' flavors = self.listFlavorsById() used_quota = QuotaInformation() for server in self.listNodes(): meta = server.get('metadata', {}) nodepool_provider_name = meta.get('nodepool_provider_name') if nodepool_provider_name and \ nodepool_provider_name == self.provider.name: # This provider (regardless of the launcher) owns this server # so it must not be accounted for unmanaged quota. continue flavor = flavors.get(server.flavor.id) used_quota.add(QuotaInformation.construct_from_flavor(flavor)) return used_quota
def estimatedNodepoolQuotaUsed(self, pool=None): ''' Sums up the quota used (or planned) currently by nodepool. If pool is given it is filtered by the pool. :param pool: If given, filtered by the pool. :return: Calculated quota in use by nodepool ''' used_quota = QuotaInformation() for node in self._zk.nodeIterator(): if node.provider == self.provider.name: try: if pool and not node.pool == pool.name: continue provider_pool = self.provider.pools.get(node.pool) if not provider_pool: self.log.warning( "Cannot find provider pool for node %s" % node) # This node is in a funny state we log it for debugging # but move on and don't account it as we can't properly # calculate its cost without pool info. continue if node.type[0] not in provider_pool.labels: self.log.warning("Node type is not in provider pool " "for node %s" % node) # This node is also in a funny state; the config # may have changed under it. It should settle out # eventually when it's deleted. continue node_resources = self.quotaNeededByNodeType( node.type[0], provider_pool) used_quota.add(node_resources) except Exception: self.log.exception("Couldn't consider invalid node %s " "for quota:" % node) return used_quota
def estimatedNodepoolQuotaUsed(self, pool=None): ''' Sums up the quota used (or planned) currently by nodepool. If pool is given it is filtered by the pool. :param pool: If given, filtered by the pool. :return: Calculated quota in use by nodepool ''' used_quota = QuotaInformation() for node in self._zk.nodeIterator(): if node.provider == self.provider.name: try: if pool and not node.pool == pool.name: continue provider_pool = self.provider.pools.get(node.pool) if not provider_pool: self.log.warning( "Cannot find provider pool for node %s" % node) # This node is in a funny state we log it for debugging # but move on and don't account it as we can't properly # calculate its cost without pool info. continue if node.type[0] not in provider_pool.labels: self.log.warning("Node type is not in provider pool " "for node %s" % node) # This node is also in a funny state; the config # may have changed under it. It should settle out # eventually when it's deleted. continue node_resources = self.quotaNeededByNodeType( node.type[0], provider_pool) used_quota.add(node_resources) except Exception: self.log.exception("Couldn't consider invalid node %s " "for quota:" % node) return used_quota
def hasProviderQuota(self, node_types): needed_quota = QuotaInformation() for ntype in node_types: needed_quota.add( self.manager.quotaNeededByNodeType(ntype, self.pool)) cloud_quota = self.manager.estimatedNodepoolQuota() cloud_quota.subtract(needed_quota) if not cloud_quota.non_negative(): return False # Now calculate pool specific quota. Values indicating no quota default # to math.inf representing infinity that can be calculated with. pool_quota = QuotaInformation(cores=self.pool.max_cores, instances=self.pool.max_servers, ram=self.pool.max_ram, default=math.inf) pool_quota.subtract(needed_quota) return pool_quota.non_negative()
def hasRemainingQuota(self, ntype): ''' Apply max_servers check, ignoring other quotas. :returns: True if we have room, False otherwise. ''' needed_quota = QuotaInformation(cores=1, instances=1, ram=1, default=1) n_running = self.manager.countNodes(self.pool.name) pool_quota = QuotaInformation(cores=math.inf, instances=self.pool.max_servers - n_running, ram=math.inf, default=math.inf) pool_quota.subtract(needed_quota) self.log.debug("hasRemainingQuota({},{}) = {}".format( self.pool, ntype, pool_quota)) return pool_quota.non_negative()
def estimatedNodepoolQuota(self): ''' Determine how much quota is available for nodepool managed resources. This needs to take into account the quota of the tenant, resources used outside of nodepool and the currently used resources by nodepool, max settings in nodepool config. This is cached for MAX_QUOTA_AGE seconds. :return: Total amount of resources available which is currently available to nodepool including currently existing nodes. ''' if self._current_nodepool_quota: now = time.time() if now < self._current_nodepool_quota['timestamp'] + MAX_QUOTA_AGE: return copy.deepcopy(self._current_nodepool_quota['quota']) limits = self._client.get_compute_limits() # This is initialized with the full tenant quota and later becomes # the quota available for nodepool. nodepool_quota = QuotaInformation.construct_from_limits(limits) self.log.debug("Provider quota for %s: %s", self.provider.name, nodepool_quota) # Subtract the unmanaged quota usage from nodepool_max # to get the quota available for us. nodepool_quota.subtract(self.unmanagedQuotaUsed()) self._current_nodepool_quota = { 'quota': nodepool_quota, 'timestamp': time.time() } self.log.debug("Available quota for %s: %s", self.provider.name, nodepool_quota) return copy.deepcopy(nodepool_quota)
def estimatedNodepoolQuota(self): ''' Determine how much quota is available for nodepool managed resources. This needs to take into account the quota of the tenant, resources used outside of nodepool and the currently used resources by nodepool, max settings in nodepool config. This is cached for MAX_QUOTA_AGE seconds. :return: Total amount of resources available which is currently available to nodepool including currently existing nodes. ''' if self._current_nodepool_quota: now = time.time() if now < self._current_nodepool_quota['timestamp'] + MAX_QUOTA_AGE: return copy.deepcopy(self._current_nodepool_quota['quota']) limits = self._client.get_compute_limits() # This is initialized with the full tenant quota and later becomes # the quota available for nodepool. nodepool_quota = QuotaInformation.construct_from_limits(limits) self.log.debug("Provider quota for %s: %s", self.provider.name, nodepool_quota) # Subtract the unmanaged quota usage from nodepool_max # to get the quota available for us. nodepool_quota.subtract(self.unmanagedQuotaUsed()) self._current_nodepool_quota = { 'quota': nodepool_quota, 'timestamp': time.time() } self.log.debug("Available quota for %s: %s", self.provider.name, nodepool_quota) return copy.deepcopy(nodepool_quota)
def hasProviderQuota(self, node_types): ''' Apply max_servers check to a whole request :returns: True if we have room, False otherwise. ''' needed_quota = QuotaInformation(cores=1, instances=len(node_types), ram=1, default=1) pool_quota = QuotaInformation(cores=math.inf, instances=self.pool.max_servers, ram=math.inf, default=math.inf) pool_quota.subtract(needed_quota) self.log.debug("hasProviderQuota({},{}) = {}".format( self.pool, node_types, pool_quota)) return pool_quota.non_negative()
def hasProviderQuota(self, node_types): needed_quota = QuotaInformation() for ntype in node_types: needed_quota.add( self.manager.quotaNeededByNodeType(ntype, self.pool)) if not self.pool.ignore_provider_quota: cloud_quota = self.manager.estimatedNodepoolQuota() cloud_quota.subtract(needed_quota) if not cloud_quota.non_negative(): return False # Now calculate pool specific quota. Values indicating no quota default # to math.inf representing infinity that can be calculated with. pool_quota = QuotaInformation(cores=self.pool.max_cores, instances=self.pool.max_servers, ram=self.pool.max_ram, default=math.inf) pool_quota.subtract(needed_quota) return pool_quota.non_negative()