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
@@ -0,0 +1,24 @@
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
+ """Decommissioning profile definitions."""
16
+
17
+ from .atlas import atlas_move
18
+ from .generic import generic_delete, generic_move
19
+
20
+ PROFILE_MAP = {
21
+ 'generic_delete': generic_delete,
22
+ 'generic_move': generic_move,
23
+ 'atlas_move': atlas_move
24
+ }
@@ -0,0 +1,60 @@
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
+ """ATLAS-specific decommissioning profiles."""
16
+
17
+ import logging
18
+ from typing import TYPE_CHECKING, Any
19
+
20
+ from rucio.core.did import get_metadata
21
+
22
+ from .generic import _call_for_attention, generic_move
23
+
24
+ if TYPE_CHECKING:
25
+ from rucio.common.types import LoggerFunction
26
+
27
+ from .types import DecommissioningProfile
28
+
29
+
30
+ def atlas_move(rse: dict[str, Any], config: dict[str, Any]) -> 'DecommissioningProfile':
31
+ """Return a profile for moving rules that satisfy conditions to a specific destination.
32
+
33
+ The "ATLAS move" profile lists out all rules that are locking replicas
34
+ at the given RSE, and moves them to the specified destination if either
35
+ one of the following is true:
36
+
37
+ - The RSE expression of the rule is trivial (the RSE name itself).
38
+ - There are no replicas locked by the rule that reside on another RSE.
39
+ - The datatype of the DID is not "log".
40
+
41
+ :param rse: RSE to decommission.
42
+ :param config: Decommissioning configuration dictionary.
43
+ :returns: A decommissioning profile dictionary.
44
+ """
45
+ profile = generic_move(rse, config)
46
+ # Insert before the trivial RSE expression handler
47
+ idx = next(pos for pos, handler in enumerate(profile.handlers)
48
+ if handler[0].__name__ == '_has_trivial_rse_expression')
49
+ profile.handlers.insert(idx, (_is_log_file, _call_for_attention))
50
+ return profile
51
+
52
+
53
+ def _is_log_file(
54
+ rule: dict[str, Any],
55
+ rse: dict[str, Any],
56
+ *,
57
+ logger: "LoggerFunction" = logging.log
58
+ ) -> bool:
59
+ """Check if the datatype metadata is 'log'."""
60
+ return get_metadata(rule['scope'], rule['name'])['datatype'] == 'log'
@@ -0,0 +1,451 @@
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
+ """Generic decommissioning profiles."""
16
+ import logging
17
+ from collections.abc import Callable, Iterable
18
+ from datetime import datetime, timedelta
19
+ from typing import TYPE_CHECKING, Any
20
+
21
+ from sqlalchemy.exc import NoResultFound
22
+
23
+ from rucio.common.constants import RseAttr
24
+ from rucio.common.exception import Duplicate, ReplicaNotFound, RequestNotFound, RucioException, RuleNotFound, RuleReplaceFailed, UnsupportedOperation
25
+ from rucio.core.lock import get_replica_locks, get_replica_locks_for_rule_id
26
+ from rucio.core.replica import list_replicas_per_rse, set_tombstone, update_replica_state
27
+ from rucio.core.request import get_request_by_did
28
+ from rucio.core.rse import add_rse_attribute, update_rse
29
+ from rucio.core.rule import list_rules_for_rse_decommissioning, move_rule, update_rule
30
+ from rucio.db.sqla.constants import ReplicaState
31
+
32
+ from .types import DecommissioningProfile, HandlerOutcome
33
+
34
+ if TYPE_CHECKING:
35
+ from rucio.common.types import LoggerFunction
36
+
37
+
38
+ def generic_delete(rse: dict[str, Any], config: dict[str, Any]) -> DecommissioningProfile:
39
+ """Return a profile for simply deleting rules.
40
+
41
+ The "generic delete" profile lists out all rules that are locking replicas at the given RSE,
42
+ and deletes them if either one of the following is true:
43
+
44
+ - The RSE expression of the rule is trivial (the RSE name itself).
45
+ - There are no replicas locked by the rule that reside on another RSE.
46
+
47
+ :param rse: RSE to decommission.
48
+ :param config: Decommissioning configuration dictionary.
49
+ :returns: A decommissioning profile.
50
+ """
51
+ return DecommissioningProfile(
52
+ rse=rse,
53
+ initializer=_generic_initialize,
54
+ discoverer=_generic_discover,
55
+ handlers=[
56
+ (_is_locked, _call_for_attention),
57
+ (_is_being_deleted, _count_as_processed),
58
+ (_has_trivial_rse_expression, _delete_rule),
59
+ (_has_all_replicas_on_rse, _delete_rule),
60
+ ],
61
+ finalizer=_generic_finalize
62
+ )
63
+
64
+
65
+ def generic_move(rse: dict[str, Any], config: dict[str, Any]) -> DecommissioningProfile:
66
+ """Return a profile for moving rules that satisfy conditions to a specific destination.
67
+
68
+ The "generic move" profile lists out all rules that are locking replicas at the given RSE,
69
+ and moves them to the specified destination if either one of the following is true:
70
+
71
+ - The RSE expression of the rule is trivial (the RSE name itself).
72
+ - There are no replicas locked by the rule that reside on another RSE.
73
+
74
+ :param rse: RSE to decommission.
75
+ :param config: Decommissioning configuration dictionary.
76
+ :returns: A decommissioning profile dictionary.
77
+ """
78
+ try:
79
+ destination = config['destination']
80
+ except KeyError as exc:
81
+ raise RucioException('Configuration parameter "destination" not set') from exc
82
+
83
+ move_to_destination = RuleMover(destination)
84
+ return DecommissioningProfile(
85
+ rse=rse,
86
+ initializer=_generic_initialize,
87
+ discoverer=_generic_discover,
88
+ handlers=[
89
+ (_is_locked, _call_for_attention),
90
+ (_is_being_deleted, _count_as_processed),
91
+ (_has_child_rule_id, _count_as_processed),
92
+ (_has_trivial_rse_expression, move_to_destination),
93
+ (_has_all_replicas_on_rse, move_to_destination),
94
+ ],
95
+ finalizer=_generic_finalize
96
+ )
97
+
98
+
99
+ def _generic_initialize(
100
+ rse: dict[str, Any],
101
+ *,
102
+ logger: "LoggerFunction" = logging.log,
103
+ ) -> None:
104
+ """Initializer function that sets the RSE availability flags and the decommissioning status.
105
+
106
+ When an RSE is initialized for decommissioning, the availability flags are set as follows:
107
+
108
+ - `availability_read=True`
109
+ - `availability_write=False`
110
+ - `availability_delete=True`
111
+
112
+ and the following attribute is set:
113
+
114
+ - `greedyDeletion=True`
115
+
116
+ :param rse: RSE table entry as a dictionary.
117
+ :param logger: Logging function.
118
+ """
119
+ logger(logging.INFO,
120
+ '(%s) Setting RSE availability flags of to read !write delete, greedyDeletion=true.',
121
+ rse['rse'])
122
+
123
+ parameters = {
124
+ 'availability_read': True,
125
+ 'availability_write': False,
126
+ 'availability_delete': True
127
+ }
128
+ update_rse(rse['id'], parameters)
129
+
130
+ try:
131
+ add_rse_attribute(rse['id'], RseAttr.GREEDYDELETION, True)
132
+ except Duplicate:
133
+ pass
134
+
135
+
136
+ def _generic_discover(
137
+ rse: dict[str, Any],
138
+ *,
139
+ logger: "LoggerFunction" = logging.log
140
+ ) -> Iterable[dict[str, Any]]:
141
+ """Discoverer function that calls the listing function from core.rule.
142
+
143
+ :param rse: RSE table entry as a dictionary.
144
+ :param logger: Logging function.
145
+ :returns: An iterable of rule dictionaries.
146
+ """
147
+ return list_rules_for_rse_decommissioning(rse['id'])
148
+
149
+
150
+ def _generic_finalize(
151
+ rse: dict[str, Any],
152
+ *,
153
+ logger: "LoggerFunction" = logging.log
154
+ ) -> bool:
155
+ """Check for remaining replicas at the RSE and resolve where possible.
156
+
157
+ If for some reason there are still a large number of replicas remaining at the RSE, this
158
+ function cuts off automatic processing attempts at a hard-coded 100.
159
+
160
+ :param rse: RSE table entry as a dictionary.
161
+ :param logger: Logging function.
162
+ :returns: A boolean flag indicating the completion status.
163
+ """
164
+ num_remaining_replicas = _process_replicas_with_no_locks(rse,
165
+ list_replicas_per_rse(rse['id']),
166
+ limit=100,
167
+ logger=logger)
168
+
169
+ if num_remaining_replicas == 0:
170
+ # The RSE is now really completely cleared.
171
+ logger(logging.INFO,
172
+ '(%s) Completed deleting the RSE contents. Updating the decommissioning status'
173
+ ' to "done".',
174
+ rse['rse'])
175
+ return True
176
+
177
+ logger(logging.WARNING,
178
+ '(%s) %d replicas remain on the RSE even though there are no more rules creating'
179
+ ' locks on them.',
180
+ rse['rse'], num_remaining_replicas)
181
+
182
+ # We now wait for reaper to pick the updated replicas (if there are any). Hopefully all
183
+ # replicas will be deleted at the next cycle.
184
+ return False
185
+
186
+
187
+ def _process_replicas_with_no_locks(
188
+ rse: dict[str, Any],
189
+ replicas: Iterable[dict[str, Any]],
190
+ limit: int = 0,
191
+ *,
192
+ logger: "LoggerFunction" = logging.log,
193
+ ) -> int:
194
+ """Process replicas that remain at the RSE after the decommissioning.
195
+
196
+ Replicas that remain at the RSE at this point should be unlocked. While this is a situation
197
+ that should not happen, it can due to various errors. This function checks each such replica
198
+ and attempt to resolve the invalid state for known cases.
199
+
200
+ :param rse: RSE table entry as a dictionary.
201
+ :param replicas: Sequence or generator of replica objects.
202
+ :param logger: Logging function.
203
+ :returns: Number of processed replicas.
204
+ """
205
+ num_processed = 0
206
+ num_updated = 0
207
+ num_waiting_reap = 0
208
+
209
+ # Check if each replica satisfies the condition in list_and_mark_unlocked_replicas() (in
210
+ # core.replica).
211
+ # For each of the conditions, in case of failure, handle cases that can be handled.
212
+ for replica in replicas:
213
+ num_processed += 1
214
+ if limit > 0 and num_processed == limit:
215
+ break
216
+
217
+ updated = False
218
+
219
+ # If the replica fails any of the following three conditions, reaper won't pick it up.
220
+ reap_pending = True
221
+
222
+ # 1. Condition on the replica state & update time
223
+ in_stable_state = replica['state'] in [ReplicaState.AVAILABLE,
224
+ ReplicaState.UNAVAILABLE,
225
+ ReplicaState.BAD]
226
+
227
+ ten_minutes_ago = datetime.utcnow() - timedelta(seconds=600)
228
+ deleting_stuck = (replica['state'] == ReplicaState.BEING_DELETED
229
+ and replica['updated_at'] < ten_minutes_ago)
230
+
231
+ if not (in_stable_state or deleting_stuck):
232
+ # 1.1 If COPYING without a valid request
233
+ # -> Update the state to UNAVAILABLE
234
+ if replica['state'] == ReplicaState.COPYING:
235
+ # Should we allow requests in certain states? Depends on the details of the
236
+ # request state machine.
237
+ try:
238
+ get_request_by_did(replica['scope'], replica['name'], rse['id'])
239
+ except RequestNotFound:
240
+ logger(logging.INFO,
241
+ '(%s) Replica %s:%s is in COPYING state without a valid request.'
242
+ ' Changing state to UNAVAILABLE.',
243
+ rse['rse'], replica['scope'], replica['name'])
244
+
245
+ try:
246
+ update_replica_state(rse['id'], replica['scope'], replica['name'],
247
+ ReplicaState.UNAVAILABLE)
248
+ except ReplicaNotFound:
249
+ logger(logging.WARNING,
250
+ '(%s) Replica %s:%s disappeared during cleanup',
251
+ rse['rse'], replica['scope'], replica['name'])
252
+ except UnsupportedOperation as error:
253
+ logger(logging.ERROR, '(%s) %s', rse['rse'], str(error))
254
+ else:
255
+ updated = True
256
+
257
+ reap_pending = False
258
+
259
+ # 2. Condition on the lock count
260
+ if replica['lock_cnt'] != 0:
261
+ # 2.1 No actual lock -> Reset the lock count
262
+ try:
263
+ locks = get_replica_locks(replica['scope'], replica['name'],
264
+ restrict_rses=[rse['id']])
265
+ except NoResultFound:
266
+ logger(logging.WARNING,
267
+ '(%s) Replica %s:%s disappeared during cleanup',
268
+ rse['rse'], replica['scope'], replica['name'])
269
+ else:
270
+ if len(locks) == 0:
271
+ logger(logging.WARNING,
272
+ '(%s) Replica %s:%s has lock count %s but zero actual locks. Please'
273
+ ' fix the counts.',
274
+ rse['rse'], replica['scope'], replica['name'], replica['lock_cnt'])
275
+
276
+ reap_pending = False
277
+
278
+ # 3. Tombstone missing or in the future -> RIP now
279
+ if replica['tombstone'] is None or replica['tombstone'] >= datetime.utcnow():
280
+ logger(logging.INFO, '(%s) Marking tombstone of replica %s:%s as UTCNOW.',
281
+ rse['rse'], replica['scope'], replica['name'])
282
+
283
+ try:
284
+ set_tombstone(rse['id'], replica['scope'], replica['name'],
285
+ tombstone=datetime.utcnow())
286
+ except ReplicaNotFound as error:
287
+ logger(logging.WARNING, '(%s) %s', rse['rse'], str(error))
288
+ else:
289
+ updated = True
290
+
291
+ reap_pending = False
292
+
293
+ if updated:
294
+ num_updated += 1
295
+
296
+ if reap_pending:
297
+ num_waiting_reap += 1
298
+
299
+ logger(logging.INFO, '(%s) %s replicas have been updated. %s replicas are pending deletion.'
300
+ ' >=%s replicas remain unhandled.',
301
+ rse['rse'], num_updated, num_waiting_reap,
302
+ num_processed - num_updated - num_waiting_reap)
303
+
304
+ return num_processed
305
+
306
+
307
+ # Condition functions
308
+
309
+ def _is_locked(
310
+ rule: dict[str, Any],
311
+ rse: dict[str, Any],
312
+ *,
313
+ logger: "LoggerFunction" = logging.log
314
+ ) -> bool:
315
+ logger(logging.INFO,
316
+ '(%s) Rule %s for %s:%s is locked',
317
+ rse['rse'], rule['id'], rule['scope'], rule['name'])
318
+ return rule['locked']
319
+
320
+
321
+ def _is_being_deleted(
322
+ rule: dict[str, Any],
323
+ rse: dict[str, Any],
324
+ *,
325
+ logger: "LoggerFunction" = logging.log
326
+ ) -> bool:
327
+ """Check if the rule is already set to be deleted."""
328
+ if rule['expires_at'] is not None and rule['expires_at'] < datetime.utcnow():
329
+ logger(logging.DEBUG,
330
+ '(%s) Rule %s for %s:%s is bound for deletion',
331
+ rse['rse'], rule['id'], rule['scope'], rule['name'])
332
+ return True
333
+ return False
334
+
335
+
336
+ def _has_trivial_rse_expression(
337
+ rule: dict[str, Any],
338
+ rse: dict[str, Any],
339
+ *,
340
+ logger: "LoggerFunction" = logging.log
341
+ ) -> bool:
342
+ """Check for a trivial rse_expression."""
343
+ return rule['rse_expression'] == rse['rse']
344
+
345
+
346
+ def _has_all_replicas_on_rse(
347
+ rule: dict[str, Any],
348
+ rse: dict[str, Any],
349
+ *,
350
+ logger: "LoggerFunction" = logging.log
351
+ ) -> bool:
352
+ """Check if the all the replicas are on the RSE."""
353
+ # Check the list of RSEs that the replicas locked by this rule reside on.
354
+ try:
355
+ locks = get_replica_locks_for_rule_id(rule['id'])
356
+ except NoResultFound:
357
+ # No replica is locked to begin with -> treat as having the last replica on this RSE
358
+ return True
359
+
360
+ replica_rse_ids = set(lock['rse_id'] for lock in locks)
361
+
362
+ return len(replica_rse_ids) == 1 and list(replica_rse_ids)[0] == rse['id']
363
+
364
+
365
+ def _has_child_rule_id(
366
+ rule: dict[str, Any],
367
+ rse: dict[str, Any],
368
+ *,
369
+ logger: "LoggerFunction" = logging.log
370
+ ) -> bool:
371
+ """Check for non-empty child_rule_id."""
372
+ if rule['child_rule_id']:
373
+ logger(logging.DEBUG, '(%s) Rule %s for %s:%s is being moved',
374
+ rse['rse'], rule['id'], rule['scope'], rule['name'])
375
+ return True
376
+ return False
377
+
378
+
379
+ # Action functions
380
+
381
+ def _call_for_attention(
382
+ rule: dict[str, Any],
383
+ rse: dict[str, Any],
384
+ *,
385
+ logger: Callable[..., None] = logging.log
386
+ ) -> HandlerOutcome:
387
+ return HandlerOutcome.NEED_ATTENTION
388
+
389
+
390
+ def _count_as_processed(
391
+ rule: dict[str, Any],
392
+ rse: dict[str, Any],
393
+ *,
394
+ logger: "LoggerFunction" = logging.log
395
+ ) -> HandlerOutcome:
396
+ """Do nothing but increment the limit checker."""
397
+ return HandlerOutcome.UNTOUCHED
398
+
399
+
400
+ def _delete_rule(
401
+ rule: dict[str, Any],
402
+ rse: dict[str, Any],
403
+ *,
404
+ logger: "LoggerFunction" = logging.log
405
+ ) -> HandlerOutcome:
406
+ """Delete the rule."""
407
+ logger(logging.DEBUG, '(%s) Deleting rule %s for %s:%s',
408
+ rse['rse'], rule['id'], rule['scope'], rule['name'])
409
+
410
+ try:
411
+ update_rule(rule['id'], {'lifetime': 0})
412
+ except RuleNotFound:
413
+ logger(logging.WARNING, '(%s) Rule %s for %s:%s disappeared before deleting',
414
+ rse['rse'], rule['id'], rule['scope'], rule['name'])
415
+ return HandlerOutcome.UNTOUCHED
416
+
417
+ return HandlerOutcome.REMOVED
418
+
419
+
420
+ class RuleMover:
421
+ """Callable object with a destination attribute."""
422
+ def __init__(self, destination: str) -> None:
423
+ self.destination = destination
424
+
425
+ def __call__(
426
+ self,
427
+ rule: dict[str, Any],
428
+ rse: dict[str, Any],
429
+ *,
430
+ logger: "LoggerFunction" = logging.log
431
+ ) -> HandlerOutcome:
432
+ """Move the rule."""
433
+ # Move the rule.
434
+ logger(logging.DEBUG, '(%s) Moving rule %s for %s:%s to %s',
435
+ rse['rse'], rule['id'], rule['scope'], rule['name'],
436
+ self.destination)
437
+
438
+ try:
439
+ move_rule(rule['id'], self.destination,
440
+ override={'weight': None, 'source_replica_expression': None})
441
+ except RuleNotFound:
442
+ logger(logging.WARNING, '(%s) Rule %s for %s:%s disappeared before moving',
443
+ rse['rse'], rule['id'], rule['scope'], rule['name'])
444
+ return HandlerOutcome.UNTOUCHED
445
+ except RuleReplaceFailed:
446
+ logger(logging.ERROR,
447
+ '(%s) Failed to move rule %s for %s:%s to %s',
448
+ rse['rse'], rule['id'], rule['scope'], rule['name'],
449
+ self.destination)
450
+ return HandlerOutcome.NEED_ATTENTION
451
+ return HandlerOutcome.REMOVED
@@ -0,0 +1,92 @@
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
+ """Types used for profile definitions."""
16
+ import logging
17
+ from collections.abc import Callable, Iterable
18
+ from dataclasses import dataclass
19
+ from enum import Enum
20
+ from typing import TYPE_CHECKING, Any
21
+
22
+ if TYPE_CHECKING:
23
+ from rucio.common.types import LoggerFunction
24
+
25
+
26
+ class HandlerOutcome(Enum):
27
+ """Possible outcomes of the handler functions."""
28
+ UNTOUCHED = "Untouched"
29
+ REMOVED = "Removed"
30
+ NEED_ATTENTION = "NeedAttention"
31
+
32
+
33
+ @dataclass
34
+ class DecommissioningProfile:
35
+ """Collection of functions that define the action of the decommissioning daemon on an RSE.
36
+
37
+ :param rse: RSE to decommission.
38
+ :param initializer: Profile initialization function.
39
+ :param discoverer: Function to find and list the rules to process.
40
+ :param handlers: A list of (condition, action) functions.
41
+ :param finalizer: Finalization function.
42
+ """
43
+
44
+ rse: dict[str, Any]
45
+ initializer: Callable[..., None]
46
+ discoverer: Callable[..., Iterable[dict[str, Any]]]
47
+ handlers: list[tuple[Callable[..., bool], Callable[..., HandlerOutcome]]]
48
+ finalizer: Callable[..., bool]
49
+
50
+ def initialize(
51
+ self,
52
+ *,
53
+ logger: "LoggerFunction" = logging.log
54
+ ) -> None:
55
+ """Call the initializer."""
56
+ self.initializer(self.rse, logger=logger)
57
+
58
+ def discover(
59
+ self,
60
+ *,
61
+ logger: "LoggerFunction" = logging.log
62
+ ) -> Iterable[dict[str, Any]]:
63
+ """Call the discoverer."""
64
+ return self.discoverer(self.rse, logger=logger)
65
+
66
+ def process(
67
+ self,
68
+ rule: dict[str, Any],
69
+ *,
70
+ logger: "LoggerFunction" = logging.log
71
+ ) -> HandlerOutcome:
72
+ """Process a rule.
73
+
74
+ :param rule: Rule dict.
75
+ :returns: Boolean indicating whether the rule was removed. None if no condition matches.
76
+ """
77
+ for condition, action in self.handlers:
78
+ if condition(rule, self.rse, logger=logger):
79
+ return action(rule, self.rse, logger=logger)
80
+
81
+ logger(logging.INFO,
82
+ '(%s) No handler matched rule %s for %s:%s',
83
+ rule['rse'], rule['id'], rule['scope'], rule['name'])
84
+ return HandlerOutcome.NEED_ATTENTION
85
+
86
+ def finalize(
87
+ self,
88
+ *,
89
+ logger: "LoggerFunction" = logging.log
90
+ ) -> bool:
91
+ """Call the finalizer."""
92
+ return self.finalizer(self.rse, logger=logger)