rucio 32.8.6__py3-none-any.whl → 35.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rucio might be problematic. Click here for more details.
- rucio/__init__.py +0 -1
- rucio/alembicrevision.py +1 -2
- rucio/client/__init__.py +0 -1
- rucio/client/accountclient.py +45 -25
- rucio/client/accountlimitclient.py +37 -9
- rucio/client/baseclient.py +199 -154
- rucio/client/client.py +2 -3
- rucio/client/configclient.py +19 -6
- rucio/client/credentialclient.py +9 -4
- rucio/client/didclient.py +238 -63
- rucio/client/diracclient.py +13 -5
- rucio/client/downloadclient.py +162 -51
- rucio/client/exportclient.py +4 -4
- rucio/client/fileclient.py +3 -4
- rucio/client/importclient.py +4 -4
- rucio/client/lifetimeclient.py +21 -5
- rucio/client/lockclient.py +18 -8
- rucio/client/{metaclient.py → metaconventionsclient.py} +18 -15
- rucio/client/pingclient.py +0 -1
- rucio/client/replicaclient.py +15 -5
- rucio/client/requestclient.py +35 -19
- rucio/client/rseclient.py +133 -51
- rucio/client/ruleclient.py +29 -22
- rucio/client/scopeclient.py +8 -6
- rucio/client/subscriptionclient.py +47 -35
- rucio/client/touchclient.py +8 -4
- rucio/client/uploadclient.py +166 -82
- rucio/common/__init__.py +0 -1
- rucio/common/cache.py +4 -4
- rucio/common/config.py +52 -47
- rucio/common/constants.py +69 -2
- rucio/common/constraints.py +0 -1
- rucio/common/didtype.py +24 -22
- rucio/common/dumper/__init__.py +70 -41
- rucio/common/dumper/consistency.py +26 -22
- rucio/common/dumper/data_models.py +16 -23
- rucio/common/dumper/path_parsing.py +0 -1
- rucio/common/exception.py +281 -222
- rucio/common/extra.py +0 -1
- rucio/common/logging.py +54 -38
- rucio/common/pcache.py +122 -101
- rucio/common/plugins.py +153 -0
- rucio/common/policy.py +4 -4
- rucio/common/schema/__init__.py +17 -10
- rucio/common/schema/atlas.py +7 -5
- rucio/common/schema/belleii.py +7 -5
- rucio/common/schema/domatpc.py +7 -5
- rucio/common/schema/escape.py +7 -5
- rucio/common/schema/generic.py +8 -6
- rucio/common/schema/generic_multi_vo.py +7 -5
- rucio/common/schema/icecube.py +7 -5
- rucio/common/stomp_utils.py +0 -1
- rucio/common/stopwatch.py +0 -1
- rucio/common/test_rucio_server.py +2 -2
- rucio/common/types.py +262 -17
- rucio/common/utils.py +743 -451
- rucio/core/__init__.py +0 -1
- rucio/core/account.py +99 -29
- rucio/core/account_counter.py +89 -24
- rucio/core/account_limit.py +90 -24
- rucio/core/authentication.py +86 -29
- rucio/core/config.py +108 -38
- rucio/core/credential.py +14 -7
- rucio/core/did.py +680 -782
- rucio/core/did_meta_plugins/__init__.py +8 -6
- rucio/core/did_meta_plugins/did_column_meta.py +17 -12
- rucio/core/did_meta_plugins/did_meta_plugin_interface.py +60 -11
- rucio/core/did_meta_plugins/filter_engine.py +90 -50
- rucio/core/did_meta_plugins/json_meta.py +41 -16
- rucio/core/did_meta_plugins/mongo_meta.py +25 -8
- rucio/core/did_meta_plugins/postgres_meta.py +3 -4
- rucio/core/dirac.py +46 -17
- rucio/core/distance.py +66 -43
- rucio/core/exporter.py +5 -5
- rucio/core/heartbeat.py +181 -81
- rucio/core/identity.py +22 -12
- rucio/core/importer.py +23 -12
- rucio/core/lifetime_exception.py +32 -32
- rucio/core/lock.py +244 -142
- rucio/core/message.py +79 -38
- rucio/core/{meta.py → meta_conventions.py} +57 -44
- rucio/core/monitor.py +19 -13
- rucio/core/naming_convention.py +68 -27
- rucio/core/nongrid_trace.py +17 -5
- rucio/core/oidc.py +151 -29
- rucio/core/permission/__init__.py +18 -6
- rucio/core/permission/atlas.py +50 -35
- rucio/core/permission/belleii.py +6 -5
- rucio/core/permission/escape.py +8 -6
- rucio/core/permission/generic.py +82 -80
- rucio/core/permission/generic_multi_vo.py +9 -7
- rucio/core/quarantined_replica.py +91 -58
- rucio/core/replica.py +1303 -772
- rucio/core/replica_sorter.py +10 -12
- rucio/core/request.py +1133 -285
- rucio/core/rse.py +142 -102
- rucio/core/rse_counter.py +49 -18
- rucio/core/rse_expression_parser.py +6 -7
- rucio/core/rse_selector.py +41 -16
- rucio/core/rule.py +1538 -474
- rucio/core/rule_grouping.py +213 -68
- rucio/core/scope.py +50 -22
- rucio/core/subscription.py +92 -44
- rucio/core/topology.py +66 -24
- rucio/core/trace.py +42 -28
- rucio/core/transfer.py +543 -259
- rucio/core/vo.py +36 -18
- rucio/core/volatile_replica.py +59 -32
- rucio/daemons/__init__.py +0 -1
- rucio/daemons/abacus/__init__.py +0 -1
- rucio/daemons/abacus/account.py +29 -19
- rucio/daemons/abacus/collection_replica.py +21 -10
- rucio/daemons/abacus/rse.py +22 -12
- rucio/daemons/atropos/__init__.py +0 -1
- rucio/daemons/atropos/atropos.py +1 -2
- rucio/daemons/auditor/__init__.py +56 -28
- rucio/daemons/auditor/hdfs.py +17 -6
- rucio/daemons/auditor/srmdumps.py +116 -45
- rucio/daemons/automatix/__init__.py +0 -1
- rucio/daemons/automatix/automatix.py +30 -18
- rucio/daemons/badreplicas/__init__.py +0 -1
- rucio/daemons/badreplicas/minos.py +29 -18
- rucio/daemons/badreplicas/minos_temporary_expiration.py +5 -7
- rucio/daemons/badreplicas/necromancer.py +9 -13
- rucio/daemons/bb8/__init__.py +0 -1
- rucio/daemons/bb8/bb8.py +10 -13
- rucio/daemons/bb8/common.py +151 -154
- rucio/daemons/bb8/nuclei_background_rebalance.py +15 -9
- rucio/daemons/bb8/t2_background_rebalance.py +15 -8
- rucio/daemons/c3po/__init__.py +0 -1
- rucio/daemons/c3po/algorithms/__init__.py +0 -1
- rucio/daemons/c3po/algorithms/simple.py +8 -5
- rucio/daemons/c3po/algorithms/t2_free_space.py +10 -7
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +10 -7
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +30 -15
- rucio/daemons/c3po/c3po.py +81 -52
- rucio/daemons/c3po/collectors/__init__.py +0 -1
- rucio/daemons/c3po/collectors/agis.py +17 -17
- rucio/daemons/c3po/collectors/free_space.py +32 -13
- rucio/daemons/c3po/collectors/jedi_did.py +14 -5
- rucio/daemons/c3po/collectors/mock_did.py +11 -6
- rucio/daemons/c3po/collectors/network_metrics.py +12 -4
- rucio/daemons/c3po/collectors/workload.py +21 -19
- rucio/daemons/c3po/utils/__init__.py +0 -1
- rucio/daemons/c3po/utils/dataset_cache.py +15 -5
- rucio/daemons/c3po/utils/expiring_dataset_cache.py +16 -5
- rucio/daemons/c3po/utils/expiring_list.py +6 -7
- rucio/daemons/c3po/utils/popularity.py +5 -2
- rucio/daemons/c3po/utils/timeseries.py +25 -12
- rucio/daemons/cache/__init__.py +0 -1
- rucio/daemons/cache/consumer.py +21 -15
- rucio/daemons/common.py +42 -18
- rucio/daemons/conveyor/__init__.py +0 -1
- rucio/daemons/conveyor/common.py +69 -37
- rucio/daemons/conveyor/finisher.py +83 -46
- rucio/daemons/conveyor/poller.py +101 -69
- rucio/daemons/conveyor/preparer.py +35 -28
- rucio/daemons/conveyor/receiver.py +64 -21
- rucio/daemons/conveyor/stager.py +33 -28
- rucio/daemons/conveyor/submitter.py +71 -47
- rucio/daemons/conveyor/throttler.py +99 -35
- rucio/daemons/follower/__init__.py +0 -1
- rucio/daemons/follower/follower.py +12 -8
- rucio/daemons/hermes/__init__.py +0 -1
- rucio/daemons/hermes/hermes.py +57 -21
- rucio/daemons/judge/__init__.py +0 -1
- rucio/daemons/judge/cleaner.py +27 -17
- rucio/daemons/judge/evaluator.py +31 -18
- rucio/daemons/judge/injector.py +31 -23
- rucio/daemons/judge/repairer.py +28 -18
- rucio/daemons/oauthmanager/__init__.py +0 -1
- rucio/daemons/oauthmanager/oauthmanager.py +7 -8
- rucio/daemons/reaper/__init__.py +0 -1
- rucio/daemons/reaper/dark_reaper.py +15 -9
- rucio/daemons/reaper/reaper.py +109 -67
- rucio/daemons/replicarecoverer/__init__.py +0 -1
- rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +255 -116
- rucio/{api → daemons/rsedecommissioner}/__init__.py +0 -1
- rucio/daemons/rsedecommissioner/config.py +81 -0
- rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
- rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
- rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
- rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
- rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
- rucio/daemons/storage/__init__.py +0 -1
- rucio/daemons/storage/consistency/__init__.py +0 -1
- rucio/daemons/storage/consistency/actions.py +152 -59
- rucio/daemons/tracer/__init__.py +0 -1
- rucio/daemons/tracer/kronos.py +47 -24
- rucio/daemons/transmogrifier/__init__.py +0 -1
- rucio/daemons/transmogrifier/transmogrifier.py +35 -26
- rucio/daemons/undertaker/__init__.py +0 -1
- rucio/daemons/undertaker/undertaker.py +10 -10
- rucio/db/__init__.py +0 -1
- rucio/db/sqla/__init__.py +16 -2
- rucio/db/sqla/constants.py +10 -1
- rucio/db/sqla/migrate_repo/__init__.py +0 -1
- rucio/db/sqla/migrate_repo/env.py +0 -1
- rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +0 -1
- rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +1 -3
- rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +0 -3
- rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +1 -4
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +0 -1
- rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +0 -2
- rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +0 -2
- rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +0 -1
- rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +0 -3
- rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +0 -1
- rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +1 -2
- rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +0 -1
- rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +0 -3
- rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +1 -4
- rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +0 -2
- rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +0 -3
- rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +1 -2
- rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +0 -1
- rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +0 -1
- rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +0 -2
- rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +0 -2
- rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +1 -4
- rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +1 -4
- rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +0 -1
- rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +1 -3
- rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +0 -2
- rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +1 -3
- rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +1 -2
- rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +1 -3
- rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +0 -2
- rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +2 -3
- rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +1 -4
- rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +0 -1
- rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +0 -1
- rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +0 -1
- rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +0 -2
- rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +0 -3
- rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +0 -2
- rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +2 -4
- rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +0 -2
- rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +1 -4
- rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +0 -2
- rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +0 -3
- rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
- rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +0 -2
- rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +0 -2
- rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +0 -3
- rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +0 -3
- rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +1 -3
- rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +1 -3
- rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +0 -3
- rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +0 -3
- rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +1 -2
- rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +2 -4
- rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +0 -1
- rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +1 -4
- rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +0 -2
- rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +1 -2
- rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +0 -3
- rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +1 -3
- rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +0 -3
- rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +0 -1
- rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +0 -2
- rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +1 -3
- rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +0 -2
- rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +1 -4
- rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +0 -1
- rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +0 -1
- rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +1 -4
- rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +0 -1
- rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
- rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +0 -3
- rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
- rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +1 -2
- rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +1 -3
- rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +1 -5
- rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +1 -3
- rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +0 -3
- rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +1 -3
- rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +1 -2
- rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +0 -3
- rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +1 -4
- rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +1 -2
- rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +1 -4
- rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +1 -3
- rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +1 -4
- rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +0 -2
- rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +1 -3
- rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +0 -3
- rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +1 -3
- rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +0 -1
- rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +1 -2
- rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +1 -3
- rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +0 -2
- rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +0 -1
- rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +1 -2
- rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +0 -3
- rucio/db/sqla/models.py +122 -216
- rucio/db/sqla/sautils.py +12 -5
- rucio/db/sqla/session.py +71 -43
- rucio/db/sqla/types.py +3 -4
- rucio/db/sqla/util.py +91 -69
- rucio/gateway/__init__.py +13 -0
- rucio/{api → gateway}/account.py +119 -46
- rucio/{api → gateway}/account_limit.py +12 -13
- rucio/{api → gateway}/authentication.py +106 -33
- rucio/{api → gateway}/config.py +12 -13
- rucio/{api → gateway}/credential.py +15 -4
- rucio/{api → gateway}/did.py +384 -140
- rucio/{api → gateway}/dirac.py +16 -6
- rucio/{api → gateway}/exporter.py +3 -4
- rucio/{api → gateway}/heartbeat.py +17 -5
- rucio/{api → gateway}/identity.py +63 -19
- rucio/{api → gateway}/importer.py +3 -4
- rucio/{api → gateway}/lifetime_exception.py +35 -10
- rucio/{api → gateway}/lock.py +34 -12
- rucio/{api/meta.py → gateway/meta_conventions.py} +18 -16
- rucio/{api → gateway}/permission.py +4 -5
- rucio/{api → gateway}/quarantined_replica.py +13 -4
- rucio/{api → gateway}/replica.py +12 -11
- rucio/{api → gateway}/request.py +129 -28
- rucio/{api → gateway}/rse.py +11 -12
- rucio/{api → gateway}/rule.py +117 -35
- rucio/{api → gateway}/scope.py +24 -14
- rucio/{api → gateway}/subscription.py +65 -43
- rucio/{api → gateway}/vo.py +17 -7
- rucio/rse/__init__.py +3 -4
- rucio/rse/protocols/__init__.py +0 -1
- rucio/rse/protocols/bittorrent.py +184 -0
- rucio/rse/protocols/cache.py +1 -2
- rucio/rse/protocols/dummy.py +1 -2
- rucio/rse/protocols/gfal.py +12 -10
- rucio/rse/protocols/globus.py +7 -7
- rucio/rse/protocols/gsiftp.py +2 -3
- rucio/rse/protocols/http_cache.py +1 -2
- rucio/rse/protocols/mock.py +1 -2
- rucio/rse/protocols/ngarc.py +1 -2
- rucio/rse/protocols/posix.py +12 -13
- rucio/rse/protocols/protocol.py +116 -52
- rucio/rse/protocols/rclone.py +6 -7
- rucio/rse/protocols/rfio.py +4 -5
- rucio/rse/protocols/srm.py +9 -10
- rucio/rse/protocols/ssh.py +8 -9
- rucio/rse/protocols/storm.py +2 -3
- rucio/rse/protocols/webdav.py +17 -14
- rucio/rse/protocols/xrootd.py +23 -17
- rucio/rse/rsemanager.py +19 -7
- rucio/tests/__init__.py +0 -1
- rucio/tests/common.py +43 -17
- rucio/tests/common_server.py +3 -3
- rucio/transfertool/__init__.py +0 -1
- rucio/transfertool/bittorrent.py +199 -0
- rucio/transfertool/bittorrent_driver.py +52 -0
- rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
- rucio/transfertool/fts3.py +250 -138
- rucio/transfertool/fts3_plugins.py +152 -0
- rucio/transfertool/globus.py +9 -8
- rucio/transfertool/globus_library.py +1 -2
- rucio/transfertool/mock.py +21 -12
- rucio/transfertool/transfertool.py +33 -24
- rucio/vcsversion.py +4 -4
- rucio/version.py +5 -13
- rucio/web/__init__.py +0 -1
- rucio/web/rest/__init__.py +0 -1
- rucio/web/rest/flaskapi/__init__.py +0 -1
- rucio/web/rest/flaskapi/authenticated_bp.py +0 -1
- rucio/web/rest/flaskapi/v1/__init__.py +0 -1
- rucio/web/rest/flaskapi/v1/accountlimits.py +15 -13
- rucio/web/rest/flaskapi/v1/accounts.py +49 -48
- rucio/web/rest/flaskapi/v1/archives.py +12 -10
- rucio/web/rest/flaskapi/v1/auth.py +146 -144
- rucio/web/rest/flaskapi/v1/common.py +82 -41
- rucio/web/rest/flaskapi/v1/config.py +5 -6
- rucio/web/rest/flaskapi/v1/credentials.py +7 -8
- rucio/web/rest/flaskapi/v1/dids.py +158 -28
- rucio/web/rest/flaskapi/v1/dirac.py +8 -8
- rucio/web/rest/flaskapi/v1/export.py +3 -5
- rucio/web/rest/flaskapi/v1/heartbeats.py +3 -5
- rucio/web/rest/flaskapi/v1/identities.py +3 -5
- rucio/web/rest/flaskapi/v1/import.py +3 -4
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +6 -9
- rucio/web/rest/flaskapi/v1/locks.py +2 -4
- rucio/web/rest/flaskapi/v1/main.py +10 -2
- rucio/web/rest/flaskapi/v1/{meta.py → meta_conventions.py} +26 -11
- rucio/web/rest/flaskapi/v1/metrics.py +1 -2
- rucio/web/rest/flaskapi/v1/nongrid_traces.py +4 -4
- rucio/web/rest/flaskapi/v1/ping.py +6 -7
- rucio/web/rest/flaskapi/v1/redirect.py +8 -9
- rucio/web/rest/flaskapi/v1/replicas.py +43 -19
- rucio/web/rest/flaskapi/v1/requests.py +178 -21
- rucio/web/rest/flaskapi/v1/rses.py +61 -26
- rucio/web/rest/flaskapi/v1/rules.py +48 -18
- rucio/web/rest/flaskapi/v1/scopes.py +3 -5
- rucio/web/rest/flaskapi/v1/subscriptions.py +22 -18
- rucio/web/rest/flaskapi/v1/traces.py +4 -4
- rucio/web/rest/flaskapi/v1/types.py +20 -0
- rucio/web/rest/flaskapi/v1/vos.py +3 -5
- rucio/web/rest/main.py +0 -1
- rucio/web/rest/metrics.py +0 -1
- rucio/web/rest/ping.py +27 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/ldap.cfg.template +1 -1
- rucio-35.8.0.data/data/rucio/requirements.server.txt +268 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/bootstrap.py +3 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/merge_rucio_configs.py +2 -5
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/reset_database.py +3 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio +87 -85
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-account +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-collection-replica +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-rse +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-admin +45 -32
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-atropos +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-auditor +13 -7
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-automatix +1 -2
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-bb8 +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-c3po +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-client +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-consumer +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-finisher +1 -2
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-poller +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-preparer +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-receiver +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-stager +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-submitter +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-throttler +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dark-reaper +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dumper +11 -10
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-follower +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-hermes +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-cleaner +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-evaluator +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-injector +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-repairer +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-kronos +1 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos-temporary-expiration +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-necromancer +1 -2
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-oauth-manager +2 -3
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-reaper +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-replica-recoverer +6 -7
- rucio-35.8.0.data/scripts/rucio-rse-decommissioner +66 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-storage-consistency-actions +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-transmogrifier +0 -1
- {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-undertaker +1 -2
- rucio-35.8.0.dist-info/METADATA +72 -0
- rucio-35.8.0.dist-info/RECORD +493 -0
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/WHEEL +1 -1
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/AUTHORS.rst +3 -0
- rucio/api/temporary_did.py +0 -49
- rucio/common/schema/cms.py +0 -478
- rucio/common/schema/lsst.py +0 -423
- rucio/core/permission/cms.py +0 -1166
- rucio/core/temporary_did.py +0 -188
- rucio/daemons/reaper/light_reaper.py +0 -255
- rucio/web/rest/flaskapi/v1/tmp_dids.py +0 -115
- rucio-32.8.6.data/data/rucio/requirements.txt +0 -55
- rucio-32.8.6.data/scripts/rucio-light-reaper +0 -53
- rucio-32.8.6.dist-info/METADATA +0 -83
- rucio-32.8.6.dist-info/RECORD +0 -481
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic.ini.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
- {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
4
3
|
#
|
|
5
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -26,15 +25,16 @@ import logging
|
|
|
26
25
|
import re
|
|
27
26
|
import threading
|
|
28
27
|
import time
|
|
28
|
+
from configparser import NoOptionError, NoSectionError
|
|
29
29
|
from datetime import datetime, timedelta
|
|
30
30
|
from typing import TYPE_CHECKING, Any, Optional
|
|
31
31
|
|
|
32
32
|
import rucio.db.sqla.util
|
|
33
|
-
from rucio.common.config import config_get_bool
|
|
33
|
+
from rucio.common.config import config_get, config_get_bool
|
|
34
34
|
from rucio.common.constants import SuspiciousAvailability
|
|
35
|
-
from rucio.common.exception import DatabaseException, VONotFound
|
|
35
|
+
from rucio.common.exception import DatabaseException, DuplicateRule, VONotFound
|
|
36
36
|
from rucio.common.logging import setup_logging
|
|
37
|
-
from rucio.common.types import InternalAccount
|
|
37
|
+
from rucio.common.types import InternalAccount, LoggerFunction
|
|
38
38
|
from rucio.core.did import get_metadata
|
|
39
39
|
from rucio.core.replica import (
|
|
40
40
|
add_bad_pfns,
|
|
@@ -50,18 +50,91 @@ from rucio.daemons.common import run_daemon
|
|
|
50
50
|
from rucio.db.sqla.util import get_db_time
|
|
51
51
|
|
|
52
52
|
if TYPE_CHECKING:
|
|
53
|
+
from collections.abc import Sequence
|
|
53
54
|
from types import FrameType
|
|
54
55
|
|
|
55
56
|
GRACEFUL_STOP = threading.Event()
|
|
56
57
|
DAEMON_NAME = 'suspicious-replica-recoverer'
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
def
|
|
60
|
+
def check_suspicious_policy(policy: dict[str, str], file_metadata_datatype: str, file_metadata_scope: str) -> str:
|
|
61
|
+
match_scope = False
|
|
62
|
+
match_datatype = False
|
|
63
|
+
action = ""
|
|
64
|
+
|
|
65
|
+
if not policy.get("scope", []):
|
|
66
|
+
match_scope = True
|
|
67
|
+
for scope in policy.get("scope", []):
|
|
68
|
+
if re.match(scope, file_metadata_scope):
|
|
69
|
+
match_scope = True
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
if not policy.get("datatype", []):
|
|
73
|
+
match_datatype = True
|
|
74
|
+
for datatype in policy.get("datatype", []):
|
|
75
|
+
if re.match(datatype, file_metadata_datatype):
|
|
76
|
+
match_datatype = True
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
if match_scope and match_datatype:
|
|
80
|
+
action = policy["action"]
|
|
81
|
+
|
|
82
|
+
return action
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def parse_replica_datatype(
|
|
86
|
+
did_name_expression: Optional[str],
|
|
87
|
+
file_name: str,
|
|
88
|
+
rse_key: str,
|
|
89
|
+
logger: LoggerFunction) -> Optional[str]:
|
|
90
|
+
|
|
91
|
+
file_metadata_datatype = None
|
|
92
|
+
if did_name_expression is not None:
|
|
93
|
+
logger(logging.WARNING, f'Using {did_name_expression} as regex to determine file datatype for file: {file_name} on rse: {rse_key}')
|
|
94
|
+
try:
|
|
95
|
+
file_metadata_datatype_reg = re.compile(did_name_expression).search(file_name)
|
|
96
|
+
if file_metadata_datatype_reg is not None:
|
|
97
|
+
file_metadata_datatype = file_metadata_datatype_reg[0]
|
|
98
|
+
except (re.error, TypeError) as e:
|
|
99
|
+
logger(logging.WARNING, f'Could not determine datatype using regex {did_name_expression} on file: {file_name}, ignoring: {e}')
|
|
100
|
+
|
|
101
|
+
return file_metadata_datatype
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _read_from_config(logger: LoggerFunction) -> tuple[str, bool, Optional[str]]:
|
|
105
|
+
rule_suspicious_rse_expression = config_get("replicarecoverer", "rule_rse_expression", raise_exception=False, default='type=SCRATCHDISK')
|
|
106
|
+
logger(logging.INFO, f"Recovering with the rse expression: {rule_suspicious_rse_expression}")
|
|
107
|
+
|
|
108
|
+
use_file_metadata = config_get_bool("replicarecoverer", "use_file_metadata", raise_exception=False, default=True)
|
|
109
|
+
did_name_expression = None
|
|
110
|
+
|
|
111
|
+
if not use_file_metadata:
|
|
112
|
+
try:
|
|
113
|
+
did_name_expression = config_get('replicarecoverer', 'did_name_expression', raise_exception=True)
|
|
114
|
+
logger(logging.INFO, f"Setting replica datatype from did name with regex expression: {did_name_expression}")
|
|
115
|
+
|
|
116
|
+
except (NoOptionError, NoSectionError) as e:
|
|
117
|
+
logger(logging.WARNING, f"Cannot use option 'use_file_metadata' without setting a replacement expression with 'did_name_expression'- {e}")
|
|
118
|
+
use_file_metadata = True
|
|
119
|
+
|
|
120
|
+
logger(logging.INFO, f"Recovering with the rse expression: {rule_suspicious_rse_expression}")
|
|
121
|
+
return rule_suspicious_rse_expression, use_file_metadata, did_name_expression
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def declare_suspicious_replicas_bad(
|
|
125
|
+
once: bool = False,
|
|
126
|
+
younger_than: int = 5,
|
|
127
|
+
nattempts: int = 5,
|
|
128
|
+
vos: "Optional[Sequence[str]]" = None,
|
|
129
|
+
limit_suspicious_files_on_rse: int = 5,
|
|
130
|
+
json_file_name: str = "/opt/rucio/etc/suspicious_replica_recoverer.json",
|
|
131
|
+
sleep_time: int = 3600,
|
|
132
|
+
active_mode: bool = False) -> None:
|
|
60
133
|
"""
|
|
61
134
|
Main loop to check for available replicas which are labeled as suspicious.
|
|
62
135
|
|
|
63
136
|
Gets a list of suspicious replicas that are listed as AVAILABLE in 'replicas' table
|
|
64
|
-
and available on other RSE. Finds
|
|
137
|
+
and available on other RSE. Finds pfns of these replicas and declares them as bad.
|
|
65
138
|
Replicas that are the last remaining copy of a file have additional checks (checksum
|
|
66
139
|
comparison, etc.) before being declared bad.
|
|
67
140
|
|
|
@@ -99,10 +172,10 @@ def declare_suspicious_replicas_bad(once: bool = False, younger_than: int = 3, n
|
|
|
99
172
|
)
|
|
100
173
|
|
|
101
174
|
|
|
102
|
-
def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Optional[
|
|
103
|
-
|
|
104
|
-
worker_number, total_workers, logger = heartbeat_handler.live()
|
|
175
|
+
def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: "Optional[Sequence[str]]", limit_suspicious_files_on_rse: int, json_file_name: str, active_mode: int, **_kwargs) -> bool:
|
|
105
176
|
|
|
177
|
+
_, _, logger = heartbeat_handler.live()
|
|
178
|
+
rule_suspicious_rse_expression, use_file_metadata, did_name_expression = _read_from_config(logger=logger)
|
|
106
179
|
vos = vos or []
|
|
107
180
|
|
|
108
181
|
multi_vo = config_get_bool('common', 'multi_vo', raise_exception=False, default=False)
|
|
@@ -126,7 +199,7 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
126
199
|
json_file = open(json_file_name, mode="r")
|
|
127
200
|
logger(logging.INFO, "JSON file has been opened.")
|
|
128
201
|
except:
|
|
129
|
-
logger(logging.WARNING, "An error
|
|
202
|
+
logger(logging.WARNING, "An error occurred while trying to open the JSON file.")
|
|
130
203
|
must_sleep = True
|
|
131
204
|
return must_sleep
|
|
132
205
|
|
|
@@ -175,8 +248,8 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
175
248
|
for rse in rse_list:
|
|
176
249
|
time_start_rse = time.time()
|
|
177
250
|
rse_expr = rse['rse']
|
|
178
|
-
|
|
179
|
-
|
|
251
|
+
cnt_pfn_not_found = 0
|
|
252
|
+
cnt_pfn_not_found_nattempts_1 = 0
|
|
180
253
|
if rse_expr not in recoverable_replicas[vo]:
|
|
181
254
|
recoverable_replicas[vo][rse_expr] = {}
|
|
182
255
|
if rse_expr not in replicas_nattempts_1[vo]:
|
|
@@ -210,16 +283,16 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
210
283
|
scope = replica['scope']
|
|
211
284
|
rep_name = replica['name']
|
|
212
285
|
rse_id = replica['rse_id']
|
|
213
|
-
|
|
286
|
+
pfn_not_found = True
|
|
214
287
|
for rep in list_replicas([{'scope': scope, 'name': rep_name}]):
|
|
215
288
|
for rse_ in rep['rses']:
|
|
216
289
|
if rse_ == rse_id:
|
|
217
|
-
recoverable_replicas[vo][rse_expr][rep_name] = {'name': rep_name, 'rse_id': rse_id, 'scope': scope, '
|
|
218
|
-
|
|
290
|
+
recoverable_replicas[vo][rse_expr][rep_name] = {'name': rep_name, 'rse_id': rse_id, 'scope': scope, 'pfn': rep['rses'][rse_][0], 'available_elsewhere': True}
|
|
291
|
+
pfn_not_found = False
|
|
219
292
|
|
|
220
|
-
if
|
|
221
|
-
|
|
222
|
-
logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no
|
|
293
|
+
if pfn_not_found:
|
|
294
|
+
cnt_pfn_not_found += 1
|
|
295
|
+
logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no pfns were found.', rep_name, rse_expr)
|
|
223
296
|
|
|
224
297
|
if suspicious_replicas_last_copy:
|
|
225
298
|
for replica in suspicious_replicas_last_copy:
|
|
@@ -227,14 +300,14 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
227
300
|
scope = replica['scope']
|
|
228
301
|
rep_name = replica['name']
|
|
229
302
|
rse_id = replica['rse_id']
|
|
230
|
-
|
|
303
|
+
pfn_not_found = True
|
|
231
304
|
# Should only return one rse, as there is only one replica remaining
|
|
232
305
|
for rep in list_replicas([{'scope': scope, 'name': rep_name}]):
|
|
233
|
-
recoverable_replicas[vo][rse_expr][rep_name] = {'name': rep_name, 'rse_id': rse_id, 'scope': scope, '
|
|
234
|
-
|
|
235
|
-
if
|
|
236
|
-
|
|
237
|
-
logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no
|
|
306
|
+
recoverable_replicas[vo][rse_expr][rep_name] = {'name': rep_name, 'rse_id': rse_id, 'scope': scope, 'pfn': rep['rses'][rse_id][0], 'available_elsewhere': False}
|
|
307
|
+
pfn_not_found = False
|
|
308
|
+
if pfn_not_found:
|
|
309
|
+
cnt_pfn_not_found += 1
|
|
310
|
+
logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no pfns were found.', rep_name, rse_expr)
|
|
238
311
|
|
|
239
312
|
if suspicious_replicas_nattempts_1:
|
|
240
313
|
for replica in suspicious_replicas_nattempts_1:
|
|
@@ -242,27 +315,27 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
242
315
|
scope = replica['scope']
|
|
243
316
|
rep_name = replica['name']
|
|
244
317
|
rse_id = replica['rse_id']
|
|
245
|
-
|
|
318
|
+
pfn_not_found = True
|
|
246
319
|
for rep in list_replicas([{'scope': scope, 'name': rep_name}]):
|
|
247
320
|
for rse_ in rep['rses']:
|
|
248
321
|
if rse_ == rse_id:
|
|
249
|
-
replicas_nattempts_1[vo][rse_expr][rep_name] = {'name': rep_name, 'rse_id': rse_id, 'scope': scope, '
|
|
250
|
-
|
|
251
|
-
if
|
|
252
|
-
|
|
253
|
-
logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no
|
|
322
|
+
replicas_nattempts_1[vo][rse_expr][rep_name] = {'name': rep_name, 'rse_id': rse_id, 'scope': scope, 'pfn': rep['rses'][rse_][0], 'available_elsewhere': True}
|
|
323
|
+
pfn_not_found = False
|
|
324
|
+
if pfn_not_found:
|
|
325
|
+
cnt_pfn_not_found_nattempts_1 += 1
|
|
326
|
+
logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no pfns were found.', rep_name, rse_expr)
|
|
254
327
|
|
|
255
328
|
logger(logging.INFO, 'Suspicious replica query took %s seconds on %s and found %i suspicious replica(s) with a minimum of nattempts=%i. The pfns for %s/%s replicas were found.',
|
|
256
329
|
time.time() - time_start_rse,
|
|
257
330
|
rse_expr,
|
|
258
331
|
len(suspicious_replicas_avail_elsewhere) + len(suspicious_replicas_last_copy),
|
|
259
332
|
nattempts,
|
|
260
|
-
len(suspicious_replicas_avail_elsewhere) + len(suspicious_replicas_last_copy) -
|
|
333
|
+
len(suspicious_replicas_avail_elsewhere) + len(suspicious_replicas_last_copy) - cnt_pfn_not_found,
|
|
261
334
|
len(suspicious_replicas_avail_elsewhere) + len(suspicious_replicas_last_copy))
|
|
262
335
|
|
|
263
336
|
logger(logging.INFO, 'A total of %i replicas with exactly nattempts=1 were found. The pfns for %s/%s replicas were found.',
|
|
264
337
|
len(suspicious_replicas_nattempts_1),
|
|
265
|
-
len(suspicious_replicas_nattempts_1) -
|
|
338
|
+
len(suspicious_replicas_nattempts_1) - cnt_pfn_not_found_nattempts_1,
|
|
266
339
|
len(suspicious_replicas_nattempts_1))
|
|
267
340
|
|
|
268
341
|
if len(suspicious_replicas_avail_elsewhere) + len(suspicious_replicas_last_copy) != 0:
|
|
@@ -278,24 +351,80 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
278
351
|
logger(logging.INFO, 'All RSEs have been checked for suspicious replicas. Total time: %s seconds.', time.time() - start)
|
|
279
352
|
|
|
280
353
|
# Checking that everything is still working properly
|
|
281
|
-
|
|
354
|
+
_, _, logger = heartbeat_handler.live()
|
|
282
355
|
|
|
283
356
|
logger(logging.INFO, 'Create rules for replicas with nattempts=1.')
|
|
284
357
|
|
|
285
|
-
for
|
|
358
|
+
# Create as many rules as necessary for the replicas to be picked up by the daemon on the next run
|
|
359
|
+
# Create rules only for replicas that can be declared bad.
|
|
360
|
+
# Replicas from the auditor should be declared bad regardless of suspicious declarations, so no rules necessary.
|
|
361
|
+
for rse_key in list(replicas_nattempts_1[vo].keys()):
|
|
362
|
+
if not replicas_nattempts_1[vo][rse_key]:
|
|
363
|
+
# This is needed for testing purposes.
|
|
364
|
+
continue
|
|
365
|
+
files_to_be_declared_bad_nattempts_1 = []
|
|
286
366
|
dids_nattempts_1 = []
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
367
|
+
# Get the rse_id by going to one of the suspicious replicas from that RSE and reading it from there
|
|
368
|
+
rse_id = list(replicas_nattempts_1[vo][rse_key].values())[0]['rse_id']
|
|
369
|
+
for replica_key in replicas_nattempts_1[vo][rse_key].keys():
|
|
370
|
+
from_auditor = False
|
|
371
|
+
file_scope = replicas_nattempts_1[vo][rse_key][replica_key]["scope"]
|
|
372
|
+
file_name = replicas_nattempts_1[vo][rse_key][replica_key]["name"]
|
|
373
|
+
file_metadata = get_metadata(file_scope, file_name)
|
|
374
|
+
replicas_nattempts_1[vo][rse_key][replica_key]["datatype"] = str(file_metadata["datatype"])
|
|
375
|
+
|
|
376
|
+
# Auditor
|
|
377
|
+
suspicious_reason = get_suspicious_reason(replicas_nattempts_1[vo][rse_key][replica_key]["rse_id"], file_scope, file_name, nattempts)
|
|
378
|
+
for reason in suspicious_reason:
|
|
379
|
+
if "auditor" in reason["reason"].lower():
|
|
380
|
+
from_auditor = True
|
|
381
|
+
files_to_be_declared_bad_nattempts_1.append(replicas_nattempts_1[vo][rse_key][replica_key])
|
|
382
|
+
break
|
|
383
|
+
|
|
384
|
+
# Bad
|
|
385
|
+
if not from_auditor:
|
|
386
|
+
if (file_name.startswith("log.")) or (file_name.startswith("user")):
|
|
387
|
+
# Don't keep log files or user files
|
|
388
|
+
files_to_be_declared_bad_nattempts_1.append(replicas_nattempts_1[vo][rse_key][replica_key])
|
|
389
|
+
action = ""
|
|
390
|
+
else:
|
|
391
|
+
# Deal with replicas based on their metadata.
|
|
392
|
+
if (file_metadata["datatype"] is None) and (use_file_metadata): # "None" type has no function "split()"
|
|
393
|
+
logger(logging.WARNING, "RSE: %s, replica name %s, pfn %s: Replica does not have a data type associated with it. No action will be taken.",
|
|
394
|
+
rse_key, replica_key, replicas_nattempts_1[vo][rse_key][replica_key]['pfn'])
|
|
395
|
+
continue
|
|
396
|
+
file_metadata_datatype = str(file_metadata["datatype"])
|
|
397
|
+
if not use_file_metadata:
|
|
398
|
+
file_metadata_datatype = parse_replica_datatype(did_name_expression, file_name, rse_key, logger)
|
|
399
|
+
file_metadata_scope = str(file_metadata["scope"])
|
|
400
|
+
action = ""
|
|
401
|
+
if file_metadata_datatype:
|
|
402
|
+
# Some files don't have a datatype. They should be ignored.
|
|
403
|
+
for policy in json_data:
|
|
404
|
+
action = check_suspicious_policy(policy=policy, file_metadata_datatype=file_metadata_datatype, file_metadata_scope=file_metadata_scope)
|
|
405
|
+
if action:
|
|
406
|
+
logger(logging.INFO, "The action that will be performed is %s", action)
|
|
407
|
+
break
|
|
408
|
+
if action:
|
|
409
|
+
# Rules will be created for these replicas.
|
|
410
|
+
dids = {'scope': file_scope, 'name': file_name, 'rse': rse_key}
|
|
411
|
+
dids_nattempts_1.append(dids)
|
|
291
412
|
if active_mode:
|
|
292
|
-
# Create as many rules as necessary for the replicas to be picked up by the daemon on the next run
|
|
293
413
|
if len(dids_nattempts_1) > 0:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
414
|
+
try:
|
|
415
|
+
add_rule(dids=dids_nattempts_1, account=InternalAccount('root', vo=vo), copies=nattempts, rse_expression=rule_suspicious_rse_expression, grouping=None, weight=None, lifetime=5 * 24 * 3600, locked=False, subscription_id=None)
|
|
416
|
+
logger(logging.INFO, 'Rules have been created for %i replicas on %s.', len(dids_nattempts_1), rse_key)
|
|
417
|
+
except DuplicateRule:
|
|
418
|
+
logger(logging.INFO, 'Tried to create rules on %s, but it already exists.', rse_key)
|
|
297
419
|
else:
|
|
298
|
-
logger(logging.INFO, 'No replicas on %s
|
|
420
|
+
logger(logging.INFO, 'No replicas on %s have nattmepts=1, so no rules have been created.', rse_key)
|
|
421
|
+
if len(files_to_be_declared_bad_nattempts_1) > 0:
|
|
422
|
+
logger(logging.INFO, 'Ready to declare %s bad replica(s) with nattempts=1 on %s (RSE id: %s).', len(files_to_be_declared_bad_nattempts_1), rse_key, str(rse_id))
|
|
423
|
+
declare_bad_file_replicas(replicas=files_to_be_declared_bad_nattempts_1, reason='Suspicious. Automatic recovery.', issuer=InternalAccount('root', vo=vo), session=None)
|
|
424
|
+
else:
|
|
425
|
+
logger(logging.INFO, 'No suspicious replica(s) with nattempts=1 on %s (RSE id: %s) have been declared bad.', rse_key, str(rse_id))
|
|
426
|
+
else:
|
|
427
|
+
logger(logging.INFO, 'No replicas on %s with nattempts=1.', rse_key)
|
|
299
428
|
|
|
300
429
|
logger(logging.INFO, 'Begin check for problematic RSEs.')
|
|
301
430
|
time_start_check_probl = time.time()
|
|
@@ -306,17 +435,17 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
306
435
|
for rse_key in list(recoverable_replicas[vo].keys()):
|
|
307
436
|
if len(recoverable_replicas[vo][rse_key].values()) > limit_suspicious_files_on_rse:
|
|
308
437
|
list_problematic_rses.append(rse_key)
|
|
309
|
-
|
|
438
|
+
pfns_list = []
|
|
310
439
|
for replica_value in recoverable_replicas[vo][rse_key].values():
|
|
311
|
-
|
|
440
|
+
pfns_list.append(replica_value['pfn'])
|
|
312
441
|
|
|
313
442
|
if active_mode:
|
|
314
|
-
add_bad_pfns(pfns=
|
|
443
|
+
add_bad_pfns(pfns=pfns_list, account=InternalAccount('root', vo=vo), state='TEMPORARY_UNAVAILABLE', expires_at=datetime.utcnow() + timedelta(days=3))
|
|
315
444
|
|
|
316
445
|
logger(logging.INFO, "%s is problematic (more than %s suspicious replicas). Send a Jira ticket for the RSE (to be implemented).", rse_key, limit_suspicious_files_on_rse)
|
|
317
446
|
logger(logging.INFO, "The following files on %s have been marked as TEMPORARILY UNAVAILABLE:", rse_key)
|
|
318
447
|
for replica_values in recoverable_replicas[vo][rse_key].values():
|
|
319
|
-
logger(logging.INFO, 'Temporarily unavailable: RSE: %s Scope: %s Name: %s PFN: %s', rse_key, replica_values['scope'], replica_values['name'], replica_values['
|
|
448
|
+
logger(logging.INFO, 'Temporarily unavailable: RSE: %s Scope: %s Name: %s PFN: %s', rse_key, replica_values['scope'], replica_values['name'], replica_values['pfn'])
|
|
320
449
|
# Remove the RSE from the dictionary as it has been dealt with.
|
|
321
450
|
del recoverable_replicas[vo][rse_key]
|
|
322
451
|
|
|
@@ -325,16 +454,17 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
325
454
|
logger(logging.INFO, "%s", rse)
|
|
326
455
|
|
|
327
456
|
# Checking that everything is still working properly
|
|
328
|
-
|
|
457
|
+
_, _, logger = heartbeat_handler.live()
|
|
329
458
|
|
|
330
459
|
auditor = 0
|
|
331
460
|
checksum = 0
|
|
332
461
|
|
|
333
|
-
# Label suspicious replicas as bad if they have
|
|
462
|
+
# Label suspicious replicas as bad if they have other copies on other RSEs (that aren't also marked as suspicious).
|
|
334
463
|
# If they are the last remaining copies, deal with them differently.
|
|
335
464
|
for rse_key in list(recoverable_replicas[vo].keys()):
|
|
336
465
|
files_to_be_declared_bad = []
|
|
337
466
|
files_to_be_ignored = []
|
|
467
|
+
files_dry_run_monitoring = []
|
|
338
468
|
# Remove RSEs from dictionary that don't have any suspicious replicas
|
|
339
469
|
if len(recoverable_replicas[vo][rse_key]) == 0:
|
|
340
470
|
del recoverable_replicas[vo][rse_key]
|
|
@@ -342,86 +472,87 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
342
472
|
# Get the rse_id by going to one of the suspicious replicas from that RSE and reading it from there
|
|
343
473
|
rse_id = list(recoverable_replicas[vo][rse_key].values())[0]['rse_id']
|
|
344
474
|
for replica_key in list(recoverable_replicas[vo][rse_key].keys()):
|
|
475
|
+
from_auditor = False
|
|
345
476
|
file_scope = recoverable_replicas[vo][rse_key][replica_key]["scope"]
|
|
346
477
|
file_name = recoverable_replicas[vo][rse_key][replica_key]["name"]
|
|
347
478
|
file_metadata = get_metadata(file_scope, file_name)
|
|
348
479
|
recoverable_replicas[vo][rse_key][replica_key]["datatype"] = str(file_metadata["datatype"])
|
|
349
|
-
if recoverable_replicas[vo][rse_key][replica_key]['available_elsewhere'] is True:
|
|
350
|
-
# Replicas with other copies on at least one other RSE can safely be labeled as bad
|
|
351
|
-
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
352
|
-
# Remove replica from dictionary
|
|
353
|
-
del recoverable_replicas[vo][rse_key][replica_key]
|
|
354
|
-
elif recoverable_replicas[vo][rse_key][replica_key]['available_elsewhere'] is False:
|
|
355
|
-
if (file_name.startswith("log.")) or (file_name.startswith("user")):
|
|
356
|
-
# Don't keep log files or user files
|
|
357
|
-
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
358
|
-
del recoverable_replicas[vo][rse_key][replica_key]
|
|
359
|
-
else:
|
|
360
|
-
# Deal with replicas based on their metadata.
|
|
361
|
-
if file_metadata["datatype"] is None: # "None" type has no function "split()"
|
|
362
|
-
files_to_be_ignored.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
363
|
-
logger(logging.WARNING, "RSE: %s, replica name %s, surl %s: Replica does not have a data type associated with it. No action will be taken.",
|
|
364
|
-
rse_key, replica_key, recoverable_replicas[vo][rse_key][replica_key]['surl'])
|
|
365
|
-
continue
|
|
366
480
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
match_scope = True
|
|
378
|
-
for scope in policy.get("scope", []):
|
|
379
|
-
if re.match(scope, file_metadata_scope):
|
|
380
|
-
match_scope = True
|
|
381
|
-
break
|
|
481
|
+
suspicious_reason = get_suspicious_reason(recoverable_replicas[vo][rse_key][replica_key]["rse_id"],
|
|
482
|
+
file_scope,
|
|
483
|
+
file_name,
|
|
484
|
+
nattempts)
|
|
485
|
+
for reason in suspicious_reason:
|
|
486
|
+
if "auditor" in reason["reason"].lower():
|
|
487
|
+
auditor += 1
|
|
488
|
+
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
489
|
+
from_auditor = True
|
|
490
|
+
break
|
|
382
491
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
492
|
+
if not from_auditor:
|
|
493
|
+
if recoverable_replicas[vo][rse_key][replica_key]['available_elsewhere'] is True:
|
|
494
|
+
# Replicas with other copies on at least one other RSE can safely be labeled as bad
|
|
495
|
+
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
496
|
+
# Remove replica from dictionary
|
|
497
|
+
del recoverable_replicas[vo][rse_key][replica_key]
|
|
498
|
+
elif recoverable_replicas[vo][rse_key][replica_key]['available_elsewhere'] is False:
|
|
499
|
+
if (file_name.startswith("log.")) or (file_name.startswith("user")):
|
|
500
|
+
# Don't keep log files or user files
|
|
501
|
+
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
502
|
+
del recoverable_replicas[vo][rse_key][replica_key]
|
|
503
|
+
else:
|
|
504
|
+
# Deal with replicas based on their metadata.
|
|
505
|
+
if (file_metadata["datatype"] is None) and use_file_metadata: # "None" type has no function "split()"
|
|
506
|
+
files_to_be_ignored.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
507
|
+
logger(logging.WARNING, "RSE: %s, replica name %s, pfn %s: Replica does not have a data type associated with it. No action will be taken.",
|
|
508
|
+
rse_key, replica_key, recoverable_replicas[vo][rse_key][replica_key]['pfn'])
|
|
509
|
+
continue
|
|
510
|
+
|
|
511
|
+
file_metadata_datatype = str(file_metadata["datatype"])
|
|
512
|
+
if not use_file_metadata:
|
|
513
|
+
file_metadata_datatype = parse_replica_datatype(did_name_expression, file_name, rse_key, logger)
|
|
514
|
+
file_metadata_scope = str(file_metadata["scope"])
|
|
515
|
+
action = ""
|
|
516
|
+
if file_metadata_datatype:
|
|
517
|
+
# Some files don't have a datatype. They should be ignored.
|
|
518
|
+
for policy in json_data:
|
|
519
|
+
action = check_suspicious_policy(policy=policy, file_metadata_datatype=file_metadata_datatype, file_metadata_scope=file_metadata_scope)
|
|
520
|
+
if action:
|
|
521
|
+
logger(logging.INFO, "The action that will be performed is %s", action)
|
|
388
522
|
break
|
|
389
523
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
524
|
+
if not action:
|
|
525
|
+
logger(logging.WARNING, "No recognised actions (ignore/declare bad) found in policy file (etc/suspicious_replica_recoverer.json). Replica will be ignored by default.")
|
|
526
|
+
|
|
527
|
+
if action:
|
|
528
|
+
if action == "dry run":
|
|
529
|
+
# Monitoring purposes: Will look like a file has been declared bad, even though no
|
|
530
|
+
# actions will be taken.
|
|
531
|
+
files_dry_run_monitoring.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
532
|
+
elif action == "ignore":
|
|
533
|
+
files_to_be_ignored.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
534
|
+
elif action == "declare bad":
|
|
535
|
+
suspicious_reason = get_suspicious_reason(recoverable_replicas[vo][rse_key][replica_key]["rse_id"],
|
|
536
|
+
file_scope,
|
|
537
|
+
file_name,
|
|
538
|
+
nattempts)
|
|
539
|
+
for reason in suspicious_reason:
|
|
540
|
+
if "checksum" in reason["reason"].lower():
|
|
541
|
+
checksum += 1
|
|
542
|
+
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
543
|
+
break
|
|
544
|
+
else:
|
|
545
|
+
# If no policy has been set, default to ignoring the file (no action taken).
|
|
400
546
|
files_to_be_ignored.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
401
|
-
elif action == "declare bad":
|
|
402
|
-
suspicious_reason = get_suspicious_reason(recoverable_replicas[vo][rse_key][replica_key]["rse_id"],
|
|
403
|
-
file_scope,
|
|
404
|
-
file_name,
|
|
405
|
-
nattempts)
|
|
406
|
-
for reason in suspicious_reason:
|
|
407
|
-
if "auditor" in reason["reason"].lower():
|
|
408
|
-
auditor += 1
|
|
409
|
-
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
410
|
-
break
|
|
411
|
-
elif "checksum" in reason["reason"].lower():
|
|
412
|
-
checksum += 1
|
|
413
|
-
files_to_be_declared_bad.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
414
|
-
break
|
|
415
|
-
else:
|
|
416
|
-
# If no policy has been set, default to ignoring the file (no action taken).
|
|
417
|
-
files_to_be_ignored.append(recoverable_replicas[vo][rse_key][replica_key])
|
|
418
547
|
|
|
419
548
|
logger(logging.INFO, '(%s) Remaining replicas (pfns) that will be ignored:', rse_key)
|
|
420
549
|
for i in files_to_be_ignored:
|
|
421
|
-
logger(logging.INFO, 'Ignore: RSE: %s Scope: %s Name: %s Datatype: %s PFN: %s', rse_key, i["scope"], i["name"], i["datatype"], i["
|
|
550
|
+
logger(logging.INFO, 'Ignore: RSE: %s Scope: %s Name: %s Datatype: %s PFN: %s', rse_key, i["scope"], i["name"], i["datatype"], i["pfn"])
|
|
422
551
|
logger(logging.INFO, '(%s) Remaining replica (pfns) that will be declared BAD:', rse_key)
|
|
423
552
|
for i in files_to_be_declared_bad:
|
|
424
|
-
logger(logging.INFO, 'Declare bad: RSE: %s Scope: %s Name: %s Datatype: %s PFN: %s', rse_key, i["scope"], i["name"], i["datatype"], i["
|
|
553
|
+
logger(logging.INFO, 'Declare bad: RSE: %s Scope: %s Name: %s Datatype: %s PFN: %s', rse_key, i["scope"], i["name"], i["datatype"], i["pfn"])
|
|
554
|
+
for i in files_dry_run_monitoring:
|
|
555
|
+
logger(logging.INFO, 'Declare bad (dry run): RSE: %s Scope: %s Name: %s Datatype: %s PFN: %s', rse_key, i["scope"], i["name"], i["datatype"], i["pfn"])
|
|
425
556
|
|
|
426
557
|
if files_to_be_declared_bad:
|
|
427
558
|
logger(logging.INFO, 'Ready to declare %s bad replica(s) on %s (RSE id: %s).', len(files_to_be_declared_bad), rse_key, str(rse_id))
|
|
@@ -446,7 +577,15 @@ def run_once(heartbeat_handler: Any, younger_than: int, nattempts: int, vos: Opt
|
|
|
446
577
|
return must_sleep
|
|
447
578
|
|
|
448
579
|
|
|
449
|
-
def run(
|
|
580
|
+
def run(
|
|
581
|
+
once: bool = False,
|
|
582
|
+
younger_than: int = 5,
|
|
583
|
+
nattempts: int = 5,
|
|
584
|
+
vos: "Optional[Sequence[str]]" = None,
|
|
585
|
+
limit_suspicious_files_on_rse: int = 5,
|
|
586
|
+
json_file_name: str = "/opt/rucio/etc/suspicious_replica_recoverer.json",
|
|
587
|
+
sleep_time: int = 3600,
|
|
588
|
+
active_mode: bool = False) -> None:
|
|
450
589
|
"""
|
|
451
590
|
Starts up the Suspicious-Replica-Recoverer threads.
|
|
452
591
|
"""
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
"""Functions to manage decommissioning configurations."""
|
|
16
|
+
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from rucio.common.constants import RseAttr
|
|
21
|
+
from rucio.core.rse import add_rse_attribute, get_rse_attribute
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DecommissioningStatus(Enum):
|
|
25
|
+
"""Decommissioning status flags."""
|
|
26
|
+
|
|
27
|
+
PROCESSING = 'processing'
|
|
28
|
+
DONE = 'done'
|
|
29
|
+
SUSPENDED = 'suspended'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InvalidStatusName(Exception):
|
|
33
|
+
"""Exception for invalid decommissioning status name set from command line."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def config_to_attr(config: dict[str, Any]) -> str:
|
|
37
|
+
"""Form the attribute string from a config dictionary.
|
|
38
|
+
|
|
39
|
+
:param config: Decommissioning configuration dictionary.
|
|
40
|
+
:returns: Comma-separated key-value string encoding the configuration.
|
|
41
|
+
"""
|
|
42
|
+
attr = f'profile={config["profile"].value}'
|
|
43
|
+
if config.get('move_dest'):
|
|
44
|
+
attr += f',move_dest={config["move_dest"]}'
|
|
45
|
+
attr += f',status={config["status"].value}'
|
|
46
|
+
|
|
47
|
+
return attr
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def attr_to_config(attr: str) -> dict[str, Any]:
|
|
51
|
+
"""Form the config dictionary from an attribute string.
|
|
52
|
+
|
|
53
|
+
:param attr: Comma-separated key-value string encoding the configuration.
|
|
54
|
+
:returns: Decommissioning configuration dictionary.
|
|
55
|
+
"""
|
|
56
|
+
# The decommission attribute is a comma-separated list of key=value settings
|
|
57
|
+
config: dict[str, Any] = dict(map(lambda s: s.split('='), attr.split(',')))
|
|
58
|
+
if 'status' in config:
|
|
59
|
+
try:
|
|
60
|
+
config['status'] = DecommissioningStatus[config['status'].upper()]
|
|
61
|
+
except KeyError as exc:
|
|
62
|
+
raise InvalidStatusName() from exc
|
|
63
|
+
else:
|
|
64
|
+
config['status'] = DecommissioningStatus.PROCESSING
|
|
65
|
+
|
|
66
|
+
return config
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def set_status(
|
|
70
|
+
rse_id: str,
|
|
71
|
+
status: DecommissioningStatus
|
|
72
|
+
) -> None:
|
|
73
|
+
"""Update the decommission attribute of the RSE.
|
|
74
|
+
|
|
75
|
+
:param rse_id: RSE ID.
|
|
76
|
+
:param status: RSE decommissioning status.
|
|
77
|
+
"""
|
|
78
|
+
config = attr_to_config(get_rse_attribute(rse_id, RseAttr.DECOMMISSION))
|
|
79
|
+
config['status'] = status
|
|
80
|
+
# add_rse_attribute can handle updating existing entries too
|
|
81
|
+
add_rse_attribute(rse_id, RseAttr.DECOMMISSION, config_to_attr(config))
|