rucio 37.0.0rc1__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 (487) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/cli/__init__.py +14 -0
  4. rucio/cli/account.py +216 -0
  5. rucio/cli/bin_legacy/__init__.py +13 -0
  6. rucio/cli/bin_legacy/rucio.py +2825 -0
  7. rucio/cli/bin_legacy/rucio_admin.py +2500 -0
  8. rucio/cli/command.py +272 -0
  9. rucio/cli/config.py +72 -0
  10. rucio/cli/did.py +191 -0
  11. rucio/cli/download.py +128 -0
  12. rucio/cli/lifetime_exception.py +33 -0
  13. rucio/cli/replica.py +162 -0
  14. rucio/cli/rse.py +293 -0
  15. rucio/cli/rule.py +158 -0
  16. rucio/cli/scope.py +40 -0
  17. rucio/cli/subscription.py +73 -0
  18. rucio/cli/upload.py +60 -0
  19. rucio/cli/utils.py +226 -0
  20. rucio/client/__init__.py +15 -0
  21. rucio/client/accountclient.py +432 -0
  22. rucio/client/accountlimitclient.py +183 -0
  23. rucio/client/baseclient.py +983 -0
  24. rucio/client/client.py +120 -0
  25. rucio/client/configclient.py +126 -0
  26. rucio/client/credentialclient.py +59 -0
  27. rucio/client/didclient.py +868 -0
  28. rucio/client/diracclient.py +56 -0
  29. rucio/client/downloadclient.py +1783 -0
  30. rucio/client/exportclient.py +44 -0
  31. rucio/client/fileclient.py +50 -0
  32. rucio/client/importclient.py +42 -0
  33. rucio/client/lifetimeclient.py +90 -0
  34. rucio/client/lockclient.py +109 -0
  35. rucio/client/metaconventionsclient.py +140 -0
  36. rucio/client/pingclient.py +44 -0
  37. rucio/client/replicaclient.py +452 -0
  38. rucio/client/requestclient.py +125 -0
  39. rucio/client/richclient.py +317 -0
  40. rucio/client/rseclient.py +746 -0
  41. rucio/client/ruleclient.py +294 -0
  42. rucio/client/scopeclient.py +90 -0
  43. rucio/client/subscriptionclient.py +173 -0
  44. rucio/client/touchclient.py +82 -0
  45. rucio/client/uploadclient.py +969 -0
  46. rucio/common/__init__.py +13 -0
  47. rucio/common/bittorrent.py +234 -0
  48. rucio/common/cache.py +111 -0
  49. rucio/common/checksum.py +168 -0
  50. rucio/common/client.py +122 -0
  51. rucio/common/config.py +788 -0
  52. rucio/common/constants.py +217 -0
  53. rucio/common/constraints.py +17 -0
  54. rucio/common/didtype.py +237 -0
  55. rucio/common/dumper/__init__.py +342 -0
  56. rucio/common/dumper/consistency.py +497 -0
  57. rucio/common/dumper/data_models.py +362 -0
  58. rucio/common/dumper/path_parsing.py +75 -0
  59. rucio/common/exception.py +1208 -0
  60. rucio/common/extra.py +31 -0
  61. rucio/common/logging.py +420 -0
  62. rucio/common/pcache.py +1409 -0
  63. rucio/common/plugins.py +185 -0
  64. rucio/common/policy.py +93 -0
  65. rucio/common/schema/__init__.py +200 -0
  66. rucio/common/schema/generic.py +416 -0
  67. rucio/common/schema/generic_multi_vo.py +395 -0
  68. rucio/common/stomp_utils.py +423 -0
  69. rucio/common/stopwatch.py +55 -0
  70. rucio/common/test_rucio_server.py +154 -0
  71. rucio/common/types.py +483 -0
  72. rucio/common/utils.py +1688 -0
  73. rucio/core/__init__.py +13 -0
  74. rucio/core/account.py +496 -0
  75. rucio/core/account_counter.py +236 -0
  76. rucio/core/account_limit.py +425 -0
  77. rucio/core/authentication.py +620 -0
  78. rucio/core/config.py +437 -0
  79. rucio/core/credential.py +224 -0
  80. rucio/core/did.py +3004 -0
  81. rucio/core/did_meta_plugins/__init__.py +252 -0
  82. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  83. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  84. rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
  85. rucio/core/did_meta_plugins/filter_engine.py +672 -0
  86. rucio/core/did_meta_plugins/json_meta.py +240 -0
  87. rucio/core/did_meta_plugins/mongo_meta.py +229 -0
  88. rucio/core/did_meta_plugins/postgres_meta.py +352 -0
  89. rucio/core/dirac.py +237 -0
  90. rucio/core/distance.py +187 -0
  91. rucio/core/exporter.py +59 -0
  92. rucio/core/heartbeat.py +363 -0
  93. rucio/core/identity.py +301 -0
  94. rucio/core/importer.py +260 -0
  95. rucio/core/lifetime_exception.py +377 -0
  96. rucio/core/lock.py +577 -0
  97. rucio/core/message.py +288 -0
  98. rucio/core/meta_conventions.py +203 -0
  99. rucio/core/monitor.py +448 -0
  100. rucio/core/naming_convention.py +195 -0
  101. rucio/core/nongrid_trace.py +136 -0
  102. rucio/core/oidc.py +1463 -0
  103. rucio/core/permission/__init__.py +161 -0
  104. rucio/core/permission/generic.py +1124 -0
  105. rucio/core/permission/generic_multi_vo.py +1144 -0
  106. rucio/core/quarantined_replica.py +224 -0
  107. rucio/core/replica.py +4483 -0
  108. rucio/core/replica_sorter.py +362 -0
  109. rucio/core/request.py +3091 -0
  110. rucio/core/rse.py +2079 -0
  111. rucio/core/rse_counter.py +185 -0
  112. rucio/core/rse_expression_parser.py +459 -0
  113. rucio/core/rse_selector.py +304 -0
  114. rucio/core/rule.py +4484 -0
  115. rucio/core/rule_grouping.py +1620 -0
  116. rucio/core/scope.py +181 -0
  117. rucio/core/subscription.py +362 -0
  118. rucio/core/topology.py +490 -0
  119. rucio/core/trace.py +375 -0
  120. rucio/core/transfer.py +1531 -0
  121. rucio/core/vo.py +169 -0
  122. rucio/core/volatile_replica.py +151 -0
  123. rucio/daemons/__init__.py +13 -0
  124. rucio/daemons/abacus/__init__.py +13 -0
  125. rucio/daemons/abacus/account.py +116 -0
  126. rucio/daemons/abacus/collection_replica.py +124 -0
  127. rucio/daemons/abacus/rse.py +117 -0
  128. rucio/daemons/atropos/__init__.py +13 -0
  129. rucio/daemons/atropos/atropos.py +242 -0
  130. rucio/daemons/auditor/__init__.py +289 -0
  131. rucio/daemons/auditor/hdfs.py +97 -0
  132. rucio/daemons/auditor/srmdumps.py +355 -0
  133. rucio/daemons/automatix/__init__.py +13 -0
  134. rucio/daemons/automatix/automatix.py +304 -0
  135. rucio/daemons/badreplicas/__init__.py +13 -0
  136. rucio/daemons/badreplicas/minos.py +322 -0
  137. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  138. rucio/daemons/badreplicas/necromancer.py +196 -0
  139. rucio/daemons/bb8/__init__.py +13 -0
  140. rucio/daemons/bb8/bb8.py +353 -0
  141. rucio/daemons/bb8/common.py +759 -0
  142. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  143. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  144. rucio/daemons/cache/__init__.py +13 -0
  145. rucio/daemons/cache/consumer.py +133 -0
  146. rucio/daemons/common.py +405 -0
  147. rucio/daemons/conveyor/__init__.py +13 -0
  148. rucio/daemons/conveyor/common.py +562 -0
  149. rucio/daemons/conveyor/finisher.py +529 -0
  150. rucio/daemons/conveyor/poller.py +394 -0
  151. rucio/daemons/conveyor/preparer.py +205 -0
  152. rucio/daemons/conveyor/receiver.py +179 -0
  153. rucio/daemons/conveyor/stager.py +133 -0
  154. rucio/daemons/conveyor/submitter.py +403 -0
  155. rucio/daemons/conveyor/throttler.py +532 -0
  156. rucio/daemons/follower/__init__.py +13 -0
  157. rucio/daemons/follower/follower.py +101 -0
  158. rucio/daemons/hermes/__init__.py +13 -0
  159. rucio/daemons/hermes/hermes.py +534 -0
  160. rucio/daemons/judge/__init__.py +13 -0
  161. rucio/daemons/judge/cleaner.py +159 -0
  162. rucio/daemons/judge/evaluator.py +185 -0
  163. rucio/daemons/judge/injector.py +162 -0
  164. rucio/daemons/judge/repairer.py +154 -0
  165. rucio/daemons/oauthmanager/__init__.py +13 -0
  166. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  167. rucio/daemons/reaper/__init__.py +13 -0
  168. rucio/daemons/reaper/dark_reaper.py +282 -0
  169. rucio/daemons/reaper/reaper.py +739 -0
  170. rucio/daemons/replicarecoverer/__init__.py +13 -0
  171. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  172. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  173. rucio/daemons/rsedecommissioner/config.py +81 -0
  174. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  175. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  176. rucio/daemons/rsedecommissioner/profiles/generic.py +452 -0
  177. rucio/daemons/rsedecommissioner/profiles/types.py +93 -0
  178. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  179. rucio/daemons/storage/__init__.py +13 -0
  180. rucio/daemons/storage/consistency/__init__.py +13 -0
  181. rucio/daemons/storage/consistency/actions.py +848 -0
  182. rucio/daemons/tracer/__init__.py +13 -0
  183. rucio/daemons/tracer/kronos.py +511 -0
  184. rucio/daemons/transmogrifier/__init__.py +13 -0
  185. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  186. rucio/daemons/undertaker/__init__.py +13 -0
  187. rucio/daemons/undertaker/undertaker.py +137 -0
  188. rucio/db/__init__.py +13 -0
  189. rucio/db/sqla/__init__.py +52 -0
  190. rucio/db/sqla/constants.py +206 -0
  191. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  192. rucio/db/sqla/migrate_repo/env.py +110 -0
  193. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  194. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  195. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  196. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  197. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  198. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  199. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  200. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  201. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  202. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  203. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  204. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  205. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  206. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  207. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  208. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  209. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  210. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  211. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  212. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  213. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  214. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  215. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  216. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  217. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  218. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  219. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  220. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  221. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  222. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  223. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  224. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  225. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  226. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  227. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  228. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  229. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  230. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  231. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  232. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  233. rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
  234. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  235. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  236. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  237. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  238. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  239. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  240. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  241. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  242. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  243. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  244. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  245. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  246. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  247. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  248. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  249. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  251. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  252. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  254. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  255. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  256. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  257. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  258. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  259. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  260. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  261. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  262. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  263. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  264. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  265. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  266. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  267. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  268. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  269. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  270. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  271. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  272. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  273. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  274. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  275. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  276. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  278. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  279. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  280. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  281. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  282. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  283. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  284. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  285. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  286. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  287. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  288. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  289. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  290. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  291. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  292. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  293. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  294. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  296. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  297. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  298. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  299. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  300. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  301. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  302. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  303. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  304. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  305. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  306. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  307. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  308. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  309. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  310. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  311. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  312. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  313. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  314. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  316. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  317. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  318. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  319. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  320. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  321. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  322. rucio/db/sqla/models.py +1743 -0
  323. rucio/db/sqla/sautils.py +55 -0
  324. rucio/db/sqla/session.py +529 -0
  325. rucio/db/sqla/types.py +206 -0
  326. rucio/db/sqla/util.py +543 -0
  327. rucio/gateway/__init__.py +13 -0
  328. rucio/gateway/account.py +345 -0
  329. rucio/gateway/account_limit.py +363 -0
  330. rucio/gateway/authentication.py +381 -0
  331. rucio/gateway/config.py +227 -0
  332. rucio/gateway/credential.py +70 -0
  333. rucio/gateway/did.py +987 -0
  334. rucio/gateway/dirac.py +83 -0
  335. rucio/gateway/exporter.py +60 -0
  336. rucio/gateway/heartbeat.py +76 -0
  337. rucio/gateway/identity.py +189 -0
  338. rucio/gateway/importer.py +46 -0
  339. rucio/gateway/lifetime_exception.py +121 -0
  340. rucio/gateway/lock.py +153 -0
  341. rucio/gateway/meta_conventions.py +98 -0
  342. rucio/gateway/permission.py +74 -0
  343. rucio/gateway/quarantined_replica.py +79 -0
  344. rucio/gateway/replica.py +538 -0
  345. rucio/gateway/request.py +330 -0
  346. rucio/gateway/rse.py +632 -0
  347. rucio/gateway/rule.py +437 -0
  348. rucio/gateway/scope.py +100 -0
  349. rucio/gateway/subscription.py +280 -0
  350. rucio/gateway/vo.py +126 -0
  351. rucio/rse/__init__.py +96 -0
  352. rucio/rse/protocols/__init__.py +13 -0
  353. rucio/rse/protocols/bittorrent.py +194 -0
  354. rucio/rse/protocols/cache.py +111 -0
  355. rucio/rse/protocols/dummy.py +100 -0
  356. rucio/rse/protocols/gfal.py +708 -0
  357. rucio/rse/protocols/globus.py +243 -0
  358. rucio/rse/protocols/http_cache.py +82 -0
  359. rucio/rse/protocols/mock.py +123 -0
  360. rucio/rse/protocols/ngarc.py +209 -0
  361. rucio/rse/protocols/posix.py +250 -0
  362. rucio/rse/protocols/protocol.py +361 -0
  363. rucio/rse/protocols/rclone.py +365 -0
  364. rucio/rse/protocols/rfio.py +145 -0
  365. rucio/rse/protocols/srm.py +338 -0
  366. rucio/rse/protocols/ssh.py +414 -0
  367. rucio/rse/protocols/storm.py +195 -0
  368. rucio/rse/protocols/webdav.py +594 -0
  369. rucio/rse/protocols/xrootd.py +302 -0
  370. rucio/rse/rsemanager.py +881 -0
  371. rucio/rse/translation.py +260 -0
  372. rucio/tests/__init__.py +13 -0
  373. rucio/tests/common.py +280 -0
  374. rucio/tests/common_server.py +149 -0
  375. rucio/transfertool/__init__.py +13 -0
  376. rucio/transfertool/bittorrent.py +200 -0
  377. rucio/transfertool/bittorrent_driver.py +50 -0
  378. rucio/transfertool/bittorrent_driver_qbittorrent.py +134 -0
  379. rucio/transfertool/fts3.py +1600 -0
  380. rucio/transfertool/fts3_plugins.py +152 -0
  381. rucio/transfertool/globus.py +201 -0
  382. rucio/transfertool/globus_library.py +181 -0
  383. rucio/transfertool/mock.py +89 -0
  384. rucio/transfertool/transfertool.py +221 -0
  385. rucio/vcsversion.py +11 -0
  386. rucio/version.py +45 -0
  387. rucio/web/__init__.py +13 -0
  388. rucio/web/rest/__init__.py +13 -0
  389. rucio/web/rest/flaskapi/__init__.py +13 -0
  390. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  391. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  392. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  393. rucio/web/rest/flaskapi/v1/accounts.py +1103 -0
  394. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  395. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  396. rucio/web/rest/flaskapi/v1/common.py +426 -0
  397. rucio/web/rest/flaskapi/v1/config.py +304 -0
  398. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  399. rucio/web/rest/flaskapi/v1/dids.py +2340 -0
  400. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  401. rucio/web/rest/flaskapi/v1/export.py +75 -0
  402. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  403. rucio/web/rest/flaskapi/v1/identities.py +285 -0
  404. rucio/web/rest/flaskapi/v1/import.py +132 -0
  405. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  406. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  407. rucio/web/rest/flaskapi/v1/main.py +91 -0
  408. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  409. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  410. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  411. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  412. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  413. rucio/web/rest/flaskapi/v1/replicas.py +1894 -0
  414. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  415. rucio/web/rest/flaskapi/v1/rses.py +2250 -0
  416. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  417. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  418. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  419. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  420. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  421. rucio/web/rest/flaskapi/v1/traces.py +137 -0
  422. rucio/web/rest/flaskapi/v1/types.py +20 -0
  423. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  424. rucio/web/rest/main.py +18 -0
  425. rucio/web/rest/metrics.py +27 -0
  426. rucio/web/rest/ping.py +27 -0
  427. rucio-37.0.0rc1.data/data/rucio/etc/alembic.ini.template +71 -0
  428. rucio-37.0.0rc1.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  429. rucio-37.0.0rc1.data/data/rucio/etc/globus-config.yml.template +5 -0
  430. rucio-37.0.0rc1.data/data/rucio/etc/ldap.cfg.template +30 -0
  431. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  432. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  433. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  434. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  435. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  436. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  437. rucio-37.0.0rc1.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  438. rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.atlas.client.template +43 -0
  439. rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.template +241 -0
  440. rucio-37.0.0rc1.data/data/rucio/etc/rucio_multi_vo.cfg.template +217 -0
  441. rucio-37.0.0rc1.data/data/rucio/requirements.server.txt +297 -0
  442. rucio-37.0.0rc1.data/data/rucio/tools/bootstrap.py +34 -0
  443. rucio-37.0.0rc1.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  444. rucio-37.0.0rc1.data/data/rucio/tools/reset_database.py +40 -0
  445. rucio-37.0.0rc1.data/scripts/rucio +133 -0
  446. rucio-37.0.0rc1.data/scripts/rucio-abacus-account +74 -0
  447. rucio-37.0.0rc1.data/scripts/rucio-abacus-collection-replica +46 -0
  448. rucio-37.0.0rc1.data/scripts/rucio-abacus-rse +78 -0
  449. rucio-37.0.0rc1.data/scripts/rucio-admin +97 -0
  450. rucio-37.0.0rc1.data/scripts/rucio-atropos +60 -0
  451. rucio-37.0.0rc1.data/scripts/rucio-auditor +206 -0
  452. rucio-37.0.0rc1.data/scripts/rucio-automatix +50 -0
  453. rucio-37.0.0rc1.data/scripts/rucio-bb8 +57 -0
  454. rucio-37.0.0rc1.data/scripts/rucio-cache-client +141 -0
  455. rucio-37.0.0rc1.data/scripts/rucio-cache-consumer +42 -0
  456. rucio-37.0.0rc1.data/scripts/rucio-conveyor-finisher +58 -0
  457. rucio-37.0.0rc1.data/scripts/rucio-conveyor-poller +66 -0
  458. rucio-37.0.0rc1.data/scripts/rucio-conveyor-preparer +37 -0
  459. rucio-37.0.0rc1.data/scripts/rucio-conveyor-receiver +44 -0
  460. rucio-37.0.0rc1.data/scripts/rucio-conveyor-stager +76 -0
  461. rucio-37.0.0rc1.data/scripts/rucio-conveyor-submitter +139 -0
  462. rucio-37.0.0rc1.data/scripts/rucio-conveyor-throttler +104 -0
  463. rucio-37.0.0rc1.data/scripts/rucio-dark-reaper +53 -0
  464. rucio-37.0.0rc1.data/scripts/rucio-dumper +160 -0
  465. rucio-37.0.0rc1.data/scripts/rucio-follower +44 -0
  466. rucio-37.0.0rc1.data/scripts/rucio-hermes +54 -0
  467. rucio-37.0.0rc1.data/scripts/rucio-judge-cleaner +89 -0
  468. rucio-37.0.0rc1.data/scripts/rucio-judge-evaluator +137 -0
  469. rucio-37.0.0rc1.data/scripts/rucio-judge-injector +44 -0
  470. rucio-37.0.0rc1.data/scripts/rucio-judge-repairer +44 -0
  471. rucio-37.0.0rc1.data/scripts/rucio-kronos +44 -0
  472. rucio-37.0.0rc1.data/scripts/rucio-minos +53 -0
  473. rucio-37.0.0rc1.data/scripts/rucio-minos-temporary-expiration +50 -0
  474. rucio-37.0.0rc1.data/scripts/rucio-necromancer +120 -0
  475. rucio-37.0.0rc1.data/scripts/rucio-oauth-manager +63 -0
  476. rucio-37.0.0rc1.data/scripts/rucio-reaper +83 -0
  477. rucio-37.0.0rc1.data/scripts/rucio-replica-recoverer +248 -0
  478. rucio-37.0.0rc1.data/scripts/rucio-rse-decommissioner +66 -0
  479. rucio-37.0.0rc1.data/scripts/rucio-storage-consistency-actions +74 -0
  480. rucio-37.0.0rc1.data/scripts/rucio-transmogrifier +77 -0
  481. rucio-37.0.0rc1.data/scripts/rucio-undertaker +76 -0
  482. rucio-37.0.0rc1.dist-info/METADATA +92 -0
  483. rucio-37.0.0rc1.dist-info/RECORD +487 -0
  484. rucio-37.0.0rc1.dist-info/WHEEL +5 -0
  485. rucio-37.0.0rc1.dist-info/licenses/AUTHORS.rst +100 -0
  486. rucio-37.0.0rc1.dist-info/licenses/LICENSE +201 -0
  487. rucio-37.0.0rc1.dist-info/top_level.txt +1 -0
rucio/core/topology.py ADDED
@@ -0,0 +1,490 @@
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
+ import copy
15
+ import datetime
16
+ import itertools
17
+ import logging
18
+ import threading
19
+ import weakref
20
+ from decimal import Decimal
21
+ from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar, Union, cast
22
+
23
+ from sqlalchemy import and_, select
24
+
25
+ from rucio.common.config import config_get, config_get_int
26
+ from rucio.common.exception import InvalidRSEExpression, NoDistance, RSEProtocolNotSupported
27
+ from rucio.common.utils import PriorityQueue
28
+ from rucio.core.rse import RseCollection, RseData
29
+ from rucio.core.rse_expression_parser import parse_expression
30
+ from rucio.db.sqla import models
31
+ from rucio.db.sqla.session import read_session, transactional_session
32
+ from rucio.rse import rsemanager as rsemgr
33
+
34
+ _Number = Union[int, Decimal]
35
+ TN = TypeVar("TN", bound="Node")
36
+ TE = TypeVar("TE", bound="Edge")
37
+ ExpiringObjectCacheNewObject = TypeVar("ExpiringObjectCacheNewObject")
38
+
39
+ if TYPE_CHECKING:
40
+ from collections.abc import Callable, Iterable, Iterator
41
+ from typing import Protocol
42
+
43
+ from sqlalchemy.orm import Session
44
+ from typing_extensions import Self
45
+
46
+ from rucio.common.types import HopDict, LoggerFunction
47
+
48
+ class _StateProvider(Protocol):
49
+ @property
50
+ def cost(self) -> _Number:
51
+ ...
52
+
53
+ @property
54
+ def enabled(self) -> bool:
55
+ ...
56
+
57
+ TNState = TypeVar("TNState", bound=_StateProvider)
58
+ TEState = TypeVar("TEState", bound=_StateProvider)
59
+
60
+
61
+ DEFAULT_HOP_PENALTY = 10
62
+ INF = float('inf')
63
+
64
+
65
+ class Node(RseData):
66
+ def __init__(self, rse_id: str) -> None:
67
+ super().__init__(rse_id)
68
+
69
+ self.in_edges = weakref.WeakKeyDictionary()
70
+ self.out_edges = weakref.WeakKeyDictionary()
71
+
72
+ self.cost: _Number = 0
73
+ self.enabled: bool = True
74
+ self.used_for_multihop = False
75
+
76
+
77
+ class Edge(Generic[TN]):
78
+ def __init__(self, src_node: TN, dst_node: TN) -> None:
79
+ self._src_node = weakref.ref(src_node)
80
+ self._dst_node = weakref.ref(dst_node)
81
+
82
+ self.cost: _Number = 1
83
+ self.enabled: bool = True
84
+
85
+ self.add_to_nodes()
86
+
87
+ def add_to_nodes(self) -> None:
88
+ self.src_node.out_edges[self.dst_node] = self
89
+ self.dst_node.in_edges[self.src_node] = self
90
+
91
+ def remove_from_nodes(self) -> None:
92
+ self.src_node.out_edges.pop(self.dst_node, None)
93
+ self.dst_node.in_edges.pop(self.src_node, None)
94
+
95
+ @property
96
+ def src_node(self) -> TN:
97
+ node = self._src_node()
98
+ if node is None:
99
+ # This shouldn't happen if the Node list is correctly managed by the Topology object.
100
+ raise ReferenceError("weak reference returned None")
101
+ return node
102
+
103
+ @property
104
+ def dst_node(self) -> TN:
105
+ node = self._dst_node()
106
+ if node is None:
107
+ # This shouldn't happen if the Node list is correctly managed by the Topology object.
108
+ raise ReferenceError("weak reference returned None")
109
+ return node
110
+
111
+ def __eq__(self, other: object) -> bool:
112
+ if not isinstance(other, self.__class__):
113
+ return False
114
+ return self._src_node == other._src_node and self._dst_node == other._dst_node
115
+
116
+ def __str__(self) -> str:
117
+ return f'{self._src_node}-->{self._dst_node}'
118
+
119
+
120
+ class Topology(RseCollection, Generic[TN, TE]):
121
+ """
122
+ Helper private class used to easily fetch topological information for a subset of RSEs.
123
+ """
124
+ def __init__(
125
+ self,
126
+ rse_ids: Optional["Iterable[str]"] = None,
127
+ ignore_availability: bool = False,
128
+ node_cls: type[TN] = Node,
129
+ edge_cls: type[TE] = Edge,
130
+ ) -> None:
131
+ super().__init__(rse_ids=rse_ids, rse_data_cls=node_cls)
132
+ self._edge_cls = edge_cls
133
+ self._edges: dict[tuple[TN, TN], TE] = {}
134
+ self._edges_loaded = False
135
+ self._multihop_nodes = set()
136
+ self._hop_penalty = DEFAULT_HOP_PENALTY
137
+ self.ignore_availability = ignore_availability
138
+
139
+ self._lock = threading.RLock()
140
+
141
+ @transactional_session
142
+ def ensure_loaded(
143
+ self,
144
+ rse_ids: "Optional[Iterable[str]]" = None,
145
+ load_name: bool = False,
146
+ load_columns: bool = False,
147
+ load_attributes: bool = False,
148
+ load_info: bool = False,
149
+ load_usage: bool = False,
150
+ load_limits: bool = False,
151
+ include_deleted: bool = False,
152
+ *,
153
+ session: "Session",
154
+ ) -> None:
155
+
156
+ if not rse_ids:
157
+ with self._lock:
158
+ rse_ids = list(self.rse_id_to_data_map)
159
+ super().ensure_loaded(
160
+ rse_ids=rse_ids,
161
+ load_name=load_name,
162
+ load_columns=load_columns,
163
+ load_attributes=load_attributes,
164
+ load_info=load_info,
165
+ load_usage=load_usage,
166
+ load_limits=load_limits,
167
+ include_deleted=include_deleted,
168
+ session=session,
169
+ )
170
+
171
+ def get_or_create(self, rse_id: str) -> "TN":
172
+ rse_data = self.rse_id_to_data_map.get(rse_id)
173
+ if rse_data is None:
174
+ with self._lock:
175
+ rse_data = self.rse_id_to_data_map.get(rse_id)
176
+ if not rse_data:
177
+ self.rse_id_to_data_map[rse_id] = rse_data = self._rse_data_cls(rse_id)
178
+ # A new node added. Edges which were already loaded are probably incomplete now.
179
+ self._edges_loaded = False
180
+ return rse_data
181
+
182
+ @property
183
+ def edges(self) -> dict[tuple[TN, TN], TE]:
184
+ with self._lock:
185
+ return copy.copy(self._edges)
186
+
187
+ def edge(self, src_node: TN, dst_node: TN) -> "Optional[TE]":
188
+ return self._edges.get((src_node, dst_node))
189
+
190
+ def get_or_create_edge(self, src_node: TN, dst_node: TN) -> "TE":
191
+ edge = self._edges.get((src_node, dst_node))
192
+ if not edge:
193
+ with self._lock:
194
+ edge = self._edges.get((src_node, dst_node))
195
+ if not edge:
196
+ self._edges[src_node, dst_node] = edge = self._edge_cls(src_node, dst_node)
197
+ return edge
198
+
199
+ def delete_edge(self, src_node: TN, dst_node: TN) -> None:
200
+ with self._lock:
201
+ edge = self._edges[src_node, dst_node]
202
+ edge.remove_from_nodes()
203
+
204
+ @property
205
+ def multihop_enabled(self) -> bool:
206
+ return True if self._multihop_nodes else False
207
+
208
+ @read_session
209
+ def configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: "LoggerFunction" = logging.log) -> "Self":
210
+ with self._lock:
211
+ return self._configure_multihop(multihop_rse_ids=multihop_rse_ids, session=session, logger=logger)
212
+
213
+ def _configure_multihop(self, multihop_rse_ids: Optional[set[str]] = None, *, session: "Session", logger: "LoggerFunction" = logging.log) -> "Self":
214
+
215
+ if multihop_rse_ids is None:
216
+ multihop_rse_expression = config_get('transfers', 'multihop_rse_expression', default='available_for_multihop=true', expiration_time=600, session=session)
217
+
218
+ multihop_rse_ids = set()
219
+ if multihop_rse_expression.strip():
220
+ try:
221
+ multihop_rse_ids = {rse['id'] for rse in parse_expression(multihop_rse_expression, session=session)}
222
+ except InvalidRSEExpression:
223
+ pass
224
+ if not multihop_rse_ids:
225
+ logger(logging.WARNING, 'multihop_rse_expression is not empty, but returned no RSEs')
226
+
227
+ for node in self._multihop_nodes:
228
+ node.used_for_multihop = False
229
+
230
+ self._multihop_nodes.clear()
231
+
232
+ for rse_id in multihop_rse_ids:
233
+ node = self.get_or_create(rse_id).ensure_loaded(load_columns=True)
234
+ if self.ignore_availability or (node.columns['availability_read'] and node.columns['availability_write']):
235
+ node.used_for_multihop = True
236
+ self._multihop_nodes.add(node)
237
+
238
+ self._hop_penalty = config_get_int('transfers', 'hop_penalty', default=DEFAULT_HOP_PENALTY, session=session)
239
+ return self
240
+
241
+ @read_session
242
+ def ensure_edges_loaded(self, *, session: "Session") -> None:
243
+ """
244
+ Ensure that all edges are loaded for the (sub-)set of nodes known by this topology object
245
+ """
246
+ if self._edges_loaded:
247
+ return
248
+
249
+ with self._lock:
250
+ return self._ensure_edges_loaded(session=session)
251
+
252
+ def _ensure_edges_loaded(self, *, session: "Session") -> None:
253
+ stmt = select(
254
+ models.Distance
255
+ ).where(
256
+ and_(
257
+ models.Distance.src_rse_id.in_(self.rse_id_to_data_map.keys()),
258
+ models.Distance.dest_rse_id.in_(self.rse_id_to_data_map.keys()),
259
+ )
260
+ )
261
+
262
+ loaded_edges = set()
263
+ for distance in session.execute(stmt).scalars():
264
+ if distance.distance is None:
265
+ continue
266
+
267
+ src_node = self[distance.src_rse_id]
268
+ dst_node = self[distance.dest_rse_id]
269
+ edge = self.get_or_create_edge(src_node, dst_node)
270
+
271
+ sanitized_dist = int(distance.distance) if distance.distance >= 0 else 0
272
+ edge.cost = sanitized_dist
273
+
274
+ loaded_edges.add((src_node, dst_node))
275
+
276
+ if len(loaded_edges) != len(self._edges):
277
+ # Remove edges which don't exist in the database anymore
278
+ to_remove = set(self._edges).difference(loaded_edges)
279
+ for src_node, dst_node in to_remove:
280
+ self.delete_edge(src_node, dst_node)
281
+
282
+ self._edges_loaded = True
283
+
284
+ @read_session
285
+ def search_shortest_paths(
286
+ self,
287
+ src_nodes: "Iterable[TN]",
288
+ dst_node: TN,
289
+ operation_src: str,
290
+ operation_dest: str,
291
+ domain: str,
292
+ limit_dest_schemes: list[str],
293
+ *,
294
+ session: "Session",
295
+ ) -> dict[TN, list[dict[str, Any]]]:
296
+ """
297
+ Find the shortest paths from multiple sources towards dest_rse_id.
298
+ """
299
+
300
+ for rse in itertools.chain(src_nodes, [dst_node], self._multihop_nodes):
301
+ rse.ensure_loaded(load_attributes=True, load_info=True, session=session)
302
+ self.ensure_edges_loaded(session=session)
303
+
304
+ if self._multihop_nodes:
305
+ # Filter out island source RSEs
306
+ nodes_to_find = {node for node in src_nodes if node.out_edges}
307
+ else:
308
+ nodes_to_find = set(src_nodes)
309
+
310
+ class _NodeStateProvider:
311
+ _hop_penalty = self._hop_penalty
312
+
313
+ def __init__(self, node: TN) -> None:
314
+ self.enabled: bool = True
315
+ self.cost: _Number = 0
316
+ if node != dst_node:
317
+ try:
318
+ self.cost = int(node.attributes.get('hop_penalty', self._hop_penalty))
319
+ except ValueError:
320
+ self.cost = self._hop_penalty
321
+
322
+ scheme_missmatch_found = {}
323
+
324
+ class _EdgeStateProvider:
325
+ def __init__(self, edge: TE) -> None:
326
+ self.edge = edge
327
+ self.chosen_scheme = {}
328
+
329
+ @property
330
+ def cost(self) -> _Number:
331
+ return self.edge.cost
332
+
333
+ @property
334
+ def enabled(self) -> bool:
335
+ try:
336
+ matching_scheme = rsemgr.find_matching_scheme(
337
+ rse_settings_src=self.edge.src_node.info,
338
+ rse_settings_dest=self.edge.dst_node.info,
339
+ operation_src=operation_src,
340
+ operation_dest=operation_dest,
341
+ domain=domain,
342
+ scheme=limit_dest_schemes if self.edge.dst_node == dst_node and limit_dest_schemes else None,
343
+ )
344
+ self.chosen_scheme = {
345
+ 'source_scheme': matching_scheme[1],
346
+ 'dest_scheme': matching_scheme[0],
347
+ 'source_scheme_priority': matching_scheme[3],
348
+ 'dest_scheme_priority': matching_scheme[2],
349
+ }
350
+ return True
351
+ except RSEProtocolNotSupported:
352
+ scheme_missmatch_found[self.edge.src_node] = True
353
+ return False
354
+
355
+ paths = {dst_node: []}
356
+ for node, distance, _, edge_to_next_hop, edge_state in self.dijkstra_spf(dst_node=dst_node,
357
+ nodes_to_find=nodes_to_find,
358
+ node_state_provider=_NodeStateProvider,
359
+ edge_state_provider=_EdgeStateProvider):
360
+ nh_node = edge_to_next_hop.dst_node
361
+ edge_state = cast("_EdgeStateProvider", edge_state)
362
+ hop = {
363
+ 'source_rse': node,
364
+ 'dest_rse': nh_node,
365
+ 'hop_distance': edge_state.cost,
366
+ 'cumulated_distance': distance,
367
+ **edge_state.chosen_scheme,
368
+ }
369
+ paths[node] = [hop] + paths[nh_node]
370
+
371
+ nodes_to_find.discard(node)
372
+ if not nodes_to_find:
373
+ # We found the shortest paths to all desired nodes
374
+ break
375
+
376
+ result = {}
377
+ for node in src_nodes:
378
+ path = paths.get(node)
379
+ if path is not None:
380
+ result[node] = path
381
+ elif scheme_missmatch_found.get(node):
382
+ result[node] = []
383
+ return result
384
+
385
+ def dijkstra_spf(
386
+ self,
387
+ dst_node: TN,
388
+ nodes_to_find: Optional[set[TN]] = None,
389
+ node_state_provider: "Callable[[TN], TNState]" = lambda x: x,
390
+ edge_state_provider: "Callable[[TE], TEState]" = lambda x: x,
391
+ ) -> "Iterator[tuple[TN, _Number, TNState, TE, TEState]]":
392
+ """
393
+ Does a Backwards Dijkstra's algorithm: start from destination and follow inbound links to other nodes.
394
+ If multihop is disabled, stop after analysing direct connections to dest_rse.
395
+ If the optional nodes_to_find parameter is set, will restrict search only towards these nodes.
396
+ Otherwise, traverse the graph in integrality.
397
+
398
+ Will yield nodes in order of their distance from the destination.
399
+ """
400
+
401
+ priority_q = PriorityQueue()
402
+ priority_q[dst_node] = 0
403
+ next_hops: dict[TN, tuple[_Number, TNState, Optional[TE], Optional[TEState]]] =\
404
+ {dst_node: (0, node_state_provider(dst_node), None, None)}
405
+ while priority_q:
406
+ node = priority_q.pop()
407
+ node_dist, node_state, edge_to_nh, edge_to_nh_state = next_hops[node]
408
+
409
+ if edge_to_nh is not None and edge_to_nh_state is not None: # skip dst_node
410
+ yield node, node_dist, node_state, edge_to_nh, edge_to_nh_state
411
+
412
+ if self._multihop_nodes or edge_to_nh is None:
413
+ # If multihop is disabled, only examine neighbors of dst_node
414
+
415
+ for adjacent_node, edge in node.in_edges.items():
416
+
417
+ if nodes_to_find is None or adjacent_node in nodes_to_find or adjacent_node.used_for_multihop:
418
+
419
+ edge_state = edge_state_provider(edge)
420
+ new_adjacent_dist = node_dist + node_state.cost + edge_state.cost
421
+ if new_adjacent_dist < next_hops.get(adjacent_node, (INF, ))[0] and edge_state.enabled:
422
+ adj_node_state = node_state_provider(adjacent_node)
423
+ next_hops[adjacent_node] = new_adjacent_dist, adj_node_state, edge, edge_state
424
+ priority_q[adjacent_node] = new_adjacent_dist
425
+
426
+
427
+ class ExpiringObjectCache(Generic[ExpiringObjectCacheNewObject]):
428
+ """
429
+ Thread-safe container which builds and object with the function passed in parameter and
430
+ caches it for the TTL duration.
431
+ """
432
+
433
+ def __init__(
434
+ self,
435
+ ttl: int,
436
+ new_obj_fnc: "Callable[[], ExpiringObjectCacheNewObject]"
437
+ ) -> None:
438
+ self._lock = threading.Lock()
439
+ self._object: Optional[ExpiringObjectCacheNewObject] = None
440
+ self._creation_time: Optional[datetime.datetime] = None
441
+ self._new_obj_fnc = new_obj_fnc
442
+ self._ttl = ttl
443
+
444
+ def get(self, logger: "LoggerFunction" = logging.log) -> ExpiringObjectCacheNewObject:
445
+ with self._lock:
446
+ if not self._object \
447
+ or not self._creation_time \
448
+ or datetime.datetime.utcnow() - self._creation_time > datetime.timedelta(seconds=self._ttl):
449
+ self._object = self._new_obj_fnc()
450
+ self._creation_time = datetime.datetime.utcnow()
451
+ logger(logging.INFO, "Refreshed topology object")
452
+ return self._object
453
+
454
+
455
+ @transactional_session
456
+ def get_hops(
457
+ source_rse_id: str,
458
+ dest_rse_id: str,
459
+ multihop_rse_ids: Optional[set[str]] = None,
460
+ limit_dest_schemes: Optional[list[str]] = None,
461
+ *, session: "Session",
462
+ ) -> list["HopDict"]:
463
+ """
464
+ Get a list of hops needed to transfer date from source_rse_id to dest_rse_id.
465
+ Ideally, the list will only include one item (dest_rse_id) since no hops are needed.
466
+ :param source_rse_id: Source RSE id of the transfer.
467
+ :param dest_rse_id: Dest RSE id of the transfer.
468
+ :param multihop_rse_ids: List of RSE ids that can be used for multihop. If empty, multihop is disabled.
469
+ :param limit_dest_schemes: List of destination schemes the matching scheme algorithm should be limited to for a single hop.
470
+ :returns: List of hops in the format [{'source_rse_id': source_rse_id, 'source_scheme': 'srm', 'source_scheme_priority': N, 'dest_rse_id': dest_rse_id, 'dest_scheme': 'srm', 'dest_scheme_priority': N}]
471
+ :raises: NoDistance
472
+ """
473
+ if not limit_dest_schemes:
474
+ limit_dest_schemes = []
475
+
476
+ topology = Topology().configure_multihop(multihop_rse_ids=multihop_rse_ids)
477
+ src_node = topology[source_rse_id]
478
+ dst_node = topology[dest_rse_id]
479
+ shortest_paths = topology.search_shortest_paths(src_nodes=[src_node], dst_node=dst_node,
480
+ operation_src='third_party_copy_read', operation_dest='third_party_copy_write',
481
+ domain='wan', limit_dest_schemes=limit_dest_schemes, session=session)
482
+
483
+ path = shortest_paths.get(src_node)
484
+ if path is None:
485
+ raise NoDistance()
486
+
487
+ if not path:
488
+ raise RSEProtocolNotSupported()
489
+
490
+ return path