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,200 @@
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 functools
17
+ import logging
18
+ import re
19
+ import threading
20
+ import time
21
+ from datetime import datetime, timedelta
22
+ from typing import TYPE_CHECKING
23
+
24
+ from dogpile.cache.api import NO_VALUE
25
+ from sqlalchemy.exc import DatabaseError
26
+
27
+ import rucio.db.sqla.util
28
+ from rucio.common import exception
29
+ from rucio.common.cache import make_region_memcached
30
+ from rucio.common.config import config_get_int
31
+ from rucio.common.exception import DatabaseException
32
+ from rucio.common.logging import setup_logging
33
+ from rucio.core.monitor import MetricManager
34
+ from rucio.core.replica import list_bad_replicas, get_replicas_state, get_bad_replicas_backlog
35
+ from rucio.core.rule import (update_rules_for_lost_replica, update_rules_for_bad_replica,
36
+ get_evaluation_backlog)
37
+ from rucio.daemons.common import run_daemon
38
+ from rucio.db.sqla.constants import ReplicaState
39
+
40
+ if TYPE_CHECKING:
41
+ from types import FrameType
42
+ from typing import Optional
43
+
44
+ from rucio.daemons.common import HeartbeatHandler
45
+
46
+ graceful_stop = threading.Event()
47
+ METRICS = MetricManager(module=__name__)
48
+ REGION = make_region_memcached(expiration_time=config_get_int('necromancer', 'cache_time', False, 600))
49
+ DAEMON_NAME = 'necromancer'
50
+
51
+
52
+ def necromancer(bulk: int, once: bool = False, sleep_time: int = 60) -> None:
53
+ """
54
+ Creates a Necromancer Worker that gets a list of bad replicas for a given hash,
55
+ identify lost DIDs and for non-lost ones, set the locks and rules for reevaluation.
56
+
57
+ :param bulk: The number of requests to process.
58
+ :param once: Run only once.
59
+ :param sleep_time: Thread sleep time after each chunk of work.
60
+ """
61
+ run_daemon(
62
+ once=once,
63
+ graceful_stop=graceful_stop,
64
+ executable=DAEMON_NAME,
65
+ partition_wait_time=10,
66
+ sleep_time=sleep_time,
67
+ run_once_fnc=functools.partial(
68
+ run_once,
69
+ bulk=bulk,
70
+ ),
71
+ )
72
+
73
+
74
+ def run_once(heartbeat_handler: "HeartbeatHandler", bulk: int, **_kwargs) -> bool:
75
+ worker_number, total_workers, logger = heartbeat_handler.live()
76
+ must_sleep = True
77
+
78
+ # Check if there is a Judge Evaluator backlog
79
+ max_evaluator_backlog_count = config_get_int('necromancer', 'max_evaluator_backlog_count', default=0, raise_exception=False)
80
+ max_evaluator_backlog_duration = config_get_int('necromancer', 'max_evaluator_backlog_duration', default=0, raise_exception=False)
81
+ backlog_refresh_time = config_get_int('necromancer', 'backlog_refresh_time', default=60, raise_exception=False)
82
+ if max_evaluator_backlog_count or max_evaluator_backlog_duration:
83
+ evaluator_backlog_count, evaluator_backlog_duration = get_evaluation_backlog(expiration_time=backlog_refresh_time)
84
+ if max_evaluator_backlog_count and \
85
+ evaluator_backlog_count and \
86
+ max_evaluator_backlog_duration and \
87
+ evaluator_backlog_duration and \
88
+ evaluator_backlog_count > max_evaluator_backlog_count and \
89
+ evaluator_backlog_duration < datetime.utcnow() - timedelta(minutes=max_evaluator_backlog_duration):
90
+ logger(logging.ERROR, 'Necromancer: Judge evaluator backlog count and duration hit, stopping operation')
91
+ return must_sleep
92
+ elif max_evaluator_backlog_count and evaluator_backlog_count and evaluator_backlog_count > max_evaluator_backlog_count:
93
+ logger(logging.ERROR, 'Necromancer: Judge evaluator backlog count hit, stopping operation')
94
+ return must_sleep
95
+ elif max_evaluator_backlog_duration and evaluator_backlog_duration and evaluator_backlog_duration < datetime.utcnow() - timedelta(minutes=max_evaluator_backlog_duration):
96
+ logger(logging.ERROR, 'Necromancer: Judge evaluator backlog duration hit, stopping operation')
97
+ return must_sleep
98
+
99
+ # Check how many bad replicas are queued
100
+ max_bad_replicas_backlog_count = config_get_int('necromancer', 'max_bad_replicas_backlog_count', default=0, raise_exception=False)
101
+ bad_replicas_backlog = REGION.get('bad_replicas_backlog')
102
+ if bad_replicas_backlog is NO_VALUE:
103
+ bad_replicas_backlog = get_bad_replicas_backlog()
104
+ REGION.set('bad_replicas_backlog', bad_replicas_backlog)
105
+ tot_bad_files = sum([bad_replicas_backlog[key] for key in bad_replicas_backlog])
106
+ list_of_rses = list()
107
+ # If too many replica, call list_bad_replicas with a list of RSEs
108
+ if max_bad_replicas_backlog_count and tot_bad_files > max_bad_replicas_backlog_count and len(bad_replicas_backlog) > 1:
109
+ logger(logging.INFO, 'Backlog of bads replica too big. Apply some sharing between different RSEs')
110
+ rses = list()
111
+ cnt = 0
112
+ for key in sorted(bad_replicas_backlog, key=bad_replicas_backlog.get, reverse=True):
113
+ rses.append({'id': key})
114
+ cnt += bad_replicas_backlog[key]
115
+ if cnt >= bulk:
116
+ list_of_rses.append(rses)
117
+ rses = list()
118
+ cnt = 0
119
+ else:
120
+ list_of_rses.append(None)
121
+
122
+ tot_processed = 0
123
+ if tot_bad_files == 0:
124
+ logger(logging.INFO, 'No bad replicas to process.')
125
+ else:
126
+ ttime = time.time()
127
+ replicas = []
128
+ for rses in list_of_rses:
129
+
130
+ worker_number, total_workers, logger = heartbeat_handler.live()
131
+ replicas = list_bad_replicas(limit=bulk, thread=worker_number, total_threads=total_workers, rses=rses)
132
+ for replica in replicas:
133
+ scope, name, rse_id, rse = replica['scope'], replica['name'], replica['rse_id'], replica['rse']
134
+ logger(logging.INFO, 'Working on %s:%s on %s' % (scope, name, rse))
135
+
136
+ list_replicas = get_replicas_state(scope=scope, name=name)
137
+ if ReplicaState.AVAILABLE not in list_replicas and ReplicaState.TEMPORARY_UNAVAILABLE not in list_replicas:
138
+ logger(logging.INFO, 'File %s:%s has no other available or temporary available replicas, it will be marked as lost' % (scope, name))
139
+ try:
140
+ update_rules_for_lost_replica(scope=scope, name=name, rse_id=rse_id, nowait=True)
141
+ METRICS.counter(name='badfiles.lostfile').inc()
142
+ except (DatabaseException, DatabaseError) as error:
143
+ if re.match('.*ORA-00054.*', error.args[0]) or re.match('.*ORA-00060.*', error.args[0]) or 'ERROR 1205 (HY000)' in error.args[0]:
144
+ logger(logging.WARNING, 'Lock detected when handling request - skipping: %s', str(error))
145
+ else:
146
+ logger(logging.ERROR, str(error))
147
+
148
+ else:
149
+ rep = list_replicas.get(ReplicaState.AVAILABLE, [])
150
+ unavailable_rep = list_replicas.get(ReplicaState.TEMPORARY_UNAVAILABLE, [])
151
+ logger(logging.INFO, 'File %s:%s can be recovered. Available sources : %s + Unavailable sources : %s' % (scope, name, str(rep), str(unavailable_rep)))
152
+ try:
153
+ update_rules_for_bad_replica(scope=scope, name=name, rse_id=rse_id, nowait=True)
154
+ METRICS.counter(name='badfiles.recovering').inc()
155
+ except (DatabaseException, DatabaseError) as error:
156
+ if re.match('.*ORA-00054.*', error.args[0]) or re.match('.*ORA-00060.*', error.args[0]) or 'ERROR 1205 (HY000)' in error.args[0]:
157
+ logger(logging.WARNING, 'Lock detected when handling request - skipping: %s', str(error))
158
+ else:
159
+ logger(logging.ERROR, str(error))
160
+
161
+ tot_processed += len(replicas)
162
+ logger(logging.INFO, 'It took %s seconds to process %s replicas' % (str(time.time() - ttime), str(len(replicas))))
163
+
164
+ if tot_processed == 0 or tot_bad_files == 0:
165
+ return must_sleep
166
+ must_sleep = False
167
+ return must_sleep
168
+
169
+
170
+ def run(threads: int = 1, bulk: int = 100, once: bool = False, sleep_time: int = 60) -> None:
171
+ """
172
+ Starts up the necromancer threads.
173
+ """
174
+ setup_logging(process_name=DAEMON_NAME)
175
+
176
+ if rucio.db.sqla.util.is_old_db():
177
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
178
+
179
+ if once:
180
+ logging.info('Will run only one iteration in a single threaded mode')
181
+ necromancer(bulk=bulk, once=once)
182
+ else:
183
+ logging.info('starting necromancer threads')
184
+ thread_list = [threading.Thread(target=necromancer, kwargs={'once': once,
185
+ 'bulk': bulk,
186
+ 'sleep_time': sleep_time}) for _ in range(0, threads)]
187
+ [t.start() for t in thread_list]
188
+
189
+ logging.info('waiting for interrupts')
190
+
191
+ # Interruptible joins require a timeout.
192
+ while thread_list:
193
+ thread_list = [thread.join(timeout=3.14) for thread in thread_list if thread and thread.is_alive()]
194
+
195
+
196
+ def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
197
+ """
198
+ Graceful exit.
199
+ """
200
+ graceful_stop.set()
@@ -0,0 +1,14 @@
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.
@@ -0,0 +1,356 @@
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
+ """
17
+ BB8 is a daemon the re-balance data between RSEs.
18
+ """
19
+
20
+ import functools
21
+ import logging
22
+ import socket
23
+ import threading
24
+ from typing import TYPE_CHECKING
25
+
26
+ from rucio.common.config import config_get_float
27
+ from rucio.common.exception import InvalidRSEExpression
28
+ from rucio.common.logging import setup_logging
29
+ from rucio.core.heartbeat import sanity_check, list_payload_counts
30
+ from rucio.core.rse import get_rse_usage
31
+ from rucio.core.rse_expression_parser import parse_expression
32
+ from rucio.daemons.bb8.common import rebalance_rse, get_active_locks
33
+ from rucio.daemons.common import run_daemon
34
+
35
+ if TYPE_CHECKING:
36
+ from types import FrameType
37
+ from typing import Optional
38
+
39
+ from rucio.daemons.common import HeartbeatHandler
40
+
41
+ graceful_stop = threading.Event()
42
+ DAEMON_NAME = "rucio-bb8"
43
+
44
+
45
+ def rule_rebalancer(
46
+ rse_expression: str,
47
+ move_subscriptions: bool = False,
48
+ use_dump: bool = False,
49
+ sleep_time: int = 300,
50
+ once: bool = True,
51
+ dry_run: bool = False,
52
+ ) -> None:
53
+ """
54
+ Create a rule_rebalancer worker
55
+
56
+ :param rse_expression: The RSE expression where the rule rebalancing is applied.
57
+ :param move_subscription: To allow rebalancing of subscription rules. Not implemented yet.
58
+ :param use_dump: To use dump instead of DB query.
59
+ :param sleep_time: Time between two cycles.
60
+ :param once: Run only once.
61
+ :param dry_run: To run in dry run mode (i.e. rules are not created).
62
+ """
63
+ run_daemon(
64
+ once=once,
65
+ graceful_stop=graceful_stop,
66
+ executable=DAEMON_NAME,
67
+ partition_wait_time=1,
68
+ sleep_time=sleep_time,
69
+ run_once_fnc=functools.partial(
70
+ run_once,
71
+ rse_expression=rse_expression,
72
+ move_subscriptions=move_subscriptions,
73
+ use_dump=use_dump,
74
+ dry_run=dry_run,
75
+ ),
76
+ )
77
+
78
+
79
+ def run_once(
80
+ heartbeat_handler: "HeartbeatHandler",
81
+ rse_expression: str,
82
+ move_subscriptions: bool,
83
+ use_dump: bool,
84
+ dry_run: bool,
85
+ **_kwargs
86
+ ) -> bool:
87
+
88
+ must_sleep = False
89
+ total_rebalance_volume = 0
90
+ worker_number, total_workers, logger = heartbeat_handler.live()
91
+ logger(logging.DEBUG, "Running BB8 on rse_expression: %s", rse_expression)
92
+ tolerance = config_get_float("bb8", "tolerance", default=0.05)
93
+ max_total_rebalance_volume = config_get_float(
94
+ "bb8", "max_total_rebalance_volume", default=10 * 1e12
95
+ )
96
+ max_rse_rebalance_volume = config_get_float(
97
+ "bb8", "max_rse_rebalance_volume", default=500 * 1e9
98
+ )
99
+ min_total = config_get_float("bb8", "min_total", default=20 * 1e9)
100
+ payload_cnt = list_payload_counts(
101
+ executable=DAEMON_NAME, older_than=600, hash_executable=None, session=None
102
+ )
103
+ if rse_expression in payload_cnt:
104
+ logger(
105
+ logging.WARNING,
106
+ "One BB8 instance already running with the same RSE expression. Stopping",
107
+ )
108
+ must_sleep = True
109
+ return must_sleep
110
+ else:
111
+ # List the RSEs represented by rse_expression
112
+ try:
113
+ rses = [rse for rse in parse_expression(rse_expression)]
114
+ list_rses2 = [rse["rse"] for rse in rses]
115
+ except InvalidRSEExpression as err:
116
+ logger(logging.ERROR, err)
117
+ return must_sleep
118
+ # List the RSEs represented by all the RSE expressions stored in heartbeat payload
119
+ list_rses1 = []
120
+ for rse_exp in payload_cnt:
121
+ if rse_exp:
122
+ list_rses1 = [rse["rse"] for rse in parse_expression(rse_exp)]
123
+ for rse in list_rses2:
124
+ if rse in list_rses1:
125
+ logger(
126
+ logging.WARNING,
127
+ "Overlapping RSE expressions %s vs %s. Stopping",
128
+ rse_exp,
129
+ rse_expression,
130
+ )
131
+ return must_sleep
132
+
133
+ logger(logging.INFO, "Will process rebalancing on %s", rse_expression)
134
+ worker_number, total_workers, logger = heartbeat_handler.live()
135
+ total_primary = 0
136
+ total_secondary = 0
137
+ total_total = 0
138
+ global_ratio = float(0)
139
+ for rse in rses:
140
+ logger(logging.DEBUG, "Getting RSE usage on %s", rse["rse"])
141
+ rse_usage = get_rse_usage(rse_id=rse["id"])
142
+ usage_dict = {}
143
+ for item in rse_usage:
144
+ # TODO Check last update
145
+ usage_dict[item["source"]] = {
146
+ "used": item["used"],
147
+ "free": item["free"],
148
+ "total": item["total"],
149
+ }
150
+
151
+ try:
152
+ rse["primary"] = (
153
+ usage_dict["rucio"]["used"] - usage_dict["expired"]["used"]
154
+ )
155
+ rse["secondary"] = usage_dict["expired"]["used"]
156
+ rse["total"] = (
157
+ usage_dict["storage"]["total"]
158
+ - usage_dict["min_free_space"]["used"]
159
+ )
160
+ rse["ratio"] = float(rse["primary"]) / float(rse["total"])
161
+ except KeyError as err:
162
+ logger(
163
+ logging.ERROR,
164
+ "Missing source usage %s for RSE %s. Exiting",
165
+ err,
166
+ rse["rse"],
167
+ )
168
+ break
169
+ total_primary += rse["primary"]
170
+ total_secondary += rse["secondary"]
171
+ total_total += float(rse["total"])
172
+ rse["receive_volume"] = 0 # Already rebalanced volume in this run
173
+ global_ratio = float(total_primary) / float(total_total)
174
+ logger(logging.INFO, "Global ratio: %f" % (global_ratio))
175
+
176
+ for rse in sorted(rses, key=lambda k: k["ratio"]):
177
+ logger(
178
+ logging.INFO,
179
+ "%s Sec/Prim local ratio (%f) vs global %s",
180
+ rse["rse"],
181
+ rse["ratio"],
182
+ global_ratio,
183
+ )
184
+ rses_over_ratio = sorted(
185
+ [
186
+ rse
187
+ for rse in rses
188
+ if rse["ratio"] > global_ratio + global_ratio * tolerance
189
+ ],
190
+ key=lambda k: k["ratio"],
191
+ reverse=True,
192
+ )
193
+ rses_under_ratio = sorted(
194
+ [
195
+ rse
196
+ for rse in rses
197
+ if rse["ratio"] < global_ratio - global_ratio * tolerance
198
+ ],
199
+ key=lambda k: k["ratio"],
200
+ reverse=False,
201
+ )
202
+
203
+ # Excluding RSEs
204
+ logger(
205
+ logging.DEBUG, "Excluding RSEs as destination which are too small by size:"
206
+ )
207
+ for des in rses_under_ratio:
208
+ if des["total"] < min_total:
209
+ logger(logging.DEBUG, "Excluding %s", des["rse"])
210
+ rses_under_ratio.remove(des)
211
+ logger(logging.DEBUG, "Excluding RSEs as sources which are too small by size:")
212
+ for src in rses_over_ratio:
213
+ if src["total"] < min_total:
214
+ logger(logging.DEBUG, "Excluding %s", src["rse"])
215
+ rses_over_ratio.remove(src)
216
+ logger(
217
+ logging.DEBUG,
218
+ "Excluding RSEs as destinations which are not available for write:",
219
+ )
220
+ for des in rses_under_ratio:
221
+ if not des["availability_write"]:
222
+ logger(logging.DEBUG, "Excluding %s", des["rse"])
223
+ rses_under_ratio.remove(des)
224
+ logger(
225
+ logging.DEBUG, "Excluding RSEs as sources which are not available for read:"
226
+ )
227
+ for src in rses_over_ratio:
228
+ if not src["availability_read"]:
229
+ logger(logging.DEBUG, "Excluding %s", src["rse"])
230
+ rses_over_ratio.remove(src)
231
+
232
+ # Gets the number of active transfers per location
233
+ dict_locks = get_active_locks(session=None)
234
+
235
+ # Loop over RSEs over the ratio
236
+ for index, source_rse in enumerate(rses_over_ratio):
237
+
238
+ # The volume that would be rebalanced, not real availability of the data:
239
+ available_source_rebalance_volume = int(
240
+ (source_rse["primary"] - global_ratio * source_rse["secondary"])
241
+ / (global_ratio + 1)
242
+ )
243
+ if available_source_rebalance_volume > max_rse_rebalance_volume:
244
+ available_source_rebalance_volume = max_rse_rebalance_volume
245
+ if (
246
+ available_source_rebalance_volume
247
+ > max_total_rebalance_volume - total_rebalance_volume
248
+ ):
249
+ available_source_rebalance_volume = (
250
+ max_total_rebalance_volume - total_rebalance_volume
251
+ )
252
+
253
+ # Select a target:
254
+ for destination_rse in rses_under_ratio:
255
+ if available_source_rebalance_volume > 0:
256
+ vo_str = (
257
+ " on VO {}".format(destination_rse["vo"])
258
+ if destination_rse["vo"] != "def"
259
+ else ""
260
+ )
261
+ if index == 0 and destination_rse["id"] in dict_locks:
262
+ replicating_volume = dict_locks[destination_rse["id"]]["bytes"]
263
+ logger(
264
+ logging.DEBUG,
265
+ "Already %f TB replicating to %s%s",
266
+ replicating_volume / 1e12,
267
+ destination_rse["rse"],
268
+ vo_str,
269
+ )
270
+ destination_rse["receive_volume"] += replicating_volume
271
+ if destination_rse["receive_volume"] >= max_rse_rebalance_volume:
272
+ continue
273
+ available_target_rebalance_volume = (
274
+ max_rse_rebalance_volume - destination_rse["receive_volume"]
275
+ )
276
+ if (
277
+ available_target_rebalance_volume
278
+ >= available_source_rebalance_volume
279
+ ):
280
+ available_target_rebalance_volume = (
281
+ available_source_rebalance_volume
282
+ )
283
+
284
+ logger(
285
+ logging.INFO,
286
+ "Rebalance %d TB from %s(%f) to %s(%f)%s",
287
+ available_target_rebalance_volume / 1e12,
288
+ source_rse["rse"],
289
+ source_rse["ratio"],
290
+ destination_rse["rse"],
291
+ destination_rse["ratio"],
292
+ vo_str,
293
+ )
294
+ expr = destination_rse["rse"]
295
+ rebalance_rse(
296
+ rse_id=source_rse["id"],
297
+ max_bytes=available_target_rebalance_volume,
298
+ dry_run=dry_run,
299
+ comment="Background rebalancing",
300
+ force_expression=expr,
301
+ logger=logger,
302
+ )
303
+
304
+ destination_rse[
305
+ "receive_volume"
306
+ ] += available_target_rebalance_volume
307
+ total_rebalance_volume += available_target_rebalance_volume
308
+ available_source_rebalance_volume -= (
309
+ available_target_rebalance_volume
310
+ )
311
+
312
+ must_sleep = True
313
+ return must_sleep
314
+
315
+
316
+ def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
317
+ """
318
+ Graceful exit.
319
+ """
320
+
321
+ graceful_stop.set()
322
+
323
+
324
+ def run(
325
+ once: bool,
326
+ rse_expression: str,
327
+ move_subscriptions: bool = False,
328
+ use_dump: bool = False,
329
+ sleep_time: int = 300,
330
+ threads: int = 1,
331
+ dry_run: bool = False,
332
+ ) -> None:
333
+ """
334
+ Starts up the BB8 rebalancing threads.
335
+ """
336
+
337
+ setup_logging(process_name=DAEMON_NAME)
338
+ hostname = socket.gethostname()
339
+ sanity_check(executable=DAEMON_NAME, hostname=hostname)
340
+ logging.info("BB8 starting %s threads", str(threads))
341
+ threads = [
342
+ threading.Thread(
343
+ target=rule_rebalancer,
344
+ kwargs={
345
+ "once": once,
346
+ "rse_expression": rse_expression,
347
+ "sleep_time": sleep_time,
348
+ "dry_run": dry_run,
349
+ },
350
+ )
351
+ for _ in range(0, threads)
352
+ ]
353
+ [thread.start() for thread in threads]
354
+ # Interruptible joins require a timeout.
355
+ while threads[0].is_alive():
356
+ [thread.join(timeout=3.14) for thread in threads]