def perm_set_global_account_limit(issuer, kwargs): """ Checks if an account can set a global account limit. :param account: Account identifier which issues the command. :param kwargs: List of arguments for the action. :returns: True if account is allowed, otherwise False """ if _is_root(issuer) or has_account_attribute(account=issuer, key='admin'): return True # Check if user is a country admin admin_in_country = set() for kv in list_account_attributes(account=issuer): if kv['key'].startswith('country-') and kv['value'] == 'admin': admin_in_country.add(kv['key'].partition('-')[2]) resolved_rse_countries = { list_rse_attributes(rse_id=rse['rse_id']).get('country') for rse in parse_expression(kwargs['rse_exp']) } if resolved_rse_countries.issubset(admin_in_country): return True return False
def perm_approve_rule(issuer, kwargs): """ Checks if an issuer can approve a replication rule. :param issuer: Account identifier which issues the command. :param kwargs: List of arguments for the action. :returns: True if account is allowed to call the API call, otherwise False """ if _is_root(issuer) or has_account_attribute(account=issuer, key='admin'): return True rule = get_rule(rule_id=kwargs['rule_id']) rses = parse_expression(rule['rse_expression'], filter={'vo': issuer.vo}) # Those in rule_approvers can approve the rule for rse in rses: rse_attr = list_rse_attributes(rse_id=rse['id']) rule_approvers = rse_attr.get('rule_approvers', None) if rule_approvers and issuer.external in rule_approvers.split(','): return True return False
def perm_delete_account_limit(issuer, kwargs): """ Checks if an account can delete an account limit. :param account: Account identifier which issues the command. :param kwargs: List of arguments for the action. :returns: True if account is allowed, otherwise False """ if _is_root(issuer) or has_account_attribute(account=issuer, key='admin'): return True # Check if user is a country admin admin_in_country = [] for kv in list_account_attributes(account=issuer): if kv['key'].startswith('country-') and kv['value'] == 'admin': admin_in_country.append(kv['key'].partition('-')[2]) rse_attr = list_rse_attributes(rse_id=kwargs['rse_id']) if admin_in_country and rse_attr.get('country') in admin_in_country: return True quota_approvers = rse_attr.get('quota_approvers', None) if quota_approvers and issuer.external in quota_approvers.split(','): return True return False
def perm_add_bad_pfns(issuer, kwargs): """ Checks if an account can declare bad PFNs. :param issuer: Account identifier which issues the command. :param kwargs: List of arguments for the action. :returns: True if account is allowed, otherwise False """ if kwargs['state'] in [ str(BadPFNStatus.BAD), str(BadPFNStatus.TEMPORARY_UNAVAILABLE) ]: is_cloud_admin = bool([ acc_attr for acc_attr in list_account_attributes(account=issuer) if (acc_attr['key'].startswith('cloud-')) and ( acc_attr['value'] == 'admin') ]) return _is_root(issuer) or has_account_attribute( account=issuer, key='admin') or is_cloud_admin elif kwargs['state'] == str(BadPFNStatus.SUSPICIOUS): return True return _is_root(issuer)
def perm_get_global_account_usage(issuer, kwargs, session=None): """ Checks if an account can get the account usage of an account. :param issuer: Account identifier which issues the command. :param kwargs: List of arguments for the action. :param session: The DB session to use :returns: True if account is allowed, otherwise False """ if _is_root(issuer) or has_account_attribute(account=issuer, key='admin', session=session) or kwargs.get('account') == issuer: return True # Check if user is a country admin for all involved countries admin_in_country = set() for kv in list_account_attributes(account=issuer, session=session): if kv['key'].startswith('country-') and kv['value'] == 'admin': admin_in_country.add(kv['key'].partition('-')[2]) resolved_rse_countries = {list_rse_attributes(rse_id=rse['rse_id'], session=session).get('country') for rse in parse_expression(kwargs['rse_exp'], filter_={'vo': issuer.vo}, session=session)} if resolved_rse_countries.issubset(admin_in_country): return True return False
def perm_update_replicas_states(issuer, kwargs): """ Checks if an account can delete replicas. :param issuer: Account identifier which issues the command. :param kwargs: List of arguments for the action. :returns: True if account is allowed, otherwise False """ rse = str(kwargs.get('rse', '')) phys_group = [] for kv in list_account_attributes(account=issuer): if kv['key'].startswith('group-') and kv['value'] in ['admin', 'user']: phys_group.append(kv['key'].partition('-')[2]) rse_attr = list_rse_attributes(rse=rse) if phys_group: if rse_attr.get('type', '') == 'GROUPDISK': if rse_attr.get('physgroup', '') in phys_group: return True else: return rse_attr.get('type', '') in ['SCRATCHDISK', 'MOCK', 'LOCALGROUPDISK', 'TEST']\ or issuer == 'root'\ or has_account_attribute(account=issuer, key='admin')
def perm_delete_global_account_limit(issuer, kwargs, session=None): """ Checks if an account can delete a global account limit. :param issuer: Account identifier which issues the command. :param kwargs: List of arguments for the action. :param session: The DB session to use :returns: True if account is allowed, otherwise False """ if _is_root(issuer) or has_account_attribute( account=issuer, key='admin', session=session): return True # # Check if user is a country admin # admin_in_country = set() # for kv in list_account_attributes(account=issuer, session=session): # if kv['key'].startswith('country-') and kv['value'] == 'admin': # admin_in_country.add(kv['key'].partition('-')[2]) # if admin_in_country: # resolved_rse_countries = {list_rse_attributes(rse_id=rse['rse_id'], session=session).get('country') # for rse in parse_expression(kwargs['rse_expression'], filter={'vo': issuer.vo}, session=session)} # if resolved_rse_countries.issubset(admin_in_country): # return True return False
def perm_add_rule(issuer, kwargs): """ Checks if an account can add a replication rule. :param issuer: Account identifier which issues the command. :param kwargs: List of arguments for the action. :returns: True if account is allowed, otherwise False """ rses = parse_expression(kwargs['rse_expression'], filter_={'vo': issuer.vo}) # Keep while sync is running so it can make rules on all RSEs if _is_root(issuer) and repr(kwargs['account']).startswith('sync_'): return True if isinstance(repr(issuer), basestring) and repr(issuer).startswith('sync_'): return True # Anyone can use _Temp RSEs if a lifetime is set and under a month all_temp = True for rse in rses: rse_attr = list_rse_attributes(rse_id=rse['id']) rse_type = rse_attr.get('cms_type', None) if rse_type not in ['temp']: all_temp = False if all_temp and kwargs[ 'lifetime'] is not None and kwargs['lifetime'] < 31 * 24 * 60 * 60: return True if kwargs['account'] == issuer and not kwargs['locked']: return True if _is_root(issuer) or has_account_attribute(account=issuer, key='admin'): return True return False
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.' )
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.')