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,774 @@
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
+ Hermes is a daemon that get the messages and sends them to external services (influxDB, ES, ActiveMQ).
17
+ """
18
+
19
+ import calendar
20
+ import datetime
21
+ import functools
22
+ import json
23
+ import logging
24
+ import random
25
+ import re
26
+ import smtplib
27
+ import socket
28
+ import sys
29
+ import threading
30
+ import time
31
+ from configparser import NoOptionError, NoSectionError
32
+ from email.mime.text import MIMEText
33
+ from typing import TYPE_CHECKING, Any, Optional, Union
34
+
35
+ import requests
36
+ import stomp
37
+ from requests.auth import HTTPBasicAuth
38
+
39
+ import rucio.db.sqla.util
40
+ from rucio.common.config import (
41
+ config_get,
42
+ config_get_bool,
43
+ config_get_int,
44
+ config_get_list,
45
+ )
46
+ from rucio.common.exception import DatabaseException
47
+ from rucio.common.logging import setup_logging
48
+ from rucio.core.message import delete_messages, retrieve_messages
49
+ from rucio.core.monitor import MetricManager
50
+ from rucio.daemons.common import run_daemon
51
+
52
+ if TYPE_CHECKING:
53
+ from collections.abc import Iterable, Sequence
54
+ from types import FrameType
55
+
56
+ from stomp.utils import Frame
57
+
58
+ from rucio.common.types import LoggerFunction
59
+ from rucio.daemons.common import HeartbeatHandler
60
+
61
+ logging.getLogger("requests").setLevel(logging.CRITICAL)
62
+
63
+ METRICS = MetricManager(module=__name__)
64
+ graceful_stop = threading.Event()
65
+ DAEMON_NAME = "hermes"
66
+
67
+ RECONNECT_COUNTER = METRICS.counter(
68
+ name="reconnect.{host}",
69
+ documentation="Counts Hermes reconnects to different ActiveMQ brokers",
70
+ labelnames=("host",),
71
+ )
72
+
73
+
74
+ def default(datetype: Union[datetime.date, datetime.datetime]) -> str:
75
+ if isinstance(datetype, (datetime.date, datetime.datetime)):
76
+ return datetype.isoformat()
77
+
78
+
79
+ class HermesListener(stomp.ConnectionListener):
80
+ """
81
+ Hermes Listener
82
+ """
83
+
84
+ def __init__(self, broker: str):
85
+ """
86
+ __init__
87
+ """
88
+ self.__broker = broker
89
+
90
+ def on_error(self, frame: "Frame") -> None:
91
+ """
92
+ Error handler
93
+ """
94
+ logging.error("[broker] [%s]: %s", self.__broker, frame.body)
95
+
96
+
97
+ def setup_activemq(
98
+ logger: "LoggerFunction"
99
+ ) -> tuple[
100
+ Optional[list[stomp.Connection12]],
101
+ Optional[str],
102
+ Optional[str],
103
+ Optional[str],
104
+ Optional[bool]
105
+ ]:
106
+ """
107
+ Deliver messages to ActiveMQ
108
+
109
+ :param logger: The logger object.
110
+ """
111
+
112
+ logger(logging.INFO, "[broker] Resolving brokers")
113
+
114
+ brokers_alias = []
115
+ brokers_resolved = []
116
+ try:
117
+ brokers_alias = [
118
+ broker.strip()
119
+ for broker in config_get("messaging-hermes", "brokers").split(",")
120
+ ]
121
+ except:
122
+ raise Exception("Could not load brokers from configuration")
123
+
124
+ logger(logging.INFO, "[broker] Resolving broker dns alias: %s", brokers_alias)
125
+ brokers_resolved = []
126
+ for broker in brokers_alias:
127
+ try:
128
+ addrinfos = socket.getaddrinfo(
129
+ broker, 0, socket.AF_INET, 0, socket.IPPROTO_TCP
130
+ )
131
+ brokers_resolved.extend(ai[4][0] for ai in addrinfos)
132
+ except socket.gaierror as ex:
133
+ logger(
134
+ logging.ERROR,
135
+ "[broker] Cannot resolve domain name %s (%s)",
136
+ broker,
137
+ str(ex),
138
+ )
139
+
140
+ logger(logging.DEBUG, "[broker] Brokers resolved to %s", brokers_resolved)
141
+
142
+ if not brokers_resolved:
143
+ logger(logging.FATAL, "[broker] No brokers resolved.")
144
+ return None, None, None, None, None
145
+
146
+ broker_timeout = 3
147
+ if not broker_timeout: # Allow zero in config
148
+ broker_timeout = None
149
+
150
+ logger(logging.INFO, "[broker] Checking authentication method")
151
+ use_ssl = True
152
+ try:
153
+ use_ssl = config_get_bool("messaging-hermes", "use_ssl")
154
+ except:
155
+ logger(
156
+ logging.INFO,
157
+ "[broker] Could not find use_ssl in configuration -- please update your rucio.cfg",
158
+ )
159
+
160
+ port = config_get_int("messaging-hermes", "port")
161
+ vhost = config_get("messaging-hermes", "broker_virtual_host", raise_exception=False)
162
+ username = None
163
+ password = None
164
+ if not use_ssl:
165
+ username = config_get("messaging-hermes", "username")
166
+ password = config_get("messaging-hermes", "password")
167
+ port = config_get_int("messaging-hermes", "nonssl_port")
168
+
169
+ conns = []
170
+ for broker in brokers_resolved:
171
+ if not use_ssl:
172
+ logger(
173
+ logging.INFO,
174
+ "[broker] setting up username/password authentication: %s",
175
+ broker,
176
+ )
177
+ else:
178
+ logger(
179
+ logging.INFO,
180
+ "[broker] setting up ssl cert/key authentication: %s",
181
+ broker,
182
+ )
183
+
184
+ con = stomp.Connection12(
185
+ host_and_ports=[(broker, port)],
186
+ vhost=vhost,
187
+ keepalive=True,
188
+ timeout=broker_timeout,
189
+ )
190
+ if use_ssl:
191
+ con.set_ssl(
192
+ key_file=config_get("messaging-hermes", "ssl_key_file"),
193
+ cert_file=config_get("messaging-hermes", "ssl_cert_file"),
194
+ )
195
+
196
+ con.set_listener(
197
+ "rucio-hermes", HermesListener(con.transport._Transport__host_and_ports[0])
198
+ )
199
+
200
+ conns.append(con)
201
+ destination = config_get("messaging-hermes", "destination")
202
+ return conns, destination, username, password, use_ssl
203
+
204
+
205
+ def deliver_to_activemq(
206
+ messages: "Iterable[dict[str, Any]]",
207
+ conns: "Sequence[stomp.Connection12]",
208
+ destination: str,
209
+ username: str,
210
+ password: str,
211
+ use_ssl: bool,
212
+ logger: "LoggerFunction"
213
+ ) -> list[str]:
214
+ """
215
+ Deliver messages to ActiveMQ
216
+
217
+ :param messages: The list of messages.
218
+ :param conns: A list of connections.
219
+ :param destination: The destination topic or queue.
220
+ :param username: The username if no SSL connection.
221
+ :param password: The username if no SSL connection.
222
+ :param use_ssl: Boolean to choose if SSL connection is used.
223
+ :param logger: The logger object.
224
+
225
+ :returns: List of message_id to delete
226
+ """
227
+ to_delete = []
228
+ for message in messages:
229
+ try:
230
+ conn = random.sample(conns, 1)[0]
231
+ if not conn.is_connected():
232
+ host_and_ports = conn.transport._Transport__host_and_ports[0][0]
233
+ RECONNECT_COUNTER.labels(host=host_and_ports.split(".")[0]).inc()
234
+ if not use_ssl:
235
+ logger(
236
+ logging.INFO,
237
+ "[broker] - connecting with USERPASS to %s",
238
+ host_and_ports,
239
+ )
240
+ conn.connect(username, password, wait=True)
241
+ else:
242
+ logger(
243
+ logging.INFO,
244
+ "[broker] - connecting with SSL to %s",
245
+ host_and_ports,
246
+ )
247
+ conn.connect(wait=True)
248
+
249
+ conn.send(
250
+ body=json.dumps(
251
+ {
252
+ "event_type": str(message["event_type"]).lower(),
253
+ "payload": message["payload"],
254
+ "created_at": str(message["created_at"]),
255
+ }
256
+ ),
257
+ destination=destination,
258
+ headers={
259
+ "persistent": "true",
260
+ "event_type": str(message["event_type"]).lower(),
261
+ },
262
+ )
263
+
264
+ to_delete.append(message["id"])
265
+ except ValueError:
266
+ logger(
267
+ logging.ERROR,
268
+ "[broker] Cannot serialize payload to JSON: %s",
269
+ str(message["payload"]),
270
+ )
271
+ to_delete.append(message["id"])
272
+ continue
273
+ except stomp.exception.NotConnectedException as error:
274
+ logger(
275
+ logging.WARNING,
276
+ "[broker] Could not deliver message due to NotConnectedException: %s",
277
+ str(error),
278
+ )
279
+ continue
280
+ except stomp.exception.ConnectFailedException as error:
281
+ logger(
282
+ logging.WARNING,
283
+ "[broker] Could not deliver message due to ConnectFailedException: %s",
284
+ str(error),
285
+ )
286
+ continue
287
+ except Exception as error:
288
+ logger(logging.ERROR, "[broker] Could not deliver message: %s", str(error))
289
+ continue
290
+
291
+ if str(message["event_type"]).lower().startswith("transfer") or str(
292
+ message["event_type"]
293
+ ).lower().startswith("stagein"):
294
+ logger(
295
+ logging.DEBUG,
296
+ "[broker] - event_type: %s, scope: %s, name: %s, rse: %s, request-id: %s, transfer-id: %s, created_at: %s",
297
+ str(message["event_type"]).lower(),
298
+ message["payload"].get("scope", None),
299
+ message["payload"].get("name", None),
300
+ message["payload"].get("dst-rse", None),
301
+ message["payload"].get("request-id", None),
302
+ message["payload"].get("transfer-id", None),
303
+ str(message["created_at"]),
304
+ )
305
+
306
+ elif str(message["event_type"]).lower().startswith("dataset"):
307
+ logger(
308
+ logging.DEBUG,
309
+ "[broker] - event_type: %s, scope: %s, name: %s, rse: %s, rule-id: %s, created_at: %s)",
310
+ str(message["event_type"]).lower(),
311
+ message["payload"].get("scope", None),
312
+ message["payload"].get("name", None),
313
+ message["payload"].get("rse", None),
314
+ message["payload"].get("rule_id", None),
315
+ str(message["created_at"]),
316
+ )
317
+
318
+ elif str(message["event_type"]).lower().startswith("deletion"):
319
+ if "url" not in message["payload"]:
320
+ message["payload"]["url"] = "unknown"
321
+ logger(
322
+ logging.DEBUG,
323
+ "[broker] - event_type: %s, scope: %s, name: %s, rse: %s, url: %s, created_at: %s)",
324
+ str(message["event_type"]).lower(),
325
+ message["payload"].get("scope", None),
326
+ message["payload"].get("name", None),
327
+ message["payload"].get("rse", None),
328
+ message["payload"].get("url", None),
329
+ str(message["created_at"]),
330
+ )
331
+ else:
332
+ logger(logging.DEBUG, "[broker] Other message: %s", message)
333
+ return to_delete
334
+
335
+
336
+ def deliver_emails(
337
+ messages: "Iterable[dict[str, Any]]",
338
+ logger: "LoggerFunction"
339
+ ) -> list[str]:
340
+ """
341
+ Sends emails
342
+
343
+ :param messages: The list of messages.
344
+ :param logger: The logger object.
345
+
346
+ :returns: List of message_id to delete
347
+ """
348
+
349
+ email_from = config_get("messaging-hermes", "email_from")
350
+ send_email = config_get_bool(
351
+ "messaging-hermes", "send_email", raise_exception=False, default=True
352
+ )
353
+ to_delete = []
354
+ for message in messages:
355
+ if message['event_type'] == 'email':
356
+ msg = MIMEText(message['payload']['body'])
357
+ msg['From'] = email_from
358
+ msg['To'] = ', '.join(message['payload']['to'])
359
+ msg['Subject'] = message['payload']['subject']
360
+
361
+ try:
362
+ if send_email:
363
+ smtp = smtplib.SMTP()
364
+ smtp.connect()
365
+ smtp.sendmail(
366
+ msg["From"], message["payload"]["to"], msg.as_string()
367
+ )
368
+ smtp.quit()
369
+ to_delete.append(message["id"])
370
+ except Exception as error:
371
+ logger(logging.ERROR, "Cannot send email : %s", str(error))
372
+ else:
373
+ to_delete.append(message["id"])
374
+ continue
375
+ return to_delete
376
+
377
+
378
+ def submit_to_elastic(
379
+ messages: "Iterable[dict[str, Any]]",
380
+ endpoint: str,
381
+ logger: "LoggerFunction"
382
+ ) -> int:
383
+ """
384
+ Aggregate a list of message to ElasticSearch
385
+
386
+ :param messages: The list of messages.
387
+ :param endpoint: The ES endpoint were to send the messages.
388
+ :param logger: The logger object.
389
+
390
+ :returns: HTTP status code. 200 and 204 OK. Rest is failure.
391
+ """
392
+ text = ""
393
+ elastic_username = config_get("hermes", "elastic_username",
394
+ raise_exception=False, default=None)
395
+ elastic_password = config_get("hermes", "elastic_password",
396
+ raise_exception=False, default=None)
397
+ auth = None
398
+ if elastic_username and elastic_password:
399
+ auth = HTTPBasicAuth(elastic_username, elastic_password)
400
+
401
+ for message in messages:
402
+ text += '{ "index":{ } }\n%s\n' % json.dumps(message, default=default)
403
+ res = requests.post(
404
+ endpoint, data=text, headers={"Content-Type": "application/json"}, auth=auth
405
+ )
406
+ return res.status_code
407
+
408
+
409
+ def aggregate_to_influx(
410
+ messages: "Iterable[dict[str, Any]]",
411
+ bin_size: int,
412
+ endpoint: str,
413
+ logger: "LoggerFunction"
414
+ ) -> int:
415
+ """
416
+ Aggregate a list of message using a certain bin_size
417
+ and submit them to a InfluxDB endpoint
418
+
419
+ :param messages: The list of messages.
420
+ :param bin_size: The size of the bins for the aggregation (e.g. 10m, 1h, etc.).
421
+ :param endpoint: The InfluxDB endpoint were to send the messages.
422
+ :param logger: The logger object.
423
+
424
+ :returns: HTTP status code. 200 and 204 OK. Rest is failure.
425
+ """
426
+ bins = {}
427
+ dtime = datetime.datetime.now()
428
+ microsecond = dtime.microsecond
429
+
430
+ for message in messages:
431
+ event_type = message["event_type"]
432
+ payload = message["payload"]
433
+ if event_type in ["transfer-failed", "transfer-done"]:
434
+ if not payload["transferred_at"]:
435
+ logger(
436
+ logging.WARNING,
437
+ "No transferred_at for message. Reason : %s",
438
+ payload["reason"],
439
+ )
440
+ continue
441
+ transferred_at = time.strptime(
442
+ payload["transferred_at"], "%Y-%m-%d %H:%M:%S"
443
+ )
444
+ if bin_size == "1m":
445
+ transferred_at = int(calendar.timegm(transferred_at)) * 1000000000
446
+ transferred_at += microsecond
447
+ if transferred_at not in bins:
448
+ bins[transferred_at] = {}
449
+ src_rse, dest_rse, activity = (
450
+ payload["src-rse"],
451
+ payload["dst-rse"],
452
+ payload["activity"],
453
+ )
454
+ activity = re.sub(" ", r"\ ", activity)
455
+ key = "transfer,activity=%s,src_rse=%s,dst_rse=%s" % (
456
+ activity,
457
+ src_rse,
458
+ dest_rse,
459
+ )
460
+ if key not in bins[transferred_at]:
461
+ bins[transferred_at][key] = [0, 0, 0, 0]
462
+ if event_type == "transfer-done":
463
+ bins[transferred_at][key][0] += 1
464
+ bins[transferred_at][key][1] += payload["bytes"]
465
+ if event_type == "transfer-failed":
466
+ bins[transferred_at][key][2] += 1
467
+ bins[transferred_at][key][3] += payload["bytes"]
468
+ elif event_type in ["deletion-failed", "deletion-done"]:
469
+ created_at = message["created_at"]
470
+ if bin_size == "1m":
471
+ created_at = created_at.replace(
472
+ second=0, microsecond=0, tzinfo=datetime.timezone.utc
473
+ ).timestamp()
474
+ created_at = int(created_at) * 1000000000
475
+ created_at += microsecond
476
+ if created_at not in bins:
477
+ bins[created_at] = {}
478
+ rse = payload["rse"]
479
+ key = "deletion,rse=%s" % (rse)
480
+ if key not in bins[created_at]:
481
+ bins[created_at][key] = [0, 0, 0, 0]
482
+ if event_type == "deletion-done":
483
+ bins[created_at][key][0] += 1
484
+ bins[created_at][key][1] += payload["bytes"]
485
+ if event_type == "deletion-failed":
486
+ bins[created_at][key][2] += 1
487
+ bins[created_at][key][3] += payload["bytes"]
488
+ points = ""
489
+ for timestamp in bins:
490
+ for entry in bins[timestamp]:
491
+ metrics = bins[timestamp][entry]
492
+ event_type = entry.split(",")[0]
493
+ point = (
494
+ "%s nb_%s_done=%s,bytes_%s_done=%s,nb_%s_failed=%s,bytes_%s_failed=%s %s"
495
+ % (
496
+ entry,
497
+ event_type,
498
+ metrics[0],
499
+ event_type,
500
+ metrics[1],
501
+ event_type,
502
+ metrics[2],
503
+ event_type,
504
+ metrics[3],
505
+ timestamp,
506
+ )
507
+ )
508
+ points += point
509
+ points += "\n"
510
+ influx_token = config_get("hermes", "influxdb_token", False, None)
511
+ if influx_token:
512
+ headers = {"Authorization": "Token %s" % influx_token}
513
+ if points:
514
+ res = requests.post(endpoint, headers=headers, data=points)
515
+ logger(logging.DEBUG, "%s", str(res.text))
516
+ return res.status_code
517
+ return 204
518
+
519
+
520
+ def hermes(once: bool = False, bulk: int = 1000, sleep_time: int = 10) -> None:
521
+ """
522
+ Creates a Hermes Worker that can submit messages to different services (InfluXDB, ElasticSearch, ActiveMQ)
523
+ The list of services need to be define in the config service in the hermes section.
524
+ The list of endpoints need to be defined in rucio.cfg in the hermes section.
525
+
526
+ :param once: Run only once.
527
+ :param bulk: The number of requests to process.
528
+ :param sleep_time: Time between two cycles.
529
+ """
530
+ run_daemon(
531
+ once=once,
532
+ graceful_stop=graceful_stop,
533
+ executable=DAEMON_NAME,
534
+ partition_wait_time=1,
535
+ sleep_time=sleep_time,
536
+ run_once_fnc=functools.partial(
537
+ run_once,
538
+ bulk=bulk,
539
+ ),
540
+ )
541
+
542
+
543
+ def run_once(heartbeat_handler: "HeartbeatHandler", bulk: int, **_kwargs) -> bool:
544
+
545
+ worker_number, total_workers, logger = heartbeat_handler.live()
546
+ try:
547
+ services_list = config_get_list("hermes", "services_list")
548
+ except (NoOptionError, NoSectionError, RuntimeError):
549
+ logger(logging.DEBUG, "No services found, exiting")
550
+ sys.exit(1)
551
+
552
+ if "influx" in services_list:
553
+ influx_endpoint = None
554
+ try:
555
+ influx_endpoint = config_get("hermes", "influxdb_endpoint", False, None)
556
+ if not influx_endpoint:
557
+ logger(
558
+ logging.ERROR,
559
+ "InfluxDB defined in the services list, but no endpoint can be found",
560
+ )
561
+ except Exception as err:
562
+ logger(logging.ERROR, str(err))
563
+ if "elastic" in services_list:
564
+ elastic_endpoint = None
565
+ try:
566
+ elastic_endpoint = config_get("hermes", "elastic_endpoint", False, None)
567
+ if not elastic_endpoint:
568
+ logger(
569
+ logging.ERROR,
570
+ "Elastic defined in the services list, but no endpoint can be found",
571
+ )
572
+ except Exception as err:
573
+ logger(logging.ERROR, str(err))
574
+ conns = None
575
+ if "activemq" in services_list:
576
+ try:
577
+ conns, destination, username, password, use_ssl = setup_activemq(logger)
578
+ if not conns:
579
+ logger(
580
+ logging.ERROR,
581
+ "ActiveMQ defined in the services list, cannot be setup",
582
+ )
583
+ except Exception as err:
584
+ logger(logging.ERROR, str(err))
585
+
586
+ worker_number, total_workers, logger = heartbeat_handler.live()
587
+ message_dict = {}
588
+ message_ids = []
589
+ start_time = time.time()
590
+ messages = retrieve_messages(
591
+ bulk=bulk,
592
+ old_mode=False,
593
+ thread=worker_number,
594
+ total_threads=total_workers,
595
+ )
596
+
597
+ to_delete = []
598
+ if messages:
599
+ for message in messages:
600
+ service = message["services"]
601
+ if service not in message_dict:
602
+ message_dict[service] = []
603
+ message_dict[service].append(message)
604
+ message_ids.append(message["id"])
605
+ logger(
606
+ logging.DEBUG,
607
+ "Retrieved %i messages retrieved in %s seconds",
608
+ len(messages),
609
+ time.time() - start_time,
610
+ )
611
+
612
+ if "influx" in message_dict and influx_endpoint:
613
+ # For influxDB, bulk submission, either everything succeeds or fails
614
+ t_time = time.time()
615
+ logger(logging.DEBUG, "Will submit to influxDB")
616
+ try:
617
+ state = aggregate_to_influx(
618
+ messages=message_dict["influx"],
619
+ bin_size="1m",
620
+ endpoint=influx_endpoint,
621
+ logger=logger,
622
+ )
623
+ if state in [204, 200]:
624
+ logger(
625
+ logging.INFO,
626
+ "%s messages successfully submitted to influxDB in %s seconds",
627
+ len(message_dict["influx"]),
628
+ time.time() - t_time,
629
+ )
630
+ for message in message_dict["influx"]:
631
+ to_delete.append(message)
632
+ else:
633
+ logger(
634
+ logging.ERROR,
635
+ "Failure to submit %s messages to influxDB. Returned status: %s",
636
+ len(message_dict["influx"]),
637
+ state,
638
+ )
639
+ except Exception as error:
640
+ logger(logging.ERROR, "Error sending to InfluxDB : %s", str(error))
641
+
642
+ if "elastic" in message_dict and elastic_endpoint:
643
+ # For elastic, bulk submission, either everything succeeds or fails
644
+ t_time = time.time()
645
+ try:
646
+ state = submit_to_elastic(
647
+ messages=message_dict["elastic"],
648
+ endpoint=elastic_endpoint,
649
+ logger=logger,
650
+ )
651
+ if state in [200, 204]:
652
+ logger(
653
+ logging.INFO,
654
+ "%s messages successfully submitted to elastic in %s seconds",
655
+ len(message_dict["elastic"]),
656
+ time.time() - t_time,
657
+ )
658
+ for message in message_dict["elastic"]:
659
+ to_delete.append(message)
660
+ else:
661
+ logger(
662
+ logging.ERROR,
663
+ "Failure to submit %s messages to elastic. Returned status: %s",
664
+ len(message_dict["elastic"]),
665
+ state,
666
+ )
667
+ except Exception as error:
668
+ logger(logging.ERROR, "Error sending to Elastic : %s", str(error))
669
+
670
+ if "email" in message_dict:
671
+ t_time = time.time()
672
+ try:
673
+ messages_sent = deliver_emails(
674
+ messages=message_dict["email"], logger=logger
675
+ )
676
+ logger(
677
+ logging.INFO,
678
+ "%s messages successfully submitted by emails in %s seconds",
679
+ len(message_dict["email"]),
680
+ time.time() - t_time,
681
+ )
682
+ for message in message_dict["email"]:
683
+ if message["id"] in messages_sent:
684
+ to_delete.append(message)
685
+ except Exception as error:
686
+ logger(logging.ERROR, "Error sending email : %s", str(error))
687
+
688
+ if "activemq" in message_dict and conns:
689
+ t_time = time.time()
690
+ try:
691
+ messages_sent = deliver_to_activemq(
692
+ messages=message_dict["activemq"],
693
+ conns=conns,
694
+ destination=destination, # type: ignore (argument could be None)
695
+ username=username, # type: ignore (argument could be None)
696
+ password=password, # type: ignore (argument could be None)
697
+ use_ssl=use_ssl, # type: ignore (argument could be None)
698
+ logger=logger,
699
+ )
700
+ logger(
701
+ logging.INFO,
702
+ "%s messages successfully submitted to ActiveMQ in %s seconds",
703
+ len(message_dict["activemq"]),
704
+ time.time() - t_time,
705
+ )
706
+ for message in message_dict["activemq"]:
707
+ if message["id"] in messages_sent:
708
+ to_delete.append(message)
709
+ except Exception as error:
710
+ logger(logging.ERROR, "Error sending to ActiveMQ : %s", str(error))
711
+
712
+ logger(logging.INFO, "Deleting %s messages", len(to_delete))
713
+ to_delete = [
714
+ {
715
+ "id": message["id"],
716
+ "created_at": message["created_at"],
717
+ "updated_at": message["created_at"],
718
+ "payload": str(message["payload"]),
719
+ "event_type": message["event_type"],
720
+ }
721
+ for message in to_delete
722
+ ]
723
+ delete_messages(messages=to_delete)
724
+ must_sleep = True
725
+ return must_sleep
726
+
727
+
728
+ def stop(signum: Optional[int] = None, frame: Optional["FrameType"] = None) -> None:
729
+ """
730
+ Graceful exit.
731
+ """
732
+ logging.info("Caught CTRL-C - waiting for cycle to end before shutting down")
733
+ graceful_stop.set()
734
+
735
+
736
+ def run(
737
+ once: bool = False,
738
+ threads: int = 1,
739
+ bulk: int = 1000,
740
+ sleep_time: int = 10,
741
+ broker_timeout: int = 3,
742
+ ) -> None:
743
+ """
744
+ Starts up the hermes threads.
745
+ """
746
+ setup_logging(process_name=DAEMON_NAME)
747
+
748
+ if rucio.db.sqla.util.is_old_db():
749
+ raise DatabaseException("Database was not updated, daemon won't start")
750
+
751
+ logging.info("starting hermes threads")
752
+ thread_list = [
753
+ threading.Thread(
754
+ target=hermes,
755
+ kwargs={
756
+ "once": once,
757
+ "bulk": bulk,
758
+ "sleep_time": sleep_time,
759
+ },
760
+ )
761
+ for _ in range(0, threads)
762
+ ]
763
+
764
+ for thrd in thread_list:
765
+ thrd.start()
766
+
767
+ logging.debug(thread_list)
768
+ # Interruptible joins require a timeout.
769
+ while thread_list:
770
+ thread_list = [
771
+ thread.join(timeout=3.14)
772
+ for thread in thread_list
773
+ if thread and thread.is_alive()
774
+ ]