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,468 @@
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
+ Conveyor throttler is a daemon to manage rucio internal queue.
18
+ """
19
+ import logging
20
+ import threading
21
+ import traceback
22
+ from collections import defaultdict
23
+ from types import FrameType
24
+ from typing import TYPE_CHECKING, Optional
25
+
26
+ import math
27
+ from sqlalchemy import null
28
+
29
+ import rucio.db.sqla.util
30
+ from rucio.common import exception
31
+ from rucio.common.logging import setup_logging
32
+ from rucio.core.monitor import MetricManager
33
+ from rucio.core.request import (get_request_stats, release_all_waiting_requests, release_waiting_requests_fifo,
34
+ release_waiting_requests_grouped_fifo, set_transfer_limit_stats, re_sync_all_transfer_limits)
35
+ from rucio.core.rse import RseCollection
36
+ from rucio.core.transfer import applicable_rse_transfer_limits
37
+ from rucio.daemons.common import db_workqueue, ProducerConsumerDaemon
38
+ from rucio.db.sqla.constants import RequestState, TransferLimitDirection
39
+
40
+ if TYPE_CHECKING:
41
+ from rucio.daemons.common import HeartbeatHandler
42
+
43
+ GRACEFUL_STOP = threading.Event()
44
+ METRICS = MetricManager(module=__name__)
45
+ DAEMON_NAME = 'conveyor-throttler'
46
+
47
+
48
+ def throttler(
49
+ once=False,
50
+ sleep_time=600,
51
+ partition_wait_time=10
52
+ ):
53
+ """
54
+ Main loop to check rse transfer limits.
55
+ """
56
+
57
+ logging.info('Throttler starting')
58
+
59
+ @db_workqueue(
60
+ once=once,
61
+ graceful_stop=GRACEFUL_STOP,
62
+ executable=DAEMON_NAME,
63
+ partition_wait_time=partition_wait_time,
64
+ sleep_time=sleep_time)
65
+ def _db_producer(*, activity: str, heartbeat_handler: "HeartbeatHandler"):
66
+ worker_number, total_workers, logger = heartbeat_handler.live()
67
+ if worker_number != 0:
68
+ logger(logging.INFO, 'Throttler thread id is not 0, will sleep. Only thread 0 will work')
69
+ return True, None
70
+
71
+ re_sync_all_transfer_limits()
72
+ rse_collection = RseCollection()
73
+ release_groups = _get_request_stats(rse_collection, logger=logger)
74
+ return True, release_groups
75
+
76
+ def _consumer(release_groups):
77
+ if release_groups is None:
78
+ return
79
+ logger = logging.log
80
+ logger(logging.INFO, "Throttler - schedule requests")
81
+ try:
82
+ _handle_requests(release_groups, logger=logger)
83
+ except Exception:
84
+ logger(logging.CRITICAL, "Failed to schedule requests, error: %s" % (traceback.format_exc()))
85
+
86
+ ProducerConsumerDaemon(
87
+ producers=[_db_producer],
88
+ consumers=[_consumer],
89
+ graceful_stop=GRACEFUL_STOP,
90
+ ).run()
91
+
92
+
93
+ def stop(signum: Optional[int] = None, frame: Optional[FrameType] = None) -> None:
94
+ """
95
+ Graceful exit.
96
+ """
97
+
98
+ GRACEFUL_STOP.set()
99
+
100
+
101
+ def run(once=False, sleep_time=600):
102
+ """
103
+ Starts up the conveyer threads.
104
+ """
105
+ setup_logging(process_name=DAEMON_NAME)
106
+
107
+ if rucio.db.sqla.util.is_old_db():
108
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
109
+
110
+ throttler(once=once, sleep_time=sleep_time)
111
+
112
+
113
+ class RequestGrouper:
114
+
115
+ class RseStatistic:
116
+ def __init__(self):
117
+ self.sources_with_limits = set()
118
+ self.destinations_with_limits = set()
119
+ self.unavailable_sources = set()
120
+ self.unavailable_destinations = set()
121
+ self.has_any_per_activity_limit = False
122
+ self.any_source_has_per_activity_limit = False
123
+ self.any_destination_has_per_activity_limit = False
124
+
125
+ def __init__(self):
126
+ self.waiting_transfer_groups = {}
127
+ self.rse_stats = defaultdict(self.RseStatistic)
128
+
129
+ def record_waiting_request_group(self, source_rse, dest_rse, activity, applicable_limits):
130
+ """
131
+ Record a group of requests in waiting state, while computing some statistics about them.
132
+ """
133
+
134
+ src_info = self.rse_stats[source_rse]
135
+ dst_info = self.rse_stats[dest_rse]
136
+
137
+ if dest_rse and not dest_rse.columns['availability_write']:
138
+ src_info.unavailable_destinations.add(dest_rse)
139
+ if source_rse and not source_rse.columns['availability_read']:
140
+ dst_info.unavailable_sources.add(source_rse)
141
+ for limit_stat in applicable_limits:
142
+ limit = limit_stat['limit']
143
+ limit_is_per_activity = limit['activity'] is not None
144
+ if source_rse and limit['direction'] == TransferLimitDirection.SOURCE:
145
+ src_info.has_any_per_activity_limit |= limit_is_per_activity
146
+ dst_info.sources_with_limits.add(source_rse)
147
+ dst_info.any_source_has_per_activity_limit |= limit_is_per_activity
148
+ else:
149
+ dst_info.has_any_per_activity_limit |= limit_is_per_activity
150
+ src_info.destinations_with_limits.add(dest_rse)
151
+ src_info.any_destination_has_per_activity_limit |= limit_is_per_activity
152
+
153
+ self.waiting_transfer_groups[source_rse, dest_rse, activity] = applicable_limits
154
+
155
+ def merged_groups(self):
156
+ """
157
+ Merge groups which can be handled together
158
+ """
159
+
160
+ merged_groups = {}
161
+ for (source_rse, dest_rse, activity), applicable_limits in self.waiting_transfer_groups.items():
162
+
163
+ src_info = self.rse_stats[source_rse]
164
+ dst_info = self.rse_stats[dest_rse]
165
+
166
+ if not dst_info.sources_with_limits and not dst_info.unavailable_sources:
167
+ # None of the RSEs used as sources towards dest_rse has a limit configured
168
+ # And none of them is unavailable.
169
+ # It is possible to handle all waiting requests from any source in a single bulk
170
+ source_rse = None
171
+
172
+ if source_rse and not src_info.destinations_with_limits and not src_info.unavailable_destinations:
173
+ # All waiting requests from this source to any of the destination rses can be handled together
174
+ dest_rse = None
175
+
176
+ if source_rse and dest_rse:
177
+ if not dst_info.has_any_per_activity_limit and not src_info.has_any_per_activity_limit:
178
+ # All limits for this source/destination pair are configured per "all_activities",
179
+ # We can thus handle all activities in a single bulk.
180
+ activity = None
181
+ elif dest_rse:
182
+ if not dst_info.has_any_per_activity_limit and not dst_info.any_source_has_per_activity_limit:
183
+ # All limits, on all sources est_rse are configured
184
+ # per "all_activities". We don't need to handle each activity separately.
185
+ activity = None
186
+ elif source_rse:
187
+ if not src_info.has_any_per_activity_limit and not src_info.any_destination_has_per_activity_limit:
188
+ activity = None
189
+
190
+ merged_groups.setdefault((source_rse, dest_rse, activity), applicable_limits)
191
+
192
+ return merged_groups
193
+
194
+
195
+ def _get_request_stats(rse_collection: RseCollection, *, logger=logging.log):
196
+ """
197
+ Group waiting requests into arbitrary groups for bulk handling.
198
+ The current grouping (source rse + dest rse + activity) was dictated
199
+ by SQL queries used to release requests later in the throttler.
200
+ Any combination of the group attributes can be None. For example,
201
+ if activity and source rse are None, later code will work on all
202
+ requests towards their common destination rse in one go.
203
+
204
+ For each group, find the limits which apply to that group. The same
205
+ limit can be shared by multiple groups.
206
+
207
+ For each limit, compute the total number of active and waiting transfers
208
+ subject to that limit.
209
+ """
210
+ logging.info("Throttler retrieve requests statistics")
211
+
212
+ db_stats = get_request_stats(
213
+ state=[RequestState.QUEUED,
214
+ RequestState.SUBMITTING,
215
+ RequestState.SUBMITTED,
216
+ RequestState.WAITING],
217
+ )
218
+
219
+ # for each active limit, compute how many waiting and active transfers are currently in the database
220
+ limit_stats = {}
221
+ # for each group of (source_rse, destination_rse, activity) of waiting requests, find the limits which must be enforced
222
+ grouper = RequestGrouper()
223
+ for db_stat in db_stats:
224
+ account = db_stat.account
225
+ state = db_stat.state
226
+ counter = db_stat.counter
227
+ activity = db_stat.activity
228
+
229
+ try:
230
+ dest_rse = rse_collection[db_stat.dest_rse_id].ensure_loaded(load_transfer_limits=True, load_name=True, load_columns=True)
231
+ except exception.RSENotFound:
232
+ logger(logging.INFO, "Destination RSE {} not found. Probably deleted.", db_stat.dest_rse_id)
233
+ continue
234
+ source_rse = None
235
+ if db_stat.source_rse_id:
236
+ try:
237
+ source_rse = rse_collection[db_stat.source_rse_id].ensure_loaded(load_transfer_limits=True, load_name=True, load_columns=True)
238
+ except exception.RSENotFound:
239
+ logger(logging.INFO, "Source RSE {} not found. Probably deleted.", db_stat.source_rse_id)
240
+ continue
241
+
242
+ source_limits = list(applicable_rse_transfer_limits(activity=activity, source_rse=source_rse))
243
+ dest_limits = list(applicable_rse_transfer_limits(activity=activity, dest_rse=dest_rse))
244
+ limits = source_limits + dest_limits
245
+
246
+ if counter and (limits or state == RequestState.WAITING):
247
+ applicable_limits = []
248
+ for limit in limits:
249
+ limit_stat = limit_stats.setdefault(limit['id'], {})
250
+ applicable_limits.append(limit_stat)
251
+
252
+ if not limit_stat:
253
+ limit_stat.update({
254
+ 'limit': limit,
255
+ 'stat': {
256
+ 'waiting': 0,
257
+ 'active': 0,
258
+ 'accounts': {},
259
+ }
260
+ })
261
+
262
+ if account is None:
263
+ # account == None results in SQL queries which doesn't filter on account at all.
264
+ # While account == null() explicitly filters on "account is NULL" in the database.
265
+ # Here we want the second case.
266
+ account = null()
267
+
268
+ stat = limit_stat['stat']
269
+ if account not in stat['accounts']:
270
+ stat['accounts'][account] = {'waiting': 0, 'active': 0}
271
+
272
+ if state == RequestState.WAITING:
273
+ stat['waiting'] += counter
274
+ stat['accounts'][account]['waiting'] += counter
275
+ else:
276
+ stat['active'] += counter
277
+ stat['accounts'][account]['active'] += counter
278
+
279
+ if state == RequestState.WAITING:
280
+ grouper.record_waiting_request_group(
281
+ source_rse=source_rse,
282
+ dest_rse=dest_rse,
283
+ activity=activity,
284
+ applicable_limits=applicable_limits,
285
+ )
286
+
287
+ # Find the residual capacity in each of the limits
288
+ for limit_stat in limit_stats.values():
289
+ stat = limit_stat['stat']
290
+ limit = limit_stat['limit']
291
+
292
+ waiting = stat['waiting']
293
+ active = stat['active']
294
+
295
+ max_transfers = limit['max_transfers']
296
+
297
+ rse_expression = limit['rse_expression']
298
+ log_str = f'limit {"from" if limit["direction"] == "source" else "to"} {rse_expression} activity {limit["activity"]}'
299
+
300
+ if max_transfers is None:
301
+ # The limit was explicitly set to NULL. Release all waiting requests.
302
+ residual_capacity = math.inf
303
+ elif active < 0.8 * max_transfers:
304
+ residual_capacity = max_transfers - active
305
+ elif 0.8 * max_transfers <= active < max_transfers:
306
+ # Don't release requests yet. We desire to release transfers in bigger bulks than the currently available capacity.
307
+ logger(logging.DEBUG, "%s: will do nothing (active >= 0.8 * max_transfers)", log_str)
308
+ residual_capacity = 0
309
+ else: # active >= max_transfers
310
+ residual_capacity = 0
311
+ stat['residual_capacity'] = residual_capacity
312
+
313
+ activity = limit['activity'] or 'all_activities'
314
+ METRICS.gauge('rse_transfer_limits.{activity}.{rse}.{limit_attr}').labels(activity=activity, rse=rse_expression, limit_attr='residual_capacity').set(residual_capacity)
315
+ METRICS.gauge('rse_transfer_limits.{activity}.{rse}.{limit_attr}').labels(activity=activity, rse=rse_expression, limit_attr='max_transfers').set(max_transfers)
316
+ METRICS.gauge('rse_transfer_limits.{activity}.{rse}.{limit_attr}').labels(activity=activity, rse=rse_expression, limit_attr='active').set(active)
317
+ METRICS.gauge('rse_transfer_limits.{activity}.{rse}.{limit_attr}').labels(activity=activity, rse=rse_expression, limit_attr='waiting').set(waiting)
318
+
319
+ if waiting:
320
+ logger(logging.DEBUG, "%s: can release %s out of %s waiting requests", log_str, residual_capacity, waiting)
321
+
322
+ if waiting != limit['waitings'] or active != limit['transfers']:
323
+ set_transfer_limit_stats(limit['id'], waitings=waiting, transfers=active)
324
+
325
+ for account, to_release_for_account in _split_threshold_per_account(stat['accounts'], total_to_release=residual_capacity):
326
+ stat['accounts'][account]['residual_capacity'] = to_release_for_account
327
+
328
+ release_groups = grouper.merged_groups()
329
+ return release_groups
330
+
331
+
332
+ def _split_threshold_per_account(per_account_stats, total_to_release):
333
+ """
334
+ Compute how many requests to release for each account. Try to achieve a fair share of transfers between accounts.
335
+ :param per_account_stats: a dict with how many active and waiting transfers each account has
336
+ :param total_to_release: the total threshold allowed to be released
337
+ :return: for each account, how many requests to release
338
+ """
339
+
340
+ if not per_account_stats:
341
+ return None, total_to_release
342
+
343
+ nr_accounts = len(per_account_stats)
344
+ remaining_to_release = total_to_release
345
+ remaining_accounts = nr_accounts
346
+ for account, account_stat in sorted(per_account_stats.items(), key=lambda i: i[1]['waiting']):
347
+ threshold_per_account = math.ceil(remaining_to_release / remaining_accounts)
348
+
349
+ waiting = account_stat['waiting']
350
+ to_release_for_account = min(waiting, threshold_per_account)
351
+
352
+ yield account, to_release_for_account
353
+
354
+ remaining_accounts -= 1
355
+ remaining_to_release -= to_release_for_account
356
+
357
+
358
+ def _combine_limits(applicable_limits):
359
+ """
360
+ Take multiple limits and combines them into one single (strictest) limit which
361
+ respects the constraints of each initial limits. This is to handle cases like:
362
+ - source rse only allows 5 transfers; destination allows 10 -> keep the stricter limit (here: 5 transfers)
363
+ - an RSE has multiple limits due to overlapping rse expressions -> also keep the stricter limit
364
+
365
+ Rules:
366
+ - for `to_release`: pick the minimum available residual capacity
367
+ - for `max_transfers` and 'volume': just pick the minimum
368
+ - prioritize `grouped_fifo` strategy over `fifo` (fifo being the default when not set)
369
+ - keep the closest deadline
370
+ """
371
+ strategy_priorities = {
372
+ 'grouped_fifo': 1,
373
+ 'fifo': 2,
374
+ None: 3,
375
+ }
376
+ to_release = math.inf
377
+ max_transfers = None
378
+ strategy = 'fifo'
379
+ volume = None
380
+ deadline = None
381
+ for limit_stat in applicable_limits:
382
+ limit = limit_stat['limit']
383
+ stat = limit_stat['stat']
384
+
385
+ to_release = min(stat['residual_capacity'], to_release)
386
+ max_transfers = min(limit['max_transfers'], max_transfers, key=lambda x: x if x is not None else math.inf)
387
+ strategy = min(limit['strategy'], strategy, key=lambda x: strategy_priorities.get(x, math.inf))
388
+ volume = min(limit['volume'], volume, key=lambda x: x if x is not None else math.inf)
389
+ deadline = min(limit['deadline'], deadline, key=lambda x: x if x is not None else math.inf)
390
+
391
+ return to_release, strategy, volume, deadline
392
+
393
+
394
+ def _handle_requests(release_groups, logger):
395
+ """
396
+ Release (set to queued state) waiting requests in groups defined by release_groups.
397
+
398
+ The same limit can be shared by multiple groups. Because of that, releasing requests
399
+ from one group can impact how many requests may be released in other groups subjected
400
+ to the same limit.
401
+ """
402
+
403
+ for (source_rse, dest_rse, activity), applicable_limits in release_groups.items():
404
+
405
+ # Skip if dest_rse is blocklisted for write or src_rse is blocklisted for read
406
+ if dest_rse and not dest_rse.columns['availability_write']:
407
+ continue
408
+ if source_rse and not source_rse.columns['availability_read']:
409
+ continue
410
+
411
+ source_rse_id = source_rse.id if source_rse else None
412
+ dest_rse_id = dest_rse.id if dest_rse else None
413
+
414
+ log_str = (f' for activity "{activity}"' if activity else '') + \
415
+ (f' from rse {source_rse}' if source_rse else '') + \
416
+ (f' to rse {dest_rse}' if dest_rse else '')
417
+
418
+ to_release, strategy, volume, deadline = _combine_limits(applicable_limits)
419
+ if not to_release:
420
+ logger(logging.DEBUG, "no requests can be released%s", log_str)
421
+ total_released = 0
422
+ elif to_release == math.inf:
423
+ logger(logging.DEBUG, "will release all waiting requests%s", log_str)
424
+ total_released = release_all_waiting_requests(dest_rse_id=dest_rse_id, source_rse_id=source_rse_id, activity=activity)
425
+ elif strategy == 'grouped_fifo':
426
+ logger(logging.DEBUG, "will release %s remaining requests%s", to_release, log_str)
427
+ additional_kwargs = {}
428
+ if volume is not None:
429
+ additional_kwargs['volume'] = volume
430
+ if deadline is not None:
431
+ additional_kwargs['deadline'] = deadline
432
+ total_released = release_waiting_requests_grouped_fifo(
433
+ source_rse_id=source_rse_id,
434
+ dest_rse_id=dest_rse_id,
435
+ count=to_release,
436
+ **additional_kwargs,
437
+ )
438
+ else:
439
+ total_released = 0
440
+ to_release_for_account = {}
441
+ limits_by_account = {}
442
+ for limit_stat in applicable_limits:
443
+ for account, account_limit in limit_stat['stat']['accounts'].items():
444
+ to_release_for_account[account] = min(to_release_for_account.get(account, to_release), account_limit['residual_capacity'])
445
+ limits_by_account.setdefault(account, []).append(account_limit)
446
+
447
+ for account, to_release_account in to_release_for_account.items():
448
+ if not to_release_account:
449
+ continue
450
+
451
+ logger(logging.DEBUG, 'releasing %s waiting requests%s%s', to_release_account, log_str, f' account {account}' if account is not None else '')
452
+ nb_released = release_waiting_requests_fifo(
453
+ source_rse_id=source_rse_id,
454
+ dest_rse_id=dest_rse_id,
455
+ count=to_release_account,
456
+ activity=activity,
457
+ account=account,
458
+ )
459
+ total_released += nb_released
460
+
461
+ for stat in limits_by_account[account]:
462
+ stat['residual_capacity'] -= nb_released
463
+
464
+ if total_released:
465
+ for limit_stat in applicable_limits:
466
+ rse_expression = limit_stat['limit']['rse_expression']
467
+ limit_stat['stat']['residual_capacity'] -= total_released
468
+ METRICS.counter('released_waiting_requests.{activity}.{rse}').labels(activity=activity, rse=rse_expression).inc(total_released)
@@ -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,97 @@
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
+ import os
18
+ import socket
19
+ import threading
20
+ import time
21
+ from typing import TYPE_CHECKING
22
+
23
+ import rucio.db.sqla.util
24
+ from rucio.common import exception
25
+ from rucio.common.logging import setup_logging
26
+ from rucio.common.utils import get_thread_with_periodic_running_function
27
+ from rucio.core.did import create_reports
28
+ from rucio.core.heartbeat import live, die, sanity_check
29
+
30
+ if TYPE_CHECKING:
31
+ from types import FrameType
32
+ from typing import Optional
33
+
34
+ graceful_stop = threading.Event()
35
+ DAEMON_NAME = 'rucio-follower'
36
+
37
+
38
+ def aggregate_events(once=False):
39
+ """
40
+ Collect all the events affecting the dids followed by the corresponding account.
41
+ """
42
+
43
+ logging.info('event_aggregation: started')
44
+
45
+ hostname = socket.gethostname()
46
+ pid = os.getpid()
47
+ current_thread = threading.current_thread()
48
+ live(executable=DAEMON_NAME, hostname=hostname, pid=pid, thread=current_thread)
49
+
50
+ while not graceful_stop.is_set():
51
+ heartbeat = live(executable=DAEMON_NAME, hostname=hostname, pid=pid, thread=current_thread)
52
+ # Create a report of events and send a mail to the corresponding account.
53
+ start_time = time.time()
54
+ create_reports(total_workers=heartbeat['nr_threads'] - 1,
55
+ worker_number=heartbeat['assign_thread'])
56
+ logging.info('worker[%s/%s] took %s for creating reports' % (heartbeat['assign_thread'], heartbeat['nr_threads'] - 1, time.time() - start_time))
57
+
58
+ if once:
59
+ break
60
+
61
+ logging.info('follower: graceful stop requested')
62
+ die(executable=DAEMON_NAME, hostname=hostname, pid=pid, thread=current_thread)
63
+ logging.info('follower: graceful stop done')
64
+
65
+
66
+ def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
67
+ """
68
+ Graceful exit.
69
+ """
70
+ graceful_stop.set()
71
+
72
+
73
+ def run(once=False, threads=1):
74
+ """
75
+ Starts up the follower threads
76
+ """
77
+ setup_logging(process_name=DAEMON_NAME)
78
+
79
+ if rucio.db.sqla.util.is_old_db():
80
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
81
+
82
+ hostname = socket.gethostname()
83
+ sanity_check(executable=DAEMON_NAME, hostname=hostname)
84
+
85
+ if once:
86
+ logging.info("executing one follower iteration only")
87
+ aggregate_events(once)
88
+ else:
89
+ logging.info("starting follower threads")
90
+ # Run the follower daemon thrice a day
91
+ threads = [get_thread_with_periodic_running_function(28800, aggregate_events, graceful_stop) for i in range(threads)]
92
+ [t.start() for t in threads]
93
+
94
+ logging.info("waiting for interrupts")
95
+ # Interruptible joins require a timeout.
96
+ while threads[0].is_alive():
97
+ [t.join(timeout=3.14) for t in threads]
@@ -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.