rucio 32.8.6__py3-none-any.whl → 35.8.0__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 +0 -1
- rucio/alembicrevision.py +1 -2
- rucio/client/__init__.py +0 -1
- rucio/client/accountclient.py +45 -25
- rucio/client/accountlimitclient.py +37 -9
- rucio/client/baseclient.py +199 -154
- rucio/client/client.py +2 -3
- rucio/client/configclient.py +19 -6
- rucio/client/credentialclient.py +9 -4
- rucio/client/didclient.py +238 -63
- rucio/client/diracclient.py +13 -5
- rucio/client/downloadclient.py +162 -51
- rucio/client/exportclient.py +4 -4
- rucio/client/fileclient.py +3 -4
- rucio/client/importclient.py +4 -4
- rucio/client/lifetimeclient.py +21 -5
- rucio/client/lockclient.py +18 -8
- rucio/client/{metaclient.py → metaconventionsclient.py} +18 -15
- rucio/client/pingclient.py +0 -1
- rucio/client/replicaclient.py +15 -5
- rucio/client/requestclient.py +35 -19
- rucio/client/rseclient.py +133 -51
- rucio/client/ruleclient.py +29 -22
- rucio/client/scopeclient.py +8 -6
- rucio/client/subscriptionclient.py +47 -35
- rucio/client/touchclient.py +8 -4
- rucio/client/uploadclient.py +166 -82
- rucio/common/__init__.py +0 -1
- rucio/common/cache.py +4 -4
- rucio/common/config.py +52 -47
- rucio/common/constants.py +69 -2
- rucio/common/constraints.py +0 -1
- rucio/common/didtype.py +24 -22
- rucio/common/dumper/__init__.py +70 -41
- rucio/common/dumper/consistency.py +26 -22
- rucio/common/dumper/data_models.py +16 -23
- rucio/common/dumper/path_parsing.py +0 -1
- rucio/common/exception.py +281 -222
- rucio/common/extra.py +0 -1
- rucio/common/logging.py +54 -38
- rucio/common/pcache.py +122 -101
- rucio/common/plugins.py +153 -0
- rucio/common/policy.py +4 -4
- rucio/common/schema/__init__.py +17 -10
- rucio/common/schema/atlas.py +7 -5
- rucio/common/schema/belleii.py +7 -5
- rucio/common/schema/domatpc.py +7 -5
- rucio/common/schema/escape.py +7 -5
- rucio/common/schema/generic.py +8 -6
- rucio/common/schema/generic_multi_vo.py +7 -5
- rucio/common/schema/icecube.py +7 -5
- rucio/common/stomp_utils.py +0 -1
- rucio/common/stopwatch.py +0 -1
- rucio/common/test_rucio_server.py +2 -2
- rucio/common/types.py +262 -17
- rucio/common/utils.py +743 -451
- rucio/core/__init__.py +0 -1
- rucio/core/account.py +99 -29
- rucio/core/account_counter.py +89 -24
- rucio/core/account_limit.py +90 -24
- rucio/core/authentication.py +86 -29
- rucio/core/config.py +108 -38
- rucio/core/credential.py +14 -7
- rucio/core/did.py +680 -782
- rucio/core/did_meta_plugins/__init__.py +8 -6
- rucio/core/did_meta_plugins/did_column_meta.py +17 -12
- rucio/core/did_meta_plugins/did_meta_plugin_interface.py +60 -11
- rucio/core/did_meta_plugins/filter_engine.py +90 -50
- rucio/core/did_meta_plugins/json_meta.py +41 -16
- rucio/core/did_meta_plugins/mongo_meta.py +25 -8
- rucio/core/did_meta_plugins/postgres_meta.py +3 -4
- rucio/core/dirac.py +46 -17
- rucio/core/distance.py +66 -43
- rucio/core/exporter.py +5 -5
- rucio/core/heartbeat.py +181 -81
- rucio/core/identity.py +22 -12
- rucio/core/importer.py +23 -12
- rucio/core/lifetime_exception.py +32 -32
- rucio/core/lock.py +244 -142
- rucio/core/message.py +79 -38
- rucio/core/{meta.py → meta_conventions.py} +57 -44
- rucio/core/monitor.py +19 -13
- rucio/core/naming_convention.py +68 -27
- rucio/core/nongrid_trace.py +17 -5
- rucio/core/oidc.py +151 -29
- rucio/core/permission/__init__.py +18 -6
- rucio/core/permission/atlas.py +50 -35
- rucio/core/permission/belleii.py +6 -5
- rucio/core/permission/escape.py +8 -6
- rucio/core/permission/generic.py +82 -80
- rucio/core/permission/generic_multi_vo.py +9 -7
- rucio/core/quarantined_replica.py +91 -58
- rucio/core/replica.py +1303 -772
- rucio/core/replica_sorter.py +10 -12
- rucio/core/request.py +1133 -285
- rucio/core/rse.py +142 -102
- rucio/core/rse_counter.py +49 -18
- rucio/core/rse_expression_parser.py +6 -7
- rucio/core/rse_selector.py +41 -16
- rucio/core/rule.py +1538 -474
- rucio/core/rule_grouping.py +213 -68
- rucio/core/scope.py +50 -22
- rucio/core/subscription.py +92 -44
- rucio/core/topology.py +66 -24
- rucio/core/trace.py +42 -28
- rucio/core/transfer.py +543 -259
- rucio/core/vo.py +36 -18
- rucio/core/volatile_replica.py +59 -32
- rucio/daemons/__init__.py +0 -1
- rucio/daemons/abacus/__init__.py +0 -1
- rucio/daemons/abacus/account.py +29 -19
- rucio/daemons/abacus/collection_replica.py +21 -10
- rucio/daemons/abacus/rse.py +22 -12
- rucio/daemons/atropos/__init__.py +0 -1
- rucio/daemons/atropos/atropos.py +1 -2
- rucio/daemons/auditor/__init__.py +56 -28
- rucio/daemons/auditor/hdfs.py +17 -6
- rucio/daemons/auditor/srmdumps.py +116 -45
- rucio/daemons/automatix/__init__.py +0 -1
- rucio/daemons/automatix/automatix.py +30 -18
- rucio/daemons/badreplicas/__init__.py +0 -1
- rucio/daemons/badreplicas/minos.py +29 -18
- rucio/daemons/badreplicas/minos_temporary_expiration.py +5 -7
- rucio/daemons/badreplicas/necromancer.py +9 -13
- rucio/daemons/bb8/__init__.py +0 -1
- rucio/daemons/bb8/bb8.py +10 -13
- rucio/daemons/bb8/common.py +151 -154
- rucio/daemons/bb8/nuclei_background_rebalance.py +15 -9
- rucio/daemons/bb8/t2_background_rebalance.py +15 -8
- rucio/daemons/c3po/__init__.py +0 -1
- rucio/daemons/c3po/algorithms/__init__.py +0 -1
- rucio/daemons/c3po/algorithms/simple.py +8 -5
- rucio/daemons/c3po/algorithms/t2_free_space.py +10 -7
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +10 -7
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +30 -15
- rucio/daemons/c3po/c3po.py +81 -52
- rucio/daemons/c3po/collectors/__init__.py +0 -1
- rucio/daemons/c3po/collectors/agis.py +17 -17
- rucio/daemons/c3po/collectors/free_space.py +32 -13
- rucio/daemons/c3po/collectors/jedi_did.py +14 -5
- rucio/daemons/c3po/collectors/mock_did.py +11 -6
- rucio/daemons/c3po/collectors/network_metrics.py +12 -4
- rucio/daemons/c3po/collectors/workload.py +21 -19
- rucio/daemons/c3po/utils/__init__.py +0 -1
- rucio/daemons/c3po/utils/dataset_cache.py +15 -5
- rucio/daemons/c3po/utils/expiring_dataset_cache.py +16 -5
- rucio/daemons/c3po/utils/expiring_list.py +6 -7
- rucio/daemons/c3po/utils/popularity.py +5 -2
- rucio/daemons/c3po/utils/timeseries.py +25 -12
- rucio/daemons/cache/__init__.py +0 -1
- rucio/daemons/cache/consumer.py +21 -15
- rucio/daemons/common.py +42 -18
- rucio/daemons/conveyor/__init__.py +0 -1
- rucio/daemons/conveyor/common.py +69 -37
- rucio/daemons/conveyor/finisher.py +83 -46
- rucio/daemons/conveyor/poller.py +101 -69
- rucio/daemons/conveyor/preparer.py +35 -28
- rucio/daemons/conveyor/receiver.py +64 -21
- rucio/daemons/conveyor/stager.py +33 -28
- rucio/daemons/conveyor/submitter.py +71 -47
- rucio/daemons/conveyor/throttler.py +99 -35
- rucio/daemons/follower/__init__.py +0 -1
- rucio/daemons/follower/follower.py +12 -8
- rucio/daemons/hermes/__init__.py +0 -1
- rucio/daemons/hermes/hermes.py +57 -21
- rucio/daemons/judge/__init__.py +0 -1
- rucio/daemons/judge/cleaner.py +27 -17
- rucio/daemons/judge/evaluator.py +31 -18
- rucio/daemons/judge/injector.py +31 -23
- rucio/daemons/judge/repairer.py +28 -18
- rucio/daemons/oauthmanager/__init__.py +0 -1
- rucio/daemons/oauthmanager/oauthmanager.py +7 -8
- rucio/daemons/reaper/__init__.py +0 -1
- rucio/daemons/reaper/dark_reaper.py +15 -9
- rucio/daemons/reaper/reaper.py +109 -67
- rucio/daemons/replicarecoverer/__init__.py +0 -1
- rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +255 -116
- rucio/{api → daemons/rsedecommissioner}/__init__.py +0 -1
- 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 +451 -0
- rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
- rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
- rucio/daemons/storage/__init__.py +0 -1
- rucio/daemons/storage/consistency/__init__.py +0 -1
- rucio/daemons/storage/consistency/actions.py +152 -59
- rucio/daemons/tracer/__init__.py +0 -1
- rucio/daemons/tracer/kronos.py +47 -24
- rucio/daemons/transmogrifier/__init__.py +0 -1
- rucio/daemons/transmogrifier/transmogrifier.py +35 -26
- rucio/daemons/undertaker/__init__.py +0 -1
- rucio/daemons/undertaker/undertaker.py +10 -10
- rucio/db/__init__.py +0 -1
- rucio/db/sqla/__init__.py +16 -2
- rucio/db/sqla/constants.py +10 -1
- rucio/db/sqla/migrate_repo/__init__.py +0 -1
- rucio/db/sqla/migrate_repo/env.py +0 -1
- rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +0 -1
- rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +1 -3
- rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +0 -3
- rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +1 -4
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +0 -1
- rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +0 -2
- rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +0 -2
- rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +0 -3
- rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +0 -1
- rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +1 -2
- rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +0 -1
- rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +0 -3
- rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +1 -4
- rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +0 -2
- rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +0 -3
- rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +1 -2
- rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +0 -1
- rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +0 -1
- rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +0 -2
- rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +0 -2
- rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +1 -4
- rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +1 -4
- rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +0 -1
- rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +1 -3
- rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +0 -2
- rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +1 -3
- rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +1 -2
- rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +1 -3
- rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +0 -2
- rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +2 -3
- rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +1 -4
- rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +0 -1
- rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +0 -1
- rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +0 -1
- rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +0 -2
- rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +0 -3
- rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +0 -2
- rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +2 -4
- rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +0 -2
- rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +1 -4
- rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +0 -2
- rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +0 -3
- 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 +0 -2
- rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +0 -2
- rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +0 -3
- rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +0 -3
- rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +1 -3
- rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +1 -3
- rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +0 -3
- rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +1 -2
- rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +2 -4
- rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +0 -1
- rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +1 -4
- rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +0 -2
- rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +1 -2
- rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +0 -3
- rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +1 -3
- rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +0 -3
- rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +0 -1
- rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +0 -2
- 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 +1 -3
- rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +0 -2
- rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +1 -4
- rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +0 -1
- rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +0 -1
- rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +1 -4
- rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +0 -1
- 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 +0 -3
- 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 +1 -2
- rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +1 -3
- rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +1 -5
- rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +1 -3
- rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +0 -3
- rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +1 -3
- rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +0 -3
- rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +1 -4
- rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +1 -2
- rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +1 -4
- rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +1 -3
- rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +1 -4
- rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +0 -2
- rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +1 -3
- rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +0 -1
- rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +1 -2
- rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +1 -3
- rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +0 -2
- rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +0 -1
- rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +1 -2
- rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +0 -3
- rucio/db/sqla/models.py +122 -216
- rucio/db/sqla/sautils.py +12 -5
- rucio/db/sqla/session.py +71 -43
- rucio/db/sqla/types.py +3 -4
- rucio/db/sqla/util.py +91 -69
- rucio/gateway/__init__.py +13 -0
- rucio/{api → gateway}/account.py +119 -46
- rucio/{api → gateway}/account_limit.py +12 -13
- rucio/{api → gateway}/authentication.py +106 -33
- rucio/{api → gateway}/config.py +12 -13
- rucio/{api → gateway}/credential.py +15 -4
- rucio/{api → gateway}/did.py +384 -140
- rucio/{api → gateway}/dirac.py +16 -6
- rucio/{api → gateway}/exporter.py +3 -4
- rucio/{api → gateway}/heartbeat.py +17 -5
- rucio/{api → gateway}/identity.py +63 -19
- rucio/{api → gateway}/importer.py +3 -4
- rucio/{api → gateway}/lifetime_exception.py +35 -10
- rucio/{api → gateway}/lock.py +34 -12
- rucio/{api/meta.py → gateway/meta_conventions.py} +18 -16
- rucio/{api → gateway}/permission.py +4 -5
- rucio/{api → gateway}/quarantined_replica.py +13 -4
- rucio/{api → gateway}/replica.py +12 -11
- rucio/{api → gateway}/request.py +129 -28
- rucio/{api → gateway}/rse.py +11 -12
- rucio/{api → gateway}/rule.py +117 -35
- rucio/{api → gateway}/scope.py +24 -14
- rucio/{api → gateway}/subscription.py +65 -43
- rucio/{api → gateway}/vo.py +17 -7
- rucio/rse/__init__.py +3 -4
- rucio/rse/protocols/__init__.py +0 -1
- rucio/rse/protocols/bittorrent.py +184 -0
- rucio/rse/protocols/cache.py +1 -2
- rucio/rse/protocols/dummy.py +1 -2
- rucio/rse/protocols/gfal.py +12 -10
- rucio/rse/protocols/globus.py +7 -7
- rucio/rse/protocols/gsiftp.py +2 -3
- rucio/rse/protocols/http_cache.py +1 -2
- rucio/rse/protocols/mock.py +1 -2
- rucio/rse/protocols/ngarc.py +1 -2
- rucio/rse/protocols/posix.py +12 -13
- rucio/rse/protocols/protocol.py +116 -52
- rucio/rse/protocols/rclone.py +6 -7
- rucio/rse/protocols/rfio.py +4 -5
- rucio/rse/protocols/srm.py +9 -10
- rucio/rse/protocols/ssh.py +8 -9
- rucio/rse/protocols/storm.py +2 -3
- rucio/rse/protocols/webdav.py +17 -14
- rucio/rse/protocols/xrootd.py +23 -17
- rucio/rse/rsemanager.py +19 -7
- rucio/tests/__init__.py +0 -1
- rucio/tests/common.py +43 -17
- rucio/tests/common_server.py +3 -3
- rucio/transfertool/__init__.py +0 -1
- rucio/transfertool/bittorrent.py +199 -0
- rucio/transfertool/bittorrent_driver.py +52 -0
- rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
- rucio/transfertool/fts3.py +250 -138
- rucio/transfertool/fts3_plugins.py +152 -0
- rucio/transfertool/globus.py +9 -8
- rucio/transfertool/globus_library.py +1 -2
- rucio/transfertool/mock.py +21 -12
- rucio/transfertool/transfertool.py +33 -24
- rucio/vcsversion.py +4 -4
- rucio/version.py +5 -13
- rucio/web/__init__.py +0 -1
- rucio/web/rest/__init__.py +0 -1
- rucio/web/rest/flaskapi/__init__.py +0 -1
- rucio/web/rest/flaskapi/authenticated_bp.py +0 -1
- rucio/web/rest/flaskapi/v1/__init__.py +0 -1
- rucio/web/rest/flaskapi/v1/accountlimits.py +15 -13
- rucio/web/rest/flaskapi/v1/accounts.py +49 -48
- rucio/web/rest/flaskapi/v1/archives.py +12 -10
- rucio/web/rest/flaskapi/v1/auth.py +146 -144
- rucio/web/rest/flaskapi/v1/common.py +82 -41
- rucio/web/rest/flaskapi/v1/config.py +5 -6
- rucio/web/rest/flaskapi/v1/credentials.py +7 -8
- rucio/web/rest/flaskapi/v1/dids.py +158 -28
- rucio/web/rest/flaskapi/v1/dirac.py +8 -8
- rucio/web/rest/flaskapi/v1/export.py +3 -5
- rucio/web/rest/flaskapi/v1/heartbeats.py +3 -5
- rucio/web/rest/flaskapi/v1/identities.py +3 -5
- rucio/web/rest/flaskapi/v1/import.py +3 -4
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +6 -9
- rucio/web/rest/flaskapi/v1/locks.py +2 -4
- rucio/web/rest/flaskapi/v1/main.py +10 -2
- rucio/web/rest/flaskapi/v1/{meta.py → meta_conventions.py} +26 -11
- rucio/web/rest/flaskapi/v1/metrics.py +1 -2
- rucio/web/rest/flaskapi/v1/nongrid_traces.py +4 -4
- rucio/web/rest/flaskapi/v1/ping.py +6 -7
- rucio/web/rest/flaskapi/v1/redirect.py +8 -9
- rucio/web/rest/flaskapi/v1/replicas.py +43 -19
- rucio/web/rest/flaskapi/v1/requests.py +178 -21
- rucio/web/rest/flaskapi/v1/rses.py +61 -26
- rucio/web/rest/flaskapi/v1/rules.py +48 -18
- rucio/web/rest/flaskapi/v1/scopes.py +3 -5
- rucio/web/rest/flaskapi/v1/subscriptions.py +22 -18
- rucio/web/rest/flaskapi/v1/traces.py +4 -4
- rucio/web/rest/flaskapi/v1/types.py +20 -0
- rucio/web/rest/flaskapi/v1/vos.py +3 -5
- rucio/web/rest/main.py +0 -1
- rucio/web/rest/metrics.py +0 -1
- rucio/web/rest/ping.py +27 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/ldap.cfg.template +1 -1
- rucio-35.8.0.data/data/rucio/requirements.server.txt +268 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/bootstrap.py +3 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/merge_rucio_configs.py +2 -5
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/reset_database.py +3 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio +87 -85
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-account +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-collection-replica +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-rse +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-admin +45 -32
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-atropos +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-auditor +13 -7
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-automatix +1 -2
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-bb8 +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-c3po +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-client +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-consumer +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-finisher +1 -2
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-poller +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-preparer +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-receiver +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-stager +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-submitter +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-throttler +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dark-reaper +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dumper +11 -10
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-follower +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-hermes +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-cleaner +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-evaluator +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-injector +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-repairer +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-kronos +1 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos-temporary-expiration +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-necromancer +1 -2
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-oauth-manager +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-reaper +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-replica-recoverer +6 -7
- rucio-35.8.0.data/scripts/rucio-rse-decommissioner +66 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-storage-consistency-actions +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-transmogrifier +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-undertaker +1 -2
- rucio-35.8.0.dist-info/METADATA +72 -0
- rucio-35.8.0.dist-info/RECORD +493 -0
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/WHEEL +1 -1
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/AUTHORS.rst +3 -0
- rucio/api/temporary_did.py +0 -49
- rucio/common/schema/cms.py +0 -478
- rucio/common/schema/lsst.py +0 -423
- rucio/core/permission/cms.py +0 -1166
- rucio/core/temporary_did.py +0 -188
- rucio/daemons/reaper/light_reaper.py +0 -255
- rucio/web/rest/flaskapi/v1/tmp_dids.py +0 -115
- rucio-32.8.6.data/data/rucio/requirements.txt +0 -55
- rucio-32.8.6.data/scripts/rucio-light-reaper +0 -53
- rucio-32.8.6.dist-info/METADATA +0 -83
- rucio-32.8.6.dist-info/RECORD +0 -481
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic.ini.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/top_level.txt +0 -0
rucio/core/scope.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
3
2
|
#
|
|
4
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -15,11 +14,13 @@
|
|
|
15
14
|
|
|
16
15
|
from re import match
|
|
17
16
|
from traceback import format_exc
|
|
18
|
-
from typing import TYPE_CHECKING
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
19
18
|
|
|
19
|
+
from sqlalchemy import and_, select
|
|
20
20
|
from sqlalchemy.exc import IntegrityError
|
|
21
21
|
|
|
22
22
|
from rucio.common.exception import AccountNotFound, Duplicate, RucioException, VONotFound
|
|
23
|
+
from rucio.common.types import InternalScope
|
|
23
24
|
from rucio.core.vo import vo_exists
|
|
24
25
|
from rucio.db.sqla import models
|
|
25
26
|
from rucio.db.sqla.constants import AccountStatus, ScopeStatus
|
|
@@ -41,8 +42,13 @@ def add_scope(scope, account, *, session: "Session"):
|
|
|
41
42
|
if not vo_exists(vo=scope.vo, session=session):
|
|
42
43
|
raise VONotFound('VO {} not found'.format(scope.vo))
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
stmt = select(
|
|
46
|
+
models.Account
|
|
47
|
+
).where(
|
|
48
|
+
and_(models.Account.account == account,
|
|
49
|
+
models.Account.status == AccountStatus.ACTIVE)
|
|
50
|
+
)
|
|
51
|
+
if session.execute(stmt).first() is None:
|
|
46
52
|
raise AccountNotFound('Account ID \'%s\' does not exist' % account)
|
|
47
53
|
|
|
48
54
|
new_scope = models.Scope(scope=scope, account=account, status=ScopeStatus.OPEN)
|
|
@@ -80,7 +86,7 @@ def bulk_add_scopes(scopes, account, skipExisting=False, *, session: "Session"):
|
|
|
80
86
|
|
|
81
87
|
|
|
82
88
|
@read_session
|
|
83
|
-
def list_scopes(filter_=
|
|
89
|
+
def list_scopes(filter_: Optional[dict[str, Any]] = None, *, session: "Session") -> list[InternalScope]:
|
|
84
90
|
"""
|
|
85
91
|
Lists all scopes.
|
|
86
92
|
:param filter_: Dictionary of attributes by which the input data should be filtered
|
|
@@ -88,19 +94,25 @@ def list_scopes(filter_={}, *, session: "Session"):
|
|
|
88
94
|
|
|
89
95
|
:returns: A list containing all scopes.
|
|
90
96
|
"""
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
filter_ = filter_ or {}
|
|
98
|
+
stmt = select(
|
|
99
|
+
models.Scope.scope
|
|
100
|
+
).where(
|
|
101
|
+
models.Scope.status != ScopeStatus.DELETED
|
|
102
|
+
)
|
|
93
103
|
for filter_type in filter_:
|
|
94
104
|
if filter_type == 'scope':
|
|
95
105
|
if '*' in filter_['scope'].internal:
|
|
96
106
|
scope_str = filter_['scope'].internal.replace('*', '%')
|
|
97
|
-
|
|
107
|
+
stmt = stmt.where(
|
|
108
|
+
models.Scope.scope.like(scope_str)
|
|
109
|
+
)
|
|
98
110
|
else:
|
|
99
|
-
|
|
111
|
+
stmt = stmt.where(
|
|
112
|
+
models.Scope.scope == filter_['scope']
|
|
113
|
+
)
|
|
100
114
|
|
|
101
|
-
|
|
102
|
-
scope_list.append(s.scope)
|
|
103
|
-
return scope_list
|
|
115
|
+
return list(session.execute(stmt).scalars().all())
|
|
104
116
|
|
|
105
117
|
|
|
106
118
|
@read_session
|
|
@@ -113,17 +125,22 @@ def get_scopes(account, *, session: "Session"):
|
|
|
113
125
|
:returns: a list of all scope names for this account.
|
|
114
126
|
"""
|
|
115
127
|
|
|
116
|
-
|
|
128
|
+
stmt = select(
|
|
129
|
+
models.Account
|
|
130
|
+
).where(
|
|
131
|
+
models.Account.account == account
|
|
132
|
+
)
|
|
117
133
|
|
|
118
|
-
if
|
|
134
|
+
if session.execute(stmt).first() is None:
|
|
119
135
|
raise AccountNotFound('Account ID \'%s\' does not exist' % account)
|
|
120
136
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
137
|
+
stmt = select(
|
|
138
|
+
models.Scope.scope
|
|
139
|
+
).where(
|
|
140
|
+
and_(models.Scope.account == account,
|
|
141
|
+
models.Scope.status != ScopeStatus.DELETED)
|
|
142
|
+
)
|
|
143
|
+
return session.execute(stmt).scalars().all()
|
|
127
144
|
|
|
128
145
|
|
|
129
146
|
@read_session
|
|
@@ -136,7 +153,12 @@ def check_scope(scope_to_check, *, session: "Session"):
|
|
|
136
153
|
:returns: True or false
|
|
137
154
|
"""
|
|
138
155
|
|
|
139
|
-
|
|
156
|
+
stmt = select(
|
|
157
|
+
models.Scope
|
|
158
|
+
).where(
|
|
159
|
+
models.Scope.scope == scope_to_check
|
|
160
|
+
)
|
|
161
|
+
return bool(session.execute(stmt).scalar())
|
|
140
162
|
|
|
141
163
|
|
|
142
164
|
@read_session
|
|
@@ -149,4 +171,10 @@ def is_scope_owner(scope, account, *, session: "Session"):
|
|
|
149
171
|
|
|
150
172
|
:returns: True or false
|
|
151
173
|
"""
|
|
152
|
-
|
|
174
|
+
stmt = select(
|
|
175
|
+
models.Scope
|
|
176
|
+
).where(
|
|
177
|
+
and_(models.Scope.scope == scope,
|
|
178
|
+
models.Scope.account == account)
|
|
179
|
+
)
|
|
180
|
+
return bool(session.execute(stmt).scalar())
|
rucio/core/subscription.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
3
2
|
#
|
|
4
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -18,23 +17,23 @@ import logging
|
|
|
18
17
|
import re
|
|
19
18
|
from configparser import NoOptionError, NoSectionError
|
|
20
19
|
from json import dumps
|
|
21
|
-
from typing import TYPE_CHECKING
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
22
21
|
|
|
23
|
-
from sqlalchemy import func
|
|
24
|
-
from sqlalchemy.exc import IntegrityError, StatementError
|
|
22
|
+
from sqlalchemy import and_, delete, func, select
|
|
23
|
+
from sqlalchemy.exc import IntegrityError, NoResultFound, StatementError
|
|
25
24
|
from sqlalchemy.orm import aliased
|
|
26
|
-
from sqlalchemy.orm.exc import NoResultFound
|
|
27
25
|
|
|
28
|
-
from rucio.common.config import
|
|
29
|
-
from rucio.common.exception import
|
|
26
|
+
from rucio.common.config import config_get_bool
|
|
27
|
+
from rucio.common.exception import RucioException, SubscriptionDuplicate, SubscriptionNotFound
|
|
30
28
|
from rucio.db.sqla import models
|
|
31
|
-
from rucio.db.sqla.constants import SubscriptionState
|
|
32
|
-
from rucio.db.sqla.session import
|
|
29
|
+
from rucio.db.sqla.constants import RuleState, SubscriptionState
|
|
30
|
+
from rucio.db.sqla.session import read_session, stream_session, transactional_session
|
|
33
31
|
|
|
34
32
|
if TYPE_CHECKING:
|
|
35
33
|
from collections.abc import Callable, Iterator
|
|
36
|
-
|
|
34
|
+
|
|
37
35
|
from sqlalchemy.orm import Session
|
|
36
|
+
|
|
38
37
|
from rucio.common.types import InternalAccount
|
|
39
38
|
LoggerFunction = Callable[..., Any]
|
|
40
39
|
SubscriptionType = dict
|
|
@@ -46,10 +45,10 @@ def add_subscription(name: str,
|
|
|
46
45
|
filter_: str,
|
|
47
46
|
replication_rules: str,
|
|
48
47
|
comments: str,
|
|
49
|
-
lifetime:
|
|
50
|
-
retroactive:
|
|
51
|
-
dry_run:
|
|
52
|
-
priority:
|
|
48
|
+
lifetime: Optional[int] = None,
|
|
49
|
+
retroactive: Optional[bool] = False,
|
|
50
|
+
dry_run: Optional[bool] = False,
|
|
51
|
+
priority: Optional[int] = 3,
|
|
53
52
|
*, session: "Session") -> str:
|
|
54
53
|
"""
|
|
55
54
|
Adds a new subscription which will be verified against every new added file and dataset
|
|
@@ -78,7 +77,7 @@ def add_subscription(name: str,
|
|
|
78
77
|
:returns: The subscriptionid
|
|
79
78
|
"""
|
|
80
79
|
try:
|
|
81
|
-
keep_history =
|
|
80
|
+
keep_history = config_get_bool('subscriptions', 'keep_history')
|
|
82
81
|
except (NoOptionError, NoSectionError, RuntimeError):
|
|
83
82
|
keep_history = False
|
|
84
83
|
|
|
@@ -109,7 +108,9 @@ def add_subscription(name: str,
|
|
|
109
108
|
lifetime=new_subscription.lifetime,
|
|
110
109
|
retroactive=new_subscription.retroactive,
|
|
111
110
|
policyid=new_subscription.policyid,
|
|
112
|
-
comments=new_subscription.comments
|
|
111
|
+
comments=new_subscription.comments,
|
|
112
|
+
created_at=datetime.datetime.utcnow(),
|
|
113
|
+
updated_at=datetime.datetime.utcnow())
|
|
113
114
|
try:
|
|
114
115
|
new_subscription.save(session=session)
|
|
115
116
|
if keep_history:
|
|
@@ -146,10 +147,10 @@ def update_subscription(name: str,
|
|
|
146
147
|
:raises: SubscriptionNotFound if subscription is not found
|
|
147
148
|
"""
|
|
148
149
|
try:
|
|
149
|
-
keep_history =
|
|
150
|
+
keep_history = config_get_bool('subscriptions', 'keep_history')
|
|
150
151
|
except (NoOptionError, NoSectionError, RuntimeError):
|
|
151
152
|
keep_history = False
|
|
152
|
-
values = {'state': SubscriptionState.UPDATED}
|
|
153
|
+
values: dict[str, Any] = {'state': SubscriptionState.UPDATED}
|
|
153
154
|
if 'filter' in metadata and metadata['filter']:
|
|
154
155
|
values['filter'] = dumps(metadata['filter'])
|
|
155
156
|
if 'replication_rules' in metadata and metadata['replication_rules']:
|
|
@@ -172,7 +173,13 @@ def update_subscription(name: str,
|
|
|
172
173
|
|
|
173
174
|
SubscriptionHistory = models.SubscriptionHistory
|
|
174
175
|
try:
|
|
175
|
-
|
|
176
|
+
stmt = select(
|
|
177
|
+
models.Subscription
|
|
178
|
+
).where(
|
|
179
|
+
and_(models.Subscription.name == name,
|
|
180
|
+
models.Subscription.account == account)
|
|
181
|
+
)
|
|
182
|
+
subscription = session.execute(stmt).scalar_one()
|
|
176
183
|
|
|
177
184
|
# To avoid update in the subscription history table whenever last processed field is changed
|
|
178
185
|
current_subscription_state = subscription.to_dict()
|
|
@@ -196,7 +203,7 @@ def update_subscription(name: str,
|
|
|
196
203
|
comments=subscription.comments,
|
|
197
204
|
last_processed=subscription.last_processed,
|
|
198
205
|
expired_at=subscription.expired_at,
|
|
199
|
-
updated_at=
|
|
206
|
+
updated_at=datetime.datetime.utcnow(),
|
|
200
207
|
created_at=subscription.created_at)
|
|
201
208
|
subscription_history.save(session=session)
|
|
202
209
|
except NoResultFound:
|
|
@@ -204,9 +211,9 @@ def update_subscription(name: str,
|
|
|
204
211
|
|
|
205
212
|
|
|
206
213
|
@stream_session
|
|
207
|
-
def list_subscriptions(name:
|
|
214
|
+
def list_subscriptions(name: Optional[str] = None,
|
|
208
215
|
account: "Optional[InternalAccount]" = None,
|
|
209
|
-
state:
|
|
216
|
+
state: Optional[SubscriptionState] = None,
|
|
210
217
|
*, session: "Session",
|
|
211
218
|
logger: "LoggerFunction" = logging.log) -> "Iterator[SubscriptionType]":
|
|
212
219
|
"""
|
|
@@ -224,23 +231,33 @@ def list_subscriptions(name: "Optional[str]" = None,
|
|
|
224
231
|
:rtype: Dict
|
|
225
232
|
:raises: exception.NotFound if subscription is not found
|
|
226
233
|
"""
|
|
227
|
-
|
|
234
|
+
stmt = select(
|
|
235
|
+
models.Subscription
|
|
236
|
+
)
|
|
228
237
|
try:
|
|
229
238
|
if name:
|
|
230
|
-
|
|
239
|
+
stmt = stmt.where(
|
|
240
|
+
models.Subscription.name == name
|
|
241
|
+
)
|
|
231
242
|
if account:
|
|
232
|
-
if '*' in account.internal:
|
|
243
|
+
if account.internal is not None and '*' in account.internal:
|
|
233
244
|
account_str = account.internal.replace('*', '%')
|
|
234
|
-
|
|
245
|
+
stmt = stmt.where(
|
|
246
|
+
models.Subscription.account.like(account_str)
|
|
247
|
+
)
|
|
235
248
|
else:
|
|
236
|
-
|
|
249
|
+
stmt = stmt.where(
|
|
250
|
+
models.Subscription.account == account
|
|
251
|
+
)
|
|
237
252
|
if state:
|
|
238
|
-
|
|
253
|
+
stmt = stmt.where(
|
|
254
|
+
models.Subscription.state == state
|
|
255
|
+
)
|
|
239
256
|
except IntegrityError as error:
|
|
240
257
|
logger(logging.ERROR, str(error))
|
|
241
258
|
raise RucioException(error.args)
|
|
242
259
|
found = False
|
|
243
|
-
for row in
|
|
260
|
+
for row in session.execute(stmt).scalars().all():
|
|
244
261
|
found = True
|
|
245
262
|
yield row.to_dict()
|
|
246
263
|
if not found:
|
|
@@ -255,11 +272,22 @@ def delete_subscription(subscription_id: str, *, session: "Session") -> None:
|
|
|
255
272
|
:param subscription_id: Subscription identifier
|
|
256
273
|
:type subscription_id: String
|
|
257
274
|
"""
|
|
258
|
-
|
|
275
|
+
stmt = delete(
|
|
276
|
+
models.Subscription
|
|
277
|
+
).where(
|
|
278
|
+
models.Subscription.id == subscription_id
|
|
279
|
+
)
|
|
280
|
+
session.execute(stmt)
|
|
259
281
|
|
|
260
282
|
|
|
261
283
|
@stream_session
|
|
262
|
-
def list_subscription_rule_states(
|
|
284
|
+
def list_subscription_rule_states(
|
|
285
|
+
name: Optional[str] = None,
|
|
286
|
+
account: Optional["InternalAccount"] = None,
|
|
287
|
+
*,
|
|
288
|
+
session: "Session",
|
|
289
|
+
logger: "LoggerFunction" = logging.log
|
|
290
|
+
) -> "Iterator[tuple[InternalAccount, str, RuleState, int]]":
|
|
263
291
|
"""Returns a list of with the number of rules per state for a subscription.
|
|
264
292
|
|
|
265
293
|
:param name: Name of the subscription
|
|
@@ -271,33 +299,49 @@ def list_subscription_rule_states(name=None, account=None, *, session: "Session"
|
|
|
271
299
|
subscription = aliased(models.Subscription)
|
|
272
300
|
rule = aliased(models.ReplicationRule)
|
|
273
301
|
# count needs a label to allow conversion to dict (label name can be changed)
|
|
274
|
-
|
|
275
|
-
subscription.account,
|
|
276
|
-
|
|
302
|
+
stmt = select(
|
|
303
|
+
subscription.account,
|
|
304
|
+
subscription.name,
|
|
305
|
+
rule.state,
|
|
306
|
+
func.count().label('count')
|
|
307
|
+
).join(
|
|
308
|
+
rule,
|
|
309
|
+
subscription.id == rule.subscription_id
|
|
310
|
+
)
|
|
277
311
|
|
|
278
312
|
try:
|
|
279
313
|
if name:
|
|
280
|
-
|
|
314
|
+
stmt = stmt.where(
|
|
315
|
+
subscription.name == name
|
|
316
|
+
)
|
|
281
317
|
|
|
282
318
|
if account:
|
|
283
|
-
if '*' in account.internal:
|
|
319
|
+
if account.internal and '*' in account.internal:
|
|
284
320
|
account_str = account.internal.replace('*', '%')
|
|
285
|
-
|
|
321
|
+
stmt = stmt.where(
|
|
322
|
+
subscription.account.like(account_str)
|
|
323
|
+
)
|
|
286
324
|
else:
|
|
287
|
-
|
|
325
|
+
stmt = stmt.where(
|
|
326
|
+
subscription.account == account
|
|
327
|
+
)
|
|
288
328
|
|
|
289
329
|
except IntegrityError as error:
|
|
290
330
|
logger(logging.ERROR, str(error))
|
|
291
331
|
raise RucioException(error.args)
|
|
292
332
|
|
|
293
|
-
|
|
333
|
+
stmt = stmt.group_by(
|
|
334
|
+
subscription.account,
|
|
335
|
+
subscription.name,
|
|
336
|
+
rule.state
|
|
337
|
+
)
|
|
294
338
|
|
|
295
|
-
for row in
|
|
296
|
-
yield row
|
|
339
|
+
for row in session.execute(stmt).all():
|
|
340
|
+
yield row._tuple()
|
|
297
341
|
|
|
298
342
|
|
|
299
343
|
@read_session
|
|
300
|
-
def get_subscription_by_id(subscription_id, *, session: "Session"):
|
|
344
|
+
def get_subscription_by_id(subscription_id: str, *, session: "Session") -> "SubscriptionType":
|
|
301
345
|
"""
|
|
302
346
|
Get a specific subscription by id.
|
|
303
347
|
|
|
@@ -308,8 +352,12 @@ def get_subscription_by_id(subscription_id, *, session: "Session"):
|
|
|
308
352
|
"""
|
|
309
353
|
|
|
310
354
|
try:
|
|
311
|
-
|
|
312
|
-
|
|
355
|
+
stmt = select(
|
|
356
|
+
models.Subscription
|
|
357
|
+
).where(
|
|
358
|
+
models.Subscription.id == subscription_id
|
|
359
|
+
)
|
|
360
|
+
return session.execute(stmt).scalar_one().to_dict()
|
|
313
361
|
except NoResultFound:
|
|
314
362
|
raise SubscriptionNotFound('No subscription with the id %s found' % (subscription_id))
|
|
315
363
|
except StatementError:
|
rucio/core/topology.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
3
2
|
#
|
|
4
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -12,6 +11,7 @@
|
|
|
12
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
12
|
# See the License for the specific language governing permissions and
|
|
14
13
|
# limitations under the License.
|
|
14
|
+
import copy
|
|
15
15
|
import datetime
|
|
16
16
|
import itertools
|
|
17
17
|
import logging
|
|
@@ -19,12 +19,12 @@ import threading
|
|
|
19
19
|
import weakref
|
|
20
20
|
from collections.abc import Callable, Iterable, Iterator
|
|
21
21
|
from decimal import Decimal
|
|
22
|
-
from typing import TYPE_CHECKING,
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union, cast
|
|
23
23
|
|
|
24
24
|
from sqlalchemy import and_, select
|
|
25
25
|
|
|
26
|
-
from rucio.common.config import
|
|
27
|
-
from rucio.common.exception import NoDistance, RSEProtocolNotSupported
|
|
26
|
+
from rucio.common.config import config_get, config_get_int
|
|
27
|
+
from rucio.common.exception import InvalidRSEExpression, NoDistance, RSEProtocolNotSupported
|
|
28
28
|
from rucio.common.utils import PriorityQueue
|
|
29
29
|
from rucio.core.rse import RseCollection, RseData
|
|
30
30
|
from rucio.core.rse_expression_parser import parse_expression
|
|
@@ -32,15 +32,18 @@ from rucio.db.sqla import models
|
|
|
32
32
|
from rucio.db.sqla.session import read_session, transactional_session
|
|
33
33
|
from rucio.rse import rsemanager as rsemgr
|
|
34
34
|
|
|
35
|
-
LoggerFunction = Callable[..., Any]
|
|
36
35
|
_Number = Union[int, Decimal]
|
|
37
36
|
TN = TypeVar("TN", bound="Node")
|
|
38
37
|
TE = TypeVar("TE", bound="Edge")
|
|
38
|
+
ExpiringObjectCacheNewObject = TypeVar("ExpiringObjectCacheNewObject")
|
|
39
39
|
|
|
40
40
|
if TYPE_CHECKING:
|
|
41
|
+
from typing import Protocol
|
|
42
|
+
|
|
41
43
|
from sqlalchemy.orm import Session
|
|
44
|
+
from typing_extensions import Self
|
|
42
45
|
|
|
43
|
-
from
|
|
46
|
+
from rucio.common.types import HopDict, LoggerFunction
|
|
44
47
|
|
|
45
48
|
class _StateProvider(Protocol):
|
|
46
49
|
@property
|
|
@@ -81,11 +84,11 @@ class Edge(Generic[TN]):
|
|
|
81
84
|
|
|
82
85
|
self.add_to_nodes()
|
|
83
86
|
|
|
84
|
-
def add_to_nodes(self):
|
|
87
|
+
def add_to_nodes(self) -> None:
|
|
85
88
|
self.src_node.out_edges[self.dst_node] = self
|
|
86
89
|
self.dst_node.in_edges[self.src_node] = self
|
|
87
90
|
|
|
88
|
-
def remove_from_nodes(self):
|
|
91
|
+
def remove_from_nodes(self) -> None:
|
|
89
92
|
self.src_node.out_edges.pop(self.dst_node, None)
|
|
90
93
|
self.dst_node.in_edges.pop(self.src_node, None)
|
|
91
94
|
|
|
@@ -105,12 +108,12 @@ class Edge(Generic[TN]):
|
|
|
105
108
|
raise ReferenceError("weak reference returned None")
|
|
106
109
|
return node
|
|
107
110
|
|
|
108
|
-
def __eq__(self, other):
|
|
111
|
+
def __eq__(self, other: object) -> bool:
|
|
109
112
|
if not isinstance(other, self.__class__):
|
|
110
113
|
return False
|
|
111
114
|
return self._src_node == other._src_node and self._dst_node == other._dst_node
|
|
112
115
|
|
|
113
|
-
def __str__(self):
|
|
116
|
+
def __str__(self) -> str:
|
|
114
117
|
return f'{self._src_node}-->{self._dst_node}'
|
|
115
118
|
|
|
116
119
|
|
|
@@ -127,7 +130,7 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
127
130
|
):
|
|
128
131
|
super().__init__(rse_ids=rse_ids, rse_data_cls=node_cls)
|
|
129
132
|
self._edge_cls = edge_cls
|
|
130
|
-
self._edges = {}
|
|
133
|
+
self._edges: dict[tuple[TN, TN], TE] = {}
|
|
131
134
|
self._edges_loaded = False
|
|
132
135
|
self._multihop_nodes = set()
|
|
133
136
|
self._hop_penalty = DEFAULT_HOP_PENALTY
|
|
@@ -135,6 +138,36 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
135
138
|
|
|
136
139
|
self._lock = threading.RLock()
|
|
137
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
|
+
|
|
138
171
|
def get_or_create(self, rse_id: str) -> "TN":
|
|
139
172
|
rse_data = self.rse_id_to_data_map.get(rse_id)
|
|
140
173
|
if rse_data is None:
|
|
@@ -146,6 +179,11 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
146
179
|
self._edges_loaded = False
|
|
147
180
|
return rse_data
|
|
148
181
|
|
|
182
|
+
@property
|
|
183
|
+
def edges(self) -> dict[tuple[TN, TN], TE]:
|
|
184
|
+
with self._lock:
|
|
185
|
+
return copy.copy(self._edges)
|
|
186
|
+
|
|
149
187
|
def edge(self, src_node: TN, dst_node: TN) -> "Optional[TE]":
|
|
150
188
|
return self._edges.get((src_node, dst_node))
|
|
151
189
|
|
|
@@ -158,7 +196,7 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
158
196
|
self._edges[src_node, dst_node] = edge = self._edge_cls(src_node, dst_node)
|
|
159
197
|
return edge
|
|
160
198
|
|
|
161
|
-
def delete_edge(self, src_node: TN, dst_node: TN):
|
|
199
|
+
def delete_edge(self, src_node: TN, dst_node: TN) -> None:
|
|
162
200
|
with self._lock:
|
|
163
201
|
edge = self._edges[src_node, dst_node]
|
|
164
202
|
edge.remove_from_nodes()
|
|
@@ -168,11 +206,11 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
168
206
|
return True if self._multihop_nodes else False
|
|
169
207
|
|
|
170
208
|
@read_session
|
|
171
|
-
def configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: LoggerFunction = logging.log):
|
|
209
|
+
def configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: "LoggerFunction" = logging.log) -> "Self":
|
|
172
210
|
with self._lock:
|
|
173
211
|
return self._configure_multihop(multihop_rse_ids=multihop_rse_ids, session=session, logger=logger)
|
|
174
212
|
|
|
175
|
-
def _configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: LoggerFunction = logging.log):
|
|
213
|
+
def _configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: "LoggerFunction" = logging.log) -> "Self":
|
|
176
214
|
|
|
177
215
|
if multihop_rse_ids is None:
|
|
178
216
|
multihop_rse_expression = config_get('transfers', 'multihop_rse_expression', default='available_for_multihop=true', expiration_time=600, session=session)
|
|
@@ -201,7 +239,7 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
201
239
|
return self
|
|
202
240
|
|
|
203
241
|
@read_session
|
|
204
|
-
def ensure_edges_loaded(self, *, session: "Session"):
|
|
242
|
+
def ensure_edges_loaded(self, *, session: "Session") -> None:
|
|
205
243
|
"""
|
|
206
244
|
Ensure that all edges are loaded for the (sub-)set of nodes known by this topology object
|
|
207
245
|
"""
|
|
@@ -211,7 +249,7 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
211
249
|
with self._lock:
|
|
212
250
|
return self._ensure_edges_loaded(session=session)
|
|
213
251
|
|
|
214
|
-
def _ensure_edges_loaded(self, *, session: "Session"):
|
|
252
|
+
def _ensure_edges_loaded(self, *, session: "Session") -> None:
|
|
215
253
|
stmt = select(
|
|
216
254
|
models.Distance
|
|
217
255
|
).where(
|
|
@@ -246,7 +284,7 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
246
284
|
@read_session
|
|
247
285
|
def search_shortest_paths(
|
|
248
286
|
self,
|
|
249
|
-
src_nodes:
|
|
287
|
+
src_nodes: Iterable[TN],
|
|
250
288
|
dst_node: TN,
|
|
251
289
|
operation_src: str,
|
|
252
290
|
operation_dest: str,
|
|
@@ -320,7 +358,7 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
320
358
|
node_state_provider=_NodeStateProvider,
|
|
321
359
|
edge_state_provider=_EdgeStateProvider):
|
|
322
360
|
nh_node = edge_to_next_hop.dst_node
|
|
323
|
-
edge_state = cast(_EdgeStateProvider, edge_state)
|
|
361
|
+
edge_state = cast("_EdgeStateProvider", edge_state)
|
|
324
362
|
hop = {
|
|
325
363
|
'source_rse': node,
|
|
326
364
|
'dest_rse': nh_node,
|
|
@@ -386,20 +424,24 @@ class Topology(RseCollection, Generic[TN, TE]):
|
|
|
386
424
|
priority_q[adjacent_node] = new_adjacent_dist
|
|
387
425
|
|
|
388
426
|
|
|
389
|
-
class ExpiringObjectCache:
|
|
427
|
+
class ExpiringObjectCache(Generic[ExpiringObjectCacheNewObject]):
|
|
390
428
|
"""
|
|
391
429
|
Thread-safe container which builds and object with the function passed in parameter and
|
|
392
430
|
caches it for the TTL duration.
|
|
393
431
|
"""
|
|
394
432
|
|
|
395
|
-
def __init__(
|
|
433
|
+
def __init__(
|
|
434
|
+
self,
|
|
435
|
+
ttl: int,
|
|
436
|
+
new_obj_fnc: Callable[[], ExpiringObjectCacheNewObject]
|
|
437
|
+
):
|
|
396
438
|
self._lock = threading.Lock()
|
|
397
|
-
self._object = None
|
|
398
|
-
self._creation_time = None
|
|
439
|
+
self._object: Optional[ExpiringObjectCacheNewObject] = None
|
|
440
|
+
self._creation_time: Optional[datetime.datetime] = None
|
|
399
441
|
self._new_obj_fnc = new_obj_fnc
|
|
400
442
|
self._ttl = ttl
|
|
401
443
|
|
|
402
|
-
def get(self, logger=logging.log):
|
|
444
|
+
def get(self, logger: "LoggerFunction" = logging.log) -> ExpiringObjectCacheNewObject:
|
|
403
445
|
with self._lock:
|
|
404
446
|
if not self._object \
|
|
405
447
|
or not self._creation_time \
|
|
@@ -417,7 +459,7 @@ def get_hops(
|
|
|
417
459
|
multihop_rse_ids: Optional[set[str]] = None,
|
|
418
460
|
limit_dest_schemes: Optional[list[str]] = None,
|
|
419
461
|
*, session: "Session",
|
|
420
|
-
):
|
|
462
|
+
) -> list["HopDict"]:
|
|
421
463
|
"""
|
|
422
464
|
Get a list of hops needed to transfer date from source_rse_id to dest_rse_id.
|
|
423
465
|
Ideally, the list will only include one item (dest_rse_id) since no hops are needed.
|