rucio 37.0.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rucio might be problematic. Click here for more details.

Files changed (487) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/cli/__init__.py +14 -0
  4. rucio/cli/account.py +216 -0
  5. rucio/cli/bin_legacy/__init__.py +13 -0
  6. rucio/cli/bin_legacy/rucio.py +2825 -0
  7. rucio/cli/bin_legacy/rucio_admin.py +2500 -0
  8. rucio/cli/command.py +272 -0
  9. rucio/cli/config.py +72 -0
  10. rucio/cli/did.py +191 -0
  11. rucio/cli/download.py +128 -0
  12. rucio/cli/lifetime_exception.py +33 -0
  13. rucio/cli/replica.py +162 -0
  14. rucio/cli/rse.py +293 -0
  15. rucio/cli/rule.py +158 -0
  16. rucio/cli/scope.py +40 -0
  17. rucio/cli/subscription.py +73 -0
  18. rucio/cli/upload.py +60 -0
  19. rucio/cli/utils.py +226 -0
  20. rucio/client/__init__.py +15 -0
  21. rucio/client/accountclient.py +432 -0
  22. rucio/client/accountlimitclient.py +183 -0
  23. rucio/client/baseclient.py +983 -0
  24. rucio/client/client.py +120 -0
  25. rucio/client/configclient.py +126 -0
  26. rucio/client/credentialclient.py +59 -0
  27. rucio/client/didclient.py +868 -0
  28. rucio/client/diracclient.py +56 -0
  29. rucio/client/downloadclient.py +1783 -0
  30. rucio/client/exportclient.py +44 -0
  31. rucio/client/fileclient.py +50 -0
  32. rucio/client/importclient.py +42 -0
  33. rucio/client/lifetimeclient.py +90 -0
  34. rucio/client/lockclient.py +109 -0
  35. rucio/client/metaconventionsclient.py +140 -0
  36. rucio/client/pingclient.py +44 -0
  37. rucio/client/replicaclient.py +452 -0
  38. rucio/client/requestclient.py +125 -0
  39. rucio/client/richclient.py +317 -0
  40. rucio/client/rseclient.py +746 -0
  41. rucio/client/ruleclient.py +294 -0
  42. rucio/client/scopeclient.py +90 -0
  43. rucio/client/subscriptionclient.py +173 -0
  44. rucio/client/touchclient.py +82 -0
  45. rucio/client/uploadclient.py +969 -0
  46. rucio/common/__init__.py +13 -0
  47. rucio/common/bittorrent.py +234 -0
  48. rucio/common/cache.py +111 -0
  49. rucio/common/checksum.py +168 -0
  50. rucio/common/client.py +122 -0
  51. rucio/common/config.py +788 -0
  52. rucio/common/constants.py +217 -0
  53. rucio/common/constraints.py +17 -0
  54. rucio/common/didtype.py +237 -0
  55. rucio/common/dumper/__init__.py +342 -0
  56. rucio/common/dumper/consistency.py +497 -0
  57. rucio/common/dumper/data_models.py +362 -0
  58. rucio/common/dumper/path_parsing.py +75 -0
  59. rucio/common/exception.py +1208 -0
  60. rucio/common/extra.py +31 -0
  61. rucio/common/logging.py +420 -0
  62. rucio/common/pcache.py +1409 -0
  63. rucio/common/plugins.py +185 -0
  64. rucio/common/policy.py +93 -0
  65. rucio/common/schema/__init__.py +200 -0
  66. rucio/common/schema/generic.py +416 -0
  67. rucio/common/schema/generic_multi_vo.py +395 -0
  68. rucio/common/stomp_utils.py +423 -0
  69. rucio/common/stopwatch.py +55 -0
  70. rucio/common/test_rucio_server.py +154 -0
  71. rucio/common/types.py +483 -0
  72. rucio/common/utils.py +1688 -0
  73. rucio/core/__init__.py +13 -0
  74. rucio/core/account.py +496 -0
  75. rucio/core/account_counter.py +236 -0
  76. rucio/core/account_limit.py +425 -0
  77. rucio/core/authentication.py +620 -0
  78. rucio/core/config.py +437 -0
  79. rucio/core/credential.py +224 -0
  80. rucio/core/did.py +3004 -0
  81. rucio/core/did_meta_plugins/__init__.py +252 -0
  82. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  83. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  84. rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
  85. rucio/core/did_meta_plugins/filter_engine.py +672 -0
  86. rucio/core/did_meta_plugins/json_meta.py +240 -0
  87. rucio/core/did_meta_plugins/mongo_meta.py +229 -0
  88. rucio/core/did_meta_plugins/postgres_meta.py +352 -0
  89. rucio/core/dirac.py +237 -0
  90. rucio/core/distance.py +187 -0
  91. rucio/core/exporter.py +59 -0
  92. rucio/core/heartbeat.py +363 -0
  93. rucio/core/identity.py +301 -0
  94. rucio/core/importer.py +260 -0
  95. rucio/core/lifetime_exception.py +377 -0
  96. rucio/core/lock.py +577 -0
  97. rucio/core/message.py +288 -0
  98. rucio/core/meta_conventions.py +203 -0
  99. rucio/core/monitor.py +448 -0
  100. rucio/core/naming_convention.py +195 -0
  101. rucio/core/nongrid_trace.py +136 -0
  102. rucio/core/oidc.py +1463 -0
  103. rucio/core/permission/__init__.py +161 -0
  104. rucio/core/permission/generic.py +1124 -0
  105. rucio/core/permission/generic_multi_vo.py +1144 -0
  106. rucio/core/quarantined_replica.py +224 -0
  107. rucio/core/replica.py +4483 -0
  108. rucio/core/replica_sorter.py +362 -0
  109. rucio/core/request.py +3091 -0
  110. rucio/core/rse.py +2079 -0
  111. rucio/core/rse_counter.py +185 -0
  112. rucio/core/rse_expression_parser.py +459 -0
  113. rucio/core/rse_selector.py +304 -0
  114. rucio/core/rule.py +4484 -0
  115. rucio/core/rule_grouping.py +1620 -0
  116. rucio/core/scope.py +181 -0
  117. rucio/core/subscription.py +362 -0
  118. rucio/core/topology.py +490 -0
  119. rucio/core/trace.py +375 -0
  120. rucio/core/transfer.py +1531 -0
  121. rucio/core/vo.py +169 -0
  122. rucio/core/volatile_replica.py +151 -0
  123. rucio/daemons/__init__.py +13 -0
  124. rucio/daemons/abacus/__init__.py +13 -0
  125. rucio/daemons/abacus/account.py +116 -0
  126. rucio/daemons/abacus/collection_replica.py +124 -0
  127. rucio/daemons/abacus/rse.py +117 -0
  128. rucio/daemons/atropos/__init__.py +13 -0
  129. rucio/daemons/atropos/atropos.py +242 -0
  130. rucio/daemons/auditor/__init__.py +289 -0
  131. rucio/daemons/auditor/hdfs.py +97 -0
  132. rucio/daemons/auditor/srmdumps.py +355 -0
  133. rucio/daemons/automatix/__init__.py +13 -0
  134. rucio/daemons/automatix/automatix.py +304 -0
  135. rucio/daemons/badreplicas/__init__.py +13 -0
  136. rucio/daemons/badreplicas/minos.py +322 -0
  137. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  138. rucio/daemons/badreplicas/necromancer.py +196 -0
  139. rucio/daemons/bb8/__init__.py +13 -0
  140. rucio/daemons/bb8/bb8.py +353 -0
  141. rucio/daemons/bb8/common.py +759 -0
  142. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  143. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  144. rucio/daemons/cache/__init__.py +13 -0
  145. rucio/daemons/cache/consumer.py +133 -0
  146. rucio/daemons/common.py +405 -0
  147. rucio/daemons/conveyor/__init__.py +13 -0
  148. rucio/daemons/conveyor/common.py +562 -0
  149. rucio/daemons/conveyor/finisher.py +529 -0
  150. rucio/daemons/conveyor/poller.py +394 -0
  151. rucio/daemons/conveyor/preparer.py +205 -0
  152. rucio/daemons/conveyor/receiver.py +179 -0
  153. rucio/daemons/conveyor/stager.py +133 -0
  154. rucio/daemons/conveyor/submitter.py +403 -0
  155. rucio/daemons/conveyor/throttler.py +532 -0
  156. rucio/daemons/follower/__init__.py +13 -0
  157. rucio/daemons/follower/follower.py +101 -0
  158. rucio/daemons/hermes/__init__.py +13 -0
  159. rucio/daemons/hermes/hermes.py +534 -0
  160. rucio/daemons/judge/__init__.py +13 -0
  161. rucio/daemons/judge/cleaner.py +159 -0
  162. rucio/daemons/judge/evaluator.py +185 -0
  163. rucio/daemons/judge/injector.py +162 -0
  164. rucio/daemons/judge/repairer.py +154 -0
  165. rucio/daemons/oauthmanager/__init__.py +13 -0
  166. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  167. rucio/daemons/reaper/__init__.py +13 -0
  168. rucio/daemons/reaper/dark_reaper.py +282 -0
  169. rucio/daemons/reaper/reaper.py +739 -0
  170. rucio/daemons/replicarecoverer/__init__.py +13 -0
  171. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  172. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  173. rucio/daemons/rsedecommissioner/config.py +81 -0
  174. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  175. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  176. rucio/daemons/rsedecommissioner/profiles/generic.py +452 -0
  177. rucio/daemons/rsedecommissioner/profiles/types.py +93 -0
  178. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  179. rucio/daemons/storage/__init__.py +13 -0
  180. rucio/daemons/storage/consistency/__init__.py +13 -0
  181. rucio/daemons/storage/consistency/actions.py +848 -0
  182. rucio/daemons/tracer/__init__.py +13 -0
  183. rucio/daemons/tracer/kronos.py +511 -0
  184. rucio/daemons/transmogrifier/__init__.py +13 -0
  185. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  186. rucio/daemons/undertaker/__init__.py +13 -0
  187. rucio/daemons/undertaker/undertaker.py +137 -0
  188. rucio/db/__init__.py +13 -0
  189. rucio/db/sqla/__init__.py +52 -0
  190. rucio/db/sqla/constants.py +206 -0
  191. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  192. rucio/db/sqla/migrate_repo/env.py +110 -0
  193. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  194. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  195. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  196. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  197. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  198. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  199. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  200. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  201. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  202. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  203. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  204. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  205. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  206. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  207. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  208. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  209. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  210. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  211. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  212. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  213. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  214. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  215. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  216. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  217. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  218. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  219. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  220. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  221. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  222. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  223. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  224. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  225. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  226. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  227. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  228. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  229. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  230. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  231. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  232. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  233. rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
  234. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  235. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  236. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  237. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  238. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  239. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  240. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  241. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  242. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  243. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  244. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  245. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  246. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  247. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  248. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  249. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  251. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  252. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  254. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  255. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  256. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  257. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  258. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  259. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  260. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  261. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  262. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  263. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  264. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  265. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  266. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  267. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  268. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  269. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  270. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  271. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  272. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  273. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  274. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  275. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  276. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  278. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  279. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  280. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  281. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  282. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  283. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  284. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  285. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  286. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  287. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  288. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  289. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  290. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  291. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  292. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  293. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  294. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  296. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  297. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  298. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  299. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  300. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  301. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  302. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  303. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  304. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  305. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  306. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  307. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  308. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  309. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  310. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  311. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  312. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  313. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  314. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  316. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  317. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  318. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  319. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  320. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  321. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  322. rucio/db/sqla/models.py +1743 -0
  323. rucio/db/sqla/sautils.py +55 -0
  324. rucio/db/sqla/session.py +529 -0
  325. rucio/db/sqla/types.py +206 -0
  326. rucio/db/sqla/util.py +543 -0
  327. rucio/gateway/__init__.py +13 -0
  328. rucio/gateway/account.py +345 -0
  329. rucio/gateway/account_limit.py +363 -0
  330. rucio/gateway/authentication.py +381 -0
  331. rucio/gateway/config.py +227 -0
  332. rucio/gateway/credential.py +70 -0
  333. rucio/gateway/did.py +987 -0
  334. rucio/gateway/dirac.py +83 -0
  335. rucio/gateway/exporter.py +60 -0
  336. rucio/gateway/heartbeat.py +76 -0
  337. rucio/gateway/identity.py +189 -0
  338. rucio/gateway/importer.py +46 -0
  339. rucio/gateway/lifetime_exception.py +121 -0
  340. rucio/gateway/lock.py +153 -0
  341. rucio/gateway/meta_conventions.py +98 -0
  342. rucio/gateway/permission.py +74 -0
  343. rucio/gateway/quarantined_replica.py +79 -0
  344. rucio/gateway/replica.py +538 -0
  345. rucio/gateway/request.py +330 -0
  346. rucio/gateway/rse.py +632 -0
  347. rucio/gateway/rule.py +437 -0
  348. rucio/gateway/scope.py +100 -0
  349. rucio/gateway/subscription.py +280 -0
  350. rucio/gateway/vo.py +126 -0
  351. rucio/rse/__init__.py +96 -0
  352. rucio/rse/protocols/__init__.py +13 -0
  353. rucio/rse/protocols/bittorrent.py +194 -0
  354. rucio/rse/protocols/cache.py +111 -0
  355. rucio/rse/protocols/dummy.py +100 -0
  356. rucio/rse/protocols/gfal.py +708 -0
  357. rucio/rse/protocols/globus.py +243 -0
  358. rucio/rse/protocols/http_cache.py +82 -0
  359. rucio/rse/protocols/mock.py +123 -0
  360. rucio/rse/protocols/ngarc.py +209 -0
  361. rucio/rse/protocols/posix.py +250 -0
  362. rucio/rse/protocols/protocol.py +361 -0
  363. rucio/rse/protocols/rclone.py +365 -0
  364. rucio/rse/protocols/rfio.py +145 -0
  365. rucio/rse/protocols/srm.py +338 -0
  366. rucio/rse/protocols/ssh.py +414 -0
  367. rucio/rse/protocols/storm.py +195 -0
  368. rucio/rse/protocols/webdav.py +594 -0
  369. rucio/rse/protocols/xrootd.py +302 -0
  370. rucio/rse/rsemanager.py +881 -0
  371. rucio/rse/translation.py +260 -0
  372. rucio/tests/__init__.py +13 -0
  373. rucio/tests/common.py +280 -0
  374. rucio/tests/common_server.py +149 -0
  375. rucio/transfertool/__init__.py +13 -0
  376. rucio/transfertool/bittorrent.py +200 -0
  377. rucio/transfertool/bittorrent_driver.py +50 -0
  378. rucio/transfertool/bittorrent_driver_qbittorrent.py +134 -0
  379. rucio/transfertool/fts3.py +1600 -0
  380. rucio/transfertool/fts3_plugins.py +152 -0
  381. rucio/transfertool/globus.py +201 -0
  382. rucio/transfertool/globus_library.py +181 -0
  383. rucio/transfertool/mock.py +89 -0
  384. rucio/transfertool/transfertool.py +221 -0
  385. rucio/vcsversion.py +11 -0
  386. rucio/version.py +45 -0
  387. rucio/web/__init__.py +13 -0
  388. rucio/web/rest/__init__.py +13 -0
  389. rucio/web/rest/flaskapi/__init__.py +13 -0
  390. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  391. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  392. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  393. rucio/web/rest/flaskapi/v1/accounts.py +1103 -0
  394. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  395. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  396. rucio/web/rest/flaskapi/v1/common.py +426 -0
  397. rucio/web/rest/flaskapi/v1/config.py +304 -0
  398. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  399. rucio/web/rest/flaskapi/v1/dids.py +2340 -0
  400. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  401. rucio/web/rest/flaskapi/v1/export.py +75 -0
  402. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  403. rucio/web/rest/flaskapi/v1/identities.py +285 -0
  404. rucio/web/rest/flaskapi/v1/import.py +132 -0
  405. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  406. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  407. rucio/web/rest/flaskapi/v1/main.py +91 -0
  408. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  409. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  410. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  411. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  412. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  413. rucio/web/rest/flaskapi/v1/replicas.py +1894 -0
  414. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  415. rucio/web/rest/flaskapi/v1/rses.py +2250 -0
  416. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  417. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  418. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  419. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  420. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  421. rucio/web/rest/flaskapi/v1/traces.py +137 -0
  422. rucio/web/rest/flaskapi/v1/types.py +20 -0
  423. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  424. rucio/web/rest/main.py +18 -0
  425. rucio/web/rest/metrics.py +27 -0
  426. rucio/web/rest/ping.py +27 -0
  427. rucio-37.0.0rc1.data/data/rucio/etc/alembic.ini.template +71 -0
  428. rucio-37.0.0rc1.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  429. rucio-37.0.0rc1.data/data/rucio/etc/globus-config.yml.template +5 -0
  430. rucio-37.0.0rc1.data/data/rucio/etc/ldap.cfg.template +30 -0
  431. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  432. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  433. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  434. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  435. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  436. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  437. rucio-37.0.0rc1.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  438. rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.atlas.client.template +43 -0
  439. rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.template +241 -0
  440. rucio-37.0.0rc1.data/data/rucio/etc/rucio_multi_vo.cfg.template +217 -0
  441. rucio-37.0.0rc1.data/data/rucio/requirements.server.txt +297 -0
  442. rucio-37.0.0rc1.data/data/rucio/tools/bootstrap.py +34 -0
  443. rucio-37.0.0rc1.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  444. rucio-37.0.0rc1.data/data/rucio/tools/reset_database.py +40 -0
  445. rucio-37.0.0rc1.data/scripts/rucio +133 -0
  446. rucio-37.0.0rc1.data/scripts/rucio-abacus-account +74 -0
  447. rucio-37.0.0rc1.data/scripts/rucio-abacus-collection-replica +46 -0
  448. rucio-37.0.0rc1.data/scripts/rucio-abacus-rse +78 -0
  449. rucio-37.0.0rc1.data/scripts/rucio-admin +97 -0
  450. rucio-37.0.0rc1.data/scripts/rucio-atropos +60 -0
  451. rucio-37.0.0rc1.data/scripts/rucio-auditor +206 -0
  452. rucio-37.0.0rc1.data/scripts/rucio-automatix +50 -0
  453. rucio-37.0.0rc1.data/scripts/rucio-bb8 +57 -0
  454. rucio-37.0.0rc1.data/scripts/rucio-cache-client +141 -0
  455. rucio-37.0.0rc1.data/scripts/rucio-cache-consumer +42 -0
  456. rucio-37.0.0rc1.data/scripts/rucio-conveyor-finisher +58 -0
  457. rucio-37.0.0rc1.data/scripts/rucio-conveyor-poller +66 -0
  458. rucio-37.0.0rc1.data/scripts/rucio-conveyor-preparer +37 -0
  459. rucio-37.0.0rc1.data/scripts/rucio-conveyor-receiver +44 -0
  460. rucio-37.0.0rc1.data/scripts/rucio-conveyor-stager +76 -0
  461. rucio-37.0.0rc1.data/scripts/rucio-conveyor-submitter +139 -0
  462. rucio-37.0.0rc1.data/scripts/rucio-conveyor-throttler +104 -0
  463. rucio-37.0.0rc1.data/scripts/rucio-dark-reaper +53 -0
  464. rucio-37.0.0rc1.data/scripts/rucio-dumper +160 -0
  465. rucio-37.0.0rc1.data/scripts/rucio-follower +44 -0
  466. rucio-37.0.0rc1.data/scripts/rucio-hermes +54 -0
  467. rucio-37.0.0rc1.data/scripts/rucio-judge-cleaner +89 -0
  468. rucio-37.0.0rc1.data/scripts/rucio-judge-evaluator +137 -0
  469. rucio-37.0.0rc1.data/scripts/rucio-judge-injector +44 -0
  470. rucio-37.0.0rc1.data/scripts/rucio-judge-repairer +44 -0
  471. rucio-37.0.0rc1.data/scripts/rucio-kronos +44 -0
  472. rucio-37.0.0rc1.data/scripts/rucio-minos +53 -0
  473. rucio-37.0.0rc1.data/scripts/rucio-minos-temporary-expiration +50 -0
  474. rucio-37.0.0rc1.data/scripts/rucio-necromancer +120 -0
  475. rucio-37.0.0rc1.data/scripts/rucio-oauth-manager +63 -0
  476. rucio-37.0.0rc1.data/scripts/rucio-reaper +83 -0
  477. rucio-37.0.0rc1.data/scripts/rucio-replica-recoverer +248 -0
  478. rucio-37.0.0rc1.data/scripts/rucio-rse-decommissioner +66 -0
  479. rucio-37.0.0rc1.data/scripts/rucio-storage-consistency-actions +74 -0
  480. rucio-37.0.0rc1.data/scripts/rucio-transmogrifier +77 -0
  481. rucio-37.0.0rc1.data/scripts/rucio-undertaker +76 -0
  482. rucio-37.0.0rc1.dist-info/METADATA +92 -0
  483. rucio-37.0.0rc1.dist-info/RECORD +487 -0
  484. rucio-37.0.0rc1.dist-info/WHEEL +5 -0
  485. rucio-37.0.0rc1.dist-info/licenses/AUTHORS.rst +100 -0
  486. rucio-37.0.0rc1.dist-info/licenses/LICENSE +201 -0
  487. rucio-37.0.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,759 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import logging
16
+ from datetime import date, datetime, timedelta
17
+ from string import Template
18
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
19
+
20
+ from requests import get
21
+ from sqlalchemy import BigInteger, and_, case, cast, false, func, or_, select
22
+ from sqlalchemy.orm import Session, aliased
23
+
24
+ from rucio.common.config import config_get, config_get_bool, config_get_int
25
+ from rucio.common.exception import (
26
+ DuplicateRule,
27
+ InsufficientAccountLimit,
28
+ InsufficientTargetRSEs,
29
+ RuleNotFound,
30
+ )
31
+ from rucio.common.types import InternalAccount, InternalScope
32
+ from rucio.core.lock import get_dataset_locks
33
+ from rucio.core.rse import get_rse_name, get_rse_vo, list_rse_attributes
34
+ from rucio.core.rse_expression_parser import parse_expression
35
+ from rucio.core.rse_selector import RSESelector
36
+ from rucio.core.rule import add_rule, get_rule, update_rule
37
+ from rucio.db.sqla import models
38
+ from rucio.db.sqla.constants import DIDType, LockState, RuleGrouping, RuleState
39
+ from rucio.db.sqla.session import read_session, transactional_session
40
+
41
+ if TYPE_CHECKING:
42
+ from collections.abc import Mapping, Sequence
43
+
44
+ from sqlalchemy.engine import Row
45
+
46
+ from rucio.common.types import LoggerFunction
47
+
48
+
49
+ @transactional_session
50
+ def rebalance_rule(
51
+ parent_rule: "Mapping[str, Any]",
52
+ activity: str,
53
+ rse_expression: str,
54
+ priority: int,
55
+ source_replica_expression: str = "*\\bb8-enabled=false",
56
+ comment: Optional[str] = None,
57
+ *,
58
+ session: Session,
59
+ ) -> Optional[list[str]]:
60
+ """
61
+ Rebalance a replication rule to a new RSE
62
+ :param parent_rule: Replication rule to be rebalanced.
63
+ :param activity: Activity to be used for the rebalancing.
64
+ :param rse_expression: RSE expression of the new rule.
65
+ :param priority: Priority of the newly created rule.
66
+ :param source_replica_expression: Source replica expression of the new rule.
67
+ :param comment: Comment to set on the new rules.
68
+ :returns: The new child rule id.
69
+ """
70
+
71
+ if parent_rule["expires_at"] is None:
72
+ lifetime = None
73
+ else:
74
+ lifetime = (parent_rule["expires_at"] - datetime.utcnow()).days * 24 * 3600 + (
75
+ parent_rule["expires_at"] - datetime.utcnow()
76
+ ).seconds
77
+
78
+ if parent_rule["grouping"] == RuleGrouping.ALL:
79
+ grouping = "ALL"
80
+ elif parent_rule["grouping"] == RuleGrouping.NONE:
81
+ grouping = "NONE"
82
+ else:
83
+ grouping = "DATASET"
84
+
85
+ # check if concurrent replica at target rse does not exist
86
+ concurrent_replica = False
87
+ for lock in get_dataset_locks(parent_rule["scope"], parent_rule["name"]):
88
+ lock_rse_expr = lock["rse"]
89
+ if lock_rse_expr == rse_expression:
90
+ concurrent_replica = True
91
+
92
+ if concurrent_replica:
93
+ return None
94
+
95
+ child_rule = add_rule(
96
+ dids=[{"scope": parent_rule["scope"], "name": parent_rule["name"]}],
97
+ account=parent_rule["account"],
98
+ copies=parent_rule["copies"],
99
+ rse_expression=rse_expression,
100
+ grouping=grouping,
101
+ weight=parent_rule["weight"],
102
+ lifetime=lifetime,
103
+ locked=parent_rule["locked"],
104
+ subscription_id=parent_rule["subscription_id"],
105
+ source_replica_expression=source_replica_expression,
106
+ activity=activity,
107
+ notify=parent_rule["notification"],
108
+ purge_replicas=parent_rule["purge_replicas"],
109
+ ignore_availability=False,
110
+ comment=parent_rule["comments"] if not comment else comment,
111
+ ask_approval=False,
112
+ asynchronous=False,
113
+ ignore_account_limit=True,
114
+ priority=priority,
115
+ session=session,
116
+ )[0]
117
+
118
+ update_rule(
119
+ rule_id=parent_rule["id"],
120
+ options={"child_rule_id": child_rule, "lifetime": 0},
121
+ session=session,
122
+ )
123
+ return child_rule
124
+
125
+
126
+ def __dump_url(
127
+ rse_id: str,
128
+ logger: "LoggerFunction" = logging.log
129
+ ) -> Union[list[str], Literal[False]]:
130
+ """
131
+ getting potential urls of the dump over last week
132
+ :param rse_id: RSE where the dump is released.
133
+ :param logger: Logger.
134
+ """
135
+
136
+ rse = get_rse_name(rse_id=rse_id)
137
+ vo = get_rse_vo(rse_id=rse_id)
138
+
139
+ # get the date of the most recent dump
140
+ today = date.today()
141
+ dump_dates = []
142
+ dump_production_day = config_get(
143
+ "bb8", "dump_production_day", raise_exception=False, default=None
144
+ )
145
+ if dump_production_day is None:
146
+ for idx in range(0, 7):
147
+ dump_date = today - timedelta(idx)
148
+ dump_dates.append(dump_date.strftime("%d-%m-%Y"))
149
+ else:
150
+ weekdays = {
151
+ "Sunday": 6,
152
+ "Monday": 0,
153
+ "Tuesday": 1,
154
+ "Wednesday": 2,
155
+ "Thursday": 3,
156
+ "Friday": 4,
157
+ "Saturday": 5,
158
+ }
159
+ if dump_production_day not in weekdays:
160
+ logger(
161
+ logging.WARNING,
162
+ "ERROR: please set the day of a dump creation in bb8 config correctly, e.g. Monday",
163
+ )
164
+ return False
165
+ today_idx = (today.weekday() - weekdays[dump_production_day]) % 7
166
+ dump_date = today - timedelta(today_idx)
167
+ dump_dates = [dump_date.strftime("%d-%m-%Y")]
168
+
169
+ # getting structure (template) of url location of a dump
170
+ url_template_str = config_get(
171
+ "bb8",
172
+ "dump_url_template",
173
+ raise_exception=False,
174
+ )
175
+ url_template = Template(url_template_str)
176
+
177
+ # populating url template
178
+ urls = []
179
+ for d in dump_dates:
180
+ url = url_template.substitute({"date": d, "rse": rse, "vo": vo})
181
+ urls.append(url)
182
+ return urls
183
+
184
+
185
+ def _list_rebalance_rule_candidates_dump(
186
+ rse_id: str,
187
+ mode: Optional[str] = None,
188
+ logger: "LoggerFunction" = logging.log
189
+ ) -> list[tuple]:
190
+ """
191
+ Download dump to temporary directory
192
+ :param rse_id: RSE of the source.
193
+ :param mode: Rebalancing mode.
194
+ :param logger: Logger.
195
+ """
196
+
197
+ # fetching the dump
198
+ candidates = []
199
+ rules = {}
200
+ rse_dump_urls = __dump_url(rse_id=rse_id)
201
+ resp = None
202
+ if not rse_dump_urls:
203
+ logger(logging.DEBUG, "URL of the dump was not built from template.")
204
+ return candidates
205
+ rse_dump_urls.reverse()
206
+ success = False
207
+ while not success and len(rse_dump_urls):
208
+ url = rse_dump_urls.pop()
209
+ resp = get(url, stream=True)
210
+ if resp:
211
+ success = True
212
+ if not resp or resp is None:
213
+ logger(logging.WARNING, "RSE dump not available")
214
+ return candidates
215
+
216
+ # looping over the dump and selecting the rules
217
+ for line in resp.iter_lines():
218
+ if line:
219
+ _, _, rule_id, rse_expression, account, file_size, state = line.split("\t")
220
+ if rule_id not in rules:
221
+ rule_info = {}
222
+ try:
223
+ rule_info = get_rule(rule_id=rule_id)
224
+ except Exception as err:
225
+ rules[rule_id] = {"state": "DELETED"}
226
+ logger(logging.ERROR, str(err))
227
+ continue
228
+ if rule_info["child_rule_id"]:
229
+ rules[rule_id] = {"state": "DELETED"}
230
+ continue
231
+ rules[rule_id] = {
232
+ "scope": rule_info["scope"],
233
+ "name": rule_info["name"],
234
+ "rse_expression": rse_expression,
235
+ "subscription_id": rule_info["subscription_id"],
236
+ "length": 1,
237
+ "state": "ACTIVE",
238
+ "bytes": int(file_size),
239
+ }
240
+ elif rules[rule_id]["state"] == "ACTIVE":
241
+ rules[rule_id]["length"] += 1
242
+ rules[rule_id]["bytes"] += int(file_size)
243
+
244
+ # looping over agragated rules collected from dump
245
+ for r_id in rules:
246
+ if mode == "decommission": # other modes can be added later
247
+ if rules[r_id]["state"] == "DELETED":
248
+ continue
249
+ if int(rules[r_id]["length"]) == 0:
250
+ continue
251
+ candidates.append(
252
+ (
253
+ rules[r_id]["scope"],
254
+ rules[r_id]["name"],
255
+ r_id,
256
+ rules[r_id]["rse_expression"],
257
+ rules[r_id]["subscription_id"],
258
+ rules[r_id]["bytes"],
259
+ rules[r_id]["length"],
260
+ int(rules[r_id]["bytes"] / rules[r_id]["length"]),
261
+ )
262
+ )
263
+ return candidates
264
+
265
+
266
+ @transactional_session
267
+ def list_rebalance_rule_candidates(
268
+ rse_id: str,
269
+ mode: Optional[str] = None,
270
+ *,
271
+ session: Optional[Session] = None
272
+ ) -> Union[list[tuple], list["Row[tuple]"]]:
273
+ """
274
+ List the rebalance rule candidates based on the agreed on specification
275
+ :param rse_id: RSE of the source.
276
+ :param mode: Rebalancing mode.
277
+ :param session: DB Session.
278
+ """
279
+
280
+ vo = get_rse_vo(rse_id=rse_id)
281
+
282
+ # dumps can be applied only for decommission since the dumps doesn't contain info from dids
283
+ if mode == "decommission":
284
+ return _list_rebalance_rule_candidates_dump(rse_id, mode)
285
+
286
+ # If no decommissioning use SQLAlchemy
287
+
288
+ # Rules constraints. By default only moves rules in state OK that have no children and have only one copy
289
+ # Additional constraints can be imposed by setting specific configuration
290
+ rule_clause = [
291
+ models.ReplicationRule.state == RuleState.OK,
292
+ models.ReplicationRule.child_rule_id.is_(None),
293
+ models.ReplicationRule.copies == 1,
294
+ ]
295
+
296
+ # Only move rules w/o expiration date, or rules with expiration_date > >min_expires_date_in_days> days
297
+ expiration_clause = models.ReplicationRule.expires_at.is_(None)
298
+ min_expires_date_in_days = config_get_int(
299
+ section="bb8",
300
+ option="min_expires_date_in_days",
301
+ raise_exception=False,
302
+ default=-1,
303
+ expiration_time=3600,
304
+ )
305
+ if min_expires_date_in_days > 0:
306
+ min_expires_date_in_days = datetime.utcnow() + timedelta(
307
+ days=min_expires_date_in_days
308
+ )
309
+ expiration_clause = or_(models.ReplicationRule.expires_at > min_expires_date_in_days,
310
+ models.ReplicationRule.expires_at.is_(None))
311
+ rule_clause.append(expiration_clause)
312
+
313
+ # Only move rules which were created more than <min_created_days> days ago
314
+ min_created_days = config_get_int(
315
+ section="bb8",
316
+ option="min_created_days",
317
+ raise_exception=False,
318
+ default=-1,
319
+ expiration_time=3600,
320
+ )
321
+ if min_created_days > 0:
322
+ min_created_days = datetime.utcnow() - timedelta(days=min_created_days)
323
+ rule_clause.append(models.ReplicationRule.created_at < min_created_days)
324
+
325
+ # Only move rules which are owned by <allowed_accounts> (coma separated accounts, e.g. panda,root,ddmadmin,jdoe)
326
+ allowed_accounts = config_get(
327
+ section="bb8",
328
+ option="allowed_accounts",
329
+ raise_exception=False,
330
+ default=None,
331
+ expiration_time=3600,
332
+ )
333
+ if allowed_accounts:
334
+ allowed_accounts = [
335
+ InternalAccount(acc.strip(" "), vo=vo)
336
+ for acc in allowed_accounts.split(",")
337
+ ]
338
+ rule_clause.append(models.ReplicationRule.account.in_(allowed_accounts))
339
+
340
+ # Only move rules which with scope <allowed_scopes> (coma separated scopes, e.g. mc16_13TeV,data18_13TeV)
341
+ allowed_scopes = config_get(
342
+ section="bb8",
343
+ option="allowed_scopes",
344
+ raise_exception=False,
345
+ default=None,
346
+ expiration_time=3600,
347
+ )
348
+ if allowed_scopes:
349
+ allowed_scopes = [
350
+ InternalScope(scope.strip(" "), vo=vo)
351
+ for scope in allowed_scopes.split(",")
352
+ ]
353
+ rule_clause.append(models.ReplicationRule.scope.in_(allowed_scopes))
354
+
355
+ # Only move rules that have a certain grouping <allowed_grouping> (accepted values : all, dataset, none)
356
+ rule_grouping_mapping = {
357
+ "all": RuleGrouping.ALL,
358
+ "dataset": RuleGrouping.DATASET,
359
+ "none": RuleGrouping.NONE,
360
+ }
361
+ allowed_grouping = config_get(
362
+ section="bb8",
363
+ option="allowed_grouping",
364
+ raise_exception=False,
365
+ default=None,
366
+ expiration_time=3600,
367
+ )
368
+ if allowed_grouping:
369
+ rule_clause.append(
370
+ models.ReplicationRule.grouping
371
+ == rule_grouping_mapping.get(allowed_grouping)
372
+ )
373
+
374
+ # DIDs constraints. By default only moves rules of DID where we can compute the size
375
+ # Additional constraints can be imposed by setting specific configuration
376
+ did_clause = [models.DataIdentifier.bytes.isnot(None)]
377
+
378
+ type_to_did_type_mapping = {
379
+ "all": [DIDType.CONTAINER, DIDType.DATASET, DIDType.FILE],
380
+ "collection": [DIDType.CONTAINER, DIDType.DATASET],
381
+ "container": [DIDType.CONTAINER],
382
+ "dataset": [DIDType.DATASET],
383
+ "file": [DIDType.FILE],
384
+ }
385
+
386
+ # Only allows to migrate rules of a certain did_type <allowed_did_type> (accepted values : all, collection, container, dataset, file)
387
+ allowed_did_type = config_get(
388
+ section="bb8",
389
+ option="allowed_did_type",
390
+ raise_exception=False,
391
+ default=None,
392
+ expiration_time=3600,
393
+ )
394
+ if allowed_did_type:
395
+ allowed_did_type = [
396
+ models.DataIdentifier.did_type == did_type
397
+ for did_type in type_to_did_type_mapping.get(allowed_did_type)
398
+ ]
399
+ did_clause.append(or_(*allowed_did_type))
400
+
401
+ # Only allows to migrate rules of closed DID is <only_move_closed_did> is set
402
+ only_move_closed_did = config_get_bool(
403
+ section="bb8",
404
+ option="only_move_closed_did",
405
+ raise_exception=False,
406
+ default=None,
407
+ expiration_time=3600,
408
+ )
409
+ if only_move_closed_did:
410
+ did_clause.append(models.DataIdentifier.is_open == false())
411
+
412
+ # Now build the query
413
+ external_dsl = aliased(models.DatasetLock)
414
+ count_locks = select(
415
+ func.count()
416
+ ).select_from(
417
+ models.DatasetLock
418
+ ).where(
419
+ and_(external_dsl.scope == models.DatasetLock.scope,
420
+ external_dsl.name == models.DatasetLock.name,
421
+ external_dsl.rse_id == models.DatasetLock.rse_id)
422
+ ).scalar_subquery()
423
+
424
+ stmt = select(
425
+ models.DatasetLock.scope,
426
+ models.DatasetLock.name,
427
+ models.ReplicationRule.id,
428
+ models.ReplicationRule.rse_expression,
429
+ models.ReplicationRule.subscription_id,
430
+ models.DataIdentifier.bytes,
431
+ models.DataIdentifier.length,
432
+ case(
433
+ (
434
+ or_(models.DatasetLock.length < 1,
435
+ models.DatasetLock.length.is_(None)),
436
+ 0
437
+ ),
438
+ else_=cast(
439
+ models.DatasetLock.bytes / models.DatasetLock.length, BigInteger
440
+ )
441
+ )
442
+ ).join(
443
+ models.ReplicationRule,
444
+ models.ReplicationRule.id == models.DatasetLock.rule_id
445
+ ).join(
446
+ models.DataIdentifier,
447
+ and_(models.DatasetLock.scope == models.DataIdentifier.scope,
448
+ models.DatasetLock.name == models.DataIdentifier.name)
449
+ ).where(
450
+ and_(models.DatasetLock.rse_id == rse_id,
451
+ *rule_clause,
452
+ *did_clause,
453
+ case(
454
+ (
455
+ or_(models.DatasetLock.length < 1,
456
+ models.DatasetLock.length.is_(None)),
457
+ 0
458
+ ),
459
+ else_=cast(
460
+ models.DatasetLock.bytes / models.DatasetLock.length, BigInteger
461
+ )
462
+ ) > 1000000000,
463
+ count_locks == 1)
464
+ ).order_by(
465
+ case(
466
+ (
467
+ or_(models.DatasetLock.length < 1,
468
+ models.DatasetLock.length.is_(None)),
469
+ 0
470
+ ),
471
+ else_=cast(
472
+ models.DatasetLock.bytes / models.DatasetLock.length, BigInteger
473
+ )
474
+ ),
475
+ models.DatasetLock.accessed_at
476
+ )
477
+ return list(session.execute(stmt).all()) # type: ignore (session could be None)
478
+
479
+
480
+ @read_session
481
+ def select_target_rse(
482
+ parent_rule: "Mapping[str, Any]",
483
+ current_rse_id: str,
484
+ rse_expression: str,
485
+ subscription_id: str,
486
+ rse_attributes: "Mapping[str, Any]",
487
+ other_rses: Optional["Sequence[str]"] = None,
488
+ exclude_expression: Optional[str] = None,
489
+ force_expression: Optional[str] = None,
490
+ *,
491
+ session: Optional[Session] = None,
492
+ ) -> str:
493
+ """
494
+ Select a new target RSE for a rebalanced rule.
495
+ :param parent_rule rule that is rebalanced.
496
+ :param current_rse_id: RSE of the source.
497
+ :param rse_expression: RSE Expression of the source rule.
498
+ :param subscription_id: Subscription ID of the source rule.
499
+ :param rse_attributes: The attributes of the source rse.
500
+ :param other_rses: Other RSEs with existing dataset replicas.
501
+ :param exclude_expression: Exclude this rse_expression from being target_rses.
502
+ :param force_expression: Force a specific rse_expression as target.
503
+ :param session: The DB Session.
504
+ :returns: New RSE expression.
505
+ """
506
+
507
+ other_rses = other_rses or []
508
+ current_rse = get_rse_name(rse_id=current_rse_id)
509
+ current_rse_expr = current_rse
510
+ # if parent rule has a vo, enforce it
511
+ vo = parent_rule["scope"].vo
512
+ if exclude_expression:
513
+ target_rse = "((%s)|(%s))" % (exclude_expression, current_rse_expr)
514
+ else:
515
+ target_rse = current_rse_expr
516
+ list_target_rses = [
517
+ rse["rse"]
518
+ for rse in parse_expression(
519
+ expression=target_rse, filter_={"vo": vo}, session=session
520
+ )
521
+ ]
522
+ list_target_rses.sort()
523
+
524
+ rses = parse_expression(
525
+ expression=rse_expression, filter_={"vo": vo}, session=session
526
+ )
527
+
528
+ # TODO: Implement subscription rebalancing
529
+
530
+ if force_expression is not None:
531
+ if parent_rule["grouping"] != RuleGrouping.NONE:
532
+ rses = parse_expression(
533
+ expression="(%s)\\%s" % (force_expression, target_rse),
534
+ filter_={"vo": vo, "availability_write": True},
535
+ session=session,
536
+ )
537
+ else:
538
+ # in order to avoid replication of the part of distributed dataset not present at rebalanced rse -> rses in force_expression
539
+ # this will be extended with development of delayed rule
540
+ rses = parse_expression(
541
+ expression="((%s)|(%s))\\%s"
542
+ % (force_expression, rse_expression, target_rse),
543
+ filter_={"vo": vo, "availability_write": True},
544
+ session=session,
545
+ )
546
+ else:
547
+ # force_expression is not set, RSEs will be selected as rse_expression\rse
548
+ list_rses = [rse["rse"] for rse in rses]
549
+ list_rses.sort()
550
+ if list_rses == list_target_rses:
551
+ raise InsufficientTargetRSEs(
552
+ "Not enough RSEs to rebalance rule %s" % parent_rule["id"]
553
+ )
554
+ else:
555
+ rses = parse_expression(
556
+ expression="(%s)\\%s" % (rse_expression, target_rse),
557
+ filter_={"vo": vo, "availability_write": True},
558
+ session=session,
559
+ )
560
+ rseselector = RSESelector(
561
+ account=InternalAccount("root", vo=vo),
562
+ rses=rses,
563
+ weight="freespace",
564
+ copies=1,
565
+ ignore_account_limit=True,
566
+ session=session,
567
+ )
568
+ return get_rse_name(
569
+ [
570
+ rse_id
571
+ for rse_id, _, _ in rseselector.select_rse(
572
+ size=0, preferred_rse_ids=[], blocklist=other_rses
573
+ )
574
+ ][0],
575
+ session=session,
576
+ )
577
+
578
+
579
+ @transactional_session
580
+ def rebalance_rse(
581
+ rse_id: str,
582
+ max_bytes: float = 1e9,
583
+ max_files: Optional[int] = None,
584
+ dry_run: bool = False,
585
+ exclude_expression: Optional[str] = None,
586
+ comment: Optional[str] = None,
587
+ force_expression: Optional[str] = None,
588
+ mode: Optional[str] = None,
589
+ priority: int = 3,
590
+ source_replica_expression: str = "*\\bb8-enabled=false",
591
+ *,
592
+ session: Optional[Session] = None,
593
+ logger: "LoggerFunction" = logging.log,
594
+ ) -> list[tuple]:
595
+ """
596
+ Rebalance data from an RSE
597
+ :param rse_id: RSE to rebalance data from.
598
+ :param max_bytes: Maximum amount of bytes to rebalance.
599
+ :param max_files: Maximum amount of files to rebalance.
600
+ :param dry_run: Only run in dry-run mode.
601
+ :param exclude_expression: Exclude this rse_expression from being target_rses.
602
+ :param comment: Comment to set on the new rules.
603
+ :param force_expression: Force a specific rse_expression as target.
604
+ :param mode: BB8 mode to execute (None=normal, 'decomission'=Decomission mode)
605
+ :param priority: Priority of the new created rules.
606
+ :param source_replica_expression: Source replica expression of the new created rules.
607
+ :param session: The database session.
608
+ :param logger: Logger.
609
+ :returns: List of rebalanced datasets.
610
+ """
611
+ rebalanced_bytes = 0
612
+ rebalanced_files = 0
613
+ rebalanced_datasets = []
614
+
615
+ rse_attributes = list_rse_attributes(rse_id=rse_id, session=session)
616
+ src_rse = get_rse_name(rse_id=rse_id)
617
+
618
+ logger(logging.INFO, "***************************")
619
+ logger(logging.INFO, "BB8 - Execution Summary")
620
+ logger(logging.INFO, "Mode: %s" % ("STANDARD" if mode is None else mode.upper()))
621
+ logger(logging.INFO, "Dry Run: %s" % (dry_run))
622
+ logger(logging.INFO, "***************************")
623
+
624
+ for (
625
+ scope,
626
+ name,
627
+ rule_id,
628
+ rse_expression,
629
+ subscription_id,
630
+ bytes_,
631
+ length,
632
+ fsize,
633
+ ) in list_rebalance_rule_candidates(rse_id=rse_id, mode=mode):
634
+ if force_expression is not None and subscription_id is not None:
635
+ continue
636
+
637
+ if rebalanced_bytes + bytes_ > max_bytes:
638
+ continue
639
+ if max_files:
640
+ if rebalanced_files + length > max_files:
641
+ continue
642
+
643
+ try:
644
+ rule = get_rule(rule_id=rule_id)
645
+ other_rses = [
646
+ r["rse_id"] for r in get_dataset_locks(scope, name, session=session)
647
+ ]
648
+ # Select the target RSE for this rule
649
+ try:
650
+ target_rse_exp = select_target_rse(
651
+ parent_rule=rule,
652
+ current_rse_id=rse_id,
653
+ rse_expression=rse_expression,
654
+ subscription_id=subscription_id,
655
+ rse_attributes=rse_attributes,
656
+ other_rses=other_rses,
657
+ exclude_expression=exclude_expression,
658
+ force_expression=force_expression,
659
+ session=session,
660
+ )
661
+ # Rebalance this rule
662
+ if not dry_run:
663
+ child_rule_id = rebalance_rule(
664
+ parent_rule=rule,
665
+ activity="Data Rebalancing",
666
+ rse_expression=target_rse_exp,
667
+ priority=priority,
668
+ source_replica_expression=source_replica_expression,
669
+ comment=comment,
670
+ )
671
+ else:
672
+ child_rule_id = ""
673
+ except (
674
+ InsufficientTargetRSEs,
675
+ DuplicateRule,
676
+ RuleNotFound,
677
+ InsufficientAccountLimit,
678
+ ) as err:
679
+ logger(logging.ERROR, str(err))
680
+ continue
681
+ if child_rule_id is None:
682
+ logger(
683
+ logging.WARNING,
684
+ "A rule for %s:%s already exists on %s. It cannot be rebalanced",
685
+ scope,
686
+ name,
687
+ target_rse_exp,
688
+ )
689
+ continue
690
+ logger(
691
+ logging.INFO,
692
+ "Rebalancing %s:%s rule %s (%f GB) from %s to %s. New rule %s",
693
+ scope,
694
+ name,
695
+ str(rule_id),
696
+ bytes_ / 1e9,
697
+ rule["rse_expression"],
698
+ target_rse_exp,
699
+ child_rule_id,
700
+ )
701
+ rebalanced_bytes += bytes_
702
+ rebalanced_files += length
703
+ rebalanced_datasets.append(
704
+ (scope, name, bytes_, length, target_rse_exp, rule_id, child_rule_id)
705
+ )
706
+ except Exception as error:
707
+ logger(
708
+ logging.ERROR,
709
+ "Exception %s occurred while rebalancing %s:%s, rule_id: %s!",
710
+ str(error),
711
+ scope,
712
+ name,
713
+ str(rule_id),
714
+ )
715
+
716
+ logger(
717
+ logging.INFO,
718
+ "BB8 is rebalancing %d GB of data (%d rules) from %s",
719
+ rebalanced_bytes / 1e9,
720
+ len(rebalanced_datasets),
721
+ src_rse,
722
+ )
723
+ return rebalanced_datasets
724
+
725
+
726
+ @read_session
727
+ def get_active_locks(
728
+ *,
729
+ session: Optional[Session] = None
730
+ ) -> dict[str, dict[str, int]]:
731
+ locks_dict: dict[str, dict[str, int]] = {}
732
+ stmt = select(
733
+ models.ReplicationRule.id
734
+ ).where(
735
+ and_(or_(models.ReplicationRule.state == RuleState.REPLICATING,
736
+ models.ReplicationRule.state == RuleState.STUCK),
737
+ models.ReplicationRule.comments == "Background rebalancing")
738
+ )
739
+ for rule_id in session.execute(stmt).scalars().all(): # type: ignore (session could be None)
740
+ stmt = select(
741
+ func.count(),
742
+ func.sum(models.ReplicaLock.bytes),
743
+ models.ReplicaLock.state,
744
+ models.ReplicaLock.rse_id
745
+ ).select_from(
746
+ models.ReplicaLock
747
+ ).where(
748
+ and_(models.ReplicaLock.rule_id == rule_id,
749
+ models.ReplicaLock.state != LockState.OK)
750
+ ).group_by(
751
+ models.ReplicaLock.state,
752
+ models.ReplicaLock.rse_id
753
+ )
754
+ for cnt, size, _, rse_id in session.execute(stmt).all(): # type: ignore (session could be None)
755
+ if rse_id not in locks_dict:
756
+ locks_dict[rse_id] = {"bytes": 0, "locks": 0}
757
+ locks_dict[rse_id]["locks"] += cnt
758
+ locks_dict[rse_id]["bytes"] += size
759
+ return locks_dict