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.

Files changed (502) hide show
  1. rucio/__init__.py +0 -1
  2. rucio/alembicrevision.py +1 -2
  3. rucio/client/__init__.py +0 -1
  4. rucio/client/accountclient.py +45 -25
  5. rucio/client/accountlimitclient.py +37 -9
  6. rucio/client/baseclient.py +199 -154
  7. rucio/client/client.py +2 -3
  8. rucio/client/configclient.py +19 -6
  9. rucio/client/credentialclient.py +9 -4
  10. rucio/client/didclient.py +238 -63
  11. rucio/client/diracclient.py +13 -5
  12. rucio/client/downloadclient.py +162 -51
  13. rucio/client/exportclient.py +4 -4
  14. rucio/client/fileclient.py +3 -4
  15. rucio/client/importclient.py +4 -4
  16. rucio/client/lifetimeclient.py +21 -5
  17. rucio/client/lockclient.py +18 -8
  18. rucio/client/{metaclient.py → metaconventionsclient.py} +18 -15
  19. rucio/client/pingclient.py +0 -1
  20. rucio/client/replicaclient.py +15 -5
  21. rucio/client/requestclient.py +35 -19
  22. rucio/client/rseclient.py +133 -51
  23. rucio/client/ruleclient.py +29 -22
  24. rucio/client/scopeclient.py +8 -6
  25. rucio/client/subscriptionclient.py +47 -35
  26. rucio/client/touchclient.py +8 -4
  27. rucio/client/uploadclient.py +166 -82
  28. rucio/common/__init__.py +0 -1
  29. rucio/common/cache.py +4 -4
  30. rucio/common/config.py +52 -47
  31. rucio/common/constants.py +69 -2
  32. rucio/common/constraints.py +0 -1
  33. rucio/common/didtype.py +24 -22
  34. rucio/common/dumper/__init__.py +70 -41
  35. rucio/common/dumper/consistency.py +26 -22
  36. rucio/common/dumper/data_models.py +16 -23
  37. rucio/common/dumper/path_parsing.py +0 -1
  38. rucio/common/exception.py +281 -222
  39. rucio/common/extra.py +0 -1
  40. rucio/common/logging.py +54 -38
  41. rucio/common/pcache.py +122 -101
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +4 -4
  44. rucio/common/schema/__init__.py +17 -10
  45. rucio/common/schema/atlas.py +7 -5
  46. rucio/common/schema/belleii.py +7 -5
  47. rucio/common/schema/domatpc.py +7 -5
  48. rucio/common/schema/escape.py +7 -5
  49. rucio/common/schema/generic.py +8 -6
  50. rucio/common/schema/generic_multi_vo.py +7 -5
  51. rucio/common/schema/icecube.py +7 -5
  52. rucio/common/stomp_utils.py +0 -1
  53. rucio/common/stopwatch.py +0 -1
  54. rucio/common/test_rucio_server.py +2 -2
  55. rucio/common/types.py +262 -17
  56. rucio/common/utils.py +743 -451
  57. rucio/core/__init__.py +0 -1
  58. rucio/core/account.py +99 -29
  59. rucio/core/account_counter.py +89 -24
  60. rucio/core/account_limit.py +90 -24
  61. rucio/core/authentication.py +86 -29
  62. rucio/core/config.py +108 -38
  63. rucio/core/credential.py +14 -7
  64. rucio/core/did.py +680 -782
  65. rucio/core/did_meta_plugins/__init__.py +8 -6
  66. rucio/core/did_meta_plugins/did_column_meta.py +17 -12
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +60 -11
  68. rucio/core/did_meta_plugins/filter_engine.py +90 -50
  69. rucio/core/did_meta_plugins/json_meta.py +41 -16
  70. rucio/core/did_meta_plugins/mongo_meta.py +25 -8
  71. rucio/core/did_meta_plugins/postgres_meta.py +3 -4
  72. rucio/core/dirac.py +46 -17
  73. rucio/core/distance.py +66 -43
  74. rucio/core/exporter.py +5 -5
  75. rucio/core/heartbeat.py +181 -81
  76. rucio/core/identity.py +22 -12
  77. rucio/core/importer.py +23 -12
  78. rucio/core/lifetime_exception.py +32 -32
  79. rucio/core/lock.py +244 -142
  80. rucio/core/message.py +79 -38
  81. rucio/core/{meta.py → meta_conventions.py} +57 -44
  82. rucio/core/monitor.py +19 -13
  83. rucio/core/naming_convention.py +68 -27
  84. rucio/core/nongrid_trace.py +17 -5
  85. rucio/core/oidc.py +151 -29
  86. rucio/core/permission/__init__.py +18 -6
  87. rucio/core/permission/atlas.py +50 -35
  88. rucio/core/permission/belleii.py +6 -5
  89. rucio/core/permission/escape.py +8 -6
  90. rucio/core/permission/generic.py +82 -80
  91. rucio/core/permission/generic_multi_vo.py +9 -7
  92. rucio/core/quarantined_replica.py +91 -58
  93. rucio/core/replica.py +1303 -772
  94. rucio/core/replica_sorter.py +10 -12
  95. rucio/core/request.py +1133 -285
  96. rucio/core/rse.py +142 -102
  97. rucio/core/rse_counter.py +49 -18
  98. rucio/core/rse_expression_parser.py +6 -7
  99. rucio/core/rse_selector.py +41 -16
  100. rucio/core/rule.py +1538 -474
  101. rucio/core/rule_grouping.py +213 -68
  102. rucio/core/scope.py +50 -22
  103. rucio/core/subscription.py +92 -44
  104. rucio/core/topology.py +66 -24
  105. rucio/core/trace.py +42 -28
  106. rucio/core/transfer.py +543 -259
  107. rucio/core/vo.py +36 -18
  108. rucio/core/volatile_replica.py +59 -32
  109. rucio/daemons/__init__.py +0 -1
  110. rucio/daemons/abacus/__init__.py +0 -1
  111. rucio/daemons/abacus/account.py +29 -19
  112. rucio/daemons/abacus/collection_replica.py +21 -10
  113. rucio/daemons/abacus/rse.py +22 -12
  114. rucio/daemons/atropos/__init__.py +0 -1
  115. rucio/daemons/atropos/atropos.py +1 -2
  116. rucio/daemons/auditor/__init__.py +56 -28
  117. rucio/daemons/auditor/hdfs.py +17 -6
  118. rucio/daemons/auditor/srmdumps.py +116 -45
  119. rucio/daemons/automatix/__init__.py +0 -1
  120. rucio/daemons/automatix/automatix.py +30 -18
  121. rucio/daemons/badreplicas/__init__.py +0 -1
  122. rucio/daemons/badreplicas/minos.py +29 -18
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +5 -7
  124. rucio/daemons/badreplicas/necromancer.py +9 -13
  125. rucio/daemons/bb8/__init__.py +0 -1
  126. rucio/daemons/bb8/bb8.py +10 -13
  127. rucio/daemons/bb8/common.py +151 -154
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +15 -9
  129. rucio/daemons/bb8/t2_background_rebalance.py +15 -8
  130. rucio/daemons/c3po/__init__.py +0 -1
  131. rucio/daemons/c3po/algorithms/__init__.py +0 -1
  132. rucio/daemons/c3po/algorithms/simple.py +8 -5
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +10 -7
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +10 -7
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +30 -15
  136. rucio/daemons/c3po/c3po.py +81 -52
  137. rucio/daemons/c3po/collectors/__init__.py +0 -1
  138. rucio/daemons/c3po/collectors/agis.py +17 -17
  139. rucio/daemons/c3po/collectors/free_space.py +32 -13
  140. rucio/daemons/c3po/collectors/jedi_did.py +14 -5
  141. rucio/daemons/c3po/collectors/mock_did.py +11 -6
  142. rucio/daemons/c3po/collectors/network_metrics.py +12 -4
  143. rucio/daemons/c3po/collectors/workload.py +21 -19
  144. rucio/daemons/c3po/utils/__init__.py +0 -1
  145. rucio/daemons/c3po/utils/dataset_cache.py +15 -5
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +16 -5
  147. rucio/daemons/c3po/utils/expiring_list.py +6 -7
  148. rucio/daemons/c3po/utils/popularity.py +5 -2
  149. rucio/daemons/c3po/utils/timeseries.py +25 -12
  150. rucio/daemons/cache/__init__.py +0 -1
  151. rucio/daemons/cache/consumer.py +21 -15
  152. rucio/daemons/common.py +42 -18
  153. rucio/daemons/conveyor/__init__.py +0 -1
  154. rucio/daemons/conveyor/common.py +69 -37
  155. rucio/daemons/conveyor/finisher.py +83 -46
  156. rucio/daemons/conveyor/poller.py +101 -69
  157. rucio/daemons/conveyor/preparer.py +35 -28
  158. rucio/daemons/conveyor/receiver.py +64 -21
  159. rucio/daemons/conveyor/stager.py +33 -28
  160. rucio/daemons/conveyor/submitter.py +71 -47
  161. rucio/daemons/conveyor/throttler.py +99 -35
  162. rucio/daemons/follower/__init__.py +0 -1
  163. rucio/daemons/follower/follower.py +12 -8
  164. rucio/daemons/hermes/__init__.py +0 -1
  165. rucio/daemons/hermes/hermes.py +57 -21
  166. rucio/daemons/judge/__init__.py +0 -1
  167. rucio/daemons/judge/cleaner.py +27 -17
  168. rucio/daemons/judge/evaluator.py +31 -18
  169. rucio/daemons/judge/injector.py +31 -23
  170. rucio/daemons/judge/repairer.py +28 -18
  171. rucio/daemons/oauthmanager/__init__.py +0 -1
  172. rucio/daemons/oauthmanager/oauthmanager.py +7 -8
  173. rucio/daemons/reaper/__init__.py +0 -1
  174. rucio/daemons/reaper/dark_reaper.py +15 -9
  175. rucio/daemons/reaper/reaper.py +109 -67
  176. rucio/daemons/replicarecoverer/__init__.py +0 -1
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +255 -116
  178. rucio/{api → daemons/rsedecommissioner}/__init__.py +0 -1
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +0 -1
  186. rucio/daemons/storage/consistency/__init__.py +0 -1
  187. rucio/daemons/storage/consistency/actions.py +152 -59
  188. rucio/daemons/tracer/__init__.py +0 -1
  189. rucio/daemons/tracer/kronos.py +47 -24
  190. rucio/daemons/transmogrifier/__init__.py +0 -1
  191. rucio/daemons/transmogrifier/transmogrifier.py +35 -26
  192. rucio/daemons/undertaker/__init__.py +0 -1
  193. rucio/daemons/undertaker/undertaker.py +10 -10
  194. rucio/db/__init__.py +0 -1
  195. rucio/db/sqla/__init__.py +16 -2
  196. rucio/db/sqla/constants.py +10 -1
  197. rucio/db/sqla/migrate_repo/__init__.py +0 -1
  198. rucio/db/sqla/migrate_repo/env.py +0 -1
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +0 -1
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +0 -3
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +1 -3
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +0 -3
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +1 -3
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +1 -3
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +0 -3
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +1 -4
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +0 -1
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +0 -2
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +0 -1
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +0 -1
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +0 -2
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +0 -1
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +1 -3
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +0 -1
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +0 -3
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +0 -1
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +1 -2
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +0 -1
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +0 -3
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +1 -3
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +1 -4
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +0 -2
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +0 -3
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +0 -3
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +1 -2
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +0 -1
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +0 -1
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +0 -2
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +0 -3
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +1 -3
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +0 -2
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +1 -4
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +0 -3
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +1 -4
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +0 -1
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +1 -3
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +0 -2
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +1 -3
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +1 -3
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +1 -2
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +1 -3
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +1 -3
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +0 -2
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +1 -3
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +2 -3
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +0 -3
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +1 -4
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +0 -1
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +0 -1
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +0 -3
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +0 -1
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +0 -2
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +0 -3
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +0 -2
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +2 -4
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +0 -2
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +1 -3
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +1 -4
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +0 -3
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +0 -3
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +0 -2
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +1 -3
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +0 -3
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +0 -2
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +0 -2
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +0 -3
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +0 -3
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +0 -3
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +1 -2
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +0 -3
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +1 -3
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +1 -3
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +1 -2
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +0 -3
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +0 -3
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +1 -2
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +2 -4
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +0 -1
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +1 -4
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +0 -2
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +0 -3
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +1 -2
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +0 -3
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +1 -3
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +0 -3
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +0 -1
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +1 -2
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +0 -2
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +1 -3
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +0 -2
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +1 -4
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +0 -1
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +0 -1
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +1 -3
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +1 -4
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +0 -1
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +0 -3
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +1 -2
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +1 -3
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +0 -3
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +1 -5
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +1 -3
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +0 -3
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +1 -3
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +1 -2
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +0 -3
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +1 -4
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +1 -2
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +1 -4
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +1 -3
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +1 -4
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +0 -2
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +1 -3
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +0 -3
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +1 -3
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +0 -1
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +1 -2
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +1 -3
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +0 -2
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +0 -1
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +1 -2
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +0 -3
  327. rucio/db/sqla/models.py +122 -216
  328. rucio/db/sqla/sautils.py +12 -5
  329. rucio/db/sqla/session.py +71 -43
  330. rucio/db/sqla/types.py +3 -4
  331. rucio/db/sqla/util.py +91 -69
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/{api → gateway}/account.py +119 -46
  334. rucio/{api → gateway}/account_limit.py +12 -13
  335. rucio/{api → gateway}/authentication.py +106 -33
  336. rucio/{api → gateway}/config.py +12 -13
  337. rucio/{api → gateway}/credential.py +15 -4
  338. rucio/{api → gateway}/did.py +384 -140
  339. rucio/{api → gateway}/dirac.py +16 -6
  340. rucio/{api → gateway}/exporter.py +3 -4
  341. rucio/{api → gateway}/heartbeat.py +17 -5
  342. rucio/{api → gateway}/identity.py +63 -19
  343. rucio/{api → gateway}/importer.py +3 -4
  344. rucio/{api → gateway}/lifetime_exception.py +35 -10
  345. rucio/{api → gateway}/lock.py +34 -12
  346. rucio/{api/meta.py → gateway/meta_conventions.py} +18 -16
  347. rucio/{api → gateway}/permission.py +4 -5
  348. rucio/{api → gateway}/quarantined_replica.py +13 -4
  349. rucio/{api → gateway}/replica.py +12 -11
  350. rucio/{api → gateway}/request.py +129 -28
  351. rucio/{api → gateway}/rse.py +11 -12
  352. rucio/{api → gateway}/rule.py +117 -35
  353. rucio/{api → gateway}/scope.py +24 -14
  354. rucio/{api → gateway}/subscription.py +65 -43
  355. rucio/{api → gateway}/vo.py +17 -7
  356. rucio/rse/__init__.py +3 -4
  357. rucio/rse/protocols/__init__.py +0 -1
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +1 -2
  360. rucio/rse/protocols/dummy.py +1 -2
  361. rucio/rse/protocols/gfal.py +12 -10
  362. rucio/rse/protocols/globus.py +7 -7
  363. rucio/rse/protocols/gsiftp.py +2 -3
  364. rucio/rse/protocols/http_cache.py +1 -2
  365. rucio/rse/protocols/mock.py +1 -2
  366. rucio/rse/protocols/ngarc.py +1 -2
  367. rucio/rse/protocols/posix.py +12 -13
  368. rucio/rse/protocols/protocol.py +116 -52
  369. rucio/rse/protocols/rclone.py +6 -7
  370. rucio/rse/protocols/rfio.py +4 -5
  371. rucio/rse/protocols/srm.py +9 -10
  372. rucio/rse/protocols/ssh.py +8 -9
  373. rucio/rse/protocols/storm.py +2 -3
  374. rucio/rse/protocols/webdav.py +17 -14
  375. rucio/rse/protocols/xrootd.py +23 -17
  376. rucio/rse/rsemanager.py +19 -7
  377. rucio/tests/__init__.py +0 -1
  378. rucio/tests/common.py +43 -17
  379. rucio/tests/common_server.py +3 -3
  380. rucio/transfertool/__init__.py +0 -1
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +250 -138
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +9 -8
  387. rucio/transfertool/globus_library.py +1 -2
  388. rucio/transfertool/mock.py +21 -12
  389. rucio/transfertool/transfertool.py +33 -24
  390. rucio/vcsversion.py +4 -4
  391. rucio/version.py +5 -13
  392. rucio/web/__init__.py +0 -1
  393. rucio/web/rest/__init__.py +0 -1
  394. rucio/web/rest/flaskapi/__init__.py +0 -1
  395. rucio/web/rest/flaskapi/authenticated_bp.py +0 -1
  396. rucio/web/rest/flaskapi/v1/__init__.py +0 -1
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +15 -13
  398. rucio/web/rest/flaskapi/v1/accounts.py +49 -48
  399. rucio/web/rest/flaskapi/v1/archives.py +12 -10
  400. rucio/web/rest/flaskapi/v1/auth.py +146 -144
  401. rucio/web/rest/flaskapi/v1/common.py +82 -41
  402. rucio/web/rest/flaskapi/v1/config.py +5 -6
  403. rucio/web/rest/flaskapi/v1/credentials.py +7 -8
  404. rucio/web/rest/flaskapi/v1/dids.py +158 -28
  405. rucio/web/rest/flaskapi/v1/dirac.py +8 -8
  406. rucio/web/rest/flaskapi/v1/export.py +3 -5
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +3 -5
  408. rucio/web/rest/flaskapi/v1/identities.py +3 -5
  409. rucio/web/rest/flaskapi/v1/import.py +3 -4
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +6 -9
  411. rucio/web/rest/flaskapi/v1/locks.py +2 -4
  412. rucio/web/rest/flaskapi/v1/main.py +10 -2
  413. rucio/web/rest/flaskapi/v1/{meta.py → meta_conventions.py} +26 -11
  414. rucio/web/rest/flaskapi/v1/metrics.py +1 -2
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +4 -4
  416. rucio/web/rest/flaskapi/v1/ping.py +6 -7
  417. rucio/web/rest/flaskapi/v1/redirect.py +8 -9
  418. rucio/web/rest/flaskapi/v1/replicas.py +43 -19
  419. rucio/web/rest/flaskapi/v1/requests.py +178 -21
  420. rucio/web/rest/flaskapi/v1/rses.py +61 -26
  421. rucio/web/rest/flaskapi/v1/rules.py +48 -18
  422. rucio/web/rest/flaskapi/v1/scopes.py +3 -5
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +22 -18
  424. rucio/web/rest/flaskapi/v1/traces.py +4 -4
  425. rucio/web/rest/flaskapi/v1/types.py +20 -0
  426. rucio/web/rest/flaskapi/v1/vos.py +3 -5
  427. rucio/web/rest/main.py +0 -1
  428. rucio/web/rest/metrics.py +0 -1
  429. rucio/web/rest/ping.py +27 -0
  430. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/ldap.cfg.template +1 -1
  431. rucio-35.8.0.data/data/rucio/requirements.server.txt +268 -0
  432. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/bootstrap.py +3 -3
  433. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/merge_rucio_configs.py +2 -5
  434. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/tools/reset_database.py +3 -3
  435. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio +87 -85
  436. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-account +0 -1
  437. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-collection-replica +0 -1
  438. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-abacus-rse +0 -1
  439. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-admin +45 -32
  440. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-atropos +0 -1
  441. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-auditor +13 -7
  442. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-automatix +1 -2
  443. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-bb8 +0 -1
  444. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-c3po +0 -1
  445. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-client +2 -3
  446. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-cache-consumer +0 -1
  447. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-finisher +1 -2
  448. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-poller +0 -1
  449. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-preparer +0 -1
  450. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-receiver +0 -1
  451. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-stager +0 -1
  452. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-submitter +2 -3
  453. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-conveyor-throttler +0 -1
  454. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dark-reaper +0 -1
  455. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-dumper +11 -10
  456. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-follower +0 -1
  457. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-hermes +0 -1
  458. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-cleaner +0 -1
  459. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-evaluator +2 -3
  460. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-injector +0 -1
  461. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-judge-repairer +0 -1
  462. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-kronos +1 -3
  463. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos +0 -1
  464. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-minos-temporary-expiration +0 -1
  465. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-necromancer +1 -2
  466. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-oauth-manager +2 -3
  467. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-reaper +0 -1
  468. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-replica-recoverer +6 -7
  469. rucio-35.8.0.data/scripts/rucio-rse-decommissioner +66 -0
  470. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-storage-consistency-actions +0 -1
  471. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-transmogrifier +0 -1
  472. {rucio-32.8.6.data → rucio-35.8.0.data}/scripts/rucio-undertaker +1 -2
  473. rucio-35.8.0.dist-info/METADATA +72 -0
  474. rucio-35.8.0.dist-info/RECORD +493 -0
  475. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/WHEEL +1 -1
  476. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/AUTHORS.rst +3 -0
  477. rucio/api/temporary_did.py +0 -49
  478. rucio/common/schema/cms.py +0 -478
  479. rucio/common/schema/lsst.py +0 -423
  480. rucio/core/permission/cms.py +0 -1166
  481. rucio/core/temporary_did.py +0 -188
  482. rucio/daemons/reaper/light_reaper.py +0 -255
  483. rucio/web/rest/flaskapi/v1/tmp_dids.py +0 -115
  484. rucio-32.8.6.data/data/rucio/requirements.txt +0 -55
  485. rucio-32.8.6.data/scripts/rucio-light-reaper +0 -53
  486. rucio-32.8.6.dist-info/METADATA +0 -83
  487. rucio-32.8.6.dist-info/RECORD +0 -481
  488. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  489. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  490. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  491. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  492. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  493. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  494. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  495. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  496. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  497. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  498. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  499. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
  500. {rucio-32.8.6.data → rucio-35.8.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
  501. {rucio-32.8.6.dist-info → rucio-35.8.0.dist-info}/licenses/LICENSE +0 -0
  502. {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 declare_suspicious_replicas_bad(once: bool = False, younger_than: int = 3, nattempts: int = 10, vos: Optional[list[str]] = None, limit_suspicious_files_on_rse: int = 5, json_file_name: str = "/opt/rucio/etc/suspicious_replica_recoverer.json", sleep_time: int = 3600, active_mode: bool = False) -> None:
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 surls of these replicas and declares them as bad.
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[list[str]], limit_suspicious_files_on_rse: int, json_file_name: str, active_mode: int, **_kwargs) -> bool:
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 occured while trying to open the JSON file.")
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
- cnt_surl_not_found = 0
179
- cnt_surl_not_found_nattempts_1 = 0
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
- surl_not_found = True
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, 'surl': rep['rses'][rse_][0], 'available_elsewhere': True}
218
- surl_not_found = False
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 surl_not_found:
221
- cnt_surl_not_found += 1
222
- logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no surls were found.', rep_name, rse_expr)
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
- surl_not_found = True
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, 'surl': rep['rses'][rse_id][0], 'available_elsewhere': False}
234
- surl_not_found = False
235
- if surl_not_found:
236
- cnt_surl_not_found += 1
237
- logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no surls were found.', rep_name, rse_expr)
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
- surl_not_found = True
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, 'surl': rep['rses'][rse_][0], 'available_elsewhere': True}
250
- surl_not_found = False
251
- if surl_not_found:
252
- cnt_surl_not_found_nattempts_1 += 1
253
- logger(logging.WARNING, 'Skipping suspicious replica %s on %s, no surls were found.', rep_name, rse_expr)
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) - cnt_surl_not_found,
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) - cnt_surl_not_found_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
- worker_number, total_workers, logger = heartbeat_handler.live()
354
+ _, _, logger = heartbeat_handler.live()
282
355
 
283
356
  logger(logging.INFO, 'Create rules for replicas with nattempts=1.')
284
357
 
285
- for rse_key in replicas_nattempts_1[vo]:
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
- for replica_values in replicas_nattempts_1[vo][rse_key].values():
288
- dids = {'scope': replica_values['scope'], 'name': replica_values['name'], 'rse': rse_key}
289
- dids_nattempts_1.append(dids)
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
- add_rule(dids=dids_nattempts_1, account=InternalAccount('root', vo=vo), copies=nattempts, rse_expression='type=SCRATCHDISK', grouping=None, weight=None, lifetime=24 * 3600, locked=False, subscription_id=None)
295
-
296
- logger(logging.INFO, 'Rules have been created for %i replicas on %s.', len(dids_nattempts_1), rse_key)
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 with nattempts=1.', rse_key)
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
- surls_list = []
438
+ pfns_list = []
310
439
  for replica_value in recoverable_replicas[vo][rse_key].values():
311
- surls_list.append(replica_value['surl'])
440
+ pfns_list.append(replica_value['pfn'])
312
441
 
313
442
  if active_mode:
314
- add_bad_pfns(pfns=surls_list, account=InternalAccount('root', vo=vo), state='TEMPORARY_UNAVAILABLE', expires_at=datetime.utcnow() + timedelta(days=3))
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['surl'])
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
- worker_number, total_workers, logger = heartbeat_handler.live()
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 oher copies on other RSEs (that aren't also marked as suspicious).
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
- file_metadata_datatype = str(file_metadata["datatype"])
368
- file_metadata_scope = str(file_metadata["scope"])
369
- action = ""
370
- if file_metadata_datatype:
371
- # Some files don't have a datatype. They should be ignored.
372
- for policy in json_data:
373
- match_scope = False
374
- match_datatype = False
375
-
376
- if not policy.get("scope", []):
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
- if not policy.get("datatype", []):
384
- match_datatype = True
385
- for datatype in policy.get("datatype", []):
386
- if re.match(datatype, file_metadata_datatype):
387
- match_datatype = True
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
- if match_scope and match_datatype:
391
- action = policy["action"]
392
- logger(logging.INFO, "The action that will be performed is %s", action)
393
- break
394
-
395
- if not action:
396
- logger(logging.WARNING, "No recognised actions (ignore/declare bad) found in policy file (etc/suspicious_replica_recoverer.json). Replica will be ignored by default.")
397
-
398
- if action:
399
- if action == "ignore":
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["surl"])
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["surl"])
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(once: bool = False, younger_than: int = 3, nattempts: int = 10, vos: list[str] = None, limit_suspicious_files_on_rse: int = 5, json_file_name: str = "/opt/rucio/etc/suspicious_replica_recoverer.json", sleep_time: int = 3600, active_mode: bool = False) -> None:
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
  """
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # Copyright European Organization for Nuclear Research (CERN) since 2012
3
2
  #
4
3
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -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))