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
|
@@ -0,0 +1,762 @@
|
|
|
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 functools
|
|
16
|
+
import logging
|
|
17
|
+
import re
|
|
18
|
+
import threading
|
|
19
|
+
import time
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from json import dumps, loads
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
23
|
+
|
|
24
|
+
import rucio.db.sqla.util
|
|
25
|
+
from rucio.common.config import config_get
|
|
26
|
+
from rucio.common.constants import RseAttr
|
|
27
|
+
from rucio.common.exception import (
|
|
28
|
+
DatabaseException,
|
|
29
|
+
DuplicateRule,
|
|
30
|
+
InsufficientAccountLimit,
|
|
31
|
+
InsufficientTargetRSEs,
|
|
32
|
+
InvalidReplicationRule,
|
|
33
|
+
InvalidRSEExpression,
|
|
34
|
+
InvalidRuleWeight,
|
|
35
|
+
RSEOverQuota,
|
|
36
|
+
StagingAreaRuleRequiresLifetime,
|
|
37
|
+
SubscriptionNotFound,
|
|
38
|
+
SubscriptionWrongParameter,
|
|
39
|
+
)
|
|
40
|
+
from rucio.common.logging import setup_logging
|
|
41
|
+
from rucio.common.stopwatch import Stopwatch
|
|
42
|
+
from rucio.common.types import InternalAccount, InternalScope, LoggerFunction
|
|
43
|
+
from rucio.common.utils import chunks
|
|
44
|
+
from rucio.core.did import get_metadata, list_new_dids, set_new_dids
|
|
45
|
+
from rucio.core.monitor import MetricManager
|
|
46
|
+
from rucio.core.rse import get_rse_id, list_rse_attributes, list_rses, rse_exists
|
|
47
|
+
from rucio.core.rse_expression_parser import parse_expression
|
|
48
|
+
from rucio.core.rse_selector import resolve_rse_expression
|
|
49
|
+
from rucio.core.rule import add_rule, get_rule, list_rules
|
|
50
|
+
from rucio.core.subscription import list_subscriptions, update_subscription
|
|
51
|
+
from rucio.daemons.common import run_daemon
|
|
52
|
+
from rucio.db.sqla.constants import DIDType, SubscriptionState
|
|
53
|
+
|
|
54
|
+
if TYPE_CHECKING:
|
|
55
|
+
from types import FrameType
|
|
56
|
+
|
|
57
|
+
from rucio.daemons.common import HeartbeatHandler
|
|
58
|
+
|
|
59
|
+
METRICS = MetricManager(module=__name__)
|
|
60
|
+
graceful_stop = threading.Event()
|
|
61
|
+
DAEMON_NAME = "transmogrifier"
|
|
62
|
+
|
|
63
|
+
RULES_COMMENT_LENGTH = 255
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __get_rule_dict(rule_dict: dict, subscription: dict) -> dict:
|
|
67
|
+
"""
|
|
68
|
+
Internal method to clean and enrich the rule_dict coming from the subscription.
|
|
69
|
+
|
|
70
|
+
:param rule_dict: The rule dictionary coming from a subscription.
|
|
71
|
+
:param subscription: The subscription associated to the rule.
|
|
72
|
+
:return: A dictionary that contains all the parameters associated to the rule.
|
|
73
|
+
"""
|
|
74
|
+
source_replica_expression = rule_dict.get("source_replica_expression", None)
|
|
75
|
+
rule_dict["source_replica_expression"] = source_replica_expression
|
|
76
|
+
locked = rule_dict.get("locked", None)
|
|
77
|
+
if locked == "True":
|
|
78
|
+
locked = True
|
|
79
|
+
else:
|
|
80
|
+
locked = False
|
|
81
|
+
rule_dict["locked"] = locked
|
|
82
|
+
|
|
83
|
+
purge_replicas = rule_dict.get("purge_replicas", False)
|
|
84
|
+
if purge_replicas == "True":
|
|
85
|
+
purge_replicas = True
|
|
86
|
+
else:
|
|
87
|
+
purge_replicas = False
|
|
88
|
+
rule_dict["purge_replicas"] = purge_replicas
|
|
89
|
+
|
|
90
|
+
rule_dict["rse_expression"] = str(rule_dict["rse_expression"])
|
|
91
|
+
comment = str(subscription["comments"])[:RULES_COMMENT_LENGTH]
|
|
92
|
+
if "comments" in rule_dict:
|
|
93
|
+
comment = str(rule_dict["comments"])
|
|
94
|
+
rule_dict["comment"] = comment
|
|
95
|
+
account = subscription["account"]
|
|
96
|
+
if "account" in rule_dict:
|
|
97
|
+
vo = account.vo
|
|
98
|
+
account = InternalAccount(rule_dict["account"], vo=vo)
|
|
99
|
+
rule_dict["account"] = account
|
|
100
|
+
rule_dict["copies"] = int(rule_dict["copies"])
|
|
101
|
+
default_activity = config_get("rules", "default_activity", default="default")
|
|
102
|
+
activity = rule_dict.get("activity", default_activity)
|
|
103
|
+
rule_dict["activity"] = activity
|
|
104
|
+
lifetime = rule_dict.get("lifetime", None)
|
|
105
|
+
if lifetime:
|
|
106
|
+
rule_dict["lifetime"] = int(lifetime)
|
|
107
|
+
chained_idx = rule_dict.get("chained_idx", None)
|
|
108
|
+
if chained_idx:
|
|
109
|
+
chained_idx = int(rule_dict["copies"])
|
|
110
|
+
rule_dict["chained_idx"] = chained_idx
|
|
111
|
+
delay_injection = rule_dict.get("delay_injection", None)
|
|
112
|
+
if delay_injection:
|
|
113
|
+
delay_injection = int(delay_injection)
|
|
114
|
+
rule_dict["delay_injection"] = delay_injection
|
|
115
|
+
return rule_dict
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def __split_rule_select_rses(
|
|
119
|
+
subscription_id: str,
|
|
120
|
+
subscription_name: str,
|
|
121
|
+
scope: "InternalScope",
|
|
122
|
+
name: str,
|
|
123
|
+
account: "InternalAccount",
|
|
124
|
+
weight: int,
|
|
125
|
+
rse_expression: str,
|
|
126
|
+
copies: int,
|
|
127
|
+
blocklisted_rse_id: list,
|
|
128
|
+
logger: LoggerFunction,
|
|
129
|
+
) -> tuple[list, bool, bool]:
|
|
130
|
+
"""
|
|
131
|
+
Internal method to create a list of RSEs that match RSE expression for subscriptions with split_rule.
|
|
132
|
+
|
|
133
|
+
:param subscription_id: The subscription id.
|
|
134
|
+
:param subscription_name: The subscription name.
|
|
135
|
+
:param scope: The internal DID scope.
|
|
136
|
+
:param name: The DID name.
|
|
137
|
+
:param account: The internal account.
|
|
138
|
+
:param weight: The weight of the rule.
|
|
139
|
+
:param rse_expression: The RSE expression of the rule.
|
|
140
|
+
:param copies: The number of copies.
|
|
141
|
+
:param blocklisted_rse_id: The list of blocklisted_rse_id.
|
|
142
|
+
:param logger: The logger.
|
|
143
|
+
:return: A tuple with list selected_rses, and 2 booleans create_rule, wont_reevaluate.
|
|
144
|
+
"""
|
|
145
|
+
wont_reevaluate = False
|
|
146
|
+
create_rule = True
|
|
147
|
+
preferred_rses = set()
|
|
148
|
+
for rule in list_rules(
|
|
149
|
+
filters={
|
|
150
|
+
"subscription_id": subscription_id,
|
|
151
|
+
"scope": scope,
|
|
152
|
+
"name": name,
|
|
153
|
+
}
|
|
154
|
+
):
|
|
155
|
+
for rse_dict in parse_expression(
|
|
156
|
+
rule["rse_expression"],
|
|
157
|
+
filter_={"vo": account.vo},
|
|
158
|
+
):
|
|
159
|
+
preferred_rses.add(rse_dict["rse"])
|
|
160
|
+
preferred_rses = list(preferred_rses)
|
|
161
|
+
preferred_unmatched = list()
|
|
162
|
+
selected_rses = list()
|
|
163
|
+
|
|
164
|
+
for attempt in range(0, 2):
|
|
165
|
+
# First attempt excludes blocklisted RSEs
|
|
166
|
+
# Second attempt includes blocklisted RSEs
|
|
167
|
+
try:
|
|
168
|
+
(selected_rses, preferred_unmatched,) = resolve_rse_expression(
|
|
169
|
+
rse_expression,
|
|
170
|
+
account,
|
|
171
|
+
weight=weight,
|
|
172
|
+
copies=copies,
|
|
173
|
+
size=0,
|
|
174
|
+
preferred_rses=preferred_rses,
|
|
175
|
+
blocklist=blocklisted_rse_id,
|
|
176
|
+
)
|
|
177
|
+
wont_reevaluate = True
|
|
178
|
+
break
|
|
179
|
+
except (
|
|
180
|
+
InsufficientTargetRSEs,
|
|
181
|
+
InsufficientAccountLimit,
|
|
182
|
+
InvalidRuleWeight,
|
|
183
|
+
RSEOverQuota,
|
|
184
|
+
) as error:
|
|
185
|
+
logger(
|
|
186
|
+
logging.WARNING,
|
|
187
|
+
'Problem getting RSEs for subscription "%s" for account %s : %s. %s'
|
|
188
|
+
% (
|
|
189
|
+
subscription_name,
|
|
190
|
+
account,
|
|
191
|
+
str(error),
|
|
192
|
+
'Try including blocklisted sites' if attempt == 0 else 'Skipping rule creation.'
|
|
193
|
+
),
|
|
194
|
+
)
|
|
195
|
+
# Now including the blocklisted sites
|
|
196
|
+
blocklisted_rse_id = []
|
|
197
|
+
METRICS.counter(name="addnewrule.errortype.{exception}").labels(exception=str(error.__class__.__name__)).inc()
|
|
198
|
+
wont_reevaluate = True
|
|
199
|
+
except Exception as error:
|
|
200
|
+
logger(
|
|
201
|
+
logging.ERROR,
|
|
202
|
+
"Problem resolving RSE expression %s : %s"
|
|
203
|
+
% (
|
|
204
|
+
rse_expression,
|
|
205
|
+
str(error),
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
if len(preferred_rses) - len(preferred_unmatched) >= copies:
|
|
209
|
+
create_rule = False
|
|
210
|
+
return selected_rses, create_rule, wont_reevaluate
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_subscriptions(logger: LoggerFunction = logging.log) -> list[dict]:
|
|
214
|
+
"""
|
|
215
|
+
A method to extract the list of active subscriptions and exclude the one that have bad RSE expression.
|
|
216
|
+
:param logger: The logger.
|
|
217
|
+
:return: The list of active subscriptions.
|
|
218
|
+
"""
|
|
219
|
+
subscriptions = []
|
|
220
|
+
try:
|
|
221
|
+
sub_dict = {3: []}
|
|
222
|
+
# Get the list of subscriptions. The default priority of the subscription is 3. 0 is the highest priority, 5 the lowest
|
|
223
|
+
# The priority is defined as 'policyid'
|
|
224
|
+
logger(logging.DEBUG, "Listing active subscriptions")
|
|
225
|
+
for sub in list_subscriptions(None, None):
|
|
226
|
+
rse_expression = sub.get("rse_expression")
|
|
227
|
+
skip_sub = False
|
|
228
|
+
rules = loads(sub["replication_rules"])
|
|
229
|
+
overwrite_rules = False
|
|
230
|
+
for rule in rules:
|
|
231
|
+
rse_expression = rule.get("rse_expression")
|
|
232
|
+
try:
|
|
233
|
+
list_rses_from_expression = parse_expression(rse_expression)
|
|
234
|
+
except InvalidRSEExpression:
|
|
235
|
+
logger(
|
|
236
|
+
logging.ERROR,
|
|
237
|
+
"Invalid RSE expression %s for subscription %s. Subscription removed from the list",
|
|
238
|
+
rse_expression,
|
|
239
|
+
sub["id"],
|
|
240
|
+
)
|
|
241
|
+
skip_sub = True
|
|
242
|
+
break
|
|
243
|
+
if rule.get("copies") == "*":
|
|
244
|
+
rule["copies"] = len(list_rses_from_expression)
|
|
245
|
+
overwrite_rules = True
|
|
246
|
+
if skip_sub:
|
|
247
|
+
continue
|
|
248
|
+
if overwrite_rules:
|
|
249
|
+
sub["replication_rules"] = dumps(rules)
|
|
250
|
+
if (
|
|
251
|
+
sub["state"] != SubscriptionState.INACTIVE
|
|
252
|
+
and sub["lifetime"]
|
|
253
|
+
and (datetime.now() > sub["lifetime"])
|
|
254
|
+
):
|
|
255
|
+
update_subscription(
|
|
256
|
+
name=sub["name"],
|
|
257
|
+
account=sub["account"],
|
|
258
|
+
metadata={"state": SubscriptionState.INACTIVE},
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
elif sub["state"] in [SubscriptionState.ACTIVE, SubscriptionState.UPDATED]:
|
|
262
|
+
priority = 3
|
|
263
|
+
if "policyid" in sub:
|
|
264
|
+
if int(sub["policyid"]) not in sub_dict:
|
|
265
|
+
sub_dict[int(sub["policyid"])] = []
|
|
266
|
+
priority = int(sub["policyid"])
|
|
267
|
+
sub_dict[priority].append(sub)
|
|
268
|
+
priorities = list(sub_dict.keys())
|
|
269
|
+
priorities.sort()
|
|
270
|
+
# Order the subscriptions according to their priority
|
|
271
|
+
for priority in priorities:
|
|
272
|
+
subscriptions.extend(sub_dict[priority])
|
|
273
|
+
logger(logging.INFO, "%i active subscriptions", len(subscriptions))
|
|
274
|
+
except SubscriptionNotFound as error:
|
|
275
|
+
logger(logging.WARNING, "No subscriptions defined: %s" % (str(error)))
|
|
276
|
+
return []
|
|
277
|
+
except TypeError as error:
|
|
278
|
+
logger(
|
|
279
|
+
logging.ERROR,
|
|
280
|
+
"Failed to parse subscription: %s" % (str(error)),
|
|
281
|
+
)
|
|
282
|
+
raise error
|
|
283
|
+
except Exception as error:
|
|
284
|
+
logger(
|
|
285
|
+
logging.ERROR,
|
|
286
|
+
"Failed to get list of new DIDs or subscriptions: %s" % (str(error)),
|
|
287
|
+
)
|
|
288
|
+
raise error
|
|
289
|
+
return subscriptions
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def __is_matching_subscription(
|
|
293
|
+
subscription: dict[str, Any],
|
|
294
|
+
did: dict[str, Any],
|
|
295
|
+
metadata: dict[str, Any]
|
|
296
|
+
) -> bool:
|
|
297
|
+
"""
|
|
298
|
+
Internal method to identify if a DID matches a subscription.
|
|
299
|
+
|
|
300
|
+
:param subscription: The subscription dictionary.
|
|
301
|
+
:param did: The DID dictionary
|
|
302
|
+
:param metadata: The metadata dictionary for the DID
|
|
303
|
+
:return: True/False
|
|
304
|
+
"""
|
|
305
|
+
if metadata["hidden"]:
|
|
306
|
+
return False
|
|
307
|
+
try:
|
|
308
|
+
filter_string = loads(subscription["filter"])
|
|
309
|
+
except ValueError as error:
|
|
310
|
+
logging.error("%s : Subscription will be skipped" % error)
|
|
311
|
+
return False
|
|
312
|
+
# Loop over the keys of filter_string for subscription
|
|
313
|
+
for key in filter_string:
|
|
314
|
+
values = filter_string[key]
|
|
315
|
+
if key == "pattern":
|
|
316
|
+
if not re.match(values, did["name"]):
|
|
317
|
+
return False
|
|
318
|
+
elif key == "excluded_pattern":
|
|
319
|
+
if re.match(values, did["name"]):
|
|
320
|
+
return False
|
|
321
|
+
elif key == "split_rule":
|
|
322
|
+
pass
|
|
323
|
+
elif key == "scope":
|
|
324
|
+
match_scope = False
|
|
325
|
+
for scope in values:
|
|
326
|
+
if re.match(scope, did["scope"].internal):
|
|
327
|
+
match_scope = True
|
|
328
|
+
break
|
|
329
|
+
if not match_scope:
|
|
330
|
+
return False
|
|
331
|
+
elif key == "account":
|
|
332
|
+
match_account = False
|
|
333
|
+
if not isinstance(values, list):
|
|
334
|
+
values = [values]
|
|
335
|
+
for account in values:
|
|
336
|
+
if account == metadata["account"].internal:
|
|
337
|
+
match_account = True
|
|
338
|
+
break
|
|
339
|
+
if not match_account:
|
|
340
|
+
return False
|
|
341
|
+
elif key == "did_type":
|
|
342
|
+
match_did_type = False
|
|
343
|
+
if not isinstance(values, list):
|
|
344
|
+
values = [values]
|
|
345
|
+
for did_type in values:
|
|
346
|
+
if did_type == metadata["did_type"].name:
|
|
347
|
+
match_did_type = True
|
|
348
|
+
break
|
|
349
|
+
if not match_did_type:
|
|
350
|
+
return False
|
|
351
|
+
elif key in ["min_avg_file_size", "max_avg_file_size"]:
|
|
352
|
+
length = metadata["length"]
|
|
353
|
+
size = metadata["bytes"]
|
|
354
|
+
if length and size:
|
|
355
|
+
avg_file_size = size / length
|
|
356
|
+
if key == "min_avg_file_size" and avg_file_size < values:
|
|
357
|
+
return False
|
|
358
|
+
if key == "max_avg_file_size" and avg_file_size > values:
|
|
359
|
+
return False
|
|
360
|
+
else:
|
|
361
|
+
# If the DID is evaluated at the creation, length and bytes are not set yet
|
|
362
|
+
# In that case, just ignore min_avg_file_size and max_avg_file_size filter
|
|
363
|
+
continue
|
|
364
|
+
else:
|
|
365
|
+
if not isinstance(values, list):
|
|
366
|
+
values = [
|
|
367
|
+
values,
|
|
368
|
+
]
|
|
369
|
+
has_metadata = False
|
|
370
|
+
for meta in metadata:
|
|
371
|
+
if str(meta) == str(key):
|
|
372
|
+
has_metadata = True
|
|
373
|
+
match_meta = False
|
|
374
|
+
for value in values:
|
|
375
|
+
if re.match(str(value), str(metadata[meta])):
|
|
376
|
+
match_meta = True
|
|
377
|
+
break
|
|
378
|
+
if not match_meta:
|
|
379
|
+
return False
|
|
380
|
+
if not has_metadata:
|
|
381
|
+
return False
|
|
382
|
+
return True
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def select_algorithm(
|
|
386
|
+
algorithm: str,
|
|
387
|
+
rule_ids: list[str],
|
|
388
|
+
params: dict[str, Any],
|
|
389
|
+
logger: LoggerFunction
|
|
390
|
+
) -> dict:
|
|
391
|
+
"""
|
|
392
|
+
Method used in case of chained subscriptions
|
|
393
|
+
|
|
394
|
+
:param algorithm: Algorithm used for the chained rule. Now only associated_site
|
|
395
|
+
associated_site : Choose an associated endpoint according to the RSE attribute assoiciated_site
|
|
396
|
+
:param rule_ids: List of parent rules
|
|
397
|
+
:param params: Dictionary of rules parameters to be used by the algorithm
|
|
398
|
+
"""
|
|
399
|
+
selected_rses = {}
|
|
400
|
+
for rule_id in rule_ids:
|
|
401
|
+
rule = get_rule(rule_id)
|
|
402
|
+
logging.debug("In select_algorithm, %s", str(rule))
|
|
403
|
+
rse = rule["rse_expression"]
|
|
404
|
+
vo = rule["account"].vo
|
|
405
|
+
if rse_exists(rse, vo=vo):
|
|
406
|
+
rse_id = get_rse_id(rse, vo=vo)
|
|
407
|
+
rse_attributes = list_rse_attributes(rse_id)
|
|
408
|
+
if algorithm == "associated_site":
|
|
409
|
+
associated_sites = rse_attributes.get(RseAttr.ASSOCIATED_SITES, None)
|
|
410
|
+
associated_site_idx = params.get("associated_site_idx", None)
|
|
411
|
+
if not associated_site_idx:
|
|
412
|
+
raise SubscriptionWrongParameter(
|
|
413
|
+
"Missing parameter associated_site_idx"
|
|
414
|
+
)
|
|
415
|
+
if associated_sites:
|
|
416
|
+
associated_sites = associated_sites.split(",")
|
|
417
|
+
if associated_site_idx > len(associated_sites) + 1:
|
|
418
|
+
raise SubscriptionWrongParameter(
|
|
419
|
+
"Parameter associated_site_idx is out of range"
|
|
420
|
+
)
|
|
421
|
+
associated_site = associated_sites[associated_site_idx - 1]
|
|
422
|
+
selected_rses[associated_site] = {
|
|
423
|
+
"source_replica_expression": rse,
|
|
424
|
+
"weight": None,
|
|
425
|
+
}
|
|
426
|
+
if algorithm == "exclude_site":
|
|
427
|
+
site = rse_attributes.get(RseAttr.SITE, None)
|
|
428
|
+
rse_expression = params['rse_expression'] + '\\site=%s' % site
|
|
429
|
+
(
|
|
430
|
+
selected_rses,
|
|
431
|
+
create_rule,
|
|
432
|
+
wont_reevaluate,
|
|
433
|
+
) = __split_rule_select_rses(
|
|
434
|
+
subscription_id=params["subscription_id"],
|
|
435
|
+
subscription_name=params["subscription_name"],
|
|
436
|
+
scope=rule["scope"],
|
|
437
|
+
name=rule["name"],
|
|
438
|
+
account=rule.get("account"),
|
|
439
|
+
weight=rule.get("weight"),
|
|
440
|
+
rse_expression=rse_expression,
|
|
441
|
+
copies=rule.get('copies'),
|
|
442
|
+
blocklisted_rse_id=params['blocklisted_rse_id'],
|
|
443
|
+
logger=logger,
|
|
444
|
+
)
|
|
445
|
+
dict_selected_rses = {}
|
|
446
|
+
for entry in selected_rses:
|
|
447
|
+
dict_selected_rses[entry] = {
|
|
448
|
+
"source_replica_expression": rse,
|
|
449
|
+
"weight": None,
|
|
450
|
+
}
|
|
451
|
+
selected_rses = dict_selected_rses
|
|
452
|
+
else:
|
|
453
|
+
raise SubscriptionWrongParameter(
|
|
454
|
+
"Algorithm %s only works with split_rule" % algorithm
|
|
455
|
+
)
|
|
456
|
+
if rule["copies"] != 1:
|
|
457
|
+
raise SubscriptionWrongParameter(
|
|
458
|
+
"Algorithm %s only works with split_rule" % algorithm
|
|
459
|
+
)
|
|
460
|
+
return selected_rses
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def transmogrifier(bulk: int = 5, once: bool = False, sleep_time: int = 60) -> None:
|
|
464
|
+
"""
|
|
465
|
+
Creates a Transmogrifier Worker that gets a list of new DIDs for a given hash,
|
|
466
|
+
identifies the subscriptions matching the DIDs and
|
|
467
|
+
submit a replication rule for each DID matching a subscription.
|
|
468
|
+
|
|
469
|
+
:param bulk: The number of requests to process.
|
|
470
|
+
:param once: Run only once.
|
|
471
|
+
:param sleep_time: Time between two cycles.
|
|
472
|
+
"""
|
|
473
|
+
run_daemon(
|
|
474
|
+
once=once,
|
|
475
|
+
graceful_stop=graceful_stop,
|
|
476
|
+
executable=DAEMON_NAME,
|
|
477
|
+
partition_wait_time=1,
|
|
478
|
+
sleep_time=sleep_time,
|
|
479
|
+
run_once_fnc=functools.partial(
|
|
480
|
+
run_once,
|
|
481
|
+
bulk=bulk,
|
|
482
|
+
),
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def run_once(heartbeat_handler: "HeartbeatHandler", bulk: int, **_kwargs) -> bool:
|
|
487
|
+
|
|
488
|
+
worker_number, total_workers, logger = heartbeat_handler.live()
|
|
489
|
+
stopwatch = Stopwatch()
|
|
490
|
+
blocklisted_rse_id = [rse["id"] for rse in list_rses({"availability_write": False})]
|
|
491
|
+
identifiers = []
|
|
492
|
+
# List all the active subscriptions
|
|
493
|
+
subscriptions = get_subscriptions(logger=logger)
|
|
494
|
+
|
|
495
|
+
# Loop over all the new dids
|
|
496
|
+
# Get the new DIDs based on the is_new flag
|
|
497
|
+
logger(logging.DEBUG, "Listing new dids")
|
|
498
|
+
for did in list_new_dids(
|
|
499
|
+
thread=worker_number,
|
|
500
|
+
total_threads=total_workers,
|
|
501
|
+
chunk_size=bulk,
|
|
502
|
+
did_type=None,
|
|
503
|
+
):
|
|
504
|
+
_, _, logger = heartbeat_handler.live()
|
|
505
|
+
did_success = True
|
|
506
|
+
if not (
|
|
507
|
+
did["did_type"] == DIDType.DATASET or did["did_type"] == DIDType.CONTAINER
|
|
508
|
+
):
|
|
509
|
+
identifiers.append(
|
|
510
|
+
{
|
|
511
|
+
"scope": did["scope"],
|
|
512
|
+
"name": did["name"],
|
|
513
|
+
"did_type": did["did_type"],
|
|
514
|
+
}
|
|
515
|
+
)
|
|
516
|
+
continue
|
|
517
|
+
metadata = get_metadata(did["scope"], did["name"])
|
|
518
|
+
|
|
519
|
+
# Loop over all the subscriptions
|
|
520
|
+
for subscription in subscriptions:
|
|
521
|
+
# Check if the DID match the subscription
|
|
522
|
+
if __is_matching_subscription(subscription, did, metadata) is True:
|
|
523
|
+
filter_string = loads(subscription["filter"])
|
|
524
|
+
split_rule = filter_string.get("split_rule", False)
|
|
525
|
+
stime = time.time()
|
|
526
|
+
logger(
|
|
527
|
+
logging.INFO,
|
|
528
|
+
"%s:%s matches subscription %s"
|
|
529
|
+
% (did["scope"], did["name"], subscription["name"]),
|
|
530
|
+
)
|
|
531
|
+
rules = loads(subscription["replication_rules"])
|
|
532
|
+
created_rules = {}
|
|
533
|
+
for cnt, rule_dict in enumerate(rules):
|
|
534
|
+
created_rules[cnt + 1] = []
|
|
535
|
+
# Get all the rule and subscription parameters
|
|
536
|
+
rule_dict = __get_rule_dict(rule_dict, subscription)
|
|
537
|
+
weight = rule_dict.get("weight", None)
|
|
538
|
+
source_replica_expression = rule_dict.get(
|
|
539
|
+
"source_replica_expression", None
|
|
540
|
+
)
|
|
541
|
+
copies = rule_dict["copies"]
|
|
542
|
+
success = False
|
|
543
|
+
|
|
544
|
+
chained_idx = rule_dict.get("chained_idx", None)
|
|
545
|
+
# By default selected_rses contains only the rse_expression
|
|
546
|
+
# It is overwritten in 2 cases : Chained subscription and split_rule
|
|
547
|
+
selected_rses = [rule_dict.get("rse_expression")]
|
|
548
|
+
if chained_idx:
|
|
549
|
+
# In the case of chained subscription, don't use rseselector but use the rses returned by the algorithm
|
|
550
|
+
params = {}
|
|
551
|
+
params['rse_expression'] = rule_dict.get("rse_expression")
|
|
552
|
+
params['subscription_id'] = subscription["id"]
|
|
553
|
+
params['subscription_name'] = subscription["name"]
|
|
554
|
+
params['blocklisted_rse_id'] = blocklisted_rse_id
|
|
555
|
+
if rule_dict.get("associated_site_idx", None):
|
|
556
|
+
params["associated_site_idx"] = rule_dict.get(
|
|
557
|
+
"associated_site_idx", None
|
|
558
|
+
)
|
|
559
|
+
logger(
|
|
560
|
+
logging.DEBUG,
|
|
561
|
+
"Chained subscription identified. Will use %s",
|
|
562
|
+
str(created_rules[chained_idx]),
|
|
563
|
+
)
|
|
564
|
+
algorithm = rule_dict.get("algorithm", None)
|
|
565
|
+
selected_rses = select_algorithm(
|
|
566
|
+
algorithm,
|
|
567
|
+
created_rules[chained_idx],
|
|
568
|
+
params,
|
|
569
|
+
logger
|
|
570
|
+
)
|
|
571
|
+
copies = 1
|
|
572
|
+
elif split_rule:
|
|
573
|
+
(
|
|
574
|
+
selected_rses,
|
|
575
|
+
create_rule,
|
|
576
|
+
wont_reevaluate,
|
|
577
|
+
) = __split_rule_select_rses(
|
|
578
|
+
subscription_id=subscription["id"],
|
|
579
|
+
subscription_name=subscription["name"],
|
|
580
|
+
scope=did["scope"],
|
|
581
|
+
name=did["name"],
|
|
582
|
+
account=rule_dict.get("account"),
|
|
583
|
+
weight=weight,
|
|
584
|
+
rse_expression=rule_dict.get("rse_expression"),
|
|
585
|
+
copies=copies,
|
|
586
|
+
blocklisted_rse_id=blocklisted_rse_id,
|
|
587
|
+
logger=logger,
|
|
588
|
+
)
|
|
589
|
+
copies = 1
|
|
590
|
+
if not create_rule:
|
|
591
|
+
continue
|
|
592
|
+
# The DID won't be reevaluated at the next cycle
|
|
593
|
+
did_success = did_success and wont_reevaluate
|
|
594
|
+
|
|
595
|
+
nb_rule = 0
|
|
596
|
+
# Try to create the rule
|
|
597
|
+
logger(logging.DEBUG, 'selected_rses : %s' % selected_rses)
|
|
598
|
+
try:
|
|
599
|
+
for rse in selected_rses:
|
|
600
|
+
if isinstance(selected_rses, dict):
|
|
601
|
+
# selected_rses is a dictionary only when split_rule is True or for chained subscriptions
|
|
602
|
+
source_replica_expression = selected_rses[rse].get(
|
|
603
|
+
"source_replica_expression",
|
|
604
|
+
None,
|
|
605
|
+
)
|
|
606
|
+
weight = selected_rses[rse].get("weight", None)
|
|
607
|
+
logger(
|
|
608
|
+
logging.INFO,
|
|
609
|
+
"Will insert one rule for %s:%s on %s"
|
|
610
|
+
% (did["scope"], did["name"], rse),
|
|
611
|
+
)
|
|
612
|
+
rule_ids = add_rule(
|
|
613
|
+
dids=[
|
|
614
|
+
{
|
|
615
|
+
"scope": did["scope"],
|
|
616
|
+
"name": did["name"],
|
|
617
|
+
}
|
|
618
|
+
],
|
|
619
|
+
account=rule_dict.get("account"),
|
|
620
|
+
copies=copies,
|
|
621
|
+
rse_expression=rse,
|
|
622
|
+
grouping=rule_dict.get("grouping", "DATASET"),
|
|
623
|
+
weight=weight,
|
|
624
|
+
lifetime=rule_dict.get("lifetime", None),
|
|
625
|
+
locked=rule_dict.get("locked", None),
|
|
626
|
+
subscription_id=subscription["id"],
|
|
627
|
+
source_replica_expression=source_replica_expression,
|
|
628
|
+
activity=rule_dict.get("activity"),
|
|
629
|
+
purge_replicas=rule_dict.get("purge_replicas", False),
|
|
630
|
+
ignore_availability=rule_dict.get(
|
|
631
|
+
"ignore_availability", None
|
|
632
|
+
),
|
|
633
|
+
comment=rule_dict.get("comment"),
|
|
634
|
+
delay_injection=rule_dict.get("delay_injection"),
|
|
635
|
+
)
|
|
636
|
+
created_rules[cnt + 1].append(rule_ids[0])
|
|
637
|
+
nb_rule += 1
|
|
638
|
+
if nb_rule == copies:
|
|
639
|
+
success = True
|
|
640
|
+
if split_rule:
|
|
641
|
+
success = True
|
|
642
|
+
|
|
643
|
+
METRICS.counter("addnewrule.done").inc(nb_rule)
|
|
644
|
+
METRICS.counter("addnewrule.activity.{activity}").labels(activity="".join(rule_dict.get("activity").split())).inc(nb_rule)
|
|
645
|
+
success = True
|
|
646
|
+
except (
|
|
647
|
+
InvalidReplicationRule,
|
|
648
|
+
InvalidRuleWeight,
|
|
649
|
+
InvalidRSEExpression,
|
|
650
|
+
StagingAreaRuleRequiresLifetime,
|
|
651
|
+
DuplicateRule,
|
|
652
|
+
) as error:
|
|
653
|
+
# Errors that won't be retried
|
|
654
|
+
success = True
|
|
655
|
+
logger(logging.ERROR, str(error))
|
|
656
|
+
METRICS.counter("addnewrule.errortype.{exception}").labels(exception=str(error.__class__.__name__)).inc()
|
|
657
|
+
except Exception:
|
|
658
|
+
# Errors that will be retried
|
|
659
|
+
METRICS.counter("addnewrule.errortype.{exception}").labels(exception="unknown").inc()
|
|
660
|
+
logger(logging.ERROR, "Unexpected error", exc_info=True)
|
|
661
|
+
|
|
662
|
+
did_success = did_success and success
|
|
663
|
+
if not success:
|
|
664
|
+
logger(
|
|
665
|
+
logging.ERROR,
|
|
666
|
+
"Rule for %s:%s on %s cannot be inserted"
|
|
667
|
+
% (
|
|
668
|
+
did["scope"],
|
|
669
|
+
did["name"],
|
|
670
|
+
rule_dict.get("rse_expression"),
|
|
671
|
+
),
|
|
672
|
+
)
|
|
673
|
+
else:
|
|
674
|
+
logger(
|
|
675
|
+
logging.INFO,
|
|
676
|
+
"%s rule(s) inserted in %f seconds"
|
|
677
|
+
% (str(nb_rule), time.time() - stime),
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
if did_success:
|
|
681
|
+
if did["did_type"] == str(DIDType.FILE):
|
|
682
|
+
METRICS.counter(name="files_processed").inc()
|
|
683
|
+
elif did["did_type"] == str(DIDType.DATASET):
|
|
684
|
+
METRICS.counter(name="datasets_processed").inc()
|
|
685
|
+
elif did["did_type"] == str(DIDType.CONTAINER):
|
|
686
|
+
METRICS.counter(name="containers_processed").inc()
|
|
687
|
+
METRICS.counter(name="dids_processed").inc()
|
|
688
|
+
identifiers.append(
|
|
689
|
+
{
|
|
690
|
+
"scope": did["scope"],
|
|
691
|
+
"name": did["name"],
|
|
692
|
+
"did_type": did["did_type"],
|
|
693
|
+
}
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
# Mark the DIDs as processed
|
|
697
|
+
flag_stopwatch = Stopwatch()
|
|
698
|
+
for identifier in chunks(identifiers, 100):
|
|
699
|
+
set_new_dids(identifier, None)
|
|
700
|
+
logger(logging.DEBUG, "Time to set the new flag : %f" % flag_stopwatch.elapsed)
|
|
701
|
+
|
|
702
|
+
stopwatch.stop()
|
|
703
|
+
|
|
704
|
+
for sub in subscriptions:
|
|
705
|
+
update_subscription(
|
|
706
|
+
name=sub["name"],
|
|
707
|
+
account=sub["account"],
|
|
708
|
+
metadata={"last_processed": datetime.utcnow()},
|
|
709
|
+
)
|
|
710
|
+
logger(
|
|
711
|
+
logging.INFO,
|
|
712
|
+
"It took %f seconds to process %i DIDs" % (stopwatch.elapsed, len(identifiers)),
|
|
713
|
+
)
|
|
714
|
+
logger(logging.DEBUG, "DIDs processed : %s" % (str(identifiers)))
|
|
715
|
+
METRICS.counter(name="transmogrifier.job.done").inc(1)
|
|
716
|
+
METRICS.timer("job.duration").observe(stopwatch.elapsed)
|
|
717
|
+
must_sleep = True
|
|
718
|
+
return must_sleep
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def run(
|
|
722
|
+
threads: int = 1,
|
|
723
|
+
bulk: int = 100,
|
|
724
|
+
once: bool = False,
|
|
725
|
+
sleep_time: int = 60
|
|
726
|
+
) -> None:
|
|
727
|
+
"""
|
|
728
|
+
Starts up the transmogrifier threads.
|
|
729
|
+
"""
|
|
730
|
+
setup_logging(process_name=DAEMON_NAME)
|
|
731
|
+
|
|
732
|
+
if rucio.db.sqla.util.is_old_db():
|
|
733
|
+
raise DatabaseException("Database was not updated, daemon won't start")
|
|
734
|
+
|
|
735
|
+
if once:
|
|
736
|
+
logging.info("Will run only one iteration in a single threaded mode")
|
|
737
|
+
transmogrifier(bulk=bulk, once=once)
|
|
738
|
+
else:
|
|
739
|
+
logging.info("starting transmogrifier threads")
|
|
740
|
+
thread_list = [
|
|
741
|
+
threading.Thread(
|
|
742
|
+
target=transmogrifier,
|
|
743
|
+
kwargs={"once": once, "sleep_time": sleep_time, "bulk": bulk},
|
|
744
|
+
)
|
|
745
|
+
for _ in range(0, threads)
|
|
746
|
+
]
|
|
747
|
+
[thread.start() for thread in thread_list]
|
|
748
|
+
logging.info("waiting for interrupts")
|
|
749
|
+
# Interruptible joins require a timeout.
|
|
750
|
+
while thread_list:
|
|
751
|
+
thread_list = [
|
|
752
|
+
thread.join(timeout=3.14)
|
|
753
|
+
for thread in thread_list
|
|
754
|
+
if thread and thread.is_alive()
|
|
755
|
+
]
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def stop(signum: Optional[int] = None, frame: Optional["FrameType"] = None) -> None:
|
|
759
|
+
"""
|
|
760
|
+
Graceful exit.
|
|
761
|
+
"""
|
|
762
|
+
graceful_stop.set()
|