rucio 32.8.6__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 +18 -0
- rucio/alembicrevision.py +16 -0
- rucio/api/__init__.py +14 -0
- rucio/api/account.py +266 -0
- rucio/api/account_limit.py +287 -0
- rucio/api/authentication.py +302 -0
- rucio/api/config.py +218 -0
- rucio/api/credential.py +60 -0
- rucio/api/did.py +726 -0
- rucio/api/dirac.py +71 -0
- rucio/api/exporter.py +60 -0
- rucio/api/heartbeat.py +62 -0
- rucio/api/identity.py +160 -0
- rucio/api/importer.py +46 -0
- rucio/api/lifetime_exception.py +95 -0
- rucio/api/lock.py +131 -0
- rucio/api/meta.py +85 -0
- rucio/api/permission.py +72 -0
- rucio/api/quarantined_replica.py +69 -0
- rucio/api/replica.py +528 -0
- rucio/api/request.py +220 -0
- rucio/api/rse.py +601 -0
- rucio/api/rule.py +335 -0
- rucio/api/scope.py +89 -0
- rucio/api/subscription.py +255 -0
- rucio/api/temporary_did.py +49 -0
- rucio/api/vo.py +112 -0
- rucio/client/__init__.py +16 -0
- rucio/client/accountclient.py +413 -0
- rucio/client/accountlimitclient.py +155 -0
- rucio/client/baseclient.py +929 -0
- rucio/client/client.py +77 -0
- rucio/client/configclient.py +113 -0
- rucio/client/credentialclient.py +54 -0
- rucio/client/didclient.py +691 -0
- rucio/client/diracclient.py +48 -0
- rucio/client/downloadclient.py +1674 -0
- rucio/client/exportclient.py +44 -0
- rucio/client/fileclient.py +51 -0
- rucio/client/importclient.py +42 -0
- rucio/client/lifetimeclient.py +74 -0
- rucio/client/lockclient.py +99 -0
- rucio/client/metaclient.py +137 -0
- rucio/client/pingclient.py +45 -0
- rucio/client/replicaclient.py +444 -0
- rucio/client/requestclient.py +109 -0
- rucio/client/rseclient.py +664 -0
- rucio/client/ruleclient.py +287 -0
- rucio/client/scopeclient.py +88 -0
- rucio/client/subscriptionclient.py +161 -0
- rucio/client/touchclient.py +78 -0
- rucio/client/uploadclient.py +871 -0
- rucio/common/__init__.py +14 -0
- rucio/common/cache.py +74 -0
- rucio/common/config.py +796 -0
- rucio/common/constants.py +92 -0
- rucio/common/constraints.py +18 -0
- rucio/common/didtype.py +187 -0
- rucio/common/dumper/__init__.py +306 -0
- rucio/common/dumper/consistency.py +449 -0
- rucio/common/dumper/data_models.py +325 -0
- rucio/common/dumper/path_parsing.py +65 -0
- rucio/common/exception.py +1092 -0
- rucio/common/extra.py +37 -0
- rucio/common/logging.py +404 -0
- rucio/common/pcache.py +1387 -0
- rucio/common/policy.py +84 -0
- rucio/common/schema/__init__.py +143 -0
- rucio/common/schema/atlas.py +411 -0
- rucio/common/schema/belleii.py +406 -0
- rucio/common/schema/cms.py +478 -0
- rucio/common/schema/domatpc.py +399 -0
- rucio/common/schema/escape.py +424 -0
- rucio/common/schema/generic.py +431 -0
- rucio/common/schema/generic_multi_vo.py +410 -0
- rucio/common/schema/icecube.py +404 -0
- rucio/common/schema/lsst.py +423 -0
- rucio/common/stomp_utils.py +160 -0
- rucio/common/stopwatch.py +56 -0
- rucio/common/test_rucio_server.py +148 -0
- rucio/common/types.py +158 -0
- rucio/common/utils.py +1946 -0
- rucio/core/__init__.py +14 -0
- rucio/core/account.py +426 -0
- rucio/core/account_counter.py +171 -0
- rucio/core/account_limit.py +357 -0
- rucio/core/authentication.py +563 -0
- rucio/core/config.py +386 -0
- rucio/core/credential.py +218 -0
- rucio/core/did.py +3102 -0
- rucio/core/did_meta_plugins/__init__.py +250 -0
- rucio/core/did_meta_plugins/did_column_meta.py +326 -0
- rucio/core/did_meta_plugins/did_meta_plugin_interface.py +116 -0
- rucio/core/did_meta_plugins/filter_engine.py +573 -0
- rucio/core/did_meta_plugins/json_meta.py +215 -0
- rucio/core/did_meta_plugins/mongo_meta.py +199 -0
- rucio/core/did_meta_plugins/postgres_meta.py +317 -0
- rucio/core/dirac.py +208 -0
- rucio/core/distance.py +164 -0
- rucio/core/exporter.py +59 -0
- rucio/core/heartbeat.py +263 -0
- rucio/core/identity.py +290 -0
- rucio/core/importer.py +248 -0
- rucio/core/lifetime_exception.py +377 -0
- rucio/core/lock.py +474 -0
- rucio/core/message.py +241 -0
- rucio/core/meta.py +190 -0
- rucio/core/monitor.py +441 -0
- rucio/core/naming_convention.py +154 -0
- rucio/core/nongrid_trace.py +124 -0
- rucio/core/oidc.py +1339 -0
- rucio/core/permission/__init__.py +107 -0
- rucio/core/permission/atlas.py +1333 -0
- rucio/core/permission/belleii.py +1076 -0
- rucio/core/permission/cms.py +1166 -0
- rucio/core/permission/escape.py +1076 -0
- rucio/core/permission/generic.py +1128 -0
- rucio/core/permission/generic_multi_vo.py +1148 -0
- rucio/core/quarantined_replica.py +190 -0
- rucio/core/replica.py +3627 -0
- rucio/core/replica_sorter.py +368 -0
- rucio/core/request.py +2241 -0
- rucio/core/rse.py +1835 -0
- rucio/core/rse_counter.py +155 -0
- rucio/core/rse_expression_parser.py +460 -0
- rucio/core/rse_selector.py +277 -0
- rucio/core/rule.py +3419 -0
- rucio/core/rule_grouping.py +1473 -0
- rucio/core/scope.py +152 -0
- rucio/core/subscription.py +316 -0
- rucio/core/temporary_did.py +188 -0
- rucio/core/topology.py +448 -0
- rucio/core/trace.py +361 -0
- rucio/core/transfer.py +1233 -0
- rucio/core/vo.py +151 -0
- rucio/core/volatile_replica.py +123 -0
- rucio/daemons/__init__.py +14 -0
- rucio/daemons/abacus/__init__.py +14 -0
- rucio/daemons/abacus/account.py +106 -0
- rucio/daemons/abacus/collection_replica.py +113 -0
- rucio/daemons/abacus/rse.py +107 -0
- rucio/daemons/atropos/__init__.py +14 -0
- rucio/daemons/atropos/atropos.py +243 -0
- rucio/daemons/auditor/__init__.py +261 -0
- rucio/daemons/auditor/hdfs.py +86 -0
- rucio/daemons/auditor/srmdumps.py +284 -0
- rucio/daemons/automatix/__init__.py +14 -0
- rucio/daemons/automatix/automatix.py +281 -0
- rucio/daemons/badreplicas/__init__.py +14 -0
- rucio/daemons/badreplicas/minos.py +311 -0
- rucio/daemons/badreplicas/minos_temporary_expiration.py +173 -0
- rucio/daemons/badreplicas/necromancer.py +200 -0
- rucio/daemons/bb8/__init__.py +14 -0
- rucio/daemons/bb8/bb8.py +356 -0
- rucio/daemons/bb8/common.py +762 -0
- rucio/daemons/bb8/nuclei_background_rebalance.py +147 -0
- rucio/daemons/bb8/t2_background_rebalance.py +146 -0
- rucio/daemons/c3po/__init__.py +14 -0
- rucio/daemons/c3po/algorithms/__init__.py +14 -0
- rucio/daemons/c3po/algorithms/simple.py +131 -0
- rucio/daemons/c3po/algorithms/t2_free_space.py +125 -0
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +127 -0
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +279 -0
- rucio/daemons/c3po/c3po.py +342 -0
- rucio/daemons/c3po/collectors/__init__.py +14 -0
- rucio/daemons/c3po/collectors/agis.py +108 -0
- rucio/daemons/c3po/collectors/free_space.py +62 -0
- rucio/daemons/c3po/collectors/jedi_did.py +48 -0
- rucio/daemons/c3po/collectors/mock_did.py +46 -0
- rucio/daemons/c3po/collectors/network_metrics.py +63 -0
- rucio/daemons/c3po/collectors/workload.py +110 -0
- rucio/daemons/c3po/utils/__init__.py +14 -0
- rucio/daemons/c3po/utils/dataset_cache.py +40 -0
- rucio/daemons/c3po/utils/expiring_dataset_cache.py +45 -0
- rucio/daemons/c3po/utils/expiring_list.py +63 -0
- rucio/daemons/c3po/utils/popularity.py +82 -0
- rucio/daemons/c3po/utils/timeseries.py +76 -0
- rucio/daemons/cache/__init__.py +14 -0
- rucio/daemons/cache/consumer.py +191 -0
- rucio/daemons/common.py +391 -0
- rucio/daemons/conveyor/__init__.py +14 -0
- rucio/daemons/conveyor/common.py +530 -0
- rucio/daemons/conveyor/finisher.py +492 -0
- rucio/daemons/conveyor/poller.py +372 -0
- rucio/daemons/conveyor/preparer.py +198 -0
- rucio/daemons/conveyor/receiver.py +206 -0
- rucio/daemons/conveyor/stager.py +127 -0
- rucio/daemons/conveyor/submitter.py +379 -0
- rucio/daemons/conveyor/throttler.py +468 -0
- rucio/daemons/follower/__init__.py +14 -0
- rucio/daemons/follower/follower.py +97 -0
- rucio/daemons/hermes/__init__.py +14 -0
- rucio/daemons/hermes/hermes.py +738 -0
- rucio/daemons/judge/__init__.py +14 -0
- rucio/daemons/judge/cleaner.py +149 -0
- rucio/daemons/judge/evaluator.py +172 -0
- rucio/daemons/judge/injector.py +154 -0
- rucio/daemons/judge/repairer.py +144 -0
- rucio/daemons/oauthmanager/__init__.py +14 -0
- rucio/daemons/oauthmanager/oauthmanager.py +199 -0
- rucio/daemons/reaper/__init__.py +14 -0
- rucio/daemons/reaper/dark_reaper.py +272 -0
- rucio/daemons/reaper/light_reaper.py +255 -0
- rucio/daemons/reaper/reaper.py +701 -0
- rucio/daemons/replicarecoverer/__init__.py +14 -0
- rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +487 -0
- rucio/daemons/storage/__init__.py +14 -0
- rucio/daemons/storage/consistency/__init__.py +14 -0
- rucio/daemons/storage/consistency/actions.py +753 -0
- rucio/daemons/tracer/__init__.py +14 -0
- rucio/daemons/tracer/kronos.py +513 -0
- rucio/daemons/transmogrifier/__init__.py +14 -0
- rucio/daemons/transmogrifier/transmogrifier.py +753 -0
- rucio/daemons/undertaker/__init__.py +14 -0
- rucio/daemons/undertaker/undertaker.py +137 -0
- rucio/db/__init__.py +14 -0
- rucio/db/sqla/__init__.py +38 -0
- rucio/db/sqla/constants.py +192 -0
- rucio/db/sqla/migrate_repo/__init__.py +14 -0
- rucio/db/sqla/migrate_repo/env.py +111 -0
- rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +71 -0
- rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +50 -0
- rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +61 -0
- rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +46 -0
- rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +93 -0
- rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +78 -0
- rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +46 -0
- rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +53 -0
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +69 -0
- rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +42 -0
- rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +46 -0
- rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +61 -0
- rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +42 -0
- rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +141 -0
- rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +75 -0
- rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +75 -0
- rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +46 -0
- rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +51 -0
- rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +135 -0
- rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +65 -0
- rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +42 -0
- rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +66 -0
- rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +54 -0
- rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +43 -0
- rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +46 -0
- rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +47 -0
- rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +54 -0
- rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +39 -0
- rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +48 -0
- rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +47 -0
- rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +48 -0
- rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +59 -0
- rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +47 -0
- rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +72 -0
- rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +46 -0
- rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +45 -0
- rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +48 -0
- rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +48 -0
- rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +42 -0
- rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +69 -0
- rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +46 -0
- rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +78 -0
- rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +62 -0
- rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +74 -0
- rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +44 -0
- rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +67 -0
- rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +134 -0
- rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +58 -0
- rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +79 -0
- rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +61 -0
- rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +45 -0
- rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +46 -0
- rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +65 -0
- rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +42 -0
- rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +46 -0
- rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +46 -0
- rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +80 -0
- rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +43 -0
- rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +61 -0
- rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +47 -0
- rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +46 -0
- rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +52 -0
- rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +42 -0
- rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +65 -0
- rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +46 -0
- rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +47 -0
- rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +45 -0
- rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +46 -0
- rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +48 -0
- rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +50 -0
- rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +59 -0
- rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +48 -0
- rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +108 -0
- rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +57 -0
- rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +51 -0
- rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +50 -0
- rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +46 -0
- rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +42 -0
- rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +93 -0
- rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +73 -0
- rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +52 -0
- rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +45 -0
- rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +46 -0
- rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +54 -0
- rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +48 -0
- rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +70 -0
- rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +48 -0
- rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +95 -0
- rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +55 -0
- rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +74 -0
- rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +78 -0
- rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +49 -0
- rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +124 -0
- rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +60 -0
- rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +53 -0
- rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +56 -0
- rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +67 -0
- rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +50 -0
- rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +46 -0
- rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +92 -0
- rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +42 -0
- rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +46 -0
- rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +147 -0
- rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +78 -0
- rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +53 -0
- rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +74 -0
- rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +56 -0
- rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +46 -0
- rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +68 -0
- rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +48 -0
- rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +149 -0
- rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +106 -0
- rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +47 -0
- rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +45 -0
- rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +105 -0
- rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +52 -0
- rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +106 -0
- rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +30 -0
- rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +75 -0
- rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +49 -0
- rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +45 -0
- rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +38 -0
- rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +44 -0
- rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +46 -0
- rucio/db/sqla/models.py +1834 -0
- rucio/db/sqla/sautils.py +48 -0
- rucio/db/sqla/session.py +470 -0
- rucio/db/sqla/types.py +207 -0
- rucio/db/sqla/util.py +521 -0
- rucio/rse/__init__.py +97 -0
- rucio/rse/protocols/__init__.py +14 -0
- rucio/rse/protocols/cache.py +123 -0
- rucio/rse/protocols/dummy.py +112 -0
- rucio/rse/protocols/gfal.py +701 -0
- rucio/rse/protocols/globus.py +243 -0
- rucio/rse/protocols/gsiftp.py +93 -0
- rucio/rse/protocols/http_cache.py +83 -0
- rucio/rse/protocols/mock.py +124 -0
- rucio/rse/protocols/ngarc.py +210 -0
- rucio/rse/protocols/posix.py +251 -0
- rucio/rse/protocols/protocol.py +530 -0
- rucio/rse/protocols/rclone.py +365 -0
- rucio/rse/protocols/rfio.py +137 -0
- rucio/rse/protocols/srm.py +339 -0
- rucio/rse/protocols/ssh.py +414 -0
- rucio/rse/protocols/storm.py +207 -0
- rucio/rse/protocols/webdav.py +547 -0
- rucio/rse/protocols/xrootd.py +295 -0
- rucio/rse/rsemanager.py +752 -0
- rucio/tests/__init__.py +14 -0
- rucio/tests/common.py +244 -0
- rucio/tests/common_server.py +132 -0
- rucio/transfertool/__init__.py +14 -0
- rucio/transfertool/fts3.py +1484 -0
- rucio/transfertool/globus.py +200 -0
- rucio/transfertool/globus_library.py +182 -0
- rucio/transfertool/mock.py +81 -0
- rucio/transfertool/transfertool.py +212 -0
- rucio/vcsversion.py +11 -0
- rucio/version.py +46 -0
- rucio/web/__init__.py +14 -0
- rucio/web/rest/__init__.py +14 -0
- rucio/web/rest/flaskapi/__init__.py +14 -0
- rucio/web/rest/flaskapi/authenticated_bp.py +28 -0
- rucio/web/rest/flaskapi/v1/__init__.py +14 -0
- rucio/web/rest/flaskapi/v1/accountlimits.py +234 -0
- rucio/web/rest/flaskapi/v1/accounts.py +1088 -0
- rucio/web/rest/flaskapi/v1/archives.py +100 -0
- rucio/web/rest/flaskapi/v1/auth.py +1642 -0
- rucio/web/rest/flaskapi/v1/common.py +385 -0
- rucio/web/rest/flaskapi/v1/config.py +305 -0
- rucio/web/rest/flaskapi/v1/credentials.py +213 -0
- rucio/web/rest/flaskapi/v1/dids.py +2204 -0
- rucio/web/rest/flaskapi/v1/dirac.py +116 -0
- rucio/web/rest/flaskapi/v1/export.py +77 -0
- rucio/web/rest/flaskapi/v1/heartbeats.py +129 -0
- rucio/web/rest/flaskapi/v1/identities.py +263 -0
- rucio/web/rest/flaskapi/v1/import.py +133 -0
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +315 -0
- rucio/web/rest/flaskapi/v1/locks.py +360 -0
- rucio/web/rest/flaskapi/v1/main.py +83 -0
- rucio/web/rest/flaskapi/v1/meta.py +226 -0
- rucio/web/rest/flaskapi/v1/metrics.py +37 -0
- rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
- rucio/web/rest/flaskapi/v1/ping.py +89 -0
- rucio/web/rest/flaskapi/v1/redirect.py +366 -0
- rucio/web/rest/flaskapi/v1/replicas.py +1866 -0
- rucio/web/rest/flaskapi/v1/requests.py +841 -0
- rucio/web/rest/flaskapi/v1/rses.py +2204 -0
- rucio/web/rest/flaskapi/v1/rules.py +824 -0
- rucio/web/rest/flaskapi/v1/scopes.py +161 -0
- rucio/web/rest/flaskapi/v1/subscriptions.py +646 -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/tmp_dids.py +115 -0
- rucio/web/rest/flaskapi/v1/traces.py +100 -0
- rucio/web/rest/flaskapi/v1/vos.py +280 -0
- rucio/web/rest/main.py +19 -0
- rucio/web/rest/metrics.py +28 -0
- rucio-32.8.6.data/data/rucio/etc/alembic.ini.template +71 -0
- rucio-32.8.6.data/data/rucio/etc/alembic_offline.ini.template +74 -0
- rucio-32.8.6.data/data/rucio/etc/globus-config.yml.template +5 -0
- rucio-32.8.6.data/data/rucio/etc/ldap.cfg.template +30 -0
- rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
- rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
- rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
- rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
- rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
- rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
- rucio-32.8.6.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
- rucio-32.8.6.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
- rucio-32.8.6.data/data/rucio/etc/rucio.cfg.template +257 -0
- rucio-32.8.6.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
- rucio-32.8.6.data/data/rucio/requirements.txt +55 -0
- rucio-32.8.6.data/data/rucio/tools/bootstrap.py +34 -0
- rucio-32.8.6.data/data/rucio/tools/merge_rucio_configs.py +147 -0
- rucio-32.8.6.data/data/rucio/tools/reset_database.py +40 -0
- rucio-32.8.6.data/scripts/rucio +2540 -0
- rucio-32.8.6.data/scripts/rucio-abacus-account +75 -0
- rucio-32.8.6.data/scripts/rucio-abacus-collection-replica +47 -0
- rucio-32.8.6.data/scripts/rucio-abacus-rse +79 -0
- rucio-32.8.6.data/scripts/rucio-admin +2434 -0
- rucio-32.8.6.data/scripts/rucio-atropos +61 -0
- rucio-32.8.6.data/scripts/rucio-auditor +199 -0
- rucio-32.8.6.data/scripts/rucio-automatix +51 -0
- rucio-32.8.6.data/scripts/rucio-bb8 +58 -0
- rucio-32.8.6.data/scripts/rucio-c3po +86 -0
- rucio-32.8.6.data/scripts/rucio-cache-client +135 -0
- rucio-32.8.6.data/scripts/rucio-cache-consumer +43 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-finisher +59 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-poller +67 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-preparer +38 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-receiver +44 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-stager +77 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-submitter +140 -0
- rucio-32.8.6.data/scripts/rucio-conveyor-throttler +105 -0
- rucio-32.8.6.data/scripts/rucio-dark-reaper +54 -0
- rucio-32.8.6.data/scripts/rucio-dumper +159 -0
- rucio-32.8.6.data/scripts/rucio-follower +45 -0
- rucio-32.8.6.data/scripts/rucio-hermes +55 -0
- rucio-32.8.6.data/scripts/rucio-judge-cleaner +90 -0
- rucio-32.8.6.data/scripts/rucio-judge-evaluator +138 -0
- rucio-32.8.6.data/scripts/rucio-judge-injector +45 -0
- rucio-32.8.6.data/scripts/rucio-judge-repairer +45 -0
- rucio-32.8.6.data/scripts/rucio-kronos +45 -0
- rucio-32.8.6.data/scripts/rucio-light-reaper +53 -0
- rucio-32.8.6.data/scripts/rucio-minos +54 -0
- rucio-32.8.6.data/scripts/rucio-minos-temporary-expiration +51 -0
- rucio-32.8.6.data/scripts/rucio-necromancer +121 -0
- rucio-32.8.6.data/scripts/rucio-oauth-manager +64 -0
- rucio-32.8.6.data/scripts/rucio-reaper +84 -0
- rucio-32.8.6.data/scripts/rucio-replica-recoverer +249 -0
- rucio-32.8.6.data/scripts/rucio-storage-consistency-actions +75 -0
- rucio-32.8.6.data/scripts/rucio-transmogrifier +78 -0
- rucio-32.8.6.data/scripts/rucio-undertaker +77 -0
- rucio-32.8.6.dist-info/METADATA +83 -0
- rucio-32.8.6.dist-info/RECORD +481 -0
- rucio-32.8.6.dist-info/WHEEL +5 -0
- rucio-32.8.6.dist-info/licenses/AUTHORS.rst +94 -0
- rucio-32.8.6.dist-info/licenses/LICENSE +201 -0
- rucio-32.8.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
import datetime
|
|
17
|
+
import functools
|
|
18
|
+
import logging
|
|
19
|
+
import random
|
|
20
|
+
import threading
|
|
21
|
+
from sys import exc_info
|
|
22
|
+
from traceback import format_exception
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
import rucio.core.lifetime_exception
|
|
26
|
+
import rucio.db.sqla.util
|
|
27
|
+
from rucio.common import exception
|
|
28
|
+
from rucio.common.exception import InvalidRSEExpression, RuleNotFound
|
|
29
|
+
from rucio.common.logging import setup_logging
|
|
30
|
+
from rucio.core.did import set_metadata
|
|
31
|
+
from rucio.core.lock import get_dataset_locks
|
|
32
|
+
from rucio.core.rse import get_rse_name, get_rse_vo
|
|
33
|
+
from rucio.core.rse_expression_parser import parse_expression
|
|
34
|
+
from rucio.core.rule import get_rules_beyond_eol, update_rule
|
|
35
|
+
from rucio.daemons.common import run_daemon
|
|
36
|
+
from rucio.db.sqla.constants import LifetimeExceptionsState
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from types import FrameType
|
|
40
|
+
from typing import Optional
|
|
41
|
+
|
|
42
|
+
from rucio.daemons.common import HeartbeatHandler
|
|
43
|
+
|
|
44
|
+
GRACEFUL_STOP = threading.Event()
|
|
45
|
+
DAEMON_NAME = 'atropos'
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def atropos(
|
|
49
|
+
date_check: datetime.datetime,
|
|
50
|
+
dry_run: bool,
|
|
51
|
+
grace_period: int,
|
|
52
|
+
purge_replicas: bool,
|
|
53
|
+
spread_period: bool,
|
|
54
|
+
unlock: bool,
|
|
55
|
+
once: bool,
|
|
56
|
+
sleep_time: int
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Creates an Atropos Worker that gets a list of rules which have an eol_at expired and delete them.
|
|
60
|
+
|
|
61
|
+
:param grace_period: The grace_period for the rules.
|
|
62
|
+
:param once: Run only once.
|
|
63
|
+
:param sleep_time: Thread sleep time after each chunk of work.
|
|
64
|
+
"""
|
|
65
|
+
run_daemon(
|
|
66
|
+
once=once,
|
|
67
|
+
graceful_stop=GRACEFUL_STOP,
|
|
68
|
+
executable=DAEMON_NAME,
|
|
69
|
+
partition_wait_time=10,
|
|
70
|
+
sleep_time=sleep_time,
|
|
71
|
+
run_once_fnc=functools.partial(
|
|
72
|
+
run_once,
|
|
73
|
+
date_check=date_check,
|
|
74
|
+
dry_run=dry_run,
|
|
75
|
+
grace_period=grace_period,
|
|
76
|
+
purge_replicas=purge_replicas,
|
|
77
|
+
spread_period=spread_period,
|
|
78
|
+
unlock=unlock
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def run_once(
|
|
84
|
+
heartbeat_handler: 'HeartbeatHandler',
|
|
85
|
+
activity: None, # NOQA, pylint: disable=W0613
|
|
86
|
+
date_check: datetime.datetime,
|
|
87
|
+
dry_run: bool,
|
|
88
|
+
grace_period: int,
|
|
89
|
+
purge_replicas: bool,
|
|
90
|
+
spread_period: int,
|
|
91
|
+
unlock: bool,
|
|
92
|
+
) -> None:
|
|
93
|
+
"""An iteration of an Atropos worker."""
|
|
94
|
+
worker_number, total_workers, logger = heartbeat_handler.live()
|
|
95
|
+
logger(logging.DEBUG, 'Starting worker')
|
|
96
|
+
if not dry_run and date_check > datetime.datetime.utcnow():
|
|
97
|
+
logger(logging.ERROR, 'Atropos cannot run in non-dry-run mode for date in the future')
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
# Process the list of approved exceptions. In case a DID has
|
|
101
|
+
# multiple exceptions, the one with the expiration date further in
|
|
102
|
+
# the future is what matters.
|
|
103
|
+
summary = {}
|
|
104
|
+
lifetime_exceptions = {}
|
|
105
|
+
for excep in rucio.core.lifetime_exception.list_exceptions(exception_id=None, states=[LifetimeExceptionsState.APPROVED, ], session=None):
|
|
106
|
+
key = '{}:{}'.format(excep['scope'].internal, excep['name'])
|
|
107
|
+
if key not in lifetime_exceptions:
|
|
108
|
+
lifetime_exceptions[key] = excep['expires_at']
|
|
109
|
+
elif lifetime_exceptions[key] < excep['expires_at']:
|
|
110
|
+
lifetime_exceptions[key] = excep['expires_at']
|
|
111
|
+
logger(logging.DEBUG, '%d active exceptions', len(lifetime_exceptions))
|
|
112
|
+
|
|
113
|
+
rand = random.Random(worker_number)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
rules = get_rules_beyond_eol(date_check, worker_number, total_workers, session=None)
|
|
117
|
+
logger(logging.INFO, '%d rules to process', len(rules))
|
|
118
|
+
for rule_idx, rule in enumerate(rules, start=1):
|
|
119
|
+
did = '%s:%s' % (rule.scope, rule.name)
|
|
120
|
+
did_key = '{}:{}'.format(rule.scope.internal, rule.name)
|
|
121
|
+
logger(logging.DEBUG, 'Working on rule %s on DID %s on %s', rule.id, did, rule.rse_expression)
|
|
122
|
+
|
|
123
|
+
if (rule_idx % 1000) == 0:
|
|
124
|
+
logger(logging.INFO, '%s/%s rules processed', rule_idx, len(rules))
|
|
125
|
+
|
|
126
|
+
# We compute the expected eol_at
|
|
127
|
+
try:
|
|
128
|
+
rses = parse_expression(rule.rse_expression, filter_={'vo': rule.account.vo})
|
|
129
|
+
except InvalidRSEExpression:
|
|
130
|
+
logger(logging.WARNING, 'Rule %s has an RSE expression that results in an empty set: %s', rule.id, rule.rse_expression)
|
|
131
|
+
continue
|
|
132
|
+
eol_at = rucio.core.lifetime_exception.define_eol(rule.scope, rule.name, rses)
|
|
133
|
+
if eol_at != rule.eol_at:
|
|
134
|
+
logger(logging.WARNING, 'The computed eol %s differs from the one recorded %s for rule %s on %s at %s',
|
|
135
|
+
eol_at, rule.eol_at, rule.id, did, rule.rse_expression)
|
|
136
|
+
try:
|
|
137
|
+
update_rule(rule.id, options={'eol_at': eol_at})
|
|
138
|
+
except RuleNotFound:
|
|
139
|
+
logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
# Check the exceptions
|
|
143
|
+
if did_key in lifetime_exceptions:
|
|
144
|
+
if eol_at > lifetime_exceptions[did_key]:
|
|
145
|
+
logger(logging.INFO, 'Rule %s on DID %s on %s has longer expiration date than the one requested : %s',
|
|
146
|
+
rule.id, did, rule.rse_expression, lifetime_exceptions[did_key])
|
|
147
|
+
else:
|
|
148
|
+
# If eol_at < requested extension, update eol_at
|
|
149
|
+
logger(logging.INFO, 'Updating rule %s on DID %s on %s according to the exception till %s',
|
|
150
|
+
rule.id, did, rule.rse_expression, lifetime_exceptions[did_key])
|
|
151
|
+
eol_at = lifetime_exceptions[did_key]
|
|
152
|
+
try:
|
|
153
|
+
update_rule(rule.id, options={'eol_at': lifetime_exceptions[did_key]})
|
|
154
|
+
except RuleNotFound:
|
|
155
|
+
logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
# Now check that the new eol_at is expired
|
|
159
|
+
if eol_at and eol_at <= date_check:
|
|
160
|
+
set_metadata(scope=rule.scope, name=rule.name, key='eol_at', value=eol_at)
|
|
161
|
+
no_locks = True
|
|
162
|
+
for lock in get_dataset_locks(rule.scope, rule.name):
|
|
163
|
+
if lock['rule_id'] == rule[4]:
|
|
164
|
+
no_locks = False
|
|
165
|
+
if lock['rse_id'] not in summary:
|
|
166
|
+
summary[lock['rse_id']] = {}
|
|
167
|
+
if did_key not in summary[lock['rse_id']]:
|
|
168
|
+
summary[lock['rse_id']][did_key] = {'length': lock['length'] or 0, 'bytes': lock['bytes'] or 0}
|
|
169
|
+
if no_locks:
|
|
170
|
+
logger(logging.WARNING, 'Cannot find a lock for rule %s on DID %s', rule.id, did)
|
|
171
|
+
if not dry_run:
|
|
172
|
+
lifetime = grace_period + rand.randrange(spread_period + 1)
|
|
173
|
+
logger(logging.INFO, 'Setting %s seconds lifetime for rule %s', lifetime, rule.id)
|
|
174
|
+
options = {'lifetime': lifetime}
|
|
175
|
+
if purge_replicas:
|
|
176
|
+
options['purge_replicas'] = True
|
|
177
|
+
if rule.locked and unlock:
|
|
178
|
+
logger(logging.INFO, 'Unlocking rule %s', rule.id)
|
|
179
|
+
options['locked'] = False
|
|
180
|
+
try:
|
|
181
|
+
update_rule(rule.id, options=options)
|
|
182
|
+
except RuleNotFound:
|
|
183
|
+
logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
|
|
184
|
+
continue
|
|
185
|
+
except Exception:
|
|
186
|
+
exc_type, exc_value, exc_traceback = exc_info()
|
|
187
|
+
logger(logging.CRITICAL, ''.join(format_exception(exc_type, exc_value, exc_traceback)).strip())
|
|
188
|
+
|
|
189
|
+
for rse_id in summary:
|
|
190
|
+
tot_size, tot_files, tot_datasets = 0, 0, 0
|
|
191
|
+
for did in summary[rse_id]:
|
|
192
|
+
tot_datasets += 1
|
|
193
|
+
tot_files += summary[rse_id][did].get('length', 0)
|
|
194
|
+
tot_size += summary[rse_id][did].get('bytes', 0)
|
|
195
|
+
vo = get_rse_vo(rse_id=rse_id)
|
|
196
|
+
logger(logging.INFO, 'For RSE %s%s %d datasets will be deleted representing %d files and %d bytes',
|
|
197
|
+
get_rse_name(rse_id=rse_id), '' if vo == 'def' else ' on VO ' + vo, tot_datasets, tot_files, tot_size)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def run(
|
|
201
|
+
date_check: datetime.datetime = datetime.datetime.utcnow(),
|
|
202
|
+
dry_run: bool = True,
|
|
203
|
+
grace_period: int = 86400,
|
|
204
|
+
purge_replicas: bool = False,
|
|
205
|
+
spread_period: int = 0,
|
|
206
|
+
unlock: bool = False,
|
|
207
|
+
once: bool = True,
|
|
208
|
+
sleep_time: int = 60,
|
|
209
|
+
threads: int = 1,
|
|
210
|
+
) -> None:
|
|
211
|
+
"""
|
|
212
|
+
Starts up the atropos threads.
|
|
213
|
+
"""
|
|
214
|
+
setup_logging(process_name=DAEMON_NAME)
|
|
215
|
+
|
|
216
|
+
if rucio.db.sqla.util.is_old_db():
|
|
217
|
+
raise exception.DatabaseException('Database was not updated, daemon won\'t start')
|
|
218
|
+
|
|
219
|
+
if once:
|
|
220
|
+
logging.info('Will run only one iteration')
|
|
221
|
+
logging.info('starting atropos threads')
|
|
222
|
+
thread_list = [threading.Thread(target=atropos, kwargs={'date_check': date_check,
|
|
223
|
+
'dry_run': dry_run,
|
|
224
|
+
'grace_period': grace_period,
|
|
225
|
+
'purge_replicas': purge_replicas,
|
|
226
|
+
'spread_period': spread_period,
|
|
227
|
+
'unlock': unlock,
|
|
228
|
+
'once': once,
|
|
229
|
+
'sleep_time': sleep_time}) for i in range(0, threads)]
|
|
230
|
+
[t.start() for t in thread_list]
|
|
231
|
+
|
|
232
|
+
logging.info('waiting for interrupts')
|
|
233
|
+
|
|
234
|
+
# Interruptible joins require a timeout.
|
|
235
|
+
while thread_list:
|
|
236
|
+
thread_list = [t.join(timeout=3.14) for t in thread_list if t and t.is_alive()]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
|
|
240
|
+
"""
|
|
241
|
+
Graceful exit.
|
|
242
|
+
"""
|
|
243
|
+
GRACEFUL_STOP.set()
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
import bz2
|
|
17
|
+
import glob
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
import queue as Queue
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from datetime import timedelta
|
|
23
|
+
|
|
24
|
+
import select
|
|
25
|
+
|
|
26
|
+
from rucio.common import config
|
|
27
|
+
from rucio.common.dumper import LogPipeHandler
|
|
28
|
+
from rucio.common.dumper import mkdir
|
|
29
|
+
from rucio.common.dumper import temp_file
|
|
30
|
+
from rucio.common.dumper.consistency import Consistency
|
|
31
|
+
from rucio.common.types import InternalAccount, InternalScope
|
|
32
|
+
from rucio.common.utils import chunks
|
|
33
|
+
from rucio.core.quarantined_replica import add_quarantined_replicas
|
|
34
|
+
from rucio.core.replica import declare_bad_file_replicas, list_replicas
|
|
35
|
+
from rucio.core.rse import get_rse_usage, get_rse_id
|
|
36
|
+
from rucio.daemons.auditor import srmdumps
|
|
37
|
+
from rucio.daemons.auditor.hdfs import ReplicaFromHDFS
|
|
38
|
+
from rucio.db.sqla.constants import BadFilesStatus
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def consistency(rse, delta, configuration, cache_dir, results_dir):
|
|
42
|
+
logger = logging.getLogger('auditor-worker')
|
|
43
|
+
rsedump, rsedate = srmdumps.download_rse_dump(rse, configuration, destdir=cache_dir)
|
|
44
|
+
results_path = os.path.join(results_dir, '{0}_{1}'.format(rse, rsedate.strftime('%Y%m%d'))) # pylint: disable=no-member
|
|
45
|
+
|
|
46
|
+
if os.path.exists(results_path + '.bz2') or os.path.exists(results_path):
|
|
47
|
+
logger.warning('Consistency check for "%s" (dump dated %s) already done, skipping check', rse, rsedate.strftime('%Y%m%d')) # pylint: disable=no-member
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
rrdump_prev = ReplicaFromHDFS.download(rse, rsedate - delta, cache_dir=cache_dir)
|
|
51
|
+
rrdump_next = ReplicaFromHDFS.download(rse, rsedate + delta, cache_dir=cache_dir)
|
|
52
|
+
results = Consistency.dump(
|
|
53
|
+
'consistency-manual',
|
|
54
|
+
rse,
|
|
55
|
+
rsedump,
|
|
56
|
+
rrdump_prev,
|
|
57
|
+
rrdump_next,
|
|
58
|
+
date=rsedate,
|
|
59
|
+
cache_dir=cache_dir,
|
|
60
|
+
)
|
|
61
|
+
mkdir(results_dir)
|
|
62
|
+
with temp_file(results_dir, results_path) as (output, _):
|
|
63
|
+
for result in results:
|
|
64
|
+
output.write('{0}\n'.format(result.csv()))
|
|
65
|
+
|
|
66
|
+
return results_path
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def guess_replica_info(path):
|
|
70
|
+
"""Try to extract the scope and name from a path.
|
|
71
|
+
|
|
72
|
+
``path`` should be an ``str`` with the relative path to the file on
|
|
73
|
+
the RSE.
|
|
74
|
+
|
|
75
|
+
Returns a ``tuple`` of which the first element is the scope of the
|
|
76
|
+
replica and the second element is its name.
|
|
77
|
+
"""
|
|
78
|
+
items = path.split('/')
|
|
79
|
+
if len(items) == 1:
|
|
80
|
+
return None, path
|
|
81
|
+
elif len(items) > 2 and items[0] in ['group', 'user']:
|
|
82
|
+
return '.'.join(items[0:2]), items[-1]
|
|
83
|
+
else:
|
|
84
|
+
return items[0], items[-1]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def bz2_compress_file(source, chunk_size=65000):
|
|
88
|
+
"""Compress a file with bzip2.
|
|
89
|
+
|
|
90
|
+
The destination is the path passed through ``source`` extended with
|
|
91
|
+
'.bz2'. The original file is deleted.
|
|
92
|
+
|
|
93
|
+
Errors are deliberately not handled gracefully. Any exceptions
|
|
94
|
+
should be propagated to the caller.
|
|
95
|
+
|
|
96
|
+
``source`` should be an ``str`` with the absolute path to the file
|
|
97
|
+
to compress.
|
|
98
|
+
|
|
99
|
+
``chunk_size`` should be an ``int`` with the size (in bytes) of the
|
|
100
|
+
chunks by which to read the file.
|
|
101
|
+
|
|
102
|
+
Returns an ``str`` with the destination path.
|
|
103
|
+
"""
|
|
104
|
+
destination = '{}.bz2'.format(source)
|
|
105
|
+
with open(source) as plain, bz2.BZ2File(destination, 'w') as compressed:
|
|
106
|
+
while True:
|
|
107
|
+
chunk = plain.read(chunk_size)
|
|
108
|
+
if not chunk:
|
|
109
|
+
break
|
|
110
|
+
compressed.write(chunk.encode())
|
|
111
|
+
os.remove(source)
|
|
112
|
+
return destination
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def process_output(output, sanity_check=True, compress=True):
|
|
116
|
+
"""Perform post-consistency-check actions.
|
|
117
|
+
|
|
118
|
+
DARK files are put in the quarantined-replica table so that they
|
|
119
|
+
may be deleted by the Dark Reaper. LOST files are reported as
|
|
120
|
+
suspicious so that they may be further checked by the cloud squads.
|
|
121
|
+
|
|
122
|
+
``output`` should be an ``str`` with the absolute path to the file
|
|
123
|
+
produced by ``consistency()``. It must maintain its naming
|
|
124
|
+
convention.
|
|
125
|
+
|
|
126
|
+
If ``sanity_check`` is ``True`` (default) and the number of entries
|
|
127
|
+
in the output file is deemed excessive, the actions are aborted.
|
|
128
|
+
|
|
129
|
+
If ``compress`` is ``True`` (default), the file is compressed with
|
|
130
|
+
bzip2 after the actions are successfully performed.
|
|
131
|
+
"""
|
|
132
|
+
logger = logging.getLogger('auditor-worker')
|
|
133
|
+
dark_replicas = []
|
|
134
|
+
lost_replicas = []
|
|
135
|
+
try:
|
|
136
|
+
with open(output) as f:
|
|
137
|
+
for line in f:
|
|
138
|
+
label, path = line.rstrip().split(',', 1)
|
|
139
|
+
scope, name = guess_replica_info(path)
|
|
140
|
+
if label == 'DARK':
|
|
141
|
+
dark_replicas.append({'path': path,
|
|
142
|
+
'scope': InternalScope(scope),
|
|
143
|
+
'name': name})
|
|
144
|
+
elif label == 'LOST':
|
|
145
|
+
lost_replicas.append({'scope': InternalScope(scope),
|
|
146
|
+
'name': name})
|
|
147
|
+
else:
|
|
148
|
+
raise ValueError('unexpected label')
|
|
149
|
+
# Since the file is read immediately after its creation, any error
|
|
150
|
+
# exposes a bug in the Auditor.
|
|
151
|
+
except Exception as error:
|
|
152
|
+
logger.critical('Error processing "%s"', output, exc_info=True)
|
|
153
|
+
raise error
|
|
154
|
+
|
|
155
|
+
rse = os.path.basename(output[:output.rfind('_')])
|
|
156
|
+
rse_id = get_rse_id(rse=rse)
|
|
157
|
+
usage = get_rse_usage(rse_id=rse_id, source='rucio')[0]
|
|
158
|
+
threshold = config.config_get_float('auditor', 'threshold', False, 0.2)
|
|
159
|
+
|
|
160
|
+
# Perform a basic sanity check by comparing the number of entries
|
|
161
|
+
# with the total number of files on the RSE. If the percentage is
|
|
162
|
+
# significant, there is most likely an issue with the site dump.
|
|
163
|
+
found_error = False
|
|
164
|
+
if len(dark_replicas) > threshold * usage['files']:
|
|
165
|
+
logger.warning('Number of DARK files is exceeding threshold: "%s"',
|
|
166
|
+
output)
|
|
167
|
+
found_error = True
|
|
168
|
+
if len(lost_replicas) > threshold * usage['files']:
|
|
169
|
+
logger.warning('Number of LOST files is exceeding threshold: "%s"',
|
|
170
|
+
output)
|
|
171
|
+
found_error = True
|
|
172
|
+
if found_error and sanity_check:
|
|
173
|
+
raise AssertionError('sanity check failed')
|
|
174
|
+
|
|
175
|
+
# While converting LOST replicas to PFNs, entries that do not
|
|
176
|
+
# correspond to a replica registered in Rucio are silently dropped.
|
|
177
|
+
lost_pfns = [r['rses'][rse_id][0] for chunk in chunks(lost_replicas, 1000) for r in list_replicas(chunk) if rse_id in r['rses']]
|
|
178
|
+
|
|
179
|
+
for chunk in chunks(dark_replicas, 1000):
|
|
180
|
+
add_quarantined_replicas(rse_id=rse_id, replicas=chunk)
|
|
181
|
+
logger.debug('Processed %d DARK files from "%s"', len(dark_replicas),
|
|
182
|
+
output)
|
|
183
|
+
declare_bad_file_replicas(lost_pfns, reason='Reported by Auditor',
|
|
184
|
+
issuer=InternalAccount('root'), status=BadFilesStatus.SUSPICIOUS)
|
|
185
|
+
logger.debug('Processed %d LOST files from "%s"', len(lost_replicas),
|
|
186
|
+
output)
|
|
187
|
+
|
|
188
|
+
if compress:
|
|
189
|
+
destination = bz2_compress_file(output)
|
|
190
|
+
logger.debug('Compressed "%s"', destination)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def check(queue, retry, terminate, logpipe, cache_dir, results_dir, keep_dumps, delta_in_days):
|
|
194
|
+
logger = logging.getLogger('auditor-worker')
|
|
195
|
+
lib_logger = logging.getLogger('dumper')
|
|
196
|
+
|
|
197
|
+
loglevel = logging.getLevelName(config.config_get('common', 'loglevel', False, 'DEBUG'))
|
|
198
|
+
logger.setLevel(loglevel)
|
|
199
|
+
lib_logger.setLevel(loglevel)
|
|
200
|
+
|
|
201
|
+
handler = LogPipeHandler(logpipe)
|
|
202
|
+
logger.addHandler(handler)
|
|
203
|
+
lib_logger.addHandler(handler)
|
|
204
|
+
|
|
205
|
+
formatter = logging.Formatter(
|
|
206
|
+
"%(asctime)s %(name)-22s %(levelname)-8s [PID %(process)8d] %(message)s"
|
|
207
|
+
)
|
|
208
|
+
handler.setFormatter(formatter)
|
|
209
|
+
|
|
210
|
+
delta = timedelta(days=delta_in_days)
|
|
211
|
+
|
|
212
|
+
configuration = srmdumps.parse_configuration()
|
|
213
|
+
|
|
214
|
+
while not terminate.is_set():
|
|
215
|
+
try:
|
|
216
|
+
rse, attemps = queue.get(timeout=30)
|
|
217
|
+
except Queue.Empty:
|
|
218
|
+
continue
|
|
219
|
+
start = datetime.now()
|
|
220
|
+
try:
|
|
221
|
+
logger.debug('Checking "%s"', rse)
|
|
222
|
+
output = consistency(rse, delta, configuration, cache_dir,
|
|
223
|
+
results_dir)
|
|
224
|
+
if output:
|
|
225
|
+
process_output(output)
|
|
226
|
+
except:
|
|
227
|
+
elapsed = (datetime.now() - start).total_seconds() / 60
|
|
228
|
+
logger.error('Check of "%s" failed in %d minutes, %d remaining attemps', rse, elapsed, attemps, exc_info=True)
|
|
229
|
+
success = False
|
|
230
|
+
else:
|
|
231
|
+
elapsed = (datetime.now() - start).total_seconds() / 60
|
|
232
|
+
logger.info('SUCCESS checking "%s" in %d minutes', rse, elapsed)
|
|
233
|
+
success = True
|
|
234
|
+
|
|
235
|
+
if not keep_dumps:
|
|
236
|
+
remove = glob.glob(os.path.join(cache_dir, 'replicafromhdfs_{0}_*'.format(rse)))
|
|
237
|
+
remove.extend(glob.glob(os.path.join(cache_dir, 'ddmendpoint_{0}_*'.format(rse))))
|
|
238
|
+
logger.debug('Removing: %s', remove)
|
|
239
|
+
for fil in remove:
|
|
240
|
+
os.remove(fil)
|
|
241
|
+
|
|
242
|
+
if not success and attemps > 0:
|
|
243
|
+
retry.put((rse, attemps - 1))
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def activity_logger(logpipes, logfilename, terminate):
|
|
247
|
+
handler = logging.handlers.RotatingFileHandler(
|
|
248
|
+
logfilename,
|
|
249
|
+
maxBytes=20971520,
|
|
250
|
+
backupCount=10,
|
|
251
|
+
)
|
|
252
|
+
handler.setFormatter(logging.Formatter(fmt=None))
|
|
253
|
+
logger = logging.getLogger('auditor-logger-raw')
|
|
254
|
+
logger.addHandler(handler)
|
|
255
|
+
logger.setLevel(logging.CRITICAL) # The level of this logger is irrelevant
|
|
256
|
+
|
|
257
|
+
while not terminate.is_set():
|
|
258
|
+
ready, _, _ = select.select(logpipes, tuple(), tuple(), 30)
|
|
259
|
+
if ready:
|
|
260
|
+
for logpipe in ready:
|
|
261
|
+
logger.critical(logpipe.recv())
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright European Organization for Nuclear Research (CERN) since 2012
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
import hashlib
|
|
17
|
+
import logging
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
import shutil
|
|
21
|
+
import subprocess
|
|
22
|
+
import tempfile
|
|
23
|
+
|
|
24
|
+
from rucio.common.dumper import DUMPS_CACHE_DIR
|
|
25
|
+
from rucio.common.dumper import temp_file
|
|
26
|
+
from rucio.common.dumper.data_models import Replica
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _hdfs_get(src_url, dst_path):
|
|
30
|
+
cmd = ['hadoop', 'fs', '-get', src_url, dst_path]
|
|
31
|
+
get = subprocess.Popen(
|
|
32
|
+
cmd,
|
|
33
|
+
stderr=subprocess.PIPE,
|
|
34
|
+
)
|
|
35
|
+
_, stderr = get.communicate()
|
|
36
|
+
if get.returncode != 0:
|
|
37
|
+
raise IOError('_hdfs_get(): "{0}": {1}. Return code {2}'.format(
|
|
38
|
+
' '.join(cmd),
|
|
39
|
+
stderr,
|
|
40
|
+
get.returncode,
|
|
41
|
+
))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ReplicaFromHDFS(Replica):
|
|
45
|
+
BASE_URL = '/user/rucio01/reports/{0}/replicas_per_rse/{1}/*'
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def download(cls, rse, date, cache_dir=DUMPS_CACHE_DIR, buffer_size=65536):
|
|
49
|
+
logger = logging.getLogger('auditor.hdfs')
|
|
50
|
+
|
|
51
|
+
if not os.path.isdir(cache_dir):
|
|
52
|
+
os.mkdir(cache_dir)
|
|
53
|
+
tmp_dir = tempfile.mkdtemp(dir=cache_dir)
|
|
54
|
+
|
|
55
|
+
url = cls.BASE_URL.format(date.strftime('%Y-%m-%d'), rse)
|
|
56
|
+
filename = '{0}_{1}_{2}_{3}'.format(
|
|
57
|
+
cls.__name__.lower(),
|
|
58
|
+
rse,
|
|
59
|
+
date.strftime('%d-%m-%Y'),
|
|
60
|
+
hashlib.sha1(url.encode()).hexdigest()
|
|
61
|
+
)
|
|
62
|
+
filename = re.sub(r'\W', '-', filename)
|
|
63
|
+
path = os.path.join(cache_dir, filename)
|
|
64
|
+
|
|
65
|
+
if os.path.exists(path):
|
|
66
|
+
logger.debug('Taking Rucio Replica Dump %s for %s from cache', path, rse)
|
|
67
|
+
return path
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
logging.debug('Trying to download: %s for %s', url, rse)
|
|
71
|
+
|
|
72
|
+
_hdfs_get(cls.BASE_URL.format(date.strftime('%Y-%m-%d'), rse), tmp_dir)
|
|
73
|
+
files = (os.path.join(tmp_dir, file_) for file_ in sorted(os.listdir(tmp_dir)))
|
|
74
|
+
|
|
75
|
+
with temp_file(cache_dir, filename, binary=True) as (full_dump, _):
|
|
76
|
+
for chunk_file in files:
|
|
77
|
+
with open(chunk_file, 'rb') as partial_dump:
|
|
78
|
+
while True:
|
|
79
|
+
data_chunk = partial_dump.read(buffer_size)
|
|
80
|
+
if not data_chunk:
|
|
81
|
+
break
|
|
82
|
+
full_dump.write(data_chunk)
|
|
83
|
+
finally:
|
|
84
|
+
shutil.rmtree(tmp_dir)
|
|
85
|
+
|
|
86
|
+
return path
|