rucio 32.8.6__py3-none-any.whl

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

Potentially problematic release.


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

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