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/topology.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
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
|
+
import copy
|
|
15
|
+
import datetime
|
|
16
|
+
import itertools
|
|
17
|
+
import logging
|
|
18
|
+
import threading
|
|
19
|
+
import weakref
|
|
20
|
+
from decimal import Decimal
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union, cast
|
|
22
|
+
|
|
23
|
+
from sqlalchemy import and_, select
|
|
24
|
+
|
|
25
|
+
from rucio.common.config import config_get, config_get_int
|
|
26
|
+
from rucio.common.exception import InvalidRSEExpression, NoDistance, RSEProtocolNotSupported
|
|
27
|
+
from rucio.common.utils import PriorityQueue
|
|
28
|
+
from rucio.core.rse import RseCollection, RseData
|
|
29
|
+
from rucio.core.rse_expression_parser import parse_expression
|
|
30
|
+
from rucio.db.sqla import models
|
|
31
|
+
from rucio.db.sqla.session import read_session, transactional_session
|
|
32
|
+
from rucio.rse import rsemanager as rsemgr
|
|
33
|
+
|
|
34
|
+
_Number = Union[int, Decimal]
|
|
35
|
+
TN = TypeVar("TN", bound="Node")
|
|
36
|
+
TE = TypeVar("TE", bound="Edge")
|
|
37
|
+
ExpiringObjectCacheNewObject = TypeVar("ExpiringObjectCacheNewObject")
|
|
38
|
+
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
41
|
+
from typing import Protocol
|
|
42
|
+
|
|
43
|
+
from sqlalchemy.orm import Session
|
|
44
|
+
from typing_extensions import Self
|
|
45
|
+
|
|
46
|
+
from rucio.common.types import HopDict, LoggerFunction
|
|
47
|
+
|
|
48
|
+
class _StateProvider(Protocol):
|
|
49
|
+
@property
|
|
50
|
+
def cost(self) -> _Number:
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def enabled(self) -> bool:
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
TNState = TypeVar("TNState", bound=_StateProvider)
|
|
58
|
+
TEState = TypeVar("TEState", bound=_StateProvider)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
DEFAULT_HOP_PENALTY = 10
|
|
62
|
+
INF = float('inf')
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Node(RseData):
|
|
66
|
+
def __init__(self, rse_id: str) -> None:
|
|
67
|
+
super().__init__(rse_id)
|
|
68
|
+
|
|
69
|
+
self.in_edges = weakref.WeakKeyDictionary()
|
|
70
|
+
self.out_edges = weakref.WeakKeyDictionary()
|
|
71
|
+
|
|
72
|
+
self.cost: _Number = 0
|
|
73
|
+
self.enabled: bool = True
|
|
74
|
+
self.used_for_multihop = False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Edge(Generic[TN]):
|
|
78
|
+
def __init__(self, src_node: TN, dst_node: TN) -> None:
|
|
79
|
+
self._src_node = weakref.ref(src_node)
|
|
80
|
+
self._dst_node = weakref.ref(dst_node)
|
|
81
|
+
|
|
82
|
+
self.cost: _Number = 1
|
|
83
|
+
self.enabled: bool = True
|
|
84
|
+
|
|
85
|
+
self.add_to_nodes()
|
|
86
|
+
|
|
87
|
+
def add_to_nodes(self) -> None:
|
|
88
|
+
self.src_node.out_edges[self.dst_node] = self
|
|
89
|
+
self.dst_node.in_edges[self.src_node] = self
|
|
90
|
+
|
|
91
|
+
def remove_from_nodes(self) -> None:
|
|
92
|
+
self.src_node.out_edges.pop(self.dst_node, None)
|
|
93
|
+
self.dst_node.in_edges.pop(self.src_node, None)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def src_node(self) -> TN:
|
|
97
|
+
node = self._src_node()
|
|
98
|
+
if node is None:
|
|
99
|
+
# This shouldn't happen if the Node list is correctly managed by the Topology object.
|
|
100
|
+
raise ReferenceError("weak reference returned None")
|
|
101
|
+
return node
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def dst_node(self) -> TN:
|
|
105
|
+
node = self._dst_node()
|
|
106
|
+
if node is None:
|
|
107
|
+
# This shouldn't happen if the Node list is correctly managed by the Topology object.
|
|
108
|
+
raise ReferenceError("weak reference returned None")
|
|
109
|
+
return node
|
|
110
|
+
|
|
111
|
+
def __eq__(self, other: object) -> bool:
|
|
112
|
+
if not isinstance(other, self.__class__):
|
|
113
|
+
return False
|
|
114
|
+
return self._src_node == other._src_node and self._dst_node == other._dst_node
|
|
115
|
+
|
|
116
|
+
def __str__(self) -> str:
|
|
117
|
+
return f'{self._src_node}-->{self._dst_node}'
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Topology(RseCollection, Generic[TN, TE]):
|
|
121
|
+
"""
|
|
122
|
+
Helper private class used to easily fetch topological information for a subset of RSEs.
|
|
123
|
+
"""
|
|
124
|
+
def __init__(
|
|
125
|
+
self,
|
|
126
|
+
rse_ids: Optional["Iterable[str]"] = None,
|
|
127
|
+
ignore_availability: bool = False,
|
|
128
|
+
node_cls: type[TN] = Node,
|
|
129
|
+
edge_cls: type[TE] = Edge,
|
|
130
|
+
) -> None:
|
|
131
|
+
super().__init__(rse_ids=rse_ids, rse_data_cls=node_cls)
|
|
132
|
+
self._edge_cls = edge_cls
|
|
133
|
+
self._edges: dict[tuple[TN, TN], TE] = {}
|
|
134
|
+
self._edges_loaded = False
|
|
135
|
+
self._multihop_nodes = set()
|
|
136
|
+
self._hop_penalty = DEFAULT_HOP_PENALTY
|
|
137
|
+
self.ignore_availability = ignore_availability
|
|
138
|
+
|
|
139
|
+
self._lock = threading.RLock()
|
|
140
|
+
|
|
141
|
+
@transactional_session
|
|
142
|
+
def ensure_loaded(
|
|
143
|
+
self,
|
|
144
|
+
rse_ids: "Optional[Iterable[str]]" = None,
|
|
145
|
+
load_name: bool = False,
|
|
146
|
+
load_columns: bool = False,
|
|
147
|
+
load_attributes: bool = False,
|
|
148
|
+
load_info: bool = False,
|
|
149
|
+
load_usage: bool = False,
|
|
150
|
+
load_limits: bool = False,
|
|
151
|
+
include_deleted: bool = False,
|
|
152
|
+
*,
|
|
153
|
+
session: "Session",
|
|
154
|
+
) -> None:
|
|
155
|
+
|
|
156
|
+
if not rse_ids:
|
|
157
|
+
with self._lock:
|
|
158
|
+
rse_ids = list(self.rse_id_to_data_map)
|
|
159
|
+
super().ensure_loaded(
|
|
160
|
+
rse_ids=rse_ids,
|
|
161
|
+
load_name=load_name,
|
|
162
|
+
load_columns=load_columns,
|
|
163
|
+
load_attributes=load_attributes,
|
|
164
|
+
load_info=load_info,
|
|
165
|
+
load_usage=load_usage,
|
|
166
|
+
load_limits=load_limits,
|
|
167
|
+
include_deleted=include_deleted,
|
|
168
|
+
session=session,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def get_or_create(self, rse_id: str) -> "TN":
|
|
172
|
+
rse_data = self.rse_id_to_data_map.get(rse_id)
|
|
173
|
+
if rse_data is None:
|
|
174
|
+
with self._lock:
|
|
175
|
+
rse_data = self.rse_id_to_data_map.get(rse_id)
|
|
176
|
+
if not rse_data:
|
|
177
|
+
self.rse_id_to_data_map[rse_id] = rse_data = self._rse_data_cls(rse_id)
|
|
178
|
+
# A new node added. Edges which were already loaded are probably incomplete now.
|
|
179
|
+
self._edges_loaded = False
|
|
180
|
+
return rse_data
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def edges(self) -> dict[tuple[TN, TN], TE]:
|
|
184
|
+
with self._lock:
|
|
185
|
+
return copy.copy(self._edges)
|
|
186
|
+
|
|
187
|
+
def edge(self, src_node: TN, dst_node: TN) -> "Optional[TE]":
|
|
188
|
+
return self._edges.get((src_node, dst_node))
|
|
189
|
+
|
|
190
|
+
def get_or_create_edge(self, src_node: TN, dst_node: TN) -> "TE":
|
|
191
|
+
edge = self._edges.get((src_node, dst_node))
|
|
192
|
+
if not edge:
|
|
193
|
+
with self._lock:
|
|
194
|
+
edge = self._edges.get((src_node, dst_node))
|
|
195
|
+
if not edge:
|
|
196
|
+
self._edges[src_node, dst_node] = edge = self._edge_cls(src_node, dst_node)
|
|
197
|
+
return edge
|
|
198
|
+
|
|
199
|
+
def delete_edge(self, src_node: TN, dst_node: TN) -> None:
|
|
200
|
+
with self._lock:
|
|
201
|
+
edge = self._edges[src_node, dst_node]
|
|
202
|
+
edge.remove_from_nodes()
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def multihop_enabled(self) -> bool:
|
|
206
|
+
return True if self._multihop_nodes else False
|
|
207
|
+
|
|
208
|
+
@read_session
|
|
209
|
+
def configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: "LoggerFunction" = logging.log) -> "Self":
|
|
210
|
+
with self._lock:
|
|
211
|
+
return self._configure_multihop(multihop_rse_ids=multihop_rse_ids, session=session, logger=logger)
|
|
212
|
+
|
|
213
|
+
def _configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: "LoggerFunction" = logging.log) -> "Self":
|
|
214
|
+
|
|
215
|
+
if multihop_rse_ids is None:
|
|
216
|
+
multihop_rse_expression = config_get('transfers', 'multihop_rse_expression', default='available_for_multihop=true', expiration_time=600, session=session)
|
|
217
|
+
|
|
218
|
+
multihop_rse_ids = set()
|
|
219
|
+
if multihop_rse_expression.strip():
|
|
220
|
+
try:
|
|
221
|
+
multihop_rse_ids = {rse['id'] for rse in parse_expression(multihop_rse_expression, session=session)}
|
|
222
|
+
except InvalidRSEExpression:
|
|
223
|
+
pass
|
|
224
|
+
if not multihop_rse_ids:
|
|
225
|
+
logger(logging.WARNING, 'multihop_rse_expression is not empty, but returned no RSEs')
|
|
226
|
+
|
|
227
|
+
for node in self._multihop_nodes:
|
|
228
|
+
node.used_for_multihop = False
|
|
229
|
+
|
|
230
|
+
self._multihop_nodes.clear()
|
|
231
|
+
|
|
232
|
+
for rse_id in multihop_rse_ids:
|
|
233
|
+
node = self.get_or_create(rse_id).ensure_loaded(load_columns=True)
|
|
234
|
+
if self.ignore_availability or (node.columns['availability_read'] and node.columns['availability_write']):
|
|
235
|
+
node.used_for_multihop = True
|
|
236
|
+
self._multihop_nodes.add(node)
|
|
237
|
+
|
|
238
|
+
self._hop_penalty = config_get_int('transfers', 'hop_penalty', default=DEFAULT_HOP_PENALTY, session=session)
|
|
239
|
+
return self
|
|
240
|
+
|
|
241
|
+
@read_session
|
|
242
|
+
def ensure_edges_loaded(self, *, session: "Session") -> None:
|
|
243
|
+
"""
|
|
244
|
+
Ensure that all edges are loaded for the (sub-)set of nodes known by this topology object
|
|
245
|
+
"""
|
|
246
|
+
if self._edges_loaded:
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
with self._lock:
|
|
250
|
+
return self._ensure_edges_loaded(session=session)
|
|
251
|
+
|
|
252
|
+
def _ensure_edges_loaded(self, *, session: "Session") -> None:
|
|
253
|
+
stmt = select(
|
|
254
|
+
models.Distance
|
|
255
|
+
).where(
|
|
256
|
+
and_(
|
|
257
|
+
models.Distance.src_rse_id.in_(self.rse_id_to_data_map.keys()),
|
|
258
|
+
models.Distance.dest_rse_id.in_(self.rse_id_to_data_map.keys()),
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
loaded_edges = set()
|
|
263
|
+
for distance in session.execute(stmt).scalars():
|
|
264
|
+
if distance.distance is None:
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
src_node = self[distance.src_rse_id]
|
|
268
|
+
dst_node = self[distance.dest_rse_id]
|
|
269
|
+
edge = self.get_or_create_edge(src_node, dst_node)
|
|
270
|
+
|
|
271
|
+
sanitized_dist = int(distance.distance) if distance.distance >= 0 else 0
|
|
272
|
+
edge.cost = sanitized_dist
|
|
273
|
+
|
|
274
|
+
loaded_edges.add((src_node, dst_node))
|
|
275
|
+
|
|
276
|
+
if len(loaded_edges) != len(self._edges):
|
|
277
|
+
# Remove edges which don't exist in the database anymore
|
|
278
|
+
to_remove = set(self._edges).difference(loaded_edges)
|
|
279
|
+
for src_node, dst_node in to_remove:
|
|
280
|
+
self.delete_edge(src_node, dst_node)
|
|
281
|
+
|
|
282
|
+
self._edges_loaded = True
|
|
283
|
+
|
|
284
|
+
@read_session
|
|
285
|
+
def search_shortest_paths(
|
|
286
|
+
self,
|
|
287
|
+
src_nodes: "Iterable[TN]",
|
|
288
|
+
dst_node: TN,
|
|
289
|
+
operation_src: str,
|
|
290
|
+
operation_dest: str,
|
|
291
|
+
domain: str,
|
|
292
|
+
limit_dest_schemes: list[str],
|
|
293
|
+
*,
|
|
294
|
+
session: "Session",
|
|
295
|
+
) -> dict[TN, list[dict[str, Any]]]:
|
|
296
|
+
"""
|
|
297
|
+
Find the shortest paths from multiple sources towards dest_rse_id.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
for rse in itertools.chain(src_nodes, [dst_node], self._multihop_nodes):
|
|
301
|
+
rse.ensure_loaded(load_attributes=True, load_info=True, session=session)
|
|
302
|
+
self.ensure_edges_loaded(session=session)
|
|
303
|
+
|
|
304
|
+
if self._multihop_nodes:
|
|
305
|
+
# Filter out island source RSEs
|
|
306
|
+
nodes_to_find = {node for node in src_nodes if node.out_edges}
|
|
307
|
+
else:
|
|
308
|
+
nodes_to_find = set(src_nodes)
|
|
309
|
+
|
|
310
|
+
class _NodeStateProvider:
|
|
311
|
+
_hop_penalty = self._hop_penalty
|
|
312
|
+
|
|
313
|
+
def __init__(self, node: TN) -> None:
|
|
314
|
+
self.enabled: bool = True
|
|
315
|
+
self.cost: _Number = 0
|
|
316
|
+
if node != dst_node:
|
|
317
|
+
try:
|
|
318
|
+
self.cost = int(node.attributes.get('hop_penalty', self._hop_penalty))
|
|
319
|
+
except ValueError:
|
|
320
|
+
self.cost = self._hop_penalty
|
|
321
|
+
|
|
322
|
+
scheme_missmatch_found = {}
|
|
323
|
+
|
|
324
|
+
class _EdgeStateProvider:
|
|
325
|
+
def __init__(self, edge: TE) -> None:
|
|
326
|
+
self.edge = edge
|
|
327
|
+
self.chosen_scheme = {}
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def cost(self) -> _Number:
|
|
331
|
+
return self.edge.cost
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def enabled(self) -> bool:
|
|
335
|
+
try:
|
|
336
|
+
matching_scheme = rsemgr.find_matching_scheme(
|
|
337
|
+
rse_settings_src=self.edge.src_node.info,
|
|
338
|
+
rse_settings_dest=self.edge.dst_node.info,
|
|
339
|
+
operation_src=operation_src,
|
|
340
|
+
operation_dest=operation_dest,
|
|
341
|
+
domain=domain,
|
|
342
|
+
scheme=limit_dest_schemes if self.edge.dst_node == dst_node and limit_dest_schemes else None,
|
|
343
|
+
)
|
|
344
|
+
self.chosen_scheme = {
|
|
345
|
+
'source_scheme': matching_scheme[1],
|
|
346
|
+
'dest_scheme': matching_scheme[0],
|
|
347
|
+
'source_scheme_priority': matching_scheme[3],
|
|
348
|
+
'dest_scheme_priority': matching_scheme[2],
|
|
349
|
+
}
|
|
350
|
+
return True
|
|
351
|
+
except RSEProtocolNotSupported:
|
|
352
|
+
scheme_missmatch_found[self.edge.src_node] = True
|
|
353
|
+
return False
|
|
354
|
+
|
|
355
|
+
paths = {dst_node: []}
|
|
356
|
+
for node, distance, _, edge_to_next_hop, edge_state in self.dijkstra_spf(dst_node=dst_node,
|
|
357
|
+
nodes_to_find=nodes_to_find,
|
|
358
|
+
node_state_provider=_NodeStateProvider,
|
|
359
|
+
edge_state_provider=_EdgeStateProvider):
|
|
360
|
+
nh_node = edge_to_next_hop.dst_node
|
|
361
|
+
edge_state = cast("_EdgeStateProvider", edge_state)
|
|
362
|
+
hop = {
|
|
363
|
+
'source_rse': node,
|
|
364
|
+
'dest_rse': nh_node,
|
|
365
|
+
'hop_distance': edge_state.cost,
|
|
366
|
+
'cumulated_distance': distance,
|
|
367
|
+
**edge_state.chosen_scheme,
|
|
368
|
+
}
|
|
369
|
+
paths[node] = [hop] + paths[nh_node]
|
|
370
|
+
|
|
371
|
+
nodes_to_find.discard(node)
|
|
372
|
+
if not nodes_to_find:
|
|
373
|
+
# We found the shortest paths to all desired nodes
|
|
374
|
+
break
|
|
375
|
+
|
|
376
|
+
result = {}
|
|
377
|
+
for node in src_nodes:
|
|
378
|
+
path = paths.get(node)
|
|
379
|
+
if path is not None:
|
|
380
|
+
result[node] = path
|
|
381
|
+
elif scheme_missmatch_found.get(node):
|
|
382
|
+
result[node] = []
|
|
383
|
+
return result
|
|
384
|
+
|
|
385
|
+
def dijkstra_spf(
|
|
386
|
+
self,
|
|
387
|
+
dst_node: TN,
|
|
388
|
+
nodes_to_find: Optional[set[TN]] = None,
|
|
389
|
+
node_state_provider: "Callable[[TN], TNState]" = lambda x: x,
|
|
390
|
+
edge_state_provider: "Callable[[TE], TEState]" = lambda x: x,
|
|
391
|
+
) -> "Iterator[tuple[TN, _Number, TNState, TE, TEState]]":
|
|
392
|
+
"""
|
|
393
|
+
Does a Backwards Dijkstra's algorithm: start from destination and follow inbound links to other nodes.
|
|
394
|
+
If multihop is disabled, stop after analysing direct connections to dest_rse.
|
|
395
|
+
If the optional nodes_to_find parameter is set, will restrict search only towards these nodes.
|
|
396
|
+
Otherwise, traverse the graph in integrality.
|
|
397
|
+
|
|
398
|
+
Will yield nodes in order of their distance from the destination.
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
priority_q = PriorityQueue()
|
|
402
|
+
priority_q[dst_node] = 0
|
|
403
|
+
next_hops: dict[TN, tuple[_Number, TNState, Optional[TE], Optional[TEState]]] =\
|
|
404
|
+
{dst_node: (0, node_state_provider(dst_node), None, None)}
|
|
405
|
+
while priority_q:
|
|
406
|
+
node = priority_q.pop()
|
|
407
|
+
node_dist, node_state, edge_to_nh, edge_to_nh_state = next_hops[node]
|
|
408
|
+
|
|
409
|
+
if edge_to_nh is not None and edge_to_nh_state is not None: # skip dst_node
|
|
410
|
+
yield node, node_dist, node_state, edge_to_nh, edge_to_nh_state
|
|
411
|
+
|
|
412
|
+
if self._multihop_nodes or edge_to_nh is None:
|
|
413
|
+
# If multihop is disabled, only examine neighbors of dst_node
|
|
414
|
+
|
|
415
|
+
for adjacent_node, edge in node.in_edges.items():
|
|
416
|
+
|
|
417
|
+
if nodes_to_find is None or adjacent_node in nodes_to_find or adjacent_node.used_for_multihop:
|
|
418
|
+
|
|
419
|
+
edge_state = edge_state_provider(edge)
|
|
420
|
+
new_adjacent_dist = node_dist + node_state.cost + edge_state.cost
|
|
421
|
+
if new_adjacent_dist < next_hops.get(adjacent_node, (INF, ))[0] and edge_state.enabled:
|
|
422
|
+
adj_node_state = node_state_provider(adjacent_node)
|
|
423
|
+
next_hops[adjacent_node] = new_adjacent_dist, adj_node_state, edge, edge_state
|
|
424
|
+
priority_q[adjacent_node] = new_adjacent_dist
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class ExpiringObjectCache(Generic[ExpiringObjectCacheNewObject]):
|
|
428
|
+
"""
|
|
429
|
+
Thread-safe container which builds and object with the function passed in parameter and
|
|
430
|
+
caches it for the TTL duration.
|
|
431
|
+
"""
|
|
432
|
+
|
|
433
|
+
def __init__(
|
|
434
|
+
self,
|
|
435
|
+
ttl: int,
|
|
436
|
+
new_obj_fnc: "Callable[[], ExpiringObjectCacheNewObject]"
|
|
437
|
+
) -> None:
|
|
438
|
+
self._lock = threading.Lock()
|
|
439
|
+
self._object: Optional[ExpiringObjectCacheNewObject] = None
|
|
440
|
+
self._creation_time: Optional[datetime.datetime] = None
|
|
441
|
+
self._new_obj_fnc = new_obj_fnc
|
|
442
|
+
self._ttl = ttl
|
|
443
|
+
|
|
444
|
+
def get(self, logger: "LoggerFunction" = logging.log) -> ExpiringObjectCacheNewObject:
|
|
445
|
+
with self._lock:
|
|
446
|
+
if not self._object \
|
|
447
|
+
or not self._creation_time \
|
|
448
|
+
or datetime.datetime.utcnow() - self._creation_time > datetime.timedelta(seconds=self._ttl):
|
|
449
|
+
self._object = self._new_obj_fnc()
|
|
450
|
+
self._creation_time = datetime.datetime.utcnow()
|
|
451
|
+
logger(logging.INFO, "Refreshed topology object")
|
|
452
|
+
return self._object
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
@transactional_session
|
|
456
|
+
def get_hops(
|
|
457
|
+
source_rse_id: str,
|
|
458
|
+
dest_rse_id: str,
|
|
459
|
+
multihop_rse_ids: Optional[set[str]] = None,
|
|
460
|
+
limit_dest_schemes: Optional[list[str]] = None,
|
|
461
|
+
*, session: "Session",
|
|
462
|
+
) -> list["HopDict"]:
|
|
463
|
+
"""
|
|
464
|
+
Get a list of hops needed to transfer date from source_rse_id to dest_rse_id.
|
|
465
|
+
Ideally, the list will only include one item (dest_rse_id) since no hops are needed.
|
|
466
|
+
:param source_rse_id: Source RSE id of the transfer.
|
|
467
|
+
:param dest_rse_id: Dest RSE id of the transfer.
|
|
468
|
+
:param multihop_rse_ids: List of RSE ids that can be used for multihop. If empty, multihop is disabled.
|
|
469
|
+
:param limit_dest_schemes: List of destination schemes the matching scheme algorithm should be limited to for a single hop.
|
|
470
|
+
:returns: List of hops in the format [{'source_rse_id': source_rse_id, 'source_scheme': 'srm', 'source_scheme_priority': N, 'dest_rse_id': dest_rse_id, 'dest_scheme': 'srm', 'dest_scheme_priority': N}]
|
|
471
|
+
:raises: NoDistance
|
|
472
|
+
"""
|
|
473
|
+
if not limit_dest_schemes:
|
|
474
|
+
limit_dest_schemes = []
|
|
475
|
+
|
|
476
|
+
topology = Topology().configure_multihop(multihop_rse_ids=multihop_rse_ids)
|
|
477
|
+
src_node = topology[source_rse_id]
|
|
478
|
+
dst_node = topology[dest_rse_id]
|
|
479
|
+
shortest_paths = topology.search_shortest_paths(src_nodes=[src_node], dst_node=dst_node,
|
|
480
|
+
operation_src='third_party_copy_read', operation_dest='third_party_copy_write',
|
|
481
|
+
domain='wan', limit_dest_schemes=limit_dest_schemes, session=session)
|
|
482
|
+
|
|
483
|
+
path = shortest_paths.get(src_node)
|
|
484
|
+
if path is None:
|
|
485
|
+
raise NoDistance()
|
|
486
|
+
|
|
487
|
+
if not path:
|
|
488
|
+
raise RSEProtocolNotSupported()
|
|
489
|
+
|
|
490
|
+
return path
|