rucio 35.7.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (493) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/client/__init__.py +15 -0
  4. rucio/client/accountclient.py +433 -0
  5. rucio/client/accountlimitclient.py +183 -0
  6. rucio/client/baseclient.py +974 -0
  7. rucio/client/client.py +76 -0
  8. rucio/client/configclient.py +126 -0
  9. rucio/client/credentialclient.py +59 -0
  10. rucio/client/didclient.py +866 -0
  11. rucio/client/diracclient.py +56 -0
  12. rucio/client/downloadclient.py +1785 -0
  13. rucio/client/exportclient.py +44 -0
  14. rucio/client/fileclient.py +50 -0
  15. rucio/client/importclient.py +42 -0
  16. rucio/client/lifetimeclient.py +90 -0
  17. rucio/client/lockclient.py +109 -0
  18. rucio/client/metaconventionsclient.py +140 -0
  19. rucio/client/pingclient.py +44 -0
  20. rucio/client/replicaclient.py +454 -0
  21. rucio/client/requestclient.py +125 -0
  22. rucio/client/rseclient.py +746 -0
  23. rucio/client/ruleclient.py +294 -0
  24. rucio/client/scopeclient.py +90 -0
  25. rucio/client/subscriptionclient.py +173 -0
  26. rucio/client/touchclient.py +82 -0
  27. rucio/client/uploadclient.py +955 -0
  28. rucio/common/__init__.py +13 -0
  29. rucio/common/cache.py +74 -0
  30. rucio/common/config.py +801 -0
  31. rucio/common/constants.py +159 -0
  32. rucio/common/constraints.py +17 -0
  33. rucio/common/didtype.py +189 -0
  34. rucio/common/dumper/__init__.py +335 -0
  35. rucio/common/dumper/consistency.py +452 -0
  36. rucio/common/dumper/data_models.py +318 -0
  37. rucio/common/dumper/path_parsing.py +64 -0
  38. rucio/common/exception.py +1151 -0
  39. rucio/common/extra.py +36 -0
  40. rucio/common/logging.py +420 -0
  41. rucio/common/pcache.py +1408 -0
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +84 -0
  44. rucio/common/schema/__init__.py +150 -0
  45. rucio/common/schema/atlas.py +413 -0
  46. rucio/common/schema/belleii.py +408 -0
  47. rucio/common/schema/domatpc.py +401 -0
  48. rucio/common/schema/escape.py +426 -0
  49. rucio/common/schema/generic.py +433 -0
  50. rucio/common/schema/generic_multi_vo.py +412 -0
  51. rucio/common/schema/icecube.py +406 -0
  52. rucio/common/stomp_utils.py +159 -0
  53. rucio/common/stopwatch.py +55 -0
  54. rucio/common/test_rucio_server.py +148 -0
  55. rucio/common/types.py +403 -0
  56. rucio/common/utils.py +2238 -0
  57. rucio/core/__init__.py +13 -0
  58. rucio/core/account.py +496 -0
  59. rucio/core/account_counter.py +236 -0
  60. rucio/core/account_limit.py +423 -0
  61. rucio/core/authentication.py +620 -0
  62. rucio/core/config.py +456 -0
  63. rucio/core/credential.py +225 -0
  64. rucio/core/did.py +3000 -0
  65. rucio/core/did_meta_plugins/__init__.py +252 -0
  66. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  68. rucio/core/did_meta_plugins/filter_engine.py +613 -0
  69. rucio/core/did_meta_plugins/json_meta.py +240 -0
  70. rucio/core/did_meta_plugins/mongo_meta.py +216 -0
  71. rucio/core/did_meta_plugins/postgres_meta.py +316 -0
  72. rucio/core/dirac.py +237 -0
  73. rucio/core/distance.py +187 -0
  74. rucio/core/exporter.py +59 -0
  75. rucio/core/heartbeat.py +363 -0
  76. rucio/core/identity.py +300 -0
  77. rucio/core/importer.py +259 -0
  78. rucio/core/lifetime_exception.py +377 -0
  79. rucio/core/lock.py +576 -0
  80. rucio/core/message.py +282 -0
  81. rucio/core/meta_conventions.py +203 -0
  82. rucio/core/monitor.py +447 -0
  83. rucio/core/naming_convention.py +195 -0
  84. rucio/core/nongrid_trace.py +136 -0
  85. rucio/core/oidc.py +1461 -0
  86. rucio/core/permission/__init__.py +119 -0
  87. rucio/core/permission/atlas.py +1348 -0
  88. rucio/core/permission/belleii.py +1077 -0
  89. rucio/core/permission/escape.py +1078 -0
  90. rucio/core/permission/generic.py +1130 -0
  91. rucio/core/permission/generic_multi_vo.py +1150 -0
  92. rucio/core/quarantined_replica.py +223 -0
  93. rucio/core/replica.py +4158 -0
  94. rucio/core/replica_sorter.py +366 -0
  95. rucio/core/request.py +3089 -0
  96. rucio/core/rse.py +1875 -0
  97. rucio/core/rse_counter.py +186 -0
  98. rucio/core/rse_expression_parser.py +459 -0
  99. rucio/core/rse_selector.py +302 -0
  100. rucio/core/rule.py +4483 -0
  101. rucio/core/rule_grouping.py +1618 -0
  102. rucio/core/scope.py +180 -0
  103. rucio/core/subscription.py +364 -0
  104. rucio/core/topology.py +490 -0
  105. rucio/core/trace.py +375 -0
  106. rucio/core/transfer.py +1517 -0
  107. rucio/core/vo.py +169 -0
  108. rucio/core/volatile_replica.py +150 -0
  109. rucio/daemons/__init__.py +13 -0
  110. rucio/daemons/abacus/__init__.py +13 -0
  111. rucio/daemons/abacus/account.py +116 -0
  112. rucio/daemons/abacus/collection_replica.py +124 -0
  113. rucio/daemons/abacus/rse.py +117 -0
  114. rucio/daemons/atropos/__init__.py +13 -0
  115. rucio/daemons/atropos/atropos.py +242 -0
  116. rucio/daemons/auditor/__init__.py +289 -0
  117. rucio/daemons/auditor/hdfs.py +97 -0
  118. rucio/daemons/auditor/srmdumps.py +355 -0
  119. rucio/daemons/automatix/__init__.py +13 -0
  120. rucio/daemons/automatix/automatix.py +293 -0
  121. rucio/daemons/badreplicas/__init__.py +13 -0
  122. rucio/daemons/badreplicas/minos.py +322 -0
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  124. rucio/daemons/badreplicas/necromancer.py +196 -0
  125. rucio/daemons/bb8/__init__.py +13 -0
  126. rucio/daemons/bb8/bb8.py +353 -0
  127. rucio/daemons/bb8/common.py +759 -0
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  129. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  130. rucio/daemons/c3po/__init__.py +13 -0
  131. rucio/daemons/c3po/algorithms/__init__.py +13 -0
  132. rucio/daemons/c3po/algorithms/simple.py +134 -0
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +128 -0
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +130 -0
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +294 -0
  136. rucio/daemons/c3po/c3po.py +371 -0
  137. rucio/daemons/c3po/collectors/__init__.py +13 -0
  138. rucio/daemons/c3po/collectors/agis.py +108 -0
  139. rucio/daemons/c3po/collectors/free_space.py +81 -0
  140. rucio/daemons/c3po/collectors/jedi_did.py +57 -0
  141. rucio/daemons/c3po/collectors/mock_did.py +51 -0
  142. rucio/daemons/c3po/collectors/network_metrics.py +71 -0
  143. rucio/daemons/c3po/collectors/workload.py +112 -0
  144. rucio/daemons/c3po/utils/__init__.py +13 -0
  145. rucio/daemons/c3po/utils/dataset_cache.py +50 -0
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +56 -0
  147. rucio/daemons/c3po/utils/expiring_list.py +62 -0
  148. rucio/daemons/c3po/utils/popularity.py +85 -0
  149. rucio/daemons/c3po/utils/timeseries.py +89 -0
  150. rucio/daemons/cache/__init__.py +13 -0
  151. rucio/daemons/cache/consumer.py +197 -0
  152. rucio/daemons/common.py +415 -0
  153. rucio/daemons/conveyor/__init__.py +13 -0
  154. rucio/daemons/conveyor/common.py +562 -0
  155. rucio/daemons/conveyor/finisher.py +529 -0
  156. rucio/daemons/conveyor/poller.py +404 -0
  157. rucio/daemons/conveyor/preparer.py +205 -0
  158. rucio/daemons/conveyor/receiver.py +249 -0
  159. rucio/daemons/conveyor/stager.py +132 -0
  160. rucio/daemons/conveyor/submitter.py +403 -0
  161. rucio/daemons/conveyor/throttler.py +532 -0
  162. rucio/daemons/follower/__init__.py +13 -0
  163. rucio/daemons/follower/follower.py +101 -0
  164. rucio/daemons/hermes/__init__.py +13 -0
  165. rucio/daemons/hermes/hermes.py +774 -0
  166. rucio/daemons/judge/__init__.py +13 -0
  167. rucio/daemons/judge/cleaner.py +159 -0
  168. rucio/daemons/judge/evaluator.py +185 -0
  169. rucio/daemons/judge/injector.py +162 -0
  170. rucio/daemons/judge/repairer.py +154 -0
  171. rucio/daemons/oauthmanager/__init__.py +13 -0
  172. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  173. rucio/daemons/reaper/__init__.py +13 -0
  174. rucio/daemons/reaper/dark_reaper.py +278 -0
  175. rucio/daemons/reaper/reaper.py +743 -0
  176. rucio/daemons/replicarecoverer/__init__.py +13 -0
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  178. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +13 -0
  186. rucio/daemons/storage/consistency/__init__.py +13 -0
  187. rucio/daemons/storage/consistency/actions.py +846 -0
  188. rucio/daemons/tracer/__init__.py +13 -0
  189. rucio/daemons/tracer/kronos.py +536 -0
  190. rucio/daemons/transmogrifier/__init__.py +13 -0
  191. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  192. rucio/daemons/undertaker/__init__.py +13 -0
  193. rucio/daemons/undertaker/undertaker.py +137 -0
  194. rucio/db/__init__.py +13 -0
  195. rucio/db/sqla/__init__.py +52 -0
  196. rucio/db/sqla/constants.py +201 -0
  197. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  198. rucio/db/sqla/migrate_repo/env.py +110 -0
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  327. rucio/db/sqla/models.py +1740 -0
  328. rucio/db/sqla/sautils.py +55 -0
  329. rucio/db/sqla/session.py +498 -0
  330. rucio/db/sqla/types.py +206 -0
  331. rucio/db/sqla/util.py +543 -0
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/gateway/account.py +339 -0
  334. rucio/gateway/account_limit.py +286 -0
  335. rucio/gateway/authentication.py +375 -0
  336. rucio/gateway/config.py +217 -0
  337. rucio/gateway/credential.py +71 -0
  338. rucio/gateway/did.py +970 -0
  339. rucio/gateway/dirac.py +81 -0
  340. rucio/gateway/exporter.py +59 -0
  341. rucio/gateway/heartbeat.py +74 -0
  342. rucio/gateway/identity.py +204 -0
  343. rucio/gateway/importer.py +45 -0
  344. rucio/gateway/lifetime_exception.py +120 -0
  345. rucio/gateway/lock.py +153 -0
  346. rucio/gateway/meta_conventions.py +87 -0
  347. rucio/gateway/permission.py +71 -0
  348. rucio/gateway/quarantined_replica.py +78 -0
  349. rucio/gateway/replica.py +529 -0
  350. rucio/gateway/request.py +321 -0
  351. rucio/gateway/rse.py +600 -0
  352. rucio/gateway/rule.py +417 -0
  353. rucio/gateway/scope.py +99 -0
  354. rucio/gateway/subscription.py +277 -0
  355. rucio/gateway/vo.py +122 -0
  356. rucio/rse/__init__.py +96 -0
  357. rucio/rse/protocols/__init__.py +13 -0
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +122 -0
  360. rucio/rse/protocols/dummy.py +111 -0
  361. rucio/rse/protocols/gfal.py +703 -0
  362. rucio/rse/protocols/globus.py +243 -0
  363. rucio/rse/protocols/gsiftp.py +92 -0
  364. rucio/rse/protocols/http_cache.py +82 -0
  365. rucio/rse/protocols/mock.py +123 -0
  366. rucio/rse/protocols/ngarc.py +209 -0
  367. rucio/rse/protocols/posix.py +250 -0
  368. rucio/rse/protocols/protocol.py +594 -0
  369. rucio/rse/protocols/rclone.py +364 -0
  370. rucio/rse/protocols/rfio.py +136 -0
  371. rucio/rse/protocols/srm.py +338 -0
  372. rucio/rse/protocols/ssh.py +413 -0
  373. rucio/rse/protocols/storm.py +206 -0
  374. rucio/rse/protocols/webdav.py +550 -0
  375. rucio/rse/protocols/xrootd.py +301 -0
  376. rucio/rse/rsemanager.py +764 -0
  377. rucio/tests/__init__.py +13 -0
  378. rucio/tests/common.py +270 -0
  379. rucio/tests/common_server.py +132 -0
  380. rucio/transfertool/__init__.py +13 -0
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +1596 -0
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +201 -0
  387. rucio/transfertool/globus_library.py +181 -0
  388. rucio/transfertool/mock.py +90 -0
  389. rucio/transfertool/transfertool.py +221 -0
  390. rucio/vcsversion.py +11 -0
  391. rucio/version.py +38 -0
  392. rucio/web/__init__.py +13 -0
  393. rucio/web/rest/__init__.py +13 -0
  394. rucio/web/rest/flaskapi/__init__.py +13 -0
  395. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  396. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  398. rucio/web/rest/flaskapi/v1/accounts.py +1089 -0
  399. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  400. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  401. rucio/web/rest/flaskapi/v1/common.py +426 -0
  402. rucio/web/rest/flaskapi/v1/config.py +304 -0
  403. rucio/web/rest/flaskapi/v1/credentials.py +212 -0
  404. rucio/web/rest/flaskapi/v1/dids.py +2334 -0
  405. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  406. rucio/web/rest/flaskapi/v1/export.py +75 -0
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  408. rucio/web/rest/flaskapi/v1/identities.py +261 -0
  409. rucio/web/rest/flaskapi/v1/import.py +132 -0
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  411. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  412. rucio/web/rest/flaskapi/v1/main.py +91 -0
  413. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  414. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  416. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  417. rucio/web/rest/flaskapi/v1/redirect.py +365 -0
  418. rucio/web/rest/flaskapi/v1/replicas.py +1890 -0
  419. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  420. rucio/web/rest/flaskapi/v1/rses.py +2239 -0
  421. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  422. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  424. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  425. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  426. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  427. rucio/web/rest/flaskapi/v1/types.py +20 -0
  428. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  429. rucio/web/rest/main.py +18 -0
  430. rucio/web/rest/metrics.py +27 -0
  431. rucio/web/rest/ping.py +27 -0
  432. rucio-35.7.0.data/data/rucio/etc/alembic.ini.template +71 -0
  433. rucio-35.7.0.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  434. rucio-35.7.0.data/data/rucio/etc/globus-config.yml.template +5 -0
  435. rucio-35.7.0.data/data/rucio/etc/ldap.cfg.template +30 -0
  436. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  437. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  438. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  439. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  440. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  441. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  442. rucio-35.7.0.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  443. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  444. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.template +257 -0
  445. rucio-35.7.0.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  446. rucio-35.7.0.data/data/rucio/requirements.server.txt +268 -0
  447. rucio-35.7.0.data/data/rucio/tools/bootstrap.py +34 -0
  448. rucio-35.7.0.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  449. rucio-35.7.0.data/data/rucio/tools/reset_database.py +40 -0
  450. rucio-35.7.0.data/scripts/rucio +2542 -0
  451. rucio-35.7.0.data/scripts/rucio-abacus-account +74 -0
  452. rucio-35.7.0.data/scripts/rucio-abacus-collection-replica +46 -0
  453. rucio-35.7.0.data/scripts/rucio-abacus-rse +78 -0
  454. rucio-35.7.0.data/scripts/rucio-admin +2447 -0
  455. rucio-35.7.0.data/scripts/rucio-atropos +60 -0
  456. rucio-35.7.0.data/scripts/rucio-auditor +205 -0
  457. rucio-35.7.0.data/scripts/rucio-automatix +50 -0
  458. rucio-35.7.0.data/scripts/rucio-bb8 +57 -0
  459. rucio-35.7.0.data/scripts/rucio-c3po +85 -0
  460. rucio-35.7.0.data/scripts/rucio-cache-client +134 -0
  461. rucio-35.7.0.data/scripts/rucio-cache-consumer +42 -0
  462. rucio-35.7.0.data/scripts/rucio-conveyor-finisher +58 -0
  463. rucio-35.7.0.data/scripts/rucio-conveyor-poller +66 -0
  464. rucio-35.7.0.data/scripts/rucio-conveyor-preparer +37 -0
  465. rucio-35.7.0.data/scripts/rucio-conveyor-receiver +43 -0
  466. rucio-35.7.0.data/scripts/rucio-conveyor-stager +76 -0
  467. rucio-35.7.0.data/scripts/rucio-conveyor-submitter +139 -0
  468. rucio-35.7.0.data/scripts/rucio-conveyor-throttler +104 -0
  469. rucio-35.7.0.data/scripts/rucio-dark-reaper +53 -0
  470. rucio-35.7.0.data/scripts/rucio-dumper +160 -0
  471. rucio-35.7.0.data/scripts/rucio-follower +44 -0
  472. rucio-35.7.0.data/scripts/rucio-hermes +54 -0
  473. rucio-35.7.0.data/scripts/rucio-judge-cleaner +89 -0
  474. rucio-35.7.0.data/scripts/rucio-judge-evaluator +137 -0
  475. rucio-35.7.0.data/scripts/rucio-judge-injector +44 -0
  476. rucio-35.7.0.data/scripts/rucio-judge-repairer +44 -0
  477. rucio-35.7.0.data/scripts/rucio-kronos +43 -0
  478. rucio-35.7.0.data/scripts/rucio-minos +53 -0
  479. rucio-35.7.0.data/scripts/rucio-minos-temporary-expiration +50 -0
  480. rucio-35.7.0.data/scripts/rucio-necromancer +120 -0
  481. rucio-35.7.0.data/scripts/rucio-oauth-manager +63 -0
  482. rucio-35.7.0.data/scripts/rucio-reaper +83 -0
  483. rucio-35.7.0.data/scripts/rucio-replica-recoverer +248 -0
  484. rucio-35.7.0.data/scripts/rucio-rse-decommissioner +66 -0
  485. rucio-35.7.0.data/scripts/rucio-storage-consistency-actions +74 -0
  486. rucio-35.7.0.data/scripts/rucio-transmogrifier +77 -0
  487. rucio-35.7.0.data/scripts/rucio-undertaker +76 -0
  488. rucio-35.7.0.dist-info/METADATA +72 -0
  489. rucio-35.7.0.dist-info/RECORD +493 -0
  490. rucio-35.7.0.dist-info/WHEEL +5 -0
  491. rucio-35.7.0.dist-info/licenses/AUTHORS.rst +97 -0
  492. rucio-35.7.0.dist-info/licenses/LICENSE +201 -0
  493. rucio-35.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,197 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Cache consumer is a daemon to retrieve rucio cache operation information to synchronize rucio catalog.
17
+ """
18
+
19
+ import json
20
+ import logging
21
+ import threading
22
+ import time
23
+ from traceback import format_exc
24
+ from typing import TYPE_CHECKING, Optional
25
+
26
+ import rucio.db.sqla.util
27
+ from rucio.common import exception
28
+ from rucio.common.config import config_get, config_get_bool, config_get_int, config_get_list
29
+ from rucio.common.logging import formatted_logger, setup_logging
30
+ from rucio.common.stomp_utils import StompConnectionManager
31
+ from rucio.common.types import InternalScope, LoggerFunction
32
+ from rucio.core.monitor import MetricManager
33
+ from rucio.core.rse import get_rse_id
34
+ from rucio.core.volatile_replica import add_volatile_replicas, delete_volatile_replicas
35
+
36
+ if TYPE_CHECKING:
37
+ from types import FrameType
38
+
39
+ from stomp import Connection
40
+ from stomp.utils import Frame
41
+
42
+ logging.getLogger("stomp").setLevel(logging.CRITICAL)
43
+
44
+ METRICS = MetricManager(module=__name__)
45
+ GRACEFUL_STOP = threading.Event()
46
+ DAEMON_NAME = 'cache-consumer'
47
+
48
+
49
+ class AMQConsumer:
50
+ """
51
+ class Consumer
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ broker: str,
57
+ conn: "Connection",
58
+ logger: "LoggerFunction"
59
+ ):
60
+ """
61
+ __init__
62
+ """
63
+ self.__broker = broker
64
+ self.__conn = conn
65
+ self.__logger = logger
66
+
67
+ @METRICS.count_it
68
+ def on_heartbeat_timeout(self) -> None:
69
+ self.__conn.disconnect()
70
+
71
+ @METRICS.count_it
72
+ def on_error(self, frame: "Frame") -> None:
73
+ """
74
+ on_error
75
+ """
76
+ self.__logger(logging.ERROR, 'Message receive error: [%s] %s' % (self.__broker, frame.body))
77
+
78
+ @METRICS.count_it
79
+ def on_message(self, frame: "Frame") -> None:
80
+ """
81
+ on_message
82
+ """
83
+ try:
84
+ msg = json.loads(frame.body) # type: ignore
85
+ self.__logger(logging.DEBUG, 'Message received: %s ' % msg)
86
+ if isinstance(msg, dict) and 'operation' in msg.keys():
87
+ for f in msg['files']:
88
+ f['scope'] = InternalScope(f['scope'])
89
+ if 'rse_id' in msg:
90
+ rse_id = msg['rse_id']
91
+ else:
92
+ rse_id = get_rse_id(rse=msg['rse'], vo=msg.get('vo', 'def'))
93
+
94
+ rse_vo_str = msg['rse']
95
+ if 'vo' in msg and msg['vo'] != 'def':
96
+ rse_vo_str = '{} on {}'.format(rse_vo_str, msg['vo'])
97
+ if msg['operation'] == 'add_replicas':
98
+ self.__logger(logging.INFO, 'add_replicas to RSE %s: %s ' % (rse_vo_str, str(msg['files'])))
99
+ add_volatile_replicas(rse_id=rse_id, replicas=msg['files'])
100
+ elif msg['operation'] == 'delete_replicas':
101
+ self.__logger(logging.INFO, 'delete_replicas to RSE %s: %s ' % (rse_vo_str, str(msg['files'])))
102
+ delete_volatile_replicas(rse_id=rse_id, replicas=msg['files'])
103
+ else:
104
+ self.__logger(logging.DEBUG, 'Check failed: %s %s '
105
+ % (isinstance(msg, dict), 'operation' in msg.keys()))
106
+ except:
107
+ self.__logger(logging.ERROR, str(format_exc()))
108
+
109
+
110
+ def consumer(id_: int, num_thread: int = 1) -> None:
111
+ """
112
+ Main loop to consume messages from the Rucio Cache producer.
113
+ """
114
+
115
+ logger = formatted_logger(logging.log, DAEMON_NAME + ' %s')
116
+
117
+ logger(logging.INFO, 'Rucio Cache consumer starting')
118
+
119
+ brokers = config_get_list('messaging-cache', 'brokers')
120
+
121
+ use_ssl = config_get_bool('messaging-cache', 'use_ssl', default=True, raise_exception=False)
122
+ if not use_ssl:
123
+ username = config_get('messaging-cache', 'username')
124
+ password = config_get('messaging-cache', 'password')
125
+ destination = config_get('messaging-cache', 'destination')
126
+ subscription_id = 'rucio-cache-messaging'
127
+
128
+ vhost = config_get('messaging-cache', 'broker_virtual_host', raise_exception=False)
129
+ port = config_get_int('messaging-cache', 'port')
130
+ reconnect_attempts = config_get_int('messaging-cache', 'reconnect_attempts', default=100)
131
+ ssl_key_file = config_get('messaging-cache', 'ssl_key_file', raise_exception=False)
132
+ ssl_cert_file = config_get('messaging-cache', 'ssl_cert_file', raise_exception=False)
133
+
134
+ stomp_conn_mngr = StompConnectionManager()
135
+ conns, _ = stomp_conn_mngr.re_configure(
136
+ brokers=brokers,
137
+ port=port,
138
+ use_ssl=use_ssl,
139
+ vhost=vhost,
140
+ reconnect_attempts=reconnect_attempts,
141
+ ssl_key_file=ssl_key_file,
142
+ ssl_cert_file=ssl_cert_file,
143
+ timeout=None,
144
+ logger=logger
145
+ )
146
+
147
+ logger(logging.INFO, 'consumer started')
148
+
149
+ while not GRACEFUL_STOP.is_set():
150
+ for conn in conns:
151
+ if not conn.is_connected():
152
+ host_port = conn.transport._Transport__host_and_ports[0]
153
+
154
+ logger(logging.INFO, 'connecting to %s' % host_port[0])
155
+ METRICS.counter('reconnect.{host}').labels(host=host_port[0]).inc()
156
+ conn.set_listener('rucio-cache-consumer', AMQConsumer(broker=host_port, conn=conn, logger=logger))
157
+ if not use_ssl:
158
+ conn.connect(username, password)
159
+ else:
160
+ conn.connect()
161
+
162
+ conn.subscribe(destination=destination, ack='auto', id=subscription_id)
163
+ time.sleep(1)
164
+
165
+ logger(logging.INFO, 'graceful stop requested')
166
+ stomp_conn_mngr.disconnect()
167
+ logger(logging.INFO, 'graceful stop done')
168
+
169
+
170
+ def stop(signum: Optional[int] = None, frame: Optional["FrameType"] = None) -> None:
171
+ """
172
+ Graceful exit.
173
+ """
174
+
175
+ GRACEFUL_STOP.set()
176
+
177
+
178
+ def run(num_thread: int = 1) -> None:
179
+ """
180
+ Starts up the rucio cache consumer thread
181
+ """
182
+ setup_logging(process_name=DAEMON_NAME)
183
+
184
+ if rucio.db.sqla.util.is_old_db():
185
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
186
+
187
+ logging.info('starting consumer thread')
188
+ threads = [threading.Thread(target=consumer, kwargs={'id_': i, 'num_thread': num_thread})
189
+ for i in range(0, num_thread)]
190
+
191
+ [t.start() for t in threads]
192
+
193
+ logging.info('waiting for interrupts')
194
+
195
+ # Interruptible joins require a timeout.
196
+ while threads[0].is_alive():
197
+ [t.join(timeout=3.14) for t in threads]
@@ -0,0 +1,415 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import datetime
16
+ import functools
17
+ import logging
18
+ import os
19
+ import queue
20
+ import socket
21
+ import threading
22
+ import time
23
+ from collections.abc import Callable, Generator, Iterator, Sequence
24
+ from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union
25
+
26
+ from rucio.common.logging import formatted_logger
27
+ from rucio.common.utils import PriorityQueue
28
+ from rucio.core import heartbeat as heartbeat_core
29
+ from rucio.core.monitor import MetricManager
30
+
31
+ if TYPE_CHECKING:
32
+ from rucio.common.types import LoggerFunction
33
+
34
+ T = TypeVar('T')
35
+ METRICS = MetricManager(module=__name__)
36
+
37
+
38
+ class HeartbeatHandler:
39
+ """
40
+ Simple contextmanager which sets a heartbeat and associated logger on entry and cleans up the heartbeat on exit.
41
+ """
42
+
43
+ def __init__(self, executable: str, renewal_interval: int):
44
+ """
45
+ :param executable: the executable name which will be set in heartbeats
46
+ :param renewal_interval: the interval at which the heartbeat will be renewed in the database.
47
+ Calls to live() in-between intervals will reuse the locally cached heartbeat.
48
+ """
49
+ self.executable = executable
50
+ self._hash_executable = None
51
+ self.renewal_interval = renewal_interval
52
+ self.older_than = renewal_interval * 10 if renewal_interval and renewal_interval > 0 else None # 10 was chosen without any particular reason
53
+
54
+ self.hostname = socket.getfqdn()
55
+ self.pid = os.getpid()
56
+ self.hb_thread = threading.current_thread()
57
+
58
+ self.logger = logging.log
59
+ self.last_heart_beat = None
60
+ self.last_time = None
61
+ self.last_payload = None
62
+
63
+ def __enter__(self) -> 'HeartbeatHandler':
64
+ heartbeat_core.sanity_check(executable=self.executable, hostname=self.hostname)
65
+ self.live()
66
+ return self
67
+
68
+ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
69
+ if self.last_heart_beat:
70
+ heartbeat_core.die(self.executable, self.hostname, self.pid, self.hb_thread)
71
+ if self.logger:
72
+ self.logger(logging.INFO, 'Heartbeat cleaned up')
73
+
74
+ @property
75
+ def hash_executable(self) -> str:
76
+ if not self._hash_executable:
77
+ self._hash_executable = heartbeat_core.calc_hash(self.executable)
78
+ return self._hash_executable
79
+
80
+ @property
81
+ def short_executable(self) -> str:
82
+ return min(self.executable, self.hash_executable, key=len)
83
+
84
+ def live(
85
+ self,
86
+ force_renew: bool = False,
87
+ payload: Optional[str] = None
88
+ ) -> tuple[int, int, Callable]:
89
+ """
90
+ :return: a tuple: <the number of the current worker>, <total number of workers>, <decorated logger>
91
+ """
92
+ if force_renew \
93
+ or not self.last_time \
94
+ or not self.last_heart_beat \
95
+ or self.last_time < datetime.datetime.now() - datetime.timedelta(seconds=self.renewal_interval) \
96
+ or self.last_payload != payload:
97
+ if self.older_than:
98
+ self.last_heart_beat = heartbeat_core.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload, older_than=self.older_than)
99
+ else:
100
+ self.last_heart_beat = heartbeat_core.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload)
101
+
102
+ prefix = '[%i/%i]: ' % (self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])
103
+ self.logger = formatted_logger(logging.log, prefix + '%s')
104
+
105
+ if not self.last_time:
106
+ self.logger(logging.DEBUG, 'First heartbeat set')
107
+ else:
108
+ self.logger(logging.DEBUG, 'Heartbeat renewed')
109
+ self.last_time = datetime.datetime.now()
110
+ self.last_payload = payload
111
+
112
+ return self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'], self.logger
113
+
114
+
115
+ def _activity_looper(
116
+ once: bool,
117
+ sleep_time: int,
118
+ activities: Optional[Sequence[str]],
119
+ heartbeat_handler: HeartbeatHandler,
120
+ ) -> Generator[tuple[str, float], tuple[float, bool], None]:
121
+ """
122
+ Generator which loops (either once, or indefinitely) over all activities while ensuring that `sleep_time`
123
+ passes between handling twice the same activity.
124
+
125
+ Returns an activity and how much time the calling context must sleep before handling that activity
126
+ and expects to get in return the time when the activity started to be executed and whether next
127
+ execution must be immediate.
128
+ """
129
+
130
+ # For each activity, the priority queue will keep the next absolute time when that
131
+ # activity must be handled.
132
+ activity_next_exe_time = PriorityQueue()
133
+
134
+ # On startup, we schedule to immediately handle all activities.
135
+ now = time.time()
136
+ for activity in activities or [None]:
137
+ activity_next_exe_time[activity] = now
138
+
139
+ while activity_next_exe_time:
140
+ activity = activity_next_exe_time.top()
141
+ desired_exe_time = activity_next_exe_time[activity]
142
+
143
+ if once:
144
+ time_to_sleep = 0
145
+ activity_next_exe_time.pop()
146
+ else:
147
+ time_to_sleep = desired_exe_time - time.time()
148
+
149
+ logger = heartbeat_handler.logger
150
+ if time_to_sleep > 0:
151
+ if activity:
152
+ logger(logging.DEBUG, 'Switching to activity %s and sleeping %s seconds', activity, time_to_sleep)
153
+ else:
154
+ logger(logging.DEBUG, 'Sleeping %s seconds', time_to_sleep)
155
+ else:
156
+ if activity:
157
+ logger(logging.DEBUG, 'Switching to activity %s', activity)
158
+ else:
159
+ logger(logging.DEBUG, 'Starting next iteration')
160
+
161
+ # The calling context notifies us when the activity actually got handled. And if sleeping is desired.
162
+ actual_exe_time, must_sleep = yield activity, time_to_sleep
163
+
164
+ if not once:
165
+ if must_sleep:
166
+ time_diff = time.time() - actual_exe_time
167
+ time_to_sleep = max(1, sleep_time - time_diff)
168
+ activity_next_exe_time[activity] = time.time() + time_to_sleep
169
+ else:
170
+ activity_next_exe_time[activity] = time.time() + 1
171
+
172
+
173
+ def db_workqueue(
174
+ once: bool,
175
+ graceful_stop: threading.Event,
176
+ executable: str,
177
+ partition_wait_time: int,
178
+ sleep_time: int,
179
+ activities: Optional[Sequence[str]] = None,
180
+ ) -> Callable[
181
+ [
182
+ Callable[
183
+ ...,
184
+ Union[bool, tuple[bool, T], None]
185
+ ]
186
+ ],
187
+ Callable[
188
+ [],
189
+ Iterator[Union[T, None]]
190
+ ]
191
+ ]:
192
+ """
193
+ Used to wrap a function for interacting with the database as a work queue: i.e. to select
194
+ a set of rows and perform some work on those rows while ensuring that two instances running in parallel don't
195
+ work on the same set of rows. The last condition is ensured by using heartbeats to keep track of currently
196
+ active workers.
197
+
198
+ :param once: Whether to stop after one iteration
199
+ :param graceful_stop: the threading.Event() object used for graceful stop of the daemon
200
+ :param executable: the name of the executable used for heartbeats
201
+ :param partition_wait_time: time to wait for database partition rebalancing before starting the actual daemon loop
202
+ :param sleep_time: time to sleep between the iterations of the daemon
203
+ :param activities: optional list of activities on which to work. The run_once_fnc will be called on activities one by one.
204
+ """
205
+
206
+ def _decorate(run_once_fnc: Callable[..., Optional[Union[bool, tuple[bool, T]]]]) -> Callable[[], Iterator[Optional[T]]]:
207
+
208
+ @functools.wraps(run_once_fnc)
209
+ def _generator() -> Iterator[T]:
210
+
211
+ with HeartbeatHandler(executable=executable, renewal_interval=sleep_time - 1) as heartbeat_handler:
212
+ logger = heartbeat_handler.logger
213
+ logger(logging.INFO, 'started')
214
+
215
+ if partition_wait_time:
216
+ graceful_stop.wait(partition_wait_time)
217
+ _, _, logger = heartbeat_handler.live(force_renew=True)
218
+
219
+ activity_loop = _activity_looper(once=once, sleep_time=sleep_time, activities=activities, heartbeat_handler=heartbeat_handler)
220
+ activity, time_to_sleep = next(activity_loop, (None, None))
221
+ while time_to_sleep is not None:
222
+ if graceful_stop.is_set():
223
+ break
224
+
225
+ if time_to_sleep > 0:
226
+ graceful_stop.wait(time_to_sleep)
227
+
228
+ _, _, logger = heartbeat_handler.live()
229
+
230
+ must_sleep = True
231
+ start_time = time.time()
232
+ try:
233
+ result = run_once_fnc(heartbeat_handler=heartbeat_handler, activity=activity)
234
+
235
+ # Handle return values already existing in the code
236
+ # TODO: update all existing daemons to always explicitly return (must_sleep, ret_value)
237
+ if result is None:
238
+ must_sleep = True
239
+ ret_value = None
240
+ elif isinstance(result, bool):
241
+ must_sleep = result
242
+ ret_value = None
243
+ else:
244
+ must_sleep, ret_value = result
245
+
246
+ if ret_value is not None:
247
+ yield ret_value
248
+ except Exception as e:
249
+ METRICS.counter('exceptions.{exception}').labels(exception=e.__class__.__name__).inc()
250
+ logger(logging.CRITICAL, "Exception", exc_info=True)
251
+ if once:
252
+ raise
253
+
254
+ try:
255
+ activity, time_to_sleep = activity_loop.send((start_time, must_sleep))
256
+ except StopIteration:
257
+ break
258
+
259
+ if not once:
260
+ logger(logging.INFO, 'Graceful stop requested')
261
+
262
+ return _generator
263
+
264
+ return _decorate
265
+
266
+
267
+ def run_daemon(
268
+ once: bool,
269
+ graceful_stop: threading.Event,
270
+ executable: str,
271
+ partition_wait_time: int,
272
+ sleep_time: int,
273
+ run_once_fnc: Callable[..., Optional[Union[bool, tuple[bool, Any]]]],
274
+ activities: Optional[list[str]] = None
275
+ ) -> None:
276
+ """
277
+ Run the daemon loop and call the function run_once_fnc at each iteration
278
+ """
279
+
280
+ daemon = db_workqueue(
281
+ once=once,
282
+ graceful_stop=graceful_stop,
283
+ executable=executable,
284
+ partition_wait_time=partition_wait_time,
285
+ sleep_time=sleep_time,
286
+ activities=activities,
287
+ )(run_once_fnc)
288
+
289
+ for _ in daemon():
290
+ pass
291
+
292
+
293
+ class ProducerConsumerDaemon(Generic[T]):
294
+ """
295
+ Daemon which connects N producers with M consumers via a queue.
296
+ """
297
+
298
+ def __init__(
299
+ self,
300
+ producers: Sequence[Callable[[], Iterator[T]]],
301
+ consumers: Sequence[Callable[..., None]],
302
+ graceful_stop: threading.Event,
303
+ logger: "LoggerFunction" = logging.log
304
+ ):
305
+ self.producers = producers
306
+ self.consumers = consumers
307
+
308
+ self.queue = queue.Queue()
309
+ self.lock = threading.Lock()
310
+ self.graceful_stop = graceful_stop
311
+ self.active_producers = 0
312
+ self.producers_done_event = threading.Event()
313
+ self.logger = logger
314
+
315
+ def _produce(
316
+ self,
317
+ it: Callable[[], Iterator[T]],
318
+ wait_for_consumers: bool = False
319
+ ) -> None:
320
+ """
321
+ Iterate over the generator function and put the extracted elements into the queue.
322
+
323
+ Perform a graceful shutdown when graceful_stop is set.
324
+ """
325
+
326
+ i = it()
327
+ with self.lock:
328
+ self.active_producers += 1
329
+ try:
330
+ while not self.graceful_stop.is_set():
331
+ if self.queue.qsize() > len(self.consumers):
332
+ self.graceful_stop.wait(1)
333
+ continue
334
+
335
+ try:
336
+ product = next(i)
337
+ self.queue.put(product)
338
+ except StopIteration:
339
+ break
340
+ except Exception as e:
341
+ METRICS.counter('exceptions.{exception}').labels(exception=e.__class__.__name__).inc()
342
+ self.logger(logging.CRITICAL, "Exception", exc_info=True)
343
+ finally:
344
+ with self.lock:
345
+ self.active_producers -= 1
346
+ if not self.active_producers > 0:
347
+ self.producers_done_event.set()
348
+
349
+ if wait_for_consumers:
350
+ self.queue.join()
351
+
352
+ def _consume(
353
+ self,
354
+ fnc: Callable[[T], Any]
355
+ ) -> None:
356
+ """
357
+ Wait for elements to arrive via the queue and call the given function on each element.
358
+
359
+ If producers_done_event is set, handle all remaining elements from the queue and exit gracefully.
360
+ """
361
+ while not self.producers_done_event.is_set() or self.queue.unfinished_tasks:
362
+ try:
363
+ product = self.queue.get_nowait()
364
+ except queue.Empty:
365
+ self.producers_done_event.wait(1)
366
+ continue
367
+
368
+ try:
369
+ fnc(product)
370
+ except Exception as e:
371
+ METRICS.counter('exceptions.{exception}').labels(exception=e.__class__.__name__).inc()
372
+ self.logger(logging.CRITICAL, "Exception", exc_info=True)
373
+ finally:
374
+ self.queue.task_done()
375
+
376
+ def run(self) -> None:
377
+
378
+ producer_threads: list[threading.Thread] = []
379
+ for i, producer in enumerate(self.producers):
380
+ thread = threading.Thread(
381
+ target=self._produce,
382
+ name=f'producer-{i}-{producer.__name__}',
383
+ kwargs={
384
+ 'it': producer,
385
+ 'wait_for_consumers': True
386
+ }
387
+ )
388
+ thread.start()
389
+ producer_threads.append(thread)
390
+
391
+ consumer_threads: list[threading.Thread] = []
392
+ for i, consumer in enumerate(self.consumers):
393
+ thread = threading.Thread(
394
+ target=self._consume,
395
+ name=f'consumer-{i}-{consumer.__name__}',
396
+ kwargs={
397
+ 'fnc': consumer,
398
+ }
399
+ )
400
+ thread.start()
401
+ consumer_threads.append(thread)
402
+
403
+ logging.info('waiting for interrupts')
404
+
405
+ while producer_threads:
406
+ for thread in producer_threads:
407
+ thread.join(timeout=3.14)
408
+ producer_threads = [thread for thread in producer_threads if thread.is_alive()]
409
+
410
+ self.producers_done_event.set()
411
+
412
+ while consumer_threads:
413
+ for thread in consumer_threads:
414
+ thread.join(timeout=3.14)
415
+ consumer_threads = [thread for thread in consumer_threads if thread.is_alive()]
@@ -0,0 +1,13 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.