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/db/sqla/sautils.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
'''
|
|
16
|
+
SQLAlchemy utilities
|
|
17
|
+
|
|
18
|
+
'''
|
|
19
|
+
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
21
|
+
|
|
22
|
+
from sqlalchemy.ext.compiler import compiles
|
|
23
|
+
from sqlalchemy.sql.expression import ClauseElement, Executable
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from sqlalchemy import Table
|
|
27
|
+
from sqlalchemy.engine.interfaces import SQLCompiler
|
|
28
|
+
from sqlalchemy.sql.schema import Column
|
|
29
|
+
from sqlalchemy.sql.selectable import Select
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InsertFromSelect(Executable, ClauseElement):
|
|
33
|
+
def __init__(self, insert_spec: "Union[Table, list[Column]]", select: "Select") -> None:
|
|
34
|
+
self.insert_spec = insert_spec
|
|
35
|
+
self.select = select
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@compiles(InsertFromSelect)
|
|
39
|
+
def visit_insert_from_select(element: InsertFromSelect, compiler: "SQLCompiler", **kw: Any) -> str:
|
|
40
|
+
if isinstance(element.insert_spec, list):
|
|
41
|
+
columns = []
|
|
42
|
+
for column in element.insert_spec:
|
|
43
|
+
if element.insert_spec[0].table != column.table:
|
|
44
|
+
raise Exception("Insert columns must belong to the same table")
|
|
45
|
+
columns.append(column.name)
|
|
46
|
+
|
|
47
|
+
table = compiler.process(element.insert_spec[0].table, asfrom=True)
|
|
48
|
+
columns = ", ".join(columns)
|
|
49
|
+
|
|
50
|
+
sql = "INSERT INTO %s (%s) %s" % (table, columns, compiler.process(element.select))
|
|
51
|
+
|
|
52
|
+
else:
|
|
53
|
+
sql = "INSERT INTO %s %s" % (compiler.process(element.insert_spec, asfrom=True), compiler.process(element.select))
|
|
54
|
+
|
|
55
|
+
return sql
|
rucio/db/sqla/session.py
ADDED
|
@@ -0,0 +1,529 @@
|
|
|
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 copy
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
from contextlib import contextmanager
|
|
20
|
+
from datetime import datetime, timedelta
|
|
21
|
+
from functools import update_wrapper
|
|
22
|
+
from inspect import getfullargspec, isgeneratorfunction
|
|
23
|
+
from os.path import basename
|
|
24
|
+
from threading import Lock
|
|
25
|
+
from time import sleep
|
|
26
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
27
|
+
|
|
28
|
+
from sqlalchemy import MetaData, create_engine, event, text
|
|
29
|
+
from sqlalchemy.exc import DatabaseError, DisconnectionError, OperationalError, SQLAlchemyError, TimeoutError
|
|
30
|
+
from sqlalchemy.orm import DeclarativeBase, Session, scoped_session, sessionmaker
|
|
31
|
+
from sqlalchemy.pool import NullPool, Pool, QueuePool, SingletonThreadPool
|
|
32
|
+
|
|
33
|
+
from rucio.common.config import config_get
|
|
34
|
+
from rucio.common.exception import DatabaseException, InputValidationError, RucioException
|
|
35
|
+
from rucio.common.extra import import_extras
|
|
36
|
+
from rucio.common.utils import retrying
|
|
37
|
+
from rucio.db.sqla.constants import DatabaseOperationType
|
|
38
|
+
|
|
39
|
+
EXTRA_MODULES = import_extras(['MySQLdb', 'pymysql'])
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from collections.abc import Callable, Iterator
|
|
43
|
+
from typing import Optional, ParamSpec, TypeVar
|
|
44
|
+
|
|
45
|
+
from pymysql import Connection as MySQLConnection
|
|
46
|
+
from sqlalchemy.engine.base import Engine
|
|
47
|
+
|
|
48
|
+
from rucio.common.types import LoggerFunction
|
|
49
|
+
|
|
50
|
+
P = ParamSpec('P')
|
|
51
|
+
R = TypeVar('R')
|
|
52
|
+
CallableTypeVar = TypeVar('CallableTypeVar', bound='Callable[..., Any]')
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
main_script = os.path.basename(sys.argv[0])
|
|
56
|
+
CURRENT_COMPONENT = main_script.split('-')[1]
|
|
57
|
+
except:
|
|
58
|
+
CURRENT_COMPONENT = None
|
|
59
|
+
|
|
60
|
+
DATABASE_SECTION = 'database'
|
|
61
|
+
try:
|
|
62
|
+
if CURRENT_COMPONENT:
|
|
63
|
+
sql_connection = config_get('%s-database' % CURRENT_COMPONENT, 'default', check_config_table=False).strip()
|
|
64
|
+
if sql_connection and len(sql_connection):
|
|
65
|
+
DATABASE_SECTION = '%s-database' % CURRENT_COMPONENT
|
|
66
|
+
except:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
DEFAULT_SCHEMA_NAME = config_get(DATABASE_SECTION, 'schema',
|
|
70
|
+
raise_exception=False, default=None, check_config_table=False)
|
|
71
|
+
_METADATA = MetaData(schema=DEFAULT_SCHEMA_NAME)
|
|
72
|
+
_MAKER, _ENGINE, _LOCK = None, None, Lock()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
SQLA_CONFIG_POOLCLASS_MAPPING = {
|
|
76
|
+
'queuepool': QueuePool,
|
|
77
|
+
'singletonthreadpool': SingletonThreadPool,
|
|
78
|
+
'nullpool': NullPool,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class BASE(DeclarativeBase):
|
|
83
|
+
metadata = _METADATA
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _fk_pragma_on_connect(dbapi_con, con_record) -> None:
|
|
87
|
+
# Hack for previous versions of sqlite3
|
|
88
|
+
try:
|
|
89
|
+
dbapi_con.execute('pragma foreign_keys=ON')
|
|
90
|
+
except AttributeError:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def mysql_ping_listener(
|
|
95
|
+
dbapi_conn: "MySQLConnection",
|
|
96
|
+
connection_rec,
|
|
97
|
+
connection_proxy
|
|
98
|
+
) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Ensures that MySQL connections checked out of the
|
|
101
|
+
pool are alive.
|
|
102
|
+
|
|
103
|
+
Borrowed from:
|
|
104
|
+
http://groups.google.com/group/sqlalchemy/msg/a4ce563d802c929f
|
|
105
|
+
|
|
106
|
+
:param dbapi_conn: DBAPI connection
|
|
107
|
+
:param connection_rec: connection record
|
|
108
|
+
:param connection_proxy: connection proxy
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
dbapi_conn.cursor().execute('select 1')
|
|
113
|
+
except dbapi_conn.OperationalError as ex:
|
|
114
|
+
if ex.args[0] in (2006, 2013, 2014, 2045, 2055):
|
|
115
|
+
msg = 'Got mysql server has gone away: %s' % ex
|
|
116
|
+
raise DisconnectionError(msg)
|
|
117
|
+
else:
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def mysql_convert_decimal_to_float(
|
|
122
|
+
pymysql: bool = False
|
|
123
|
+
) -> dict[Union[type[object], int], 'Callable[..., Any]']:
|
|
124
|
+
"""
|
|
125
|
+
The default datatype returned by mysql-python for numerics is decimal.Decimal.
|
|
126
|
+
This type cannot be serialised to JSON, therefore we need to autoconvert to floats.
|
|
127
|
+
Even worse, there's two types of decimals created by the MySQLdb driver, so we must
|
|
128
|
+
override both.
|
|
129
|
+
|
|
130
|
+
:return converter: Converter object
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def pymysql_converter() -> dict[Union[type[object], int], 'Callable[..., Any]']:
|
|
134
|
+
from pymysql.constants import FIELD_TYPE
|
|
135
|
+
from pymysql.converters import conversions as conv
|
|
136
|
+
converter = conv.copy()
|
|
137
|
+
converter[FIELD_TYPE.DECIMAL] = float
|
|
138
|
+
converter[FIELD_TYPE.NEWDECIMAL] = float
|
|
139
|
+
return converter
|
|
140
|
+
|
|
141
|
+
if pymysql:
|
|
142
|
+
if not EXTRA_MODULES['pymysql']:
|
|
143
|
+
raise RucioException('Trying to use pymysql without having it installed!')
|
|
144
|
+
else:
|
|
145
|
+
converter = pymysql_converter()
|
|
146
|
+
elif EXTRA_MODULES['MySQLdb']:
|
|
147
|
+
import MySQLdb.converters # pylint: disable=import-error
|
|
148
|
+
from MySQLdb.constants import FIELD_TYPE # pylint: disable=import-error
|
|
149
|
+
converter = MySQLdb.converters.conversions.copy()
|
|
150
|
+
converter[FIELD_TYPE.DECIMAL] = float
|
|
151
|
+
converter[FIELD_TYPE.NEWDECIMAL] = float
|
|
152
|
+
elif EXTRA_MODULES['pymysql']:
|
|
153
|
+
converter = pymysql_converter()
|
|
154
|
+
else:
|
|
155
|
+
raise RucioException('Trying to use MySQL without mysql-python or pymysql installed!')
|
|
156
|
+
|
|
157
|
+
return converter
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def psql_convert_decimal_to_float(dbapi_conn, connection_rec) -> None:
|
|
161
|
+
"""
|
|
162
|
+
Configure the PostgreSQL connection to return numeric types as float instead of Decimal.
|
|
163
|
+
Psycopg3 provides this functionality through type adapters.
|
|
164
|
+
|
|
165
|
+
:param dbapi_conn: DBAPI connection
|
|
166
|
+
:param connection_rec: connection record
|
|
167
|
+
"""
|
|
168
|
+
try:
|
|
169
|
+
import psycopg
|
|
170
|
+
# Register a global loader that converts numeric types to float
|
|
171
|
+
dbapi_conn.adapters.register_loader("numeric", psycopg.types.numeric.FloatLoader)
|
|
172
|
+
except ImportError:
|
|
173
|
+
raise RucioException('Trying to use PostgreSQL without psycopg installed!')
|
|
174
|
+
except Exception as error:
|
|
175
|
+
raise RucioException(f'Error setting up PostgreSQL Decimal to Float conversion: {str(error)}')
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def my_on_connect(dbapi_con, connection_record) -> None:
|
|
179
|
+
""" Adds information to track performance and resource by module.
|
|
180
|
+
Info are recorded in the V$SESSION and V$SQLAREA views.
|
|
181
|
+
"""
|
|
182
|
+
caller = basename(sys.argv[0])
|
|
183
|
+
dbapi_con.clientinfo = caller
|
|
184
|
+
dbapi_con.client_identifier = caller
|
|
185
|
+
dbapi_con.module = caller
|
|
186
|
+
dbapi_con.action = caller
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _get_engine_poolclass(poolclass: str) -> Pool:
|
|
190
|
+
"""Resolve the correct SQLAlchemy Pool type to use from the
|
|
191
|
+
poolclass config option.
|
|
192
|
+
|
|
193
|
+
:param poolclass: User-selected pool class from config file.
|
|
194
|
+
:returns: The corresponding SQLAlchemy Pool class.
|
|
195
|
+
:raises InputValidationError: if config value doesn't correspond to an SQLAlchemy Pool class.
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
poolclass = poolclass.lower()
|
|
199
|
+
|
|
200
|
+
if poolclass not in SQLA_CONFIG_POOLCLASS_MAPPING:
|
|
201
|
+
raise InputValidationError('Unknown poolclass: %s' % poolclass)
|
|
202
|
+
|
|
203
|
+
return SQLA_CONFIG_POOLCLASS_MAPPING[poolclass]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def get_engine() -> 'Engine':
|
|
207
|
+
""" Creates an engine to a specific database.
|
|
208
|
+
:returns: engine
|
|
209
|
+
"""
|
|
210
|
+
global _ENGINE
|
|
211
|
+
if not _ENGINE:
|
|
212
|
+
sql_connection = config_get(DATABASE_SECTION, 'default', check_config_table=False)
|
|
213
|
+
config_params = [('pool_size', int), ('max_overflow', int), ('pool_timeout', int),
|
|
214
|
+
('pool_recycle', int), ('echo', int), ('echo_pool', str),
|
|
215
|
+
('pool_reset_on_return', str), ('use_threadlocal', int),
|
|
216
|
+
('poolclass', _get_engine_poolclass)]
|
|
217
|
+
params = {}
|
|
218
|
+
if 'mysql' in sql_connection:
|
|
219
|
+
conv = mysql_convert_decimal_to_float(pymysql=sql_connection.startswith('mysql+pymysql'))
|
|
220
|
+
params['connect_args'] = {'conv': conv}
|
|
221
|
+
for param, param_type in config_params:
|
|
222
|
+
try:
|
|
223
|
+
params[param] = param_type(config_get(DATABASE_SECTION, param, check_config_table=False))
|
|
224
|
+
except:
|
|
225
|
+
pass
|
|
226
|
+
_ENGINE = create_engine(sql_connection, **params)
|
|
227
|
+
if 'mysql' in sql_connection:
|
|
228
|
+
event.listen(_ENGINE, 'checkout', mysql_ping_listener)
|
|
229
|
+
elif 'postgresql' in sql_connection:
|
|
230
|
+
event.listen(_ENGINE, 'connect', psql_convert_decimal_to_float)
|
|
231
|
+
elif 'sqlite' in sql_connection:
|
|
232
|
+
event.listen(_ENGINE, 'connect', _fk_pragma_on_connect)
|
|
233
|
+
elif 'oracle' in sql_connection:
|
|
234
|
+
event.listen(_ENGINE, 'connect', my_on_connect)
|
|
235
|
+
if not _ENGINE:
|
|
236
|
+
raise RuntimeError("Could not form database engine.")
|
|
237
|
+
return _ENGINE
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def get_dump_engine(
|
|
241
|
+
echo: bool = False
|
|
242
|
+
) -> 'Engine':
|
|
243
|
+
""" Creates a dump engine to a specific database.
|
|
244
|
+
:returns: engine """
|
|
245
|
+
|
|
246
|
+
statements = list()
|
|
247
|
+
|
|
248
|
+
def dump(sql, *multiparams, **params):
|
|
249
|
+
statement = str(sql.compile(dialect=engine.dialect))
|
|
250
|
+
if statement in statements:
|
|
251
|
+
return
|
|
252
|
+
statements.append(statement)
|
|
253
|
+
if statement.endswith(')\n\n'):
|
|
254
|
+
if engine.dialect.name == 'oracle':
|
|
255
|
+
print(statement.replace(')\n\n', ') PCTFREE 0;\n'))
|
|
256
|
+
else:
|
|
257
|
+
print(statement.replace(')\n\n', ');\n'))
|
|
258
|
+
elif statement.endswith(')'):
|
|
259
|
+
print(statement.replace(')', ');\n'))
|
|
260
|
+
else:
|
|
261
|
+
print(statement)
|
|
262
|
+
|
|
263
|
+
sql_connection = config_get(DATABASE_SECTION, 'default', check_config_table=False)
|
|
264
|
+
|
|
265
|
+
engine = create_engine(sql_connection, echo=echo, strategy='mock', executor=dump)
|
|
266
|
+
return engine
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def get_maker() -> sessionmaker:
|
|
270
|
+
"""
|
|
271
|
+
Return a SQLAlchemy sessionmaker.
|
|
272
|
+
May assign __MAKER if not already assigned.
|
|
273
|
+
"""
|
|
274
|
+
global _MAKER, _ENGINE
|
|
275
|
+
if not _ENGINE:
|
|
276
|
+
raise RuntimeError("Could not form database engine.")
|
|
277
|
+
if not _MAKER:
|
|
278
|
+
_MAKER = sessionmaker(bind=_ENGINE, autocommit=False, autoflush=True, expire_on_commit=True)
|
|
279
|
+
return _MAKER
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_session() -> scoped_session:
|
|
283
|
+
""" Creates a session to a specific database, assumes that schema already in place.
|
|
284
|
+
:returns: session
|
|
285
|
+
"""
|
|
286
|
+
global _MAKER, _LOCK
|
|
287
|
+
if not _MAKER:
|
|
288
|
+
_LOCK.acquire()
|
|
289
|
+
try:
|
|
290
|
+
get_engine()
|
|
291
|
+
get_maker()
|
|
292
|
+
finally:
|
|
293
|
+
_LOCK.release()
|
|
294
|
+
if not _MAKER:
|
|
295
|
+
raise RuntimeError("Session factory is not defined.")
|
|
296
|
+
session = scoped_session(_MAKER)
|
|
297
|
+
return session
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def wait_for_database(
|
|
301
|
+
timeout: int = 60,
|
|
302
|
+
interval: int = 2,
|
|
303
|
+
*,
|
|
304
|
+
logger: "LoggerFunction" = logging.log
|
|
305
|
+
) -> None:
|
|
306
|
+
""" Wait for the database for a specific amount of time """
|
|
307
|
+
|
|
308
|
+
end_time = datetime.utcnow() + timedelta(seconds=timeout)
|
|
309
|
+
while True:
|
|
310
|
+
try:
|
|
311
|
+
session = get_session()
|
|
312
|
+
if session.bind.dialect.name == 'oracle':
|
|
313
|
+
session.execute(text('select 1 from dual'))
|
|
314
|
+
else:
|
|
315
|
+
session.execute(text('select 1'))
|
|
316
|
+
session.close()
|
|
317
|
+
break
|
|
318
|
+
except SQLAlchemyError as e:
|
|
319
|
+
logger(logging.WARNING, 'Still waiting for database: %s', e)
|
|
320
|
+
if datetime.utcnow() >= end_time:
|
|
321
|
+
raise
|
|
322
|
+
|
|
323
|
+
sleep(interval)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def retry_if_db_connection_error(exception: Exception) -> bool:
|
|
327
|
+
"""Return True if error in connecting to db."""
|
|
328
|
+
if isinstance(exception, (OperationalError, DatabaseException)):
|
|
329
|
+
conn_err_codes = ('2002', '2003', '2006', # MySQL
|
|
330
|
+
'ORA-00028', # Oracle session has been killed
|
|
331
|
+
'ORA-01012', # not logged on
|
|
332
|
+
'ORA-03113', # end-of-file on communication channel
|
|
333
|
+
'ORA-03114', # not connected to ORACLE
|
|
334
|
+
'ORA-03135', # connection lost contact
|
|
335
|
+
'ORA-25408',) # can not safely replay call
|
|
336
|
+
for err_code in conn_err_codes:
|
|
337
|
+
if exception.args[0].find(err_code) != -1:
|
|
338
|
+
return True
|
|
339
|
+
return False
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _update_session_wrapper(
|
|
343
|
+
wrapper: "CallableTypeVar",
|
|
344
|
+
wrapped: 'Callable'
|
|
345
|
+
) -> "CallableTypeVar":
|
|
346
|
+
"""
|
|
347
|
+
In addition to the work done by functools.update_wrapper, this function also preservers
|
|
348
|
+
the signature of the initial function. With the exception that the 'session' parameter
|
|
349
|
+
is overridden to have a default value of 'None'.
|
|
350
|
+
|
|
351
|
+
wrapper is the function to be updated
|
|
352
|
+
wrapped is the original function
|
|
353
|
+
|
|
354
|
+
To simplify the implementation of this function, we require 'session' be a
|
|
355
|
+
keyword-only argument in the wrapped function.
|
|
356
|
+
"""
|
|
357
|
+
try:
|
|
358
|
+
arg_spec = getfullargspec(wrapped)
|
|
359
|
+
arg_spec.kwonlyargs.index('session')
|
|
360
|
+
except ValueError:
|
|
361
|
+
# We require decorated functions to have a 'session' keyword-only attribute.
|
|
362
|
+
# re-raise ValueError if not
|
|
363
|
+
raise
|
|
364
|
+
|
|
365
|
+
update_wrapper(wrapper, wrapped)
|
|
366
|
+
|
|
367
|
+
wrapper.__defaults__ = copy.copy(wrapped.__defaults__)
|
|
368
|
+
wrapper.__kwdefaults__ = copy.copy(wrapped.__kwdefaults__)
|
|
369
|
+
wrapper.__annotations__["session"] = "Optional[Session]"
|
|
370
|
+
|
|
371
|
+
# Set the default of the keyword-only attribute 'session' to None
|
|
372
|
+
if not wrapped.__kwdefaults__:
|
|
373
|
+
wrapper.__kwdefaults__ = {'session': None}
|
|
374
|
+
elif 'session' not in wrapped.__kwdefaults__:
|
|
375
|
+
wrapper.__kwdefaults__['session'] = None
|
|
376
|
+
return wrapper
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def read_session(function: "Callable[P, R]"):
|
|
380
|
+
'''
|
|
381
|
+
decorator that set the session variable to use inside a function.
|
|
382
|
+
With that decorator it's possible to use the session variable like if a global variable session is declared.
|
|
383
|
+
|
|
384
|
+
session is a sqlalchemy session, and you can get one calling get_session().
|
|
385
|
+
This is useful if only SELECTs and the like are being done; anything involving
|
|
386
|
+
INSERTs, UPDATEs etc should use transactional_session.
|
|
387
|
+
'''
|
|
388
|
+
|
|
389
|
+
@retrying(retry_on_exception=retry_if_db_connection_error,
|
|
390
|
+
wait_fixed=500,
|
|
391
|
+
stop_max_attempt_number=2)
|
|
392
|
+
def new_funct(*args: "P.args", session: "Optional[Session]" = None, **kwargs): # pylint:disable=missing-kwoa
|
|
393
|
+
if isgeneratorfunction(function):
|
|
394
|
+
raise RucioException(
|
|
395
|
+
'read_session decorator should not be used with generator. Use stream_session instead.')
|
|
396
|
+
|
|
397
|
+
if not session:
|
|
398
|
+
session_scoped = get_session()
|
|
399
|
+
session = session_scoped()
|
|
400
|
+
session.begin() # type: ignore
|
|
401
|
+
try:
|
|
402
|
+
return function(*args, session=session, **kwargs)
|
|
403
|
+
except TimeoutError as error:
|
|
404
|
+
session.rollback() # type: ignore
|
|
405
|
+
raise DatabaseException(str(error))
|
|
406
|
+
except DatabaseError as error:
|
|
407
|
+
session.rollback() # type: ignore
|
|
408
|
+
raise DatabaseException(str(error))
|
|
409
|
+
except:
|
|
410
|
+
session.rollback() # type: ignore
|
|
411
|
+
raise
|
|
412
|
+
finally:
|
|
413
|
+
session_scoped.remove()
|
|
414
|
+
try:
|
|
415
|
+
return function(*args, session=session, **kwargs)
|
|
416
|
+
except Exception:
|
|
417
|
+
raise
|
|
418
|
+
|
|
419
|
+
return _update_session_wrapper(new_funct, function)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def stream_session(function: "Callable[P, R]"):
|
|
423
|
+
'''
|
|
424
|
+
decorator that set the session variable to use inside a function.
|
|
425
|
+
With that decorator it's possible to use the session variable like if a global variable session is declared.
|
|
426
|
+
|
|
427
|
+
session is a sqlalchemy session, and you can get one calling get_session().
|
|
428
|
+
This is useful if only SELECTs and the like are being done; anything involving
|
|
429
|
+
INSERTs, UPDATEs etc should use transactional_session.
|
|
430
|
+
'''
|
|
431
|
+
|
|
432
|
+
@retrying(retry_on_exception=retry_if_db_connection_error,
|
|
433
|
+
wait_fixed=500,
|
|
434
|
+
stop_max_attempt_number=2)
|
|
435
|
+
def new_funct(*args: "P.args", session: "Optional[Session]" = None, **kwargs): # pylint:disable=missing-kwoa
|
|
436
|
+
|
|
437
|
+
if not isgeneratorfunction(function):
|
|
438
|
+
raise RucioException(
|
|
439
|
+
'stream_session decorator should be used only with generator. Use read_session instead.')
|
|
440
|
+
|
|
441
|
+
if not session:
|
|
442
|
+
session_scoped = get_session()
|
|
443
|
+
session = session_scoped()
|
|
444
|
+
session.begin() # type: ignore
|
|
445
|
+
try:
|
|
446
|
+
for row in function(*args, session=session, **kwargs):
|
|
447
|
+
yield row
|
|
448
|
+
except TimeoutError as error:
|
|
449
|
+
session.rollback() # type: ignore
|
|
450
|
+
raise DatabaseException(str(error))
|
|
451
|
+
except DatabaseError as error:
|
|
452
|
+
session.rollback() # type: ignore
|
|
453
|
+
raise DatabaseException(str(error))
|
|
454
|
+
except:
|
|
455
|
+
session.rollback() # type: ignore
|
|
456
|
+
raise
|
|
457
|
+
finally:
|
|
458
|
+
session_scoped.remove()
|
|
459
|
+
else:
|
|
460
|
+
try:
|
|
461
|
+
for row in function(*args, session=session, **kwargs):
|
|
462
|
+
yield row
|
|
463
|
+
except:
|
|
464
|
+
raise
|
|
465
|
+
return _update_session_wrapper(new_funct, function)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def transactional_session(function: "Callable[P, R]") -> 'Callable':
|
|
469
|
+
'''
|
|
470
|
+
decorator that set the session variable to use inside a function.
|
|
471
|
+
With that decorator it's possible to use the session variable like if a global variable session is declared.
|
|
472
|
+
|
|
473
|
+
session is a sqlalchemy session, and you can get one calling get_session().
|
|
474
|
+
'''
|
|
475
|
+
|
|
476
|
+
def new_funct(
|
|
477
|
+
*args: "P.args",
|
|
478
|
+
session: "Optional[Session]" = None,
|
|
479
|
+
**kwargs
|
|
480
|
+
) -> "R": # pylint:disable=missing-kwoa
|
|
481
|
+
if not session:
|
|
482
|
+
session_scoped = get_session()
|
|
483
|
+
session = session_scoped()
|
|
484
|
+
session.begin() # type: ignore
|
|
485
|
+
try:
|
|
486
|
+
result = function(*args, session=session, **kwargs)
|
|
487
|
+
session.commit() # type: ignore
|
|
488
|
+
except TimeoutError as error:
|
|
489
|
+
session.rollback() # type: ignore
|
|
490
|
+
raise DatabaseException(str(error))
|
|
491
|
+
except DatabaseError as error:
|
|
492
|
+
session.rollback() # type: ignore
|
|
493
|
+
raise DatabaseException(str(error))
|
|
494
|
+
except:
|
|
495
|
+
session.rollback() # type: ignore
|
|
496
|
+
raise
|
|
497
|
+
finally:
|
|
498
|
+
session_scoped.remove() # pylint: disable=maybe-no-member
|
|
499
|
+
else:
|
|
500
|
+
result = function(*args, session=session, **kwargs)
|
|
501
|
+
return result
|
|
502
|
+
|
|
503
|
+
return _update_session_wrapper(new_funct, function)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
@retrying(retry_on_exception=retry_if_db_connection_error,
|
|
507
|
+
wait_fixed=500,
|
|
508
|
+
stop_max_attempt_number=2)
|
|
509
|
+
@contextmanager
|
|
510
|
+
def db_session(operation: DatabaseOperationType) -> "Iterator[Session]":
|
|
511
|
+
session_scoped = get_session()
|
|
512
|
+
session = session_scoped()
|
|
513
|
+
session.begin()
|
|
514
|
+
|
|
515
|
+
try:
|
|
516
|
+
yield session
|
|
517
|
+
if operation is DatabaseOperationType.WRITE:
|
|
518
|
+
session.commit()
|
|
519
|
+
except TimeoutError as error:
|
|
520
|
+
session.rollback()
|
|
521
|
+
raise DatabaseException(str(error))
|
|
522
|
+
except DatabaseError as error:
|
|
523
|
+
session.rollback()
|
|
524
|
+
raise DatabaseException(str(error))
|
|
525
|
+
except Exception:
|
|
526
|
+
session.rollback()
|
|
527
|
+
raise
|
|
528
|
+
finally:
|
|
529
|
+
session_scoped.remove()
|