diff --git a/docker/rucio_client/scripts/k8s_sync_sites.sh b/docker/rucio_client/scripts/k8s_sync_sites.sh index 6f858596..79ff956f 100755 --- a/docker/rucio_client/scripts/k8s_sync_sites.sh +++ b/docker/rucio_client/scripts/k8s_sync_sites.sh @@ -45,3 +45,5 @@ echo "Setting wmcore_output_tape attribute" ./setWmcoreTapeOutput echo "Update DDM quota" ./updateDDMQuota +echo "Set wmcore_transferor account limits" +./updateWMCoreTransferorQuotas \ No newline at end of file diff --git a/docker/rucio_client/scripts/rseUsageMetrics.py b/docker/rucio_client/scripts/rseUsageMetrics.py new file mode 100755 index 00000000..ef799b60 --- /dev/null +++ b/docker/rucio_client/scripts/rseUsageMetrics.py @@ -0,0 +1,34 @@ +#! /usr/bin/env python3 + +from rucio.client import Client + +client = Client() + +def get_sum_of_all_rse_statics(rse_expression): + rses = [rse["rse"] for rse in client.list_rses(rse_expression=rse_expression)] + result = 0 + for rse in rses: + static, _, _ = get_rse_usage(rse) + result += static + return result + + +def get_rse_usage(rse): + rse_usage = list(client.get_rse_usage(rse)) + + required_fields = {"static", "rucio", "expired"} + relevant_info = {} + + for source in rse_usage: + # Assuming source and used keys exist + relevant_info[source["source"]] = source["used"] + + if not required_fields.issubset(relevant_info.keys()): + print("Skipping {} due to lack of relevant key in rse".format(rse)) + print("{} is not a subset of {}".format(required_fields, relevant_info.keys())) + return 0, 0, 0 + + # Apparently, python integers do not overflow, https://docs.python.org/3/library/exceptions.html#OverflowError + + static, rucio, expired = relevant_info["static"], relevant_info["rucio"], relevant_info["expired"] + return static, rucio, expired \ No newline at end of file diff --git a/docker/rucio_client/scripts/updateDDMQuota b/docker/rucio_client/scripts/updateDDMQuota index 9179df7e..3f2b8d0c 100755 --- a/docker/rucio_client/scripts/updateDDMQuota +++ b/docker/rucio_client/scripts/updateDDMQuota @@ -1,10 +1,12 @@ #! /usr/bin/env python3 import math -from rucio.client import Client from tabulate import tabulate -client = Client() +from rucio.client import Client +from rseUsageMetrics import get_rse_usage, get_sum_of_all_rse_statics + +client = Client() # See the following link for documentation and please update it if you change the logic # https://cmsdmops.docs.cern.ch/Operators/ManageDMWeight/ @@ -31,37 +33,6 @@ def print_results(): print(tabulate(table_disk, headers=headers)) print(tabulate(table_tape, headers=headers)) - -def get_sum_of_all_rse_statics(rse_expression): - rses = [rse["rse"] for rse in client.list_rses(rse_expression=rse_expression)] - result = 0 - for rse in rses: - static, _, _ = get_rse_usage(rse) - result += static - return result - - -def get_rse_usage(rse): - rse_usage = list(client.get_rse_usage(rse)) - - required_fields = {"static", "rucio", "expired"} - relevant_info = {} - - for source in rse_usage: - # Assuming source and used keys exist - relevant_info[source["source"]] = source["used"] - - if not required_fields.issubset(relevant_info.keys()): - print("Skipping {} due to lack of relevant key in rse".format(rse)) - print("{} is not a subset of {}".format(required_fields, relevant_info.keys())) - return 0, 0, 0 - - # Apparently, python integers do not overflow, https://docs.python.org/3/library/exceptions.html#OverflowError - - static, rucio, expired = relevant_info["static"], relevant_info["rucio"], relevant_info["expired"] - return static, rucio, expired - - def calculate_dm_weights(rse_expression, static_weight, free_weight, expired_weight, make_quadratic): total_static = get_sum_of_all_rse_statics(rse_expression) diff --git a/docker/rucio_client/scripts/updateWMCoreTransferorQuotas b/docker/rucio_client/scripts/updateWMCoreTransferorQuotas new file mode 100755 index 00000000..efaa68d8 --- /dev/null +++ b/docker/rucio_client/scripts/updateWMCoreTransferorQuotas @@ -0,0 +1,70 @@ +#! /usr/bin/env python3 +""" +Script to update the global and the local quotas of the wmcore_transferor account. +Its global quota is set to X % of total disk capacity of T1 and T2 sites used in production +Its local quota is Y % of an RSE's (free + expired) space. +Global quota is introduced to have a global control on the usage of this account. +Local quotas are introduced to avoid the over-usage of certain RSEs. +""" + +from rucio.client import Client +from rseUsageMetrics import get_rse_usage, get_sum_of_all_rse_statics + +DEFAULT_GLOBAL_QUOTA_PCT = 15 +DEFAULT_LOCAL_QUOTA_PCT = 50 +DRY_RUN = False + +client = Client() +account = "wmcore_transferor" +rse_expression = "rse_type=DISK&cms_type=real&tier<3&tier>0" + +def get_global_quota_pct(): + try: + global_quota_pct = int(client.get_config(section="accounts", option="wmcore_transferor_global_quota_pct")) + except Exception as e: + global_quota_pct = DEFAULT_GLOBAL_QUOTA_PCT + return global_quota_pct + +def get_local_quota_pct(): + try: + local_quota_pct = int(client.get_config(section="accounts", option="wmcore_transferor_local_quota_pct")) + except Exception as e: + local_quota_pct = DEFAULT_LOCAL_QUOTA_PCT + return local_quota_pct + + +def set_global_quota(): + + total_disk_capacity = get_sum_of_all_rse_statics(rse_expression) + global_quota_pct = get_global_quota_pct() + global_quota_bytes = (total_disk_capacity * global_quota_pct) / 100 + if DRY_RUN: + print(f"DRY-RUN: Setting global quota of wmcore_transferor to {global_quota_bytes / 1e15} PB") + else: + print(f"Setting global quota of wmcore_transferor to {global_quota_bytes / 1e15} PB") + set_global_account_limit(account, rse_expression, global_quota_bytes) + +def set_local_quotas(): + + rses = [rse["rse"] for rse in client.list_rses(rse_expression=rse_expression)] + local_quota_pct = get_local_quota_pct() + + for rse in rses: + static, rucio, expired = get_rse_usage(rse) + free = static - rucio + + # Set it to 0 if it's negative + local_quota_bytes = max((((free + expired) * local_quota_pct) / 100), 0) + if DRY_RUN: + print(f"DRY-RUN: Setting local quota of wmcore_transferor at {rse} to {local_quota_bytes / 1e12} TB") + else: + print(f"Setting local quota of wmcore_transferor at {rse} to {local_quota_bytes / 1e12} TB") + set_local_account_limit(account, rse, local_quota_bytes) + + +def main(): + set_global_quota() + set_local_quotas() + +if __name__ == "__main__": + main()