예제 #1
0
    def select_rse(self,
                   size,
                   preferred_rse_ids,
                   copies=0,
                   blacklist=[],
                   prioritize_order_over_weight=False):
        """
        Select n RSEs to replicate data to.

        :param size:                         Size of the block being replicated.
        :param preferred_rse_ids:            Ordered list of preferred rses. (If possible replicate to them)
        :param copies:                       Select this amount of copies, if 0 use the pre-defined rule value.
        :param blacklist:                    List of blacklisted rses. (Do not put replicas on these sites)
        :param prioritze_order_over_weight:  Prioritize the order of the preferred_rse_ids list over the picking done by weight.
        :returns:                            List of (RSE_id, staging_area) tuples.
        :raises:                             InsufficientAccountLimit, InsufficientTargetRSEs
        """

        result = []
        rses = self.rses
        count = self.copies if copies == 0 else copies

        # Remove blacklisted rses
        if blacklist:
            rses = [rse for rse in self.rses if rse['rse_id'] not in blacklist]
        if len(rses) < count:
            raise InsufficientTargetRSEs(
                'There are not enough target RSEs to fulfil the request at this time.'
            )
        # Remove rses which do not have enough quota
        rses = [rse for rse in rses if rse['quota_left'] > size]
        if len(rses) < count:
            raise InsufficientAccountLimit(
                'There is insufficient quota on any of the target RSE\'s to fullfill the operation.'
            )

        for copy in range(count):
            # Remove rses already in the result set
            rses = [
                rse for rse in rses
                if rse['rse_id'] not in [item[0] for item in result]
            ]
            rses_dict = {}
            for rse in rses:
                rses_dict[rse['rse_id']] = rse
            # Prioritize the preffered rses
            preferred_rses = [
                rses_dict[rse_id] for rse_id in preferred_rse_ids
                if rse_id in rses_dict
            ]
            if prioritize_order_over_weight and preferred_rses:
                rse = (preferred_rses[0]['rse_id'],
                       preferred_rses[0]['staging_area'])
            elif preferred_rses:
                rse = self.__choose_rse(preferred_rses)
            else:
                rse = self.__choose_rse(rses)
            result.append(rse)
            self.__update_quota(rse, size)
        return result
예제 #2
0
    def __init__(self,
                 account,
                 rses,
                 weight,
                 copies,
                 ignore_account_limit=False,
                 session=None):
        """
        Initialize the RSE Selector.

        :param account:               Account owning the rule.
        :param rses:                  List of rse dictionaries.
        :param weight:                Weighting to use.
        :param copies:                Number of copies to create.
        :param ignore_account_limit:  Flag if the quota should be ignored.
        :param session:               DB Session in use.
        :raises:                      InvalidRuleWeight, InsufficientAccountLimit, InsufficientTargetRSEs
        """
        self.account = account
        self.rses = []  # [{'rse_id':, 'weight':, 'staging_area'}]
        self.copies = copies
        if weight is not None:
            for rse in rses:
                attributes = list_rse_attributes(rse_id=rse['id'],
                                                 session=session)
                availability_write = True if rse.get('availability',
                                                     7) & 2 else False
                if weight not in attributes:
                    continue  # The RSE does not have the required weight set, therefore it is ignored
                try:
                    self.rses.append({
                        'rse_id': rse['id'],
                        'weight': float(attributes[weight]),
                        'mock_rse': attributes.get('mock', False),
                        'availability_write': availability_write,
                        'staging_area': rse['staging_area']
                    })
                except ValueError:
                    raise InvalidRuleWeight(
                        'The RSE \'%s\' has a non-number specified for the weight \'%s\''
                        % (rse['rse'], weight))
        else:
            for rse in rses:
                mock_rse = has_rse_attribute(rse['id'],
                                             'mock',
                                             session=session)
                availability_write = True if rse.get('availability',
                                                     7) & 2 else False
                self.rses.append({
                    'rse_id': rse['id'],
                    'weight': 1,
                    'mock_rse': mock_rse,
                    'availability_write': availability_write,
                    'staging_area': rse['staging_area']
                })

        if len(self.rses) < self.copies:
            raise InsufficientTargetRSEs(
                'Target RSE set not sufficient for number of copies. (%s copies requested, RSE set size %s)'
                % (self.copies, len(self.rses)))

        rses_with_enough_quota = []
        if has_account_attribute(account=account, key='admin',
                                 session=session) or ignore_account_limit:
            for rse in self.rses:
                rse['quota_left'] = float('inf')
                rse['space_left'] = float('inf')
                rses_with_enough_quota.append(rse)
        else:
            global_quota_limit = get_global_account_limits(account=account,
                                                           session=session)
            all_rse_usages = {
                usage['rse_id']: usage['bytes']
                for usage in get_all_rse_usages_per_account(account=account,
                                                            session=session)
            }
            for rse in self.rses:
                if rse['mock_rse']:
                    rse['quota_left'] = float('inf')
                    rse['space_left'] = float('inf')
                    rses_with_enough_quota.append(rse)
                else:
                    # check local quota
                    local_quota_left = None
                    quota_limit = get_local_account_limit(account=account,
                                                          rse_id=rse['rse_id'],
                                                          session=session)
                    if quota_limit is None:
                        local_quota_left = 0
                    else:
                        local_quota_left = quota_limit - get_usage(
                            rse_id=rse['rse_id'],
                            account=account,
                            session=session)['bytes']

                    # check global quota
                    rse['global_quota_left'] = {}
                    all_global_quota_enough = True
                    for rse_expression, limit in global_quota_limit.items():
                        if rse['rse_id'] in limit['resolved_rse_ids']:
                            quota_limit = limit['limit']
                            global_quota_left = None
                            if quota_limit is None:
                                global_quota_left = 0
                            else:
                                rse_expression_usage = 0
                                for rse_id in limit['resolved_rse_ids']:
                                    rse_expression_usage += all_rse_usages.get(
                                        rse_id, 0)
                                global_quota_left = quota_limit - rse_expression_usage
                            if global_quota_left <= 0:
                                all_global_quota_enough = False
                                break
                            else:
                                rse['global_quota_left'][
                                    rse_expression] = global_quota_left
                    if local_quota_left > 0 and all_global_quota_enough:
                        rse['quota_left'] = local_quota_left
                        space_limit = get_rse_limits(
                            name='MaxSpaceAvailable',
                            rse_id=rse['rse_id'],
                            session=session).get('MaxSpaceAvailable')
                        if space_limit is None or space_limit < 0:
                            rse['space_left'] = float('inf')
                        else:
                            rse['space_left'] = space_limit - get_rse_counter(
                                rse_id=rse['rse_id'], session=session)['bytes']
                        rses_with_enough_quota.append(rse)

        self.rses = rses_with_enough_quota
        if len(self.rses) < self.copies:
            raise InsufficientAccountLimit(
                'There is insufficient quota on any of the target RSE\'s to fullfill the operation.'
            )
예제 #3
0
    def select_rse(self,
                   size,
                   preferred_rse_ids,
                   copies=0,
                   blocklist=[],
                   prioritize_order_over_weight=False,
                   existing_rse_size=None):
        """
        Select n RSEs to replicate data to.

        :param size:                         Size of the block being replicated.
        :param preferred_rse_ids:            Ordered list of preferred rses. (If possible replicate to them)
        :param copies:                       Select this amount of copies, if 0 use the pre-defined rule value.
        :param blocklist:                    List of blocked rses. (Do not put replicas on these sites)
        :param prioritze_order_over_weight:  Prioritize the order of the preferred_rse_ids list over the picking done by weight.
        :existing_rse_size:                  Dictionary of size of files already present at each rse
        :returns:                            List of (RSE_id, staging_area, availability_write) tuples.
        :raises:                             InsufficientAccountLimit, InsufficientTargetRSEs
        """

        result = []
        rses = self.rses
        count = self.copies if copies == 0 else copies

        # Remove blocklisted rses
        if blocklist:
            rses = [rse for rse in self.rses if rse['rse_id'] not in blocklist]
        if len(rses) < count:
            raise InsufficientTargetRSEs(
                'There are not enough target RSEs to fulfil the request at this time.'
            )

        # Remove rses which do not have enough space, accounting for the files already at each rse
        if existing_rse_size is None:
            existing_rse_size = {}
        rses = [
            rse for rse in rses if rse['space_left'] >= size -
            existing_rse_size.get(rse['rse_id'], 0)
        ]
        if len(rses) < count:
            raise RSEOverQuota(
                'There is insufficient space on any of the target RSE\'s to fullfill the operation.'
            )

        # Remove rses which do not have enough local quota
        rses = [rse for rse in rses if rse['quota_left'] > size]
        if len(rses) < count:
            raise InsufficientAccountLimit(
                'There is insufficient quota on any of the target RSE\'s to fullfill the operation.'
            )

        # Remove rses which do not have enough global quota
        rses_with_enough_quota = []
        for rse in rses:
            enough_global_quota = True
            for rse_expression in rse.get('global_quota_left', []):
                if rse['global_quota_left'][rse_expression] < size:
                    enough_global_quota = False
                    break
            if enough_global_quota:
                rses_with_enough_quota.append(rse)
        rses = rses_with_enough_quota
        if len(rses) < count:
            raise InsufficientAccountLimit(
                'There is insufficient quota on any of the target RSE\'s to fullfill the operation.'
            )

        for copy in range(count):
            # Remove rses already in the result set
            rses = [
                rse for rse in rses
                if rse['rse_id'] not in [item[0] for item in result]
            ]
            rses_dict = {}
            for rse in rses:
                rses_dict[rse['rse_id']] = rse
            # Prioritize the preffered rses
            preferred_rses = [
                rses_dict[rse_id] for rse_id in preferred_rse_ids
                if rse_id in rses_dict
            ]
            if prioritize_order_over_weight and preferred_rses:
                rse = (preferred_rses[0]['rse_id'],
                       preferred_rses[0]['staging_area'],
                       preferred_rses[0]['availability_write'])
            elif preferred_rses:
                rse = self.__choose_rse(preferred_rses)
            else:
                rse = self.__choose_rse(rses)
            result.append(rse)
            self.__update_quota(rse, size)
        return result
예제 #4
0
    def __init__(self, account, rses, weight, copies, ignore_account_limit=False, session=None):
        """
        Initialize the RSE Selector.

        :param account:               Account owning the rule.
        :param rses:                  List of rse dictionaries.
        :param weight:                Weighting to use.
        :param copies:                Number of copies to create.
        :param ignore_account_limit:  Flag if the quota should be ignored.
        :param session:               DB Session in use.
        :raises:                      InvalidRuleWeight, InsufficientAccountLimit, InsufficientTargetRSEs
        """
        self.account = account
        self.rses = []  # [{'rse_id':, 'weight':, 'staging_area'}]
        self.copies = copies
        if weight is not None:
            for rse in rses:
                attributes = list_rse_attributes(rse=None, rse_id=rse['id'], session=session)
                availability_write = True if rse.get('availability', 7) & 2 else False
                if weight not in attributes:
                    continue  # The RSE does not have the required weight set, therefore it is ignored
                try:
                    self.rses.append({'rse_id': rse['id'],
                                      'weight': float(attributes[weight]),
                                      'mock_rse': attributes.get('mock', False),
                                      'availability_write': availability_write,
                                      'staging_area': rse['staging_area']})
                except ValueError:
                    raise InvalidRuleWeight('The RSE with id \'%s\' has a non-number specified for the weight \'%s\'' % (rse['id'], weight))
        else:
            for rse in rses:
                mock_rse = has_rse_attribute(rse['id'], 'mock', session=session)
                availability_write = True if rse.get('availability', 7) & 2 else False
                self.rses.append({'rse_id': rse['id'],
                                  'weight': 1,
                                  'mock_rse': mock_rse,
                                  'availability_write': availability_write,
                                  'staging_area': rse['staging_area']})

        if len(self.rses) < self.copies:
            raise InsufficientTargetRSEs('Target RSE set not sufficient for number of copies. (%s copies requested, RSE set size %s)' % (self.copies, len(self.rses)))

        if has_account_attribute(account=account, key='admin', session=session) or ignore_account_limit:
            for rse in self.rses:
                rse['quota_left'] = float('inf')
        else:
            for rse in self.rses:
                if rse['mock_rse']:
                    rse['quota_left'] = float('inf')
                else:
                    # TODO: Add RSE-space-left here!
                    limit = get_account_limit(account=account, rse_id=rse['rse_id'], session=session)
                    if limit is None:
                        rse['quota_left'] = 0
                    else:
                        rse['quota_left'] = limit - get_counter(rse_id=rse['rse_id'], account=account, session=session)['bytes']

        self.rses = [rse for rse in self.rses if rse['quota_left'] > 0]

        if len(self.rses) < self.copies:
            raise InsufficientAccountLimit('There is insufficient quota on any of the target RSE\'s to fullfill the operation.')