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/rse/translation.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
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 hashlib
|
|
15
|
+
import importlib
|
|
16
|
+
import logging
|
|
17
|
+
from configparser import NoOptionError, NoSectionError
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
19
|
+
|
|
20
|
+
from rucio.common import config
|
|
21
|
+
from rucio.common.constants import RseAttr
|
|
22
|
+
from rucio.common.exception import ConfigNotFound
|
|
23
|
+
from rucio.common.plugins import PolicyPackageAlgorithms
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Callable, Mapping
|
|
27
|
+
|
|
28
|
+
from rucio.common.types import RSESettingsDict
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RSEDeterministicScopeTranslation(PolicyPackageAlgorithms):
|
|
32
|
+
"""
|
|
33
|
+
Translates a pfn dictionary into a scope and name
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_algorithm_type = "pfn2lfn"
|
|
37
|
+
|
|
38
|
+
def __init__(self, vo: str = 'def'):
|
|
39
|
+
super().__init__()
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
algorithm_name = config.config_get('policy', self._algorithm_type)
|
|
45
|
+
except (ConfigNotFound, NoOptionError, NoSectionError, RuntimeError):
|
|
46
|
+
logger.debug("PFN2LFN: no algorithm specified in the config.")
|
|
47
|
+
if super()._supports(self._algorithm_type, vo):
|
|
48
|
+
algorithm_name = vo
|
|
49
|
+
else:
|
|
50
|
+
algorithm_name = "def"
|
|
51
|
+
logger.debug("PFN2LFN: Falling back to %s algorithm.", 'default' if algorithm_name == 'def' else algorithm_name)
|
|
52
|
+
|
|
53
|
+
self.parser = self.get_parser(algorithm_name)
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def _module_init_(cls) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Registers the included scope extraction algorithms
|
|
59
|
+
"""
|
|
60
|
+
cls.register(cls._default, "def")
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def get_parser(cls, algorithm_name: str) -> 'Callable[..., Any]':
|
|
64
|
+
return super()._get_one_algorithm(cls._algorithm_type, algorithm_name)
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def register(
|
|
68
|
+
cls,
|
|
69
|
+
pfn2lfn_callable: 'Callable',
|
|
70
|
+
name: Optional[str] = None
|
|
71
|
+
) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Provided a callable function, register it as one of the valid PFN2LFN algorithms.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
:param pfn2lfn_callable: Callable function to use.
|
|
77
|
+
:param name: Algorithm name used for registration.
|
|
78
|
+
"""
|
|
79
|
+
if name is None:
|
|
80
|
+
name = pfn2lfn_callable.__name__
|
|
81
|
+
algorithm_dict = {name: pfn2lfn_callable}
|
|
82
|
+
super()._register(cls._algorithm_type, algorithm_dict)
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _default(parsed_pfn: 'Mapping[str, str]') -> tuple[str, str]:
|
|
86
|
+
""" Translate pfn to name/scope pair
|
|
87
|
+
|
|
88
|
+
:param parsed_pfn: dictionary representing pfn containing:
|
|
89
|
+
- path: str,
|
|
90
|
+
- name: str
|
|
91
|
+
:return: tuple containing name, scope
|
|
92
|
+
"""
|
|
93
|
+
path = parsed_pfn['path']
|
|
94
|
+
scope = path.lstrip('/').split('/')[0]
|
|
95
|
+
name = parsed_pfn['name']
|
|
96
|
+
return name, scope
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
RSEDeterministicScopeTranslation._module_init_() # pylint: disable=protected-access
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class RSEDeterministicTranslation(PolicyPackageAlgorithms):
|
|
103
|
+
"""
|
|
104
|
+
Execute the logic for translating a LFN to a path.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
_DEFAULT_LFN2PFN = "hash"
|
|
108
|
+
_algorithm_type = "lfn2pfn"
|
|
109
|
+
|
|
110
|
+
def __init__(
|
|
111
|
+
self,
|
|
112
|
+
rse: Optional[str] = None,
|
|
113
|
+
rse_attributes: Optional["RSESettingsDict"] = None,
|
|
114
|
+
protocol_attributes: Optional[dict[str, Any]] = None
|
|
115
|
+
):
|
|
116
|
+
"""
|
|
117
|
+
Initialize a translator object from the RSE, its attributes, and the protocol-specific
|
|
118
|
+
attributes.
|
|
119
|
+
|
|
120
|
+
:param rse: Name of RSE for this translation.
|
|
121
|
+
:param rse_attributes: A dictionary of RSE-specific attributes for use in the translation.
|
|
122
|
+
:param protocol_attributes: A dictionary of RSE/protocol-specific attributes.
|
|
123
|
+
"""
|
|
124
|
+
super().__init__()
|
|
125
|
+
self.rse = rse
|
|
126
|
+
self.rse_attributes = rse_attributes if rse_attributes else {}
|
|
127
|
+
self.protocol_attributes = protocol_attributes if protocol_attributes else {}
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def supports(
|
|
131
|
+
cls,
|
|
132
|
+
name: str
|
|
133
|
+
) -> bool:
|
|
134
|
+
"""
|
|
135
|
+
Check to see if a specific algorithm is supported.
|
|
136
|
+
|
|
137
|
+
:param name: Name of the deterministic algorithm.
|
|
138
|
+
:returns: True if `name` is an algorithm supported by the translator class, False otherwise
|
|
139
|
+
"""
|
|
140
|
+
return super()._supports(cls._algorithm_type, name)
|
|
141
|
+
|
|
142
|
+
@classmethod
|
|
143
|
+
def register(
|
|
144
|
+
cls,
|
|
145
|
+
lfn2pfn_callable: 'Callable',
|
|
146
|
+
name: Optional[str] = None
|
|
147
|
+
) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Provided a callable function, register it as one of the valid LFN2PFN algorithms.
|
|
150
|
+
|
|
151
|
+
The callable will receive five arguments:
|
|
152
|
+
- scope: Scope of the LFN.
|
|
153
|
+
- name: LFN's path name
|
|
154
|
+
- rse: RSE name the translation is being done for.
|
|
155
|
+
- rse_attributes: Attributes of the RSE.
|
|
156
|
+
- protocol_attributes: Attributes of the RSE's protocol
|
|
157
|
+
The return value should be the last part of the PFN - it will be appended to the
|
|
158
|
+
rest of the URL.
|
|
159
|
+
|
|
160
|
+
:param lfn2pfn_callable: Callable function to use for generating paths.
|
|
161
|
+
:param name: Algorithm name used for registration. If None, then `lfn2pfn_callable.__name__` is used.
|
|
162
|
+
"""
|
|
163
|
+
if name is None:
|
|
164
|
+
name = lfn2pfn_callable.__name__
|
|
165
|
+
algorithm_dict = {name: lfn2pfn_callable}
|
|
166
|
+
super()._register(cls._algorithm_type, algorithm_dict)
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def __hash(
|
|
170
|
+
scope: str,
|
|
171
|
+
name: str,
|
|
172
|
+
rse: str,
|
|
173
|
+
rse_attrs: dict[str, Any],
|
|
174
|
+
protocol_attrs: dict[str, Any]
|
|
175
|
+
) -> str:
|
|
176
|
+
"""
|
|
177
|
+
Given a LFN, turn it into a sub-directory structure using a hash function.
|
|
178
|
+
|
|
179
|
+
This takes the MD5 of the LFN and uses the first four characters as a subdirectory
|
|
180
|
+
name.
|
|
181
|
+
|
|
182
|
+
:param scope: Scope of the LFN.
|
|
183
|
+
:param name: File name of the LFN.
|
|
184
|
+
:param rse: RSE for PFN (ignored)
|
|
185
|
+
:param rse_attrs: RSE attributes for PFN (ignored)
|
|
186
|
+
:param protocol_attrs: RSE protocol attributes for PFN (ignored)
|
|
187
|
+
:returns: Path for use in the PFN generation.
|
|
188
|
+
"""
|
|
189
|
+
del rse
|
|
190
|
+
del rse_attrs
|
|
191
|
+
del protocol_attrs
|
|
192
|
+
hstr = hashlib.md5(('%s:%s' % (scope, name)).encode('utf-8')).hexdigest()
|
|
193
|
+
if scope.startswith('user') or scope.startswith('group'):
|
|
194
|
+
scope = scope.replace('.', '/')
|
|
195
|
+
return '%s/%s/%s/%s' % (scope, hstr[0:2], hstr[2:4], name)
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def __identity(
|
|
199
|
+
scope: str,
|
|
200
|
+
name: str,
|
|
201
|
+
rse: str,
|
|
202
|
+
rse_attrs: dict[str, Any],
|
|
203
|
+
protocol_attrs: dict[str, Any]
|
|
204
|
+
) -> str:
|
|
205
|
+
"""
|
|
206
|
+
Given a LFN, convert it directly to a path using the mapping:
|
|
207
|
+
|
|
208
|
+
scope:path -> scope/path
|
|
209
|
+
|
|
210
|
+
:param scope: Scope of the LFN.
|
|
211
|
+
:param name: File name of the LFN.
|
|
212
|
+
:param rse: RSE for PFN (ignored)
|
|
213
|
+
:param rse_attrs: RSE attributes for PFN (ignored)
|
|
214
|
+
:param protocol_attrs: RSE protocol attributes for PFN (ignored)
|
|
215
|
+
:returns: Path for use in the PFN generation.
|
|
216
|
+
"""
|
|
217
|
+
del rse
|
|
218
|
+
del rse_attrs
|
|
219
|
+
del protocol_attrs
|
|
220
|
+
if scope.startswith('user') or scope.startswith('group'):
|
|
221
|
+
scope = scope.replace('.', '/')
|
|
222
|
+
return '%s/%s' % (scope, name)
|
|
223
|
+
|
|
224
|
+
@classmethod
|
|
225
|
+
def _module_init_(cls) -> None:
|
|
226
|
+
"""
|
|
227
|
+
Initialize the class object on first module load.
|
|
228
|
+
"""
|
|
229
|
+
cls.register(cls.__hash, "hash")
|
|
230
|
+
cls.register(cls.__identity, "identity")
|
|
231
|
+
policy_module = None
|
|
232
|
+
try:
|
|
233
|
+
policy_module = config.config_get('policy', 'lfn2pfn_module')
|
|
234
|
+
except (ConfigNotFound, NoOptionError, NoSectionError):
|
|
235
|
+
pass
|
|
236
|
+
if policy_module:
|
|
237
|
+
importlib.import_module(policy_module)
|
|
238
|
+
|
|
239
|
+
cls._DEFAULT_LFN2PFN = config.get_lfn2pfn_algorithm_default()
|
|
240
|
+
|
|
241
|
+
def path(
|
|
242
|
+
self,
|
|
243
|
+
scope: str,
|
|
244
|
+
name: str
|
|
245
|
+
) -> str:
|
|
246
|
+
""" Transforms the logical file name into a PFN's path.
|
|
247
|
+
|
|
248
|
+
:param lfn: filename
|
|
249
|
+
:param scope: scope
|
|
250
|
+
|
|
251
|
+
:returns: RSE specific URI of the physical file
|
|
252
|
+
"""
|
|
253
|
+
algorithm = self.rse_attributes.get(RseAttr.LFN2PFN_ALGORITHM, 'default')
|
|
254
|
+
if algorithm == 'default':
|
|
255
|
+
algorithm = RSEDeterministicTranslation._DEFAULT_LFN2PFN
|
|
256
|
+
algorithm_callable = super()._get_one_algorithm(RSEDeterministicTranslation._algorithm_type, algorithm)
|
|
257
|
+
return algorithm_callable(scope, name, self.rse, self.rse_attributes, self.protocol_attributes)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
RSEDeterministicTranslation._module_init_() # pylint: disable=protected-access
|
rucio/tests/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
rucio/tests/common.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import contextlib
|
|
16
|
+
import itertools
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import tempfile
|
|
20
|
+
from collections import namedtuple
|
|
21
|
+
from functools import wraps
|
|
22
|
+
from os import rename
|
|
23
|
+
from random import choice, choices
|
|
24
|
+
from string import ascii_letters, ascii_uppercase, digits
|
|
25
|
+
from typing import IO, TYPE_CHECKING, Any, Literal, Optional
|
|
26
|
+
|
|
27
|
+
import pytest
|
|
28
|
+
import requests
|
|
29
|
+
|
|
30
|
+
from rucio.common.config import config_get, config_get_bool, get_config_dirs
|
|
31
|
+
from rucio.common.utils import execute
|
|
32
|
+
from rucio.common.utils import generate_uuid as uuid
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
36
|
+
from types import ModuleType
|
|
37
|
+
|
|
38
|
+
from werkzeug.test import TestResponse
|
|
39
|
+
|
|
40
|
+
skip_rse_tests_with_accounts = pytest.mark.skipif(not any(os.path.exists(os.path.join(d, 'rse-accounts.cfg')) for d in get_config_dirs()),
|
|
41
|
+
reason='fails if no rse-accounts.cfg found')
|
|
42
|
+
skiplimitedsql = pytest.mark.skipif('RDBMS' in os.environ and os.environ['RDBMS'] == 'sqlite',
|
|
43
|
+
reason="does not work in SQLite because of missing features")
|
|
44
|
+
skip_multivo = pytest.mark.skipif('SUITE' in os.environ and os.environ['SUITE'] == 'multi_vo',
|
|
45
|
+
reason="does not work for multiVO")
|
|
46
|
+
skip_non_belleii = pytest.mark.skipif(not ('POLICY' in os.environ and os.environ['POLICY'] == 'belleii'),
|
|
47
|
+
reason="specific belleii tests")
|
|
48
|
+
skip_outside_gh_actions = pytest.mark.skipif(os.getenv("GITHUB_ACTIONS") != "true",
|
|
49
|
+
reason="Skipping tests outside GitHub Actions")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_influxdb_available() -> bool:
|
|
53
|
+
"""Return True if influxdb is available, else return False."""
|
|
54
|
+
try:
|
|
55
|
+
response = requests.get('http://localhost:8086/ping')
|
|
56
|
+
return response.status_code == 204
|
|
57
|
+
except requests.exceptions.ConnectionError:
|
|
58
|
+
print('InfluxDB is not running at localhost:8086')
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def is_elasticsearch_available() -> bool:
|
|
63
|
+
"""Return True if elasticsearch is available, else return False."""
|
|
64
|
+
try:
|
|
65
|
+
response = requests.get('http://localhost:9200/')
|
|
66
|
+
return response.status_code == 200
|
|
67
|
+
except requests.exceptions.ConnectionError:
|
|
68
|
+
print('Elasticsearch is not running at localhost:9200')
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
skip_missing_elasticsearch_influxdb_in_env = pytest.mark.skipif(not (is_influxdb_available() and is_elasticsearch_available()), reason='influxdb is not available')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_long_vo() -> str:
|
|
76
|
+
""" Get the VO name from the config file for testing.
|
|
77
|
+
Don't map the name to a short version.
|
|
78
|
+
:returns: VO name string.
|
|
79
|
+
"""
|
|
80
|
+
vo_name = 'def'
|
|
81
|
+
if config_get_bool('common', 'multi_vo', raise_exception=False, default=False):
|
|
82
|
+
vo = config_get('client', 'vo', raise_exception=False, default=None)
|
|
83
|
+
if vo is not None:
|
|
84
|
+
vo_name = vo
|
|
85
|
+
return vo_name
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def account_name_generator() -> str:
|
|
89
|
+
""" Generate random account name.
|
|
90
|
+
|
|
91
|
+
:returns: A random account name
|
|
92
|
+
"""
|
|
93
|
+
return 'jdoe-' + str(uuid()).lower()[:16]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def scope_name_generator() -> str:
|
|
97
|
+
""" Generate random scope name.
|
|
98
|
+
|
|
99
|
+
:returns: A random scope name
|
|
100
|
+
"""
|
|
101
|
+
return 'mock_' + str(uuid()).lower()[:16]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def did_name_generator(did_type: str = 'file', name_prefix: str = '', name_suffix: str = '', path: Optional[str] = None) -> str:
|
|
105
|
+
""" Generate random did name.
|
|
106
|
+
:param did_type: A string to create a meaningful did_name depending on the did_type (file, dataset, container)
|
|
107
|
+
:param name_prefix: String to prefix to the did name
|
|
108
|
+
:param name_suffix: String to append to the did name
|
|
109
|
+
:param path: If specified, use the path to generate the did_name
|
|
110
|
+
|
|
111
|
+
:returns: A random did name
|
|
112
|
+
"""
|
|
113
|
+
if os.getenv('POLICY') == 'belleii':
|
|
114
|
+
if path is not None:
|
|
115
|
+
return path
|
|
116
|
+
|
|
117
|
+
container_path = os.path.join("/belle", name_prefix if name_prefix else "mock", 'cont_%s' % str(uuid()))
|
|
118
|
+
if did_type == 'container':
|
|
119
|
+
return container_path
|
|
120
|
+
|
|
121
|
+
dataset_path = os.path.join(container_path, 'dataset_%s' % str(uuid()))
|
|
122
|
+
if did_type == 'dataset':
|
|
123
|
+
return dataset_path
|
|
124
|
+
|
|
125
|
+
file_path = os.path.join(dataset_path, 'file_%s%s' % (str(uuid()), name_suffix))
|
|
126
|
+
return file_path
|
|
127
|
+
|
|
128
|
+
if path is not None:
|
|
129
|
+
return os.path.basename(path)
|
|
130
|
+
|
|
131
|
+
return '%s%s_%s%s' % (name_prefix, did_type, str(uuid()), name_suffix)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def rse_name_generator(size: int = 10) -> str:
|
|
135
|
+
""" Generate random RSE name.
|
|
136
|
+
|
|
137
|
+
:returns: A random RSE name
|
|
138
|
+
"""
|
|
139
|
+
return 'MOCK-' + ''.join(choice(ascii_uppercase) for x in range(size)) # noqa: S311
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def rfc2253_dn_generator() -> str:
|
|
143
|
+
""" Generate a random DN in RFC 2253 format.
|
|
144
|
+
|
|
145
|
+
:returns: A random DN
|
|
146
|
+
"""
|
|
147
|
+
random_cn = ''.join(choices(ascii_letters + digits, k=8)) # noqa: S311
|
|
148
|
+
random_o = ''.join(choices(ascii_letters + digits, k=8)) # noqa: S311
|
|
149
|
+
random_c = ''.join(choices(ascii_letters, k=2)) # noqa: S311
|
|
150
|
+
random_dn = "CN={}, O={}, C={}".format(random_cn, random_o, random_c)
|
|
151
|
+
return random_dn
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def file_generator(size: int = 2, namelen: int = 10) -> str:
|
|
155
|
+
""" Create a bogus file and returns it's name.
|
|
156
|
+
:param size: size in bytes
|
|
157
|
+
:returns: The name of the generated file.
|
|
158
|
+
"""
|
|
159
|
+
fn = '/tmp/file_' + ''.join(choice(ascii_uppercase) for x in range(namelen)) # noqa: S311
|
|
160
|
+
execute('dd if=/dev/urandom of={0} count={1} bs=1'.format(fn, size))
|
|
161
|
+
return fn
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def make_temp_file(dir_: str, data: str) -> str:
|
|
165
|
+
"""
|
|
166
|
+
Creates a temporal file and write `data` on it.
|
|
167
|
+
:param data: String to be written on the created file.
|
|
168
|
+
:returns: Name of the temporal file.
|
|
169
|
+
"""
|
|
170
|
+
fd, path = tempfile.mkstemp(dir=dir_)
|
|
171
|
+
with os.fdopen(fd, 'w', encoding='utf-8') as f:
|
|
172
|
+
f.write(data)
|
|
173
|
+
return path
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@contextlib.contextmanager
|
|
177
|
+
def mock_open(module: "ModuleType", file_like_object: IO):
|
|
178
|
+
call_info = {}
|
|
179
|
+
|
|
180
|
+
def mocked_open(
|
|
181
|
+
filename: str,
|
|
182
|
+
mode: str = 'r'
|
|
183
|
+
) -> contextlib.closing[IO]:
|
|
184
|
+
call_info['filename'] = filename
|
|
185
|
+
call_info['mode'] = mode
|
|
186
|
+
file_like_object.close = lambda: None
|
|
187
|
+
return contextlib.closing(file_like_object)
|
|
188
|
+
|
|
189
|
+
setattr(module, 'open', mocked_open)
|
|
190
|
+
try:
|
|
191
|
+
yield call_info
|
|
192
|
+
finally:
|
|
193
|
+
file_like_object.seek(0)
|
|
194
|
+
delattr(module, 'open')
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def print_response(rest_response: "TestResponse") -> None:
|
|
198
|
+
print('Status:', rest_response.status)
|
|
199
|
+
print()
|
|
200
|
+
nohdrs = True
|
|
201
|
+
for hdr, val in rest_response.headers.items():
|
|
202
|
+
if nohdrs:
|
|
203
|
+
print('Headers:')
|
|
204
|
+
print('-------')
|
|
205
|
+
nohdrs = False
|
|
206
|
+
print('%s: %s' % (hdr, val))
|
|
207
|
+
|
|
208
|
+
if not nohdrs:
|
|
209
|
+
print()
|
|
210
|
+
|
|
211
|
+
text = rest_response.get_data(as_text=True)
|
|
212
|
+
print(text if text else '<no content>')
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def headers(*iterables: "Iterable") -> list[itertools.chain]:
|
|
216
|
+
return list(itertools.chain(*iterables))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def loginhdr(account: str, username: str, password: str) -> "Iterator[tuple[Literal['X-Rucio-Account', 'X-Rucio-Username', 'X-Rucio-Password'], str]]":
|
|
220
|
+
yield 'X-Rucio-Account', str(account)
|
|
221
|
+
yield 'X-Rucio-Username', str(username)
|
|
222
|
+
yield 'X-Rucio-Password', str(password)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def auth(token) -> "Iterator[tuple[Literal['X-Rucio-Auth-Token'], str]]":
|
|
226
|
+
yield 'X-Rucio-Auth-Token', str(token)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def vohdr(vo: str) -> "Iterator[tuple[Literal['X-Rucio-VO'], str]]":
|
|
230
|
+
if vo:
|
|
231
|
+
yield 'X-Rucio-VO', str(vo)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def hdrdict(dictionary: dict[str, Any]) -> "Iterator[tuple[str, str]]":
|
|
235
|
+
for key in dictionary:
|
|
236
|
+
yield str(key), str(dictionary[key])
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def accept(mimetype: "Mime") -> "Iterator[tuple[Literal['Accept'], Mime]]":
|
|
240
|
+
yield 'Accept', mimetype
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class Mime:
|
|
244
|
+
""" Enum-type class for mimetypes. """
|
|
245
|
+
METALINK = 'application/metalink4+xml'
|
|
246
|
+
JSON = 'application/json'
|
|
247
|
+
JSON_STREAM = 'application/x-json-stream'
|
|
248
|
+
BINARY = 'application/octet-stream'
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def load_test_conf_file(file_name: str) -> dict[str, Any]:
|
|
252
|
+
config_dir = next(filter(lambda d: os.path.exists(os.path.join(d, file_name)), get_config_dirs()))
|
|
253
|
+
with open(os.path.join(config_dir, file_name)) as f:
|
|
254
|
+
return json.load(f)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def remove_config(func: "Callable") -> "Callable":
|
|
258
|
+
@wraps(func)
|
|
259
|
+
def wrapper(*args, **kwargs) -> None:
|
|
260
|
+
for configfile in get_config_dirs():
|
|
261
|
+
# Rename the config to <config>.tmp
|
|
262
|
+
try:
|
|
263
|
+
rename(f"{configfile}rucio.cfg", f"{configfile}rucio.cfg.tmp")
|
|
264
|
+
except FileNotFoundError:
|
|
265
|
+
pass # When a test uses a os.env assigned conf, there's nothing stating the default location has something
|
|
266
|
+
try:
|
|
267
|
+
# Execute the test
|
|
268
|
+
func(*args, **kwargs)
|
|
269
|
+
finally:
|
|
270
|
+
# And put the config back
|
|
271
|
+
for configfile in get_config_dirs():
|
|
272
|
+
try:
|
|
273
|
+
rename(f"{configfile}rucio.cfg.tmp", f"{configfile}rucio.cfg")
|
|
274
|
+
except FileNotFoundError:
|
|
275
|
+
pass
|
|
276
|
+
|
|
277
|
+
return wrapper
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
RSE_namedtuple = namedtuple('RSE_namedtuple', ['name', 'id'])
|
|
@@ -0,0 +1,149 @@
|
|
|
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
|
+
from typing import TYPE_CHECKING, Optional
|
|
16
|
+
|
|
17
|
+
from sqlalchemy import and_, delete, exists, select
|
|
18
|
+
from sqlalchemy.orm import Session, aliased
|
|
19
|
+
|
|
20
|
+
from rucio.core import config as core_config
|
|
21
|
+
from rucio.core.vo import map_vo
|
|
22
|
+
from rucio.db.sqla import models
|
|
23
|
+
from rucio.db.sqla.session import get_session, transactional_session
|
|
24
|
+
|
|
25
|
+
from .common import get_long_vo
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from collections.abc import Iterator
|
|
29
|
+
|
|
30
|
+
from sqlalchemy.sql.elements import ColumnElement
|
|
31
|
+
from sqlalchemy.sql.schema import ForeignKeyConstraint
|
|
32
|
+
from sqlalchemy.sql.selectable import FromClause
|
|
33
|
+
|
|
34
|
+
# Functions containing server-only includes that can't be included in client tests
|
|
35
|
+
# For each table, get the foreign key constraints from all other tables towards this table.
|
|
36
|
+
INBOUND_FOREIGN_KEYS: "dict[FromClause, set[ForeignKeyConstraint]]" = {}
|
|
37
|
+
for __table in models.BASE.metadata.tables.values():
|
|
38
|
+
for __fk in __table.foreign_key_constraints:
|
|
39
|
+
INBOUND_FOREIGN_KEYS.setdefault(__fk.referred_table, set()).add(__fk)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _dependency_paths(
|
|
43
|
+
stack: list["ForeignKeyConstraint"],
|
|
44
|
+
nb_times_in_stack: dict["FromClause", int],
|
|
45
|
+
cur_table: "FromClause"
|
|
46
|
+
) -> "Iterator":
|
|
47
|
+
"""
|
|
48
|
+
Generates lists of foreign keys: paths starting at cur_table and
|
|
49
|
+
navigating the table graph via foreign key constraints.
|
|
50
|
+
|
|
51
|
+
For example: As of time of writing, for cur_table = models.ReplicationRule.__table__,
|
|
52
|
+
it will generate
|
|
53
|
+
[DATASET_LOCKS_RULE_ID_FK] # rule.id <-> dataset_locks.rule_id
|
|
54
|
+
[LOCKS_RULE_ID_FK] # rule.id <-> locks.rule_id
|
|
55
|
+
[RULES_CHILD_RULE_ID_FK, DATASET_LOCKS_RULE_ID_FK] # rule.id <-> rule(alias).child_rule_id, rule(alias).id <-> dataset_locks.rule_id
|
|
56
|
+
[RULES_CHILD_RULE_ID_FK, LOCKS_RULE_ID_FK] # rule.id <-> rule(alias).child_rule_id, rule(alias).id <-> locks.rule_id
|
|
57
|
+
[RULES_CHILD_RULE_ID_FK] # rule.id <-> rule(alias).child_rule_id
|
|
58
|
+
"""
|
|
59
|
+
nb_times_in_stack[cur_table] = nb_times_in_stack.get(cur_table, 0) + 1
|
|
60
|
+
|
|
61
|
+
for fk in INBOUND_FOREIGN_KEYS.get(cur_table, []):
|
|
62
|
+
if nb_times_in_stack.get(fk.table, 0) > 1:
|
|
63
|
+
# Only allow a table to appear twice in the stack.
|
|
64
|
+
# This handles recursive constraints (like the one between rules and itself)
|
|
65
|
+
continue
|
|
66
|
+
stack.append(fk)
|
|
67
|
+
yield from _dependency_paths(stack, nb_times_in_stack, fk.table)
|
|
68
|
+
|
|
69
|
+
if stack:
|
|
70
|
+
yield copy.copy(stack)
|
|
71
|
+
fk = stack.pop()
|
|
72
|
+
nb_times_in_stack[fk.table] -= 1
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@transactional_session
|
|
76
|
+
def cleanup_db_deps(
|
|
77
|
+
model: models.BASE,
|
|
78
|
+
select_rows_stmt: "ColumnElement",
|
|
79
|
+
*,
|
|
80
|
+
session: Optional["Session"] = None
|
|
81
|
+
) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Removes rows which have foreign key constraints pointing to rows
|
|
84
|
+
selected by `select_rows_stmt` in `model`. The deletion is transitive.
|
|
85
|
+
This implements a behavior similar to "ON DELETE CASCADE", but without
|
|
86
|
+
removing the initial rows from `model`, only their dependencies.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
for fk_path in _dependency_paths(stack=[], nb_times_in_stack={}, cur_table=model.__table__):
|
|
90
|
+
seen_tables = set()
|
|
91
|
+
referred_table = model.__table__
|
|
92
|
+
current_table = fk_path[-1].table
|
|
93
|
+
filters = []
|
|
94
|
+
for i, fk in enumerate(fk_path):
|
|
95
|
+
current_table = fk.table
|
|
96
|
+
if current_table in seen_tables:
|
|
97
|
+
current_table = aliased(current_table)
|
|
98
|
+
else:
|
|
99
|
+
seen_tables.add(current_table)
|
|
100
|
+
|
|
101
|
+
filters.append(and_(current_table.columns.get(e.parent.name) == referred_table.columns.get(e.column.name) for e in fk.elements)) # type: ignore
|
|
102
|
+
referred_table = current_table
|
|
103
|
+
|
|
104
|
+
if session.bind.dialect.name == 'mysql': # type: ignore (bind could be None)
|
|
105
|
+
stmt = delete(
|
|
106
|
+
current_table
|
|
107
|
+
).where(
|
|
108
|
+
and_(*filters)
|
|
109
|
+
).where(
|
|
110
|
+
select_rows_stmt
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
stmt = delete(
|
|
114
|
+
current_table,
|
|
115
|
+
).where(
|
|
116
|
+
exists(
|
|
117
|
+
select(
|
|
118
|
+
1
|
|
119
|
+
).where(
|
|
120
|
+
and_(*filters)
|
|
121
|
+
).where(
|
|
122
|
+
select_rows_stmt
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
stmt = stmt.execution_options(
|
|
128
|
+
synchronize_session=False
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
session.execute(stmt) # type: ignore (Session could be None)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def reset_config_table() -> None:
|
|
135
|
+
""" Clear the config table and install any default entries needed for the tests.
|
|
136
|
+
"""
|
|
137
|
+
db_session = get_session()
|
|
138
|
+
db_session.query(models.Config).delete()
|
|
139
|
+
db_session.commit()
|
|
140
|
+
core_config.set("vo-map", "testvo1", "tst")
|
|
141
|
+
core_config.set("vo-map", "testvo2", "ts2")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_vo() -> str:
|
|
145
|
+
""" Gets the current short/mapped VO name for testing.
|
|
146
|
+
Maps the vo name to the short name, if configured.
|
|
147
|
+
:returns: VO name string.
|
|
148
|
+
"""
|
|
149
|
+
return map_vo(get_long_vo())
|