rucio 37.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rucio might be problematic. Click here for more details.
- rucio/__init__.py +17 -0
- rucio/alembicrevision.py +15 -0
- rucio/cli/__init__.py +14 -0
- rucio/cli/account.py +216 -0
- rucio/cli/bin_legacy/__init__.py +13 -0
- rucio/cli/bin_legacy/rucio.py +2825 -0
- rucio/cli/bin_legacy/rucio_admin.py +2500 -0
- rucio/cli/command.py +272 -0
- rucio/cli/config.py +72 -0
- rucio/cli/did.py +191 -0
- rucio/cli/download.py +128 -0
- rucio/cli/lifetime_exception.py +33 -0
- rucio/cli/replica.py +162 -0
- rucio/cli/rse.py +293 -0
- rucio/cli/rule.py +158 -0
- rucio/cli/scope.py +40 -0
- rucio/cli/subscription.py +73 -0
- rucio/cli/upload.py +60 -0
- rucio/cli/utils.py +226 -0
- rucio/client/__init__.py +15 -0
- rucio/client/accountclient.py +432 -0
- rucio/client/accountlimitclient.py +183 -0
- rucio/client/baseclient.py +983 -0
- rucio/client/client.py +120 -0
- rucio/client/configclient.py +126 -0
- rucio/client/credentialclient.py +59 -0
- rucio/client/didclient.py +868 -0
- rucio/client/diracclient.py +56 -0
- rucio/client/downloadclient.py +1783 -0
- rucio/client/exportclient.py +44 -0
- rucio/client/fileclient.py +50 -0
- rucio/client/importclient.py +42 -0
- rucio/client/lifetimeclient.py +90 -0
- rucio/client/lockclient.py +109 -0
- rucio/client/metaconventionsclient.py +140 -0
- rucio/client/pingclient.py +44 -0
- rucio/client/replicaclient.py +452 -0
- rucio/client/requestclient.py +125 -0
- rucio/client/richclient.py +317 -0
- rucio/client/rseclient.py +746 -0
- rucio/client/ruleclient.py +294 -0
- rucio/client/scopeclient.py +90 -0
- rucio/client/subscriptionclient.py +173 -0
- rucio/client/touchclient.py +82 -0
- rucio/client/uploadclient.py +969 -0
- rucio/common/__init__.py +13 -0
- rucio/common/bittorrent.py +234 -0
- rucio/common/cache.py +111 -0
- rucio/common/checksum.py +168 -0
- rucio/common/client.py +122 -0
- rucio/common/config.py +788 -0
- rucio/common/constants.py +217 -0
- rucio/common/constraints.py +17 -0
- rucio/common/didtype.py +237 -0
- rucio/common/dumper/__init__.py +342 -0
- rucio/common/dumper/consistency.py +497 -0
- rucio/common/dumper/data_models.py +362 -0
- rucio/common/dumper/path_parsing.py +75 -0
- rucio/common/exception.py +1208 -0
- rucio/common/extra.py +31 -0
- rucio/common/logging.py +420 -0
- rucio/common/pcache.py +1409 -0
- rucio/common/plugins.py +185 -0
- rucio/common/policy.py +93 -0
- rucio/common/schema/__init__.py +200 -0
- rucio/common/schema/generic.py +416 -0
- rucio/common/schema/generic_multi_vo.py +395 -0
- rucio/common/stomp_utils.py +423 -0
- rucio/common/stopwatch.py +55 -0
- rucio/common/test_rucio_server.py +154 -0
- rucio/common/types.py +483 -0
- rucio/common/utils.py +1688 -0
- rucio/core/__init__.py +13 -0
- rucio/core/account.py +496 -0
- rucio/core/account_counter.py +236 -0
- rucio/core/account_limit.py +425 -0
- rucio/core/authentication.py +620 -0
- rucio/core/config.py +437 -0
- rucio/core/credential.py +224 -0
- rucio/core/did.py +3004 -0
- rucio/core/did_meta_plugins/__init__.py +252 -0
- rucio/core/did_meta_plugins/did_column_meta.py +331 -0
- rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
- rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
- rucio/core/did_meta_plugins/filter_engine.py +672 -0
- rucio/core/did_meta_plugins/json_meta.py +240 -0
- rucio/core/did_meta_plugins/mongo_meta.py +229 -0
- rucio/core/did_meta_plugins/postgres_meta.py +352 -0
- rucio/core/dirac.py +237 -0
- rucio/core/distance.py +187 -0
- rucio/core/exporter.py +59 -0
- rucio/core/heartbeat.py +363 -0
- rucio/core/identity.py +301 -0
- rucio/core/importer.py +260 -0
- rucio/core/lifetime_exception.py +377 -0
- rucio/core/lock.py +577 -0
- rucio/core/message.py +288 -0
- rucio/core/meta_conventions.py +203 -0
- rucio/core/monitor.py +448 -0
- rucio/core/naming_convention.py +195 -0
- rucio/core/nongrid_trace.py +136 -0
- rucio/core/oidc.py +1463 -0
- rucio/core/permission/__init__.py +161 -0
- rucio/core/permission/generic.py +1124 -0
- rucio/core/permission/generic_multi_vo.py +1144 -0
- rucio/core/quarantined_replica.py +224 -0
- rucio/core/replica.py +4483 -0
- rucio/core/replica_sorter.py +362 -0
- rucio/core/request.py +3091 -0
- rucio/core/rse.py +2079 -0
- rucio/core/rse_counter.py +185 -0
- rucio/core/rse_expression_parser.py +459 -0
- rucio/core/rse_selector.py +304 -0
- rucio/core/rule.py +4484 -0
- rucio/core/rule_grouping.py +1620 -0
- rucio/core/scope.py +181 -0
- rucio/core/subscription.py +362 -0
- rucio/core/topology.py +490 -0
- rucio/core/trace.py +375 -0
- rucio/core/transfer.py +1531 -0
- rucio/core/vo.py +169 -0
- rucio/core/volatile_replica.py +151 -0
- rucio/daemons/__init__.py +13 -0
- rucio/daemons/abacus/__init__.py +13 -0
- rucio/daemons/abacus/account.py +116 -0
- rucio/daemons/abacus/collection_replica.py +124 -0
- rucio/daemons/abacus/rse.py +117 -0
- rucio/daemons/atropos/__init__.py +13 -0
- rucio/daemons/atropos/atropos.py +242 -0
- rucio/daemons/auditor/__init__.py +289 -0
- rucio/daemons/auditor/hdfs.py +97 -0
- rucio/daemons/auditor/srmdumps.py +355 -0
- rucio/daemons/automatix/__init__.py +13 -0
- rucio/daemons/automatix/automatix.py +304 -0
- rucio/daemons/badreplicas/__init__.py +13 -0
- rucio/daemons/badreplicas/minos.py +322 -0
- rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
- rucio/daemons/badreplicas/necromancer.py +196 -0
- rucio/daemons/bb8/__init__.py +13 -0
- rucio/daemons/bb8/bb8.py +353 -0
- rucio/daemons/bb8/common.py +759 -0
- rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
- rucio/daemons/bb8/t2_background_rebalance.py +153 -0
- rucio/daemons/cache/__init__.py +13 -0
- rucio/daemons/cache/consumer.py +133 -0
- rucio/daemons/common.py +405 -0
- rucio/daemons/conveyor/__init__.py +13 -0
- rucio/daemons/conveyor/common.py +562 -0
- rucio/daemons/conveyor/finisher.py +529 -0
- rucio/daemons/conveyor/poller.py +394 -0
- rucio/daemons/conveyor/preparer.py +205 -0
- rucio/daemons/conveyor/receiver.py +179 -0
- rucio/daemons/conveyor/stager.py +133 -0
- rucio/daemons/conveyor/submitter.py +403 -0
- rucio/daemons/conveyor/throttler.py +532 -0
- rucio/daemons/follower/__init__.py +13 -0
- rucio/daemons/follower/follower.py +101 -0
- rucio/daemons/hermes/__init__.py +13 -0
- rucio/daemons/hermes/hermes.py +534 -0
- rucio/daemons/judge/__init__.py +13 -0
- rucio/daemons/judge/cleaner.py +159 -0
- rucio/daemons/judge/evaluator.py +185 -0
- rucio/daemons/judge/injector.py +162 -0
- rucio/daemons/judge/repairer.py +154 -0
- rucio/daemons/oauthmanager/__init__.py +13 -0
- rucio/daemons/oauthmanager/oauthmanager.py +198 -0
- rucio/daemons/reaper/__init__.py +13 -0
- rucio/daemons/reaper/dark_reaper.py +282 -0
- rucio/daemons/reaper/reaper.py +739 -0
- rucio/daemons/replicarecoverer/__init__.py +13 -0
- rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
- rucio/daemons/rsedecommissioner/__init__.py +13 -0
- rucio/daemons/rsedecommissioner/config.py +81 -0
- rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
- rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
- rucio/daemons/rsedecommissioner/profiles/generic.py +452 -0
- rucio/daemons/rsedecommissioner/profiles/types.py +93 -0
- rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
- rucio/daemons/storage/__init__.py +13 -0
- rucio/daemons/storage/consistency/__init__.py +13 -0
- rucio/daemons/storage/consistency/actions.py +848 -0
- rucio/daemons/tracer/__init__.py +13 -0
- rucio/daemons/tracer/kronos.py +511 -0
- rucio/daemons/transmogrifier/__init__.py +13 -0
- rucio/daemons/transmogrifier/transmogrifier.py +762 -0
- rucio/daemons/undertaker/__init__.py +13 -0
- rucio/daemons/undertaker/undertaker.py +137 -0
- rucio/db/__init__.py +13 -0
- rucio/db/sqla/__init__.py +52 -0
- rucio/db/sqla/constants.py +206 -0
- rucio/db/sqla/migrate_repo/__init__.py +13 -0
- rucio/db/sqla/migrate_repo/env.py +110 -0
- rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
- rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
- rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
- rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
- rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
- rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
- rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
- rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
- rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
- rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
- rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
- rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
- rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
- rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
- rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
- rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
- rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
- rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
- rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
- rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
- rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
- rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
- rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
- rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
- rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
- rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
- rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
- rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
- rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
- rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
- rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
- rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
- rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
- rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
- rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
- rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
- rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
- rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
- rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
- rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
- rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
- rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
- rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
- rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
- rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
- rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
- rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
- rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
- rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
- rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
- rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
- rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
- rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
- rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
- rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
- rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
- rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
- rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
- rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
- rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
- rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
- rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
- rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
- rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
- rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
- rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
- rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
- rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
- rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
- rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
- rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
- rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
- rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
- rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
- rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
- rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
- rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
- rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
- rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
- rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
- rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
- rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
- rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
- rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
- rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
- rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
- rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
- rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
- rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
- rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
- rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
- rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
- rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
- rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
- rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
- rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
- rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
- rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
- rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
- rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
- rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
- rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
- rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
- rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
- rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
- rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
- rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
- rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
- rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
- rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
- rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
- rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
- rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
- rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
- rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
- rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
- rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
- rucio/db/sqla/models.py +1743 -0
- rucio/db/sqla/sautils.py +55 -0
- rucio/db/sqla/session.py +529 -0
- rucio/db/sqla/types.py +206 -0
- rucio/db/sqla/util.py +543 -0
- rucio/gateway/__init__.py +13 -0
- rucio/gateway/account.py +345 -0
- rucio/gateway/account_limit.py +363 -0
- rucio/gateway/authentication.py +381 -0
- rucio/gateway/config.py +227 -0
- rucio/gateway/credential.py +70 -0
- rucio/gateway/did.py +987 -0
- rucio/gateway/dirac.py +83 -0
- rucio/gateway/exporter.py +60 -0
- rucio/gateway/heartbeat.py +76 -0
- rucio/gateway/identity.py +189 -0
- rucio/gateway/importer.py +46 -0
- rucio/gateway/lifetime_exception.py +121 -0
- rucio/gateway/lock.py +153 -0
- rucio/gateway/meta_conventions.py +98 -0
- rucio/gateway/permission.py +74 -0
- rucio/gateway/quarantined_replica.py +79 -0
- rucio/gateway/replica.py +538 -0
- rucio/gateway/request.py +330 -0
- rucio/gateway/rse.py +632 -0
- rucio/gateway/rule.py +437 -0
- rucio/gateway/scope.py +100 -0
- rucio/gateway/subscription.py +280 -0
- rucio/gateway/vo.py +126 -0
- rucio/rse/__init__.py +96 -0
- rucio/rse/protocols/__init__.py +13 -0
- rucio/rse/protocols/bittorrent.py +194 -0
- rucio/rse/protocols/cache.py +111 -0
- rucio/rse/protocols/dummy.py +100 -0
- rucio/rse/protocols/gfal.py +708 -0
- rucio/rse/protocols/globus.py +243 -0
- rucio/rse/protocols/http_cache.py +82 -0
- rucio/rse/protocols/mock.py +123 -0
- rucio/rse/protocols/ngarc.py +209 -0
- rucio/rse/protocols/posix.py +250 -0
- rucio/rse/protocols/protocol.py +361 -0
- rucio/rse/protocols/rclone.py +365 -0
- rucio/rse/protocols/rfio.py +145 -0
- rucio/rse/protocols/srm.py +338 -0
- rucio/rse/protocols/ssh.py +414 -0
- rucio/rse/protocols/storm.py +195 -0
- rucio/rse/protocols/webdav.py +594 -0
- rucio/rse/protocols/xrootd.py +302 -0
- rucio/rse/rsemanager.py +881 -0
- rucio/rse/translation.py +260 -0
- rucio/tests/__init__.py +13 -0
- rucio/tests/common.py +280 -0
- rucio/tests/common_server.py +149 -0
- rucio/transfertool/__init__.py +13 -0
- rucio/transfertool/bittorrent.py +200 -0
- rucio/transfertool/bittorrent_driver.py +50 -0
- rucio/transfertool/bittorrent_driver_qbittorrent.py +134 -0
- rucio/transfertool/fts3.py +1600 -0
- rucio/transfertool/fts3_plugins.py +152 -0
- rucio/transfertool/globus.py +201 -0
- rucio/transfertool/globus_library.py +181 -0
- rucio/transfertool/mock.py +89 -0
- rucio/transfertool/transfertool.py +221 -0
- rucio/vcsversion.py +11 -0
- rucio/version.py +45 -0
- rucio/web/__init__.py +13 -0
- rucio/web/rest/__init__.py +13 -0
- rucio/web/rest/flaskapi/__init__.py +13 -0
- rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
- rucio/web/rest/flaskapi/v1/__init__.py +13 -0
- rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
- rucio/web/rest/flaskapi/v1/accounts.py +1103 -0
- rucio/web/rest/flaskapi/v1/archives.py +102 -0
- rucio/web/rest/flaskapi/v1/auth.py +1644 -0
- rucio/web/rest/flaskapi/v1/common.py +426 -0
- rucio/web/rest/flaskapi/v1/config.py +304 -0
- rucio/web/rest/flaskapi/v1/credentials.py +213 -0
- rucio/web/rest/flaskapi/v1/dids.py +2340 -0
- rucio/web/rest/flaskapi/v1/dirac.py +116 -0
- rucio/web/rest/flaskapi/v1/export.py +75 -0
- rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
- rucio/web/rest/flaskapi/v1/identities.py +285 -0
- rucio/web/rest/flaskapi/v1/import.py +132 -0
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
- rucio/web/rest/flaskapi/v1/locks.py +358 -0
- rucio/web/rest/flaskapi/v1/main.py +91 -0
- rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
- rucio/web/rest/flaskapi/v1/metrics.py +36 -0
- rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
- rucio/web/rest/flaskapi/v1/ping.py +88 -0
- rucio/web/rest/flaskapi/v1/redirect.py +366 -0
- rucio/web/rest/flaskapi/v1/replicas.py +1894 -0
- rucio/web/rest/flaskapi/v1/requests.py +998 -0
- rucio/web/rest/flaskapi/v1/rses.py +2250 -0
- rucio/web/rest/flaskapi/v1/rules.py +854 -0
- rucio/web/rest/flaskapi/v1/scopes.py +159 -0
- rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
- rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
- rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
- rucio/web/rest/flaskapi/v1/traces.py +137 -0
- rucio/web/rest/flaskapi/v1/types.py +20 -0
- rucio/web/rest/flaskapi/v1/vos.py +278 -0
- rucio/web/rest/main.py +18 -0
- rucio/web/rest/metrics.py +27 -0
- rucio/web/rest/ping.py +27 -0
- rucio-37.0.0rc1.data/data/rucio/etc/alembic.ini.template +71 -0
- rucio-37.0.0rc1.data/data/rucio/etc/alembic_offline.ini.template +74 -0
- rucio-37.0.0rc1.data/data/rucio/etc/globus-config.yml.template +5 -0
- rucio-37.0.0rc1.data/data/rucio/etc/ldap.cfg.template +30 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
- rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.atlas.client.template +43 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.template +241 -0
- rucio-37.0.0rc1.data/data/rucio/etc/rucio_multi_vo.cfg.template +217 -0
- rucio-37.0.0rc1.data/data/rucio/requirements.server.txt +297 -0
- rucio-37.0.0rc1.data/data/rucio/tools/bootstrap.py +34 -0
- rucio-37.0.0rc1.data/data/rucio/tools/merge_rucio_configs.py +144 -0
- rucio-37.0.0rc1.data/data/rucio/tools/reset_database.py +40 -0
- rucio-37.0.0rc1.data/scripts/rucio +133 -0
- rucio-37.0.0rc1.data/scripts/rucio-abacus-account +74 -0
- rucio-37.0.0rc1.data/scripts/rucio-abacus-collection-replica +46 -0
- rucio-37.0.0rc1.data/scripts/rucio-abacus-rse +78 -0
- rucio-37.0.0rc1.data/scripts/rucio-admin +97 -0
- rucio-37.0.0rc1.data/scripts/rucio-atropos +60 -0
- rucio-37.0.0rc1.data/scripts/rucio-auditor +206 -0
- rucio-37.0.0rc1.data/scripts/rucio-automatix +50 -0
- rucio-37.0.0rc1.data/scripts/rucio-bb8 +57 -0
- rucio-37.0.0rc1.data/scripts/rucio-cache-client +141 -0
- rucio-37.0.0rc1.data/scripts/rucio-cache-consumer +42 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-finisher +58 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-poller +66 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-preparer +37 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-receiver +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-stager +76 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-submitter +139 -0
- rucio-37.0.0rc1.data/scripts/rucio-conveyor-throttler +104 -0
- rucio-37.0.0rc1.data/scripts/rucio-dark-reaper +53 -0
- rucio-37.0.0rc1.data/scripts/rucio-dumper +160 -0
- rucio-37.0.0rc1.data/scripts/rucio-follower +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-hermes +54 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-cleaner +89 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-evaluator +137 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-injector +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-judge-repairer +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-kronos +44 -0
- rucio-37.0.0rc1.data/scripts/rucio-minos +53 -0
- rucio-37.0.0rc1.data/scripts/rucio-minos-temporary-expiration +50 -0
- rucio-37.0.0rc1.data/scripts/rucio-necromancer +120 -0
- rucio-37.0.0rc1.data/scripts/rucio-oauth-manager +63 -0
- rucio-37.0.0rc1.data/scripts/rucio-reaper +83 -0
- rucio-37.0.0rc1.data/scripts/rucio-replica-recoverer +248 -0
- rucio-37.0.0rc1.data/scripts/rucio-rse-decommissioner +66 -0
- rucio-37.0.0rc1.data/scripts/rucio-storage-consistency-actions +74 -0
- rucio-37.0.0rc1.data/scripts/rucio-transmogrifier +77 -0
- rucio-37.0.0rc1.data/scripts/rucio-undertaker +76 -0
- rucio-37.0.0rc1.dist-info/METADATA +92 -0
- rucio-37.0.0rc1.dist-info/RECORD +487 -0
- rucio-37.0.0rc1.dist-info/WHEEL +5 -0
- rucio-37.0.0rc1.dist-info/licenses/AUTHORS.rst +100 -0
- rucio-37.0.0rc1.dist-info/licenses/LICENSE +201 -0
- rucio-37.0.0rc1.dist-info/top_level.txt +1 -0
rucio/core/monitor.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Graphite and prometheus metrics
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import atexit
|
|
20
|
+
import logging
|
|
21
|
+
import os
|
|
22
|
+
import string
|
|
23
|
+
from abc import abstractmethod
|
|
24
|
+
from datetime import datetime, timedelta
|
|
25
|
+
from functools import wraps
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from threading import Lock
|
|
28
|
+
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
|
29
|
+
|
|
30
|
+
from prometheus_client import REGISTRY, CollectorRegistry, Counter, Gauge, Histogram, generate_latest, multiprocess, push_to_gateway, start_http_server, values
|
|
31
|
+
from statsd import StatsClient
|
|
32
|
+
|
|
33
|
+
import __main__ as main
|
|
34
|
+
from rucio.common.config import config_get, config_get_bool, config_get_int
|
|
35
|
+
from rucio.common.stopwatch import Stopwatch
|
|
36
|
+
from rucio.common.utils import retrying
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
40
|
+
|
|
41
|
+
from rucio.common.types import LoggerFunction
|
|
42
|
+
|
|
43
|
+
_T = TypeVar('_T')
|
|
44
|
+
_M = TypeVar('_M', bound="_MultiMetric")
|
|
45
|
+
|
|
46
|
+
PROMETHEUS_MULTIPROC_DIR = os.environ.get('PROMETHEUS_MULTIPROC_DIR', os.environ.get('prometheus_multiproc_dir', None))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def cleanup_prometheus_files_at_exit() -> None:
|
|
50
|
+
if PROMETHEUS_MULTIPROC_DIR:
|
|
51
|
+
multiprocess.mark_process_dead(os.getpid())
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MultiprocessMutexValue(values.MultiProcessValue()):
|
|
55
|
+
"""
|
|
56
|
+
MultiprocessValue protected by mutex
|
|
57
|
+
|
|
58
|
+
Rucio usually is deployed using the apache MPM module, which means that it both uses multiple
|
|
59
|
+
subprocesses, and multiple threads per subprocess.
|
|
60
|
+
"""
|
|
61
|
+
def __init__(self, *args, **kwargs):
|
|
62
|
+
super().__init__(*args, **kwargs)
|
|
63
|
+
self._lock = Lock()
|
|
64
|
+
|
|
65
|
+
def inc(self, amount: float) -> None:
|
|
66
|
+
with self._lock:
|
|
67
|
+
return super().inc(amount)
|
|
68
|
+
|
|
69
|
+
def set(self, value: float) -> None:
|
|
70
|
+
with self._lock:
|
|
71
|
+
return super().set(value)
|
|
72
|
+
|
|
73
|
+
def get(self) -> float:
|
|
74
|
+
with self._lock:
|
|
75
|
+
return super().get()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if PROMETHEUS_MULTIPROC_DIR:
|
|
79
|
+
os.makedirs(PROMETHEUS_MULTIPROC_DIR, exist_ok=True)
|
|
80
|
+
values.ValueClass = MultiprocessMutexValue
|
|
81
|
+
|
|
82
|
+
atexit.register(cleanup_prometheus_files_at_exit)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
SERVER = config_get('monitor', 'carbon_server', raise_exception=False, default=None)
|
|
86
|
+
PORT = config_get_int('monitor', 'carbon_port', raise_exception=False, default=8125)
|
|
87
|
+
SCOPE = config_get('monitor', 'user_scope', raise_exception=False, default='rucio')
|
|
88
|
+
STATSD_CLIENT = None
|
|
89
|
+
if SERVER is not None:
|
|
90
|
+
STATSD_CLIENT = StatsClient(host=SERVER, port=PORT, prefix=SCOPE)
|
|
91
|
+
|
|
92
|
+
ENABLE_METRICS = config_get_bool('monitor', 'enable_metrics', raise_exception=False, default=False)
|
|
93
|
+
if ENABLE_METRICS:
|
|
94
|
+
METRICS_PORT = config_get_int('monitor', 'metrics_port', raise_exception=False, default=8080)
|
|
95
|
+
start_http_server(METRICS_PORT, registry=REGISTRY)
|
|
96
|
+
|
|
97
|
+
COUNTERS = {}
|
|
98
|
+
GAUGES = {}
|
|
99
|
+
TIMINGS = {}
|
|
100
|
+
METRICS_LOCK = Lock()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
_HISTOGRAM_DEFAULT_BUCKETS = Histogram.DEFAULT_BUCKETS
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _cleanup_old_prometheus_files(
|
|
107
|
+
path: str,
|
|
108
|
+
file_pattern: str,
|
|
109
|
+
cleanup_delay: float,
|
|
110
|
+
logger: "LoggerFunction"
|
|
111
|
+
) -> None:
|
|
112
|
+
"""cleanup behind processes which didn't finish gracefully."""
|
|
113
|
+
|
|
114
|
+
oldest_accepted_mtime = datetime.now() - timedelta(seconds=cleanup_delay)
|
|
115
|
+
for file in Path(path).glob(file_pattern):
|
|
116
|
+
if not file.is_file():
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
file_mtime = datetime.fromtimestamp(file.stat().st_mtime)
|
|
120
|
+
|
|
121
|
+
if file_mtime < oldest_accepted_mtime:
|
|
122
|
+
logger(logging.INFO, 'Cleaning up prometheus db file %s', file)
|
|
123
|
+
try:
|
|
124
|
+
os.remove(file)
|
|
125
|
+
except FileNotFoundError:
|
|
126
|
+
# Probably file already removed by another concurrent process
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def cleanup_old_prometheus_files(logger: "LoggerFunction" = logging.log) -> None:
|
|
131
|
+
path = PROMETHEUS_MULTIPROC_DIR
|
|
132
|
+
if path:
|
|
133
|
+
_cleanup_old_prometheus_files(path, file_pattern='gauge_live*.db', cleanup_delay=timedelta(hours=1).total_seconds(), logger=logger)
|
|
134
|
+
_cleanup_old_prometheus_files(path, file_pattern='*.db', cleanup_delay=timedelta(days=7).total_seconds(), logger=logger)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@retrying(retry_on_exception=lambda _: True,
|
|
138
|
+
wait_fixed=500,
|
|
139
|
+
stop_max_attempt_number=2)
|
|
140
|
+
def generate_prometheus_metrics() -> bytes:
|
|
141
|
+
cleanup_old_prometheus_files()
|
|
142
|
+
|
|
143
|
+
registry = CollectorRegistry()
|
|
144
|
+
multiprocess.MultiProcessCollector(registry)
|
|
145
|
+
return generate_latest(registry)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class _MultiMetric:
|
|
149
|
+
"""
|
|
150
|
+
Thin wrapper class allowing to record both prometheus and statsd metrics.
|
|
151
|
+
|
|
152
|
+
Inspired by the prometheus metric behavior: uses labels to parametrize metrics.
|
|
153
|
+
In case of statsd, metrics are formatted using str.format(**labels). The prometheus
|
|
154
|
+
ones using metric.labels(**labels) calls.
|
|
155
|
+
|
|
156
|
+
If the prometheus metric string is not provided, it is derived from the statsd one.
|
|
157
|
+
|
|
158
|
+
If `labelnames` is not provided, tries to extract them from the metric name by parsing
|
|
159
|
+
it as a format string.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(
|
|
163
|
+
self,
|
|
164
|
+
statsd: str,
|
|
165
|
+
prom: Optional[Union[str, Counter, Gauge, Histogram]] = None,
|
|
166
|
+
documentation: Optional[str] = None,
|
|
167
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
168
|
+
registry: Optional[CollectorRegistry] = None
|
|
169
|
+
):
|
|
170
|
+
"""
|
|
171
|
+
:param statsd: a string, eventually with keyword placeholders for the str.format(**labels) call
|
|
172
|
+
:param prom: a string prometheus metric name; or an instantiated prometheus metric object
|
|
173
|
+
"""
|
|
174
|
+
self._registry = registry or REGISTRY
|
|
175
|
+
self._documentation = documentation or ''
|
|
176
|
+
self._statsd = statsd
|
|
177
|
+
if not prom:
|
|
178
|
+
parsed_format = list(string.Formatter().parse(statsd))
|
|
179
|
+
# automatically generate a prometheus metric name
|
|
180
|
+
#
|
|
181
|
+
# remove '.{label}' from the string for each `label`
|
|
182
|
+
# substituted dots with underscores
|
|
183
|
+
if labelnames is None:
|
|
184
|
+
labelnames = tuple(field_name for _, field_name, _, _ in parsed_format if field_name)
|
|
185
|
+
prom = ''.join(literal_text.rstrip('.').replace('.', '_') for literal_text, *_ in parsed_format)
|
|
186
|
+
labelnames = labelnames or ()
|
|
187
|
+
if isinstance(prom, str):
|
|
188
|
+
self._prom = self.init_prometheus_metric(prom, self._documentation, labelnames=labelnames)
|
|
189
|
+
else:
|
|
190
|
+
self._prom = prom
|
|
191
|
+
|
|
192
|
+
self._labelnames = labelnames
|
|
193
|
+
|
|
194
|
+
@abstractmethod
|
|
195
|
+
def init_prometheus_metric(self, name: str, documentation: Optional[str], labelnames: 'Sequence[str]' = ()):
|
|
196
|
+
pass
|
|
197
|
+
|
|
198
|
+
def labels(self: _M, **labelkwargs) -> _M:
|
|
199
|
+
if not labelkwargs:
|
|
200
|
+
return self
|
|
201
|
+
|
|
202
|
+
return self.__class__(
|
|
203
|
+
prom=self._prom.labels(**labelkwargs), # type: ignore
|
|
204
|
+
statsd=self._statsd.format(**labelkwargs),
|
|
205
|
+
documentation=self._documentation,
|
|
206
|
+
labelnames=self._labelnames,
|
|
207
|
+
registry=self._registry,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class _MultiCounter(_MultiMetric):
|
|
212
|
+
|
|
213
|
+
def inc(self, delta: int = 1) -> None:
|
|
214
|
+
delta = abs(delta)
|
|
215
|
+
self._prom.inc(delta) # type: ignore
|
|
216
|
+
if STATSD_CLIENT:
|
|
217
|
+
STATSD_CLIENT.incr(self._statsd, delta)
|
|
218
|
+
|
|
219
|
+
def init_prometheus_metric(self, name: str, documentation: str, labelnames: 'Sequence[str]' = ()) -> Counter:
|
|
220
|
+
return Counter(name, documentation, labelnames=labelnames, registry=self._registry)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class _MultiGauge(_MultiMetric):
|
|
224
|
+
|
|
225
|
+
def set(self, value) -> None:
|
|
226
|
+
self._prom.set(value) # type: ignore
|
|
227
|
+
if STATSD_CLIENT:
|
|
228
|
+
STATSD_CLIENT.gauge(self._statsd, value)
|
|
229
|
+
|
|
230
|
+
def init_prometheus_metric(self, name: str, documentation: str, labelnames: 'Sequence[str]' = ()) -> Gauge:
|
|
231
|
+
return Gauge(name, documentation, labelnames=labelnames, registry=self._registry)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class _MultiTiming(_MultiMetric):
|
|
235
|
+
|
|
236
|
+
def __init__(
|
|
237
|
+
self,
|
|
238
|
+
statsd: str,
|
|
239
|
+
prom: Optional[str] = None,
|
|
240
|
+
documentation: Optional[str] = None,
|
|
241
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
242
|
+
registry: Optional[CollectorRegistry] = None,
|
|
243
|
+
buckets: 'Iterable[float]' = _HISTOGRAM_DEFAULT_BUCKETS,
|
|
244
|
+
) -> None:
|
|
245
|
+
self._stopwatch = None
|
|
246
|
+
self._histogram_buckets = tuple(buckets)
|
|
247
|
+
super().__init__(statsd, prom, documentation, labelnames, registry)
|
|
248
|
+
|
|
249
|
+
def observe(self, value: float) -> None:
|
|
250
|
+
self._prom.observe(value) # type: ignore
|
|
251
|
+
if STATSD_CLIENT:
|
|
252
|
+
STATSD_CLIENT.timing(self._statsd, value * 1000)
|
|
253
|
+
|
|
254
|
+
def init_prometheus_metric(self, name: str, documentation: str, labelnames: 'Sequence[str]' = ()) -> Histogram:
|
|
255
|
+
return Histogram(name, documentation, labelnames=labelnames, registry=self._registry, buckets=self._histogram_buckets)
|
|
256
|
+
|
|
257
|
+
def __enter__(self):
|
|
258
|
+
self._stopwatch = Stopwatch()
|
|
259
|
+
return self
|
|
260
|
+
|
|
261
|
+
def __exit__(self, typ, value, tb):
|
|
262
|
+
if self._stopwatch:
|
|
263
|
+
self._stopwatch.stop()
|
|
264
|
+
self.observe(self._stopwatch.elapsed)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _fetch_or_create_metric(
|
|
268
|
+
name: str,
|
|
269
|
+
labelnames: Optional['Sequence[str]'],
|
|
270
|
+
container: dict[str, _T],
|
|
271
|
+
factory: 'Callable[[str, Optional[Sequence[str]]], _T]'
|
|
272
|
+
) -> "_T":
|
|
273
|
+
metric = container.get(name)
|
|
274
|
+
if not metric:
|
|
275
|
+
with METRICS_LOCK:
|
|
276
|
+
metric = container.get(name)
|
|
277
|
+
if not metric:
|
|
278
|
+
container[name] = metric = factory(name, labelnames)
|
|
279
|
+
return metric
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _fetch_or_create_counter(
|
|
283
|
+
name: str,
|
|
284
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
285
|
+
documentation: Optional[str] = None,
|
|
286
|
+
registry: Optional[CollectorRegistry] = None,
|
|
287
|
+
) -> _MultiCounter:
|
|
288
|
+
return _fetch_or_create_metric(
|
|
289
|
+
name=name,
|
|
290
|
+
labelnames=labelnames,
|
|
291
|
+
container=COUNTERS,
|
|
292
|
+
factory=lambda _name, _labelnames: _MultiCounter(statsd=_name, labelnames=_labelnames,
|
|
293
|
+
documentation=documentation, registry=registry)
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def _fetch_or_create_gauge(
|
|
298
|
+
name: str,
|
|
299
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
300
|
+
documentation: Optional[str] = None,
|
|
301
|
+
registry: Optional[CollectorRegistry] = None,
|
|
302
|
+
) -> _MultiGauge:
|
|
303
|
+
return _fetch_or_create_metric(
|
|
304
|
+
name=name,
|
|
305
|
+
labelnames=labelnames,
|
|
306
|
+
container=GAUGES,
|
|
307
|
+
factory=lambda _name, _labelnames: _MultiGauge(statsd=_name, labelnames=_labelnames,
|
|
308
|
+
documentation=documentation, registry=registry)
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _fetch_or_create_timer(
|
|
313
|
+
name: str,
|
|
314
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
315
|
+
documentation: Optional[str] = None,
|
|
316
|
+
registry: Optional[CollectorRegistry] = None,
|
|
317
|
+
buckets: 'Iterable[float]' = _HISTOGRAM_DEFAULT_BUCKETS
|
|
318
|
+
) -> _MultiTiming:
|
|
319
|
+
return _fetch_or_create_metric(
|
|
320
|
+
name=name,
|
|
321
|
+
labelnames=labelnames,
|
|
322
|
+
container=TIMINGS,
|
|
323
|
+
factory=lambda _name, _labels: _MultiTiming(statsd=_name, labelnames=_labels, documentation=documentation,
|
|
324
|
+
registry=registry, buckets=buckets)
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class MetricManager:
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
Wrapper for metrics which prefixes them automatically with the given prefix or,
|
|
332
|
+
alternatively, with the path of the module.
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
def __init__(self, prefix: Optional[str] = None, module: Optional[str] = None,
|
|
336
|
+
registry: Optional[CollectorRegistry] = None, push_gateways: Optional['Sequence[str]'] = None):
|
|
337
|
+
if prefix:
|
|
338
|
+
self.prefix = prefix
|
|
339
|
+
elif module:
|
|
340
|
+
self.prefix = module
|
|
341
|
+
else:
|
|
342
|
+
self.prefix = None
|
|
343
|
+
self.registry = registry or REGISTRY
|
|
344
|
+
self.push_gateways = push_gateways or []
|
|
345
|
+
|
|
346
|
+
def full_name(self, name: str) -> str:
|
|
347
|
+
if self.prefix:
|
|
348
|
+
return f'{self.prefix}.{name}'
|
|
349
|
+
return name
|
|
350
|
+
|
|
351
|
+
def get_registry(self) -> CollectorRegistry:
|
|
352
|
+
return self.registry
|
|
353
|
+
|
|
354
|
+
def counter(
|
|
355
|
+
self,
|
|
356
|
+
name: str,
|
|
357
|
+
*,
|
|
358
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
359
|
+
documentation: Optional[str] = None,
|
|
360
|
+
) -> _MultiCounter:
|
|
361
|
+
"""
|
|
362
|
+
Log a counter.
|
|
363
|
+
|
|
364
|
+
:param name: The name (suffix) of the counter to be retrieved
|
|
365
|
+
:param labelnames: optional labels used to parametrize the metric
|
|
366
|
+
:param documentation: optional prometheus documentation for this metric
|
|
367
|
+
"""
|
|
368
|
+
return _fetch_or_create_counter(name=self.full_name(name), labelnames=labelnames, documentation=documentation)
|
|
369
|
+
|
|
370
|
+
def gauge(
|
|
371
|
+
self,
|
|
372
|
+
name: str,
|
|
373
|
+
*,
|
|
374
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
375
|
+
documentation: Optional[str] = None,
|
|
376
|
+
) -> _MultiGauge:
|
|
377
|
+
"""
|
|
378
|
+
Log gauge information for a single stat
|
|
379
|
+
|
|
380
|
+
:param name: The name (suffix) of the counter to be retrieved
|
|
381
|
+
:param labelnames: optional labels used to parametrize the metric
|
|
382
|
+
:param documentation: optional prometheus documentation for this metric
|
|
383
|
+
"""
|
|
384
|
+
return _fetch_or_create_gauge(name=self.full_name(name), labelnames=labelnames, documentation=documentation)
|
|
385
|
+
|
|
386
|
+
def timer(
|
|
387
|
+
self,
|
|
388
|
+
name: str,
|
|
389
|
+
*,
|
|
390
|
+
labelnames: Optional['Sequence[str]'] = None,
|
|
391
|
+
documentation: Optional[str] = None,
|
|
392
|
+
buckets: 'Iterable[float]' = _HISTOGRAM_DEFAULT_BUCKETS
|
|
393
|
+
) -> _MultiTiming:
|
|
394
|
+
"""
|
|
395
|
+
Log a time measurement.
|
|
396
|
+
|
|
397
|
+
:param name: The name (suffix) of the counter to be retrieved
|
|
398
|
+
:param labelnames: optional labels used to parametrize the metric
|
|
399
|
+
:param documentation: optional prometheus documentation for this metric
|
|
400
|
+
:param buckets: Optional iterable of histogram bucket separators.
|
|
401
|
+
"""
|
|
402
|
+
return _fetch_or_create_timer(name=self.full_name(name), labelnames=labelnames, documentation=documentation, buckets=buckets)
|
|
403
|
+
|
|
404
|
+
def time_it(self, original_function=None, *, buckets=_HISTOGRAM_DEFAULT_BUCKETS):
|
|
405
|
+
"""
|
|
406
|
+
Function decorator which records a timer: the amount of time spent in the function.
|
|
407
|
+
"""
|
|
408
|
+
def _decorator(func):
|
|
409
|
+
@wraps(func)
|
|
410
|
+
def _wrapper(*args, **kwargs):
|
|
411
|
+
with self.timer(name=func.__name__, buckets=buckets):
|
|
412
|
+
return func(*args, **kwargs)
|
|
413
|
+
return _wrapper
|
|
414
|
+
if original_function:
|
|
415
|
+
return _decorator(original_function)
|
|
416
|
+
return _decorator
|
|
417
|
+
|
|
418
|
+
def count_it(self, original_function=None):
|
|
419
|
+
"""
|
|
420
|
+
Function decorator which records a counter: how many times the function was executed.
|
|
421
|
+
"""
|
|
422
|
+
def _decorator(func):
|
|
423
|
+
@wraps(func)
|
|
424
|
+
def _wrapper(*args, **kwargs):
|
|
425
|
+
try:
|
|
426
|
+
return func(*args, **kwargs)
|
|
427
|
+
finally:
|
|
428
|
+
_fetch_or_create_counter(name=self.full_name(func.__name__) + '_cnt').inc()
|
|
429
|
+
return _wrapper
|
|
430
|
+
if original_function:
|
|
431
|
+
return _decorator(original_function)
|
|
432
|
+
return _decorator
|
|
433
|
+
|
|
434
|
+
def push_metrics_to_gw(self, job: Optional[str] = None, grouping_key: Optional[dict[str, Any]] = None) -> None:
|
|
435
|
+
"""
|
|
436
|
+
Push the metrics out to the prometheus push gateways. This is useful for short-running programs which don't
|
|
437
|
+
live long enough to be reliably scraped in the prometheus pull model.
|
|
438
|
+
"""
|
|
439
|
+
|
|
440
|
+
if not job:
|
|
441
|
+
job = Path(main.__file__).stem
|
|
442
|
+
grouping_key = grouping_key or {}
|
|
443
|
+
|
|
444
|
+
for server in self.push_gateways:
|
|
445
|
+
try:
|
|
446
|
+
push_to_gateway(server.strip(), job=job, registry=self.registry, grouping_key=grouping_key)
|
|
447
|
+
except:
|
|
448
|
+
continue
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from re import compile, error, match
|
|
16
|
+
from traceback import format_exc
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Optional, cast
|
|
18
|
+
|
|
19
|
+
from dogpile.cache.api import NO_VALUE
|
|
20
|
+
from sqlalchemy import and_, delete, select
|
|
21
|
+
from sqlalchemy.exc import IntegrityError
|
|
22
|
+
|
|
23
|
+
from rucio.common.cache import MemcacheRegion
|
|
24
|
+
from rucio.common.exception import Duplicate, InvalidObject, RucioException
|
|
25
|
+
from rucio.db.sqla import models
|
|
26
|
+
from rucio.db.sqla.constants import KeyType
|
|
27
|
+
from rucio.db.sqla.session import read_session, transactional_session
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from typing import TypedDict
|
|
31
|
+
|
|
32
|
+
from sqlalchemy.orm import Session
|
|
33
|
+
|
|
34
|
+
from rucio.common.types import InternalScope
|
|
35
|
+
|
|
36
|
+
class NamingConventionDict(TypedDict):
|
|
37
|
+
scope: InternalScope
|
|
38
|
+
regexp: str
|
|
39
|
+
|
|
40
|
+
REGION = MemcacheRegion(expiration_time=900)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@transactional_session
|
|
44
|
+
def add_naming_convention(
|
|
45
|
+
scope: "InternalScope",
|
|
46
|
+
regexp: str,
|
|
47
|
+
convention_type: KeyType,
|
|
48
|
+
*,
|
|
49
|
+
session: "Session"
|
|
50
|
+
) -> None:
|
|
51
|
+
"""
|
|
52
|
+
add a naming convention for a given scope
|
|
53
|
+
|
|
54
|
+
:param scope: the name for the scope.
|
|
55
|
+
:param regexp: the regular expression to validate the name.
|
|
56
|
+
:param convention_type: the did_type on which the regexp should apply.
|
|
57
|
+
:param session: The database session in use.
|
|
58
|
+
"""
|
|
59
|
+
# validate the regular expression
|
|
60
|
+
try:
|
|
61
|
+
compile(regexp)
|
|
62
|
+
except error:
|
|
63
|
+
raise RucioException('Invalid regular expression %s!' % regexp)
|
|
64
|
+
|
|
65
|
+
new_convention = models.NamingConvention(scope=scope,
|
|
66
|
+
regexp=regexp,
|
|
67
|
+
convention_type=convention_type)
|
|
68
|
+
try:
|
|
69
|
+
new_convention.save(session=session)
|
|
70
|
+
except IntegrityError:
|
|
71
|
+
raise Duplicate('Naming convention already exists!')
|
|
72
|
+
except:
|
|
73
|
+
raise RucioException(str(format_exc()))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@read_session
|
|
77
|
+
def get_naming_convention(
|
|
78
|
+
scope: "InternalScope",
|
|
79
|
+
convention_type: KeyType,
|
|
80
|
+
*,
|
|
81
|
+
session: "Session"
|
|
82
|
+
) -> Optional[str]:
|
|
83
|
+
"""
|
|
84
|
+
Get the naming convention for a given scope
|
|
85
|
+
|
|
86
|
+
:param scope: the name for the scope.
|
|
87
|
+
:param convention_type: the did_type on which the regexp should apply.
|
|
88
|
+
:param session: The database session in use.
|
|
89
|
+
|
|
90
|
+
:returns: the regular expression.
|
|
91
|
+
"""
|
|
92
|
+
stmt = select(
|
|
93
|
+
models.NamingConvention.regexp
|
|
94
|
+
).where(
|
|
95
|
+
and_(models.NamingConvention.scope == scope,
|
|
96
|
+
models.NamingConvention.convention_type == convention_type)
|
|
97
|
+
)
|
|
98
|
+
return session.execute(stmt).scalar()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@transactional_session
|
|
102
|
+
def delete_naming_convention(
|
|
103
|
+
scope: "InternalScope",
|
|
104
|
+
convention_type: KeyType,
|
|
105
|
+
*,
|
|
106
|
+
session: "Session"
|
|
107
|
+
) -> int:
|
|
108
|
+
"""
|
|
109
|
+
delete a naming convention for a given scope
|
|
110
|
+
|
|
111
|
+
:param scope: the name for the scope.
|
|
112
|
+
:param regexp: the regular expression to validate the name. (DEPRECATED)
|
|
113
|
+
:param convention_type: the did_type on which the regexp should apply.
|
|
114
|
+
:param session: The database session in use.
|
|
115
|
+
"""
|
|
116
|
+
if scope.internal is not None:
|
|
117
|
+
REGION.delete(scope.internal)
|
|
118
|
+
stmt = delete(
|
|
119
|
+
models.NamingConvention
|
|
120
|
+
).where(
|
|
121
|
+
and_(models.NamingConvention.scope == scope,
|
|
122
|
+
models.NamingConvention.convention_type == convention_type)
|
|
123
|
+
)
|
|
124
|
+
return session.execute(stmt).rowcount
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@read_session
|
|
128
|
+
def list_naming_conventions(*, session: "Session") -> list["NamingConventionDict"]:
|
|
129
|
+
"""
|
|
130
|
+
List all naming conventions.
|
|
131
|
+
|
|
132
|
+
:param session: The database session in use.
|
|
133
|
+
|
|
134
|
+
:returns: a list of dictionaries.
|
|
135
|
+
"""
|
|
136
|
+
stmt = select(
|
|
137
|
+
models.NamingConvention.scope,
|
|
138
|
+
models.NamingConvention.regexp
|
|
139
|
+
)
|
|
140
|
+
return [cast("NamingConventionDict", row._asdict()) for row in session.execute(stmt).all()]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@read_session
|
|
144
|
+
def validate_name(
|
|
145
|
+
scope: "InternalScope",
|
|
146
|
+
name: str,
|
|
147
|
+
did_type: str,
|
|
148
|
+
*,
|
|
149
|
+
session: "Session"
|
|
150
|
+
) -> Optional[dict[str, Any]]:
|
|
151
|
+
"""
|
|
152
|
+
Validate a name according to a naming convention.
|
|
153
|
+
|
|
154
|
+
:param scope: the name for the scope.
|
|
155
|
+
:param name: the name.
|
|
156
|
+
:param did_type: the type of did.
|
|
157
|
+
|
|
158
|
+
:param session: The database session in use.
|
|
159
|
+
|
|
160
|
+
:returns: a dictionary with metadata.
|
|
161
|
+
"""
|
|
162
|
+
if scope.external is not None:
|
|
163
|
+
if scope.external.startswith('user'):
|
|
164
|
+
return {'project': 'user'}
|
|
165
|
+
elif scope.external.startswith('group'):
|
|
166
|
+
return {'project': 'group'}
|
|
167
|
+
|
|
168
|
+
# Check if naming convention can be found in cache region
|
|
169
|
+
regexp = REGION.get(scope.internal)
|
|
170
|
+
if regexp is NO_VALUE: # no cached entry found
|
|
171
|
+
regexp = get_naming_convention(scope=scope,
|
|
172
|
+
convention_type=KeyType.DATASET,
|
|
173
|
+
session=session)
|
|
174
|
+
if scope.internal is not None:
|
|
175
|
+
regexp and REGION.set(scope.internal, regexp)
|
|
176
|
+
|
|
177
|
+
if not regexp:
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
# Validate with regexp
|
|
181
|
+
groups = match(regexp, str(name)) # type: ignore
|
|
182
|
+
if groups:
|
|
183
|
+
meta = groups.groupdict()
|
|
184
|
+
# Hack to get task_id from version
|
|
185
|
+
if 'version' in meta and meta['version']:
|
|
186
|
+
matched = match(r'(?P<version>\w+)_tid(?P<task_id>\d+)_\w+$', meta['version'])
|
|
187
|
+
if matched:
|
|
188
|
+
meta['version'] = matched.groupdict()['version']
|
|
189
|
+
meta['task_id'] = int(matched.groupdict()['task_id'])
|
|
190
|
+
if 'run_number' in meta and meta['run_number']:
|
|
191
|
+
meta['run_number'] = int(meta['run_number'])
|
|
192
|
+
return meta
|
|
193
|
+
|
|
194
|
+
print(f"Provided name {name} doesn't match the naming convention {regexp}")
|
|
195
|
+
raise InvalidObject(f"Provided name {name} doesn't match the naming convention {regexp}")
|