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
@@ -0,0 +1,762 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import functools
16
+ import logging
17
+ import re
18
+ import threading
19
+ import time
20
+ from datetime import datetime
21
+ from json import dumps, loads
22
+ from typing import TYPE_CHECKING, Any, Optional
23
+
24
+ import rucio.db.sqla.util
25
+ from rucio.common.config import config_get
26
+ from rucio.common.constants import RseAttr
27
+ from rucio.common.exception import (
28
+ DatabaseException,
29
+ DuplicateRule,
30
+ InsufficientAccountLimit,
31
+ InsufficientTargetRSEs,
32
+ InvalidReplicationRule,
33
+ InvalidRSEExpression,
34
+ InvalidRuleWeight,
35
+ RSEOverQuota,
36
+ StagingAreaRuleRequiresLifetime,
37
+ SubscriptionNotFound,
38
+ SubscriptionWrongParameter,
39
+ )
40
+ from rucio.common.logging import setup_logging
41
+ from rucio.common.stopwatch import Stopwatch
42
+ from rucio.common.types import InternalAccount, InternalScope, LoggerFunction
43
+ from rucio.common.utils import chunks
44
+ from rucio.core.did import get_metadata, list_new_dids, set_new_dids
45
+ from rucio.core.monitor import MetricManager
46
+ from rucio.core.rse import get_rse_id, list_rse_attributes, list_rses, rse_exists
47
+ from rucio.core.rse_expression_parser import parse_expression
48
+ from rucio.core.rse_selector import resolve_rse_expression
49
+ from rucio.core.rule import add_rule, get_rule, list_rules
50
+ from rucio.core.subscription import list_subscriptions, update_subscription
51
+ from rucio.daemons.common import run_daemon
52
+ from rucio.db.sqla.constants import DIDType, SubscriptionState
53
+
54
+ if TYPE_CHECKING:
55
+ from types import FrameType
56
+
57
+ from rucio.daemons.common import HeartbeatHandler
58
+
59
+ METRICS = MetricManager(module=__name__)
60
+ graceful_stop = threading.Event()
61
+ DAEMON_NAME = "transmogrifier"
62
+
63
+ RULES_COMMENT_LENGTH = 255
64
+
65
+
66
+ def __get_rule_dict(rule_dict: dict, subscription: dict) -> dict:
67
+ """
68
+ Internal method to clean and enrich the rule_dict coming from the subscription.
69
+
70
+ :param rule_dict: The rule dictionary coming from a subscription.
71
+ :param subscription: The subscription associated to the rule.
72
+ :return: A dictionary that contains all the parameters associated to the rule.
73
+ """
74
+ source_replica_expression = rule_dict.get("source_replica_expression", None)
75
+ rule_dict["source_replica_expression"] = source_replica_expression
76
+ locked = rule_dict.get("locked", None)
77
+ if locked == "True":
78
+ locked = True
79
+ else:
80
+ locked = False
81
+ rule_dict["locked"] = locked
82
+
83
+ purge_replicas = rule_dict.get("purge_replicas", False)
84
+ if purge_replicas == "True":
85
+ purge_replicas = True
86
+ else:
87
+ purge_replicas = False
88
+ rule_dict["purge_replicas"] = purge_replicas
89
+
90
+ rule_dict["rse_expression"] = str(rule_dict["rse_expression"])
91
+ comment = str(subscription["comments"])[:RULES_COMMENT_LENGTH]
92
+ if "comments" in rule_dict:
93
+ comment = str(rule_dict["comments"])
94
+ rule_dict["comment"] = comment
95
+ account = subscription["account"]
96
+ if "account" in rule_dict:
97
+ vo = account.vo
98
+ account = InternalAccount(rule_dict["account"], vo=vo)
99
+ rule_dict["account"] = account
100
+ rule_dict["copies"] = int(rule_dict["copies"])
101
+ default_activity = config_get("rules", "default_activity", default="default")
102
+ activity = rule_dict.get("activity", default_activity)
103
+ rule_dict["activity"] = activity
104
+ lifetime = rule_dict.get("lifetime", None)
105
+ if lifetime:
106
+ rule_dict["lifetime"] = int(lifetime)
107
+ chained_idx = rule_dict.get("chained_idx", None)
108
+ if chained_idx:
109
+ chained_idx = int(rule_dict["copies"])
110
+ rule_dict["chained_idx"] = chained_idx
111
+ delay_injection = rule_dict.get("delay_injection", None)
112
+ if delay_injection:
113
+ delay_injection = int(delay_injection)
114
+ rule_dict["delay_injection"] = delay_injection
115
+ return rule_dict
116
+
117
+
118
+ def __split_rule_select_rses(
119
+ subscription_id: str,
120
+ subscription_name: str,
121
+ scope: "InternalScope",
122
+ name: str,
123
+ account: "InternalAccount",
124
+ weight: int,
125
+ rse_expression: str,
126
+ copies: int,
127
+ blocklisted_rse_id: list,
128
+ logger: LoggerFunction,
129
+ ) -> tuple[list, bool, bool]:
130
+ """
131
+ Internal method to create a list of RSEs that match RSE expression for subscriptions with split_rule.
132
+
133
+ :param subscription_id: The subscription id.
134
+ :param subscription_name: The subscription name.
135
+ :param scope: The internal DID scope.
136
+ :param name: The DID name.
137
+ :param account: The internal account.
138
+ :param weight: The weight of the rule.
139
+ :param rse_expression: The RSE expression of the rule.
140
+ :param copies: The number of copies.
141
+ :param blocklisted_rse_id: The list of blocklisted_rse_id.
142
+ :param logger: The logger.
143
+ :return: A tuple with list selected_rses, and 2 booleans create_rule, wont_reevaluate.
144
+ """
145
+ wont_reevaluate = False
146
+ create_rule = True
147
+ preferred_rses = set()
148
+ for rule in list_rules(
149
+ filters={
150
+ "subscription_id": subscription_id,
151
+ "scope": scope,
152
+ "name": name,
153
+ }
154
+ ):
155
+ for rse_dict in parse_expression(
156
+ rule["rse_expression"],
157
+ filter_={"vo": account.vo},
158
+ ):
159
+ preferred_rses.add(rse_dict["rse"])
160
+ preferred_rses = list(preferred_rses)
161
+ preferred_unmatched = list()
162
+ selected_rses = list()
163
+
164
+ for attempt in range(0, 2):
165
+ # First attempt excludes blocklisted RSEs
166
+ # Second attempt includes blocklisted RSEs
167
+ try:
168
+ (selected_rses, preferred_unmatched,) = resolve_rse_expression(
169
+ rse_expression,
170
+ account,
171
+ weight=weight,
172
+ copies=copies,
173
+ size=0,
174
+ preferred_rses=preferred_rses,
175
+ blocklist=blocklisted_rse_id,
176
+ )
177
+ wont_reevaluate = True
178
+ break
179
+ except (
180
+ InsufficientTargetRSEs,
181
+ InsufficientAccountLimit,
182
+ InvalidRuleWeight,
183
+ RSEOverQuota,
184
+ ) as error:
185
+ logger(
186
+ logging.WARNING,
187
+ 'Problem getting RSEs for subscription "%s" for account %s : %s. %s'
188
+ % (
189
+ subscription_name,
190
+ account,
191
+ str(error),
192
+ 'Try including blocklisted sites' if attempt == 0 else 'Skipping rule creation.'
193
+ ),
194
+ )
195
+ # Now including the blocklisted sites
196
+ blocklisted_rse_id = []
197
+ METRICS.counter(name="addnewrule.errortype.{exception}").labels(exception=str(error.__class__.__name__)).inc()
198
+ wont_reevaluate = True
199
+ except Exception as error:
200
+ logger(
201
+ logging.ERROR,
202
+ "Problem resolving RSE expression %s : %s"
203
+ % (
204
+ rse_expression,
205
+ str(error),
206
+ )
207
+ )
208
+ if len(preferred_rses) - len(preferred_unmatched) >= copies:
209
+ create_rule = False
210
+ return selected_rses, create_rule, wont_reevaluate
211
+
212
+
213
+ def get_subscriptions(logger: LoggerFunction = logging.log) -> list[dict]:
214
+ """
215
+ A method to extract the list of active subscriptions and exclude the one that have bad RSE expression.
216
+ :param logger: The logger.
217
+ :return: The list of active subscriptions.
218
+ """
219
+ subscriptions = []
220
+ try:
221
+ sub_dict = {3: []}
222
+ # Get the list of subscriptions. The default priority of the subscription is 3. 0 is the highest priority, 5 the lowest
223
+ # The priority is defined as 'policyid'
224
+ logger(logging.DEBUG, "Listing active subscriptions")
225
+ for sub in list_subscriptions(None, None):
226
+ rse_expression = sub.get("rse_expression")
227
+ skip_sub = False
228
+ rules = loads(sub["replication_rules"])
229
+ overwrite_rules = False
230
+ for rule in rules:
231
+ rse_expression = rule.get("rse_expression")
232
+ try:
233
+ list_rses_from_expression = parse_expression(rse_expression)
234
+ except InvalidRSEExpression:
235
+ logger(
236
+ logging.ERROR,
237
+ "Invalid RSE expression %s for subscription %s. Subscription removed from the list",
238
+ rse_expression,
239
+ sub["id"],
240
+ )
241
+ skip_sub = True
242
+ break
243
+ if rule.get("copies") == "*":
244
+ rule["copies"] = len(list_rses_from_expression)
245
+ overwrite_rules = True
246
+ if skip_sub:
247
+ continue
248
+ if overwrite_rules:
249
+ sub["replication_rules"] = dumps(rules)
250
+ if (
251
+ sub["state"] != SubscriptionState.INACTIVE
252
+ and sub["lifetime"]
253
+ and (datetime.now() > sub["lifetime"])
254
+ ):
255
+ update_subscription(
256
+ name=sub["name"],
257
+ account=sub["account"],
258
+ metadata={"state": SubscriptionState.INACTIVE},
259
+ )
260
+
261
+ elif sub["state"] in [SubscriptionState.ACTIVE, SubscriptionState.UPDATED]:
262
+ priority = 3
263
+ if "policyid" in sub:
264
+ if int(sub["policyid"]) not in sub_dict:
265
+ sub_dict[int(sub["policyid"])] = []
266
+ priority = int(sub["policyid"])
267
+ sub_dict[priority].append(sub)
268
+ priorities = list(sub_dict.keys())
269
+ priorities.sort()
270
+ # Order the subscriptions according to their priority
271
+ for priority in priorities:
272
+ subscriptions.extend(sub_dict[priority])
273
+ logger(logging.INFO, "%i active subscriptions", len(subscriptions))
274
+ except SubscriptionNotFound as error:
275
+ logger(logging.WARNING, "No subscriptions defined: %s" % (str(error)))
276
+ return []
277
+ except TypeError as error:
278
+ logger(
279
+ logging.ERROR,
280
+ "Failed to parse subscription: %s" % (str(error)),
281
+ )
282
+ raise error
283
+ except Exception as error:
284
+ logger(
285
+ logging.ERROR,
286
+ "Failed to get list of new DIDs or subscriptions: %s" % (str(error)),
287
+ )
288
+ raise error
289
+ return subscriptions
290
+
291
+
292
+ def __is_matching_subscription(
293
+ subscription: dict[str, Any],
294
+ did: dict[str, Any],
295
+ metadata: dict[str, Any]
296
+ ) -> bool:
297
+ """
298
+ Internal method to identify if a DID matches a subscription.
299
+
300
+ :param subscription: The subscription dictionary.
301
+ :param did: The DID dictionary
302
+ :param metadata: The metadata dictionary for the DID
303
+ :return: True/False
304
+ """
305
+ if metadata["hidden"]:
306
+ return False
307
+ try:
308
+ filter_string = loads(subscription["filter"])
309
+ except ValueError as error:
310
+ logging.error("%s : Subscription will be skipped" % error)
311
+ return False
312
+ # Loop over the keys of filter_string for subscription
313
+ for key in filter_string:
314
+ values = filter_string[key]
315
+ if key == "pattern":
316
+ if not re.match(values, did["name"]):
317
+ return False
318
+ elif key == "excluded_pattern":
319
+ if re.match(values, did["name"]):
320
+ return False
321
+ elif key == "split_rule":
322
+ pass
323
+ elif key == "scope":
324
+ match_scope = False
325
+ for scope in values:
326
+ if re.match(scope, did["scope"].internal):
327
+ match_scope = True
328
+ break
329
+ if not match_scope:
330
+ return False
331
+ elif key == "account":
332
+ match_account = False
333
+ if not isinstance(values, list):
334
+ values = [values]
335
+ for account in values:
336
+ if account == metadata["account"].internal:
337
+ match_account = True
338
+ break
339
+ if not match_account:
340
+ return False
341
+ elif key == "did_type":
342
+ match_did_type = False
343
+ if not isinstance(values, list):
344
+ values = [values]
345
+ for did_type in values:
346
+ if did_type == metadata["did_type"].name:
347
+ match_did_type = True
348
+ break
349
+ if not match_did_type:
350
+ return False
351
+ elif key in ["min_avg_file_size", "max_avg_file_size"]:
352
+ length = metadata["length"]
353
+ size = metadata["bytes"]
354
+ if length and size:
355
+ avg_file_size = size / length
356
+ if key == "min_avg_file_size" and avg_file_size < values:
357
+ return False
358
+ if key == "max_avg_file_size" and avg_file_size > values:
359
+ return False
360
+ else:
361
+ # If the DID is evaluated at the creation, length and bytes are not set yet
362
+ # In that case, just ignore min_avg_file_size and max_avg_file_size filter
363
+ continue
364
+ else:
365
+ if not isinstance(values, list):
366
+ values = [
367
+ values,
368
+ ]
369
+ has_metadata = False
370
+ for meta in metadata:
371
+ if str(meta) == str(key):
372
+ has_metadata = True
373
+ match_meta = False
374
+ for value in values:
375
+ if re.match(str(value), str(metadata[meta])):
376
+ match_meta = True
377
+ break
378
+ if not match_meta:
379
+ return False
380
+ if not has_metadata:
381
+ return False
382
+ return True
383
+
384
+
385
+ def select_algorithm(
386
+ algorithm: str,
387
+ rule_ids: list[str],
388
+ params: dict[str, Any],
389
+ logger: LoggerFunction
390
+ ) -> dict:
391
+ """
392
+ Method used in case of chained subscriptions
393
+
394
+ :param algorithm: Algorithm used for the chained rule. Now only associated_site
395
+ associated_site : Choose an associated endpoint according to the RSE attribute assoiciated_site
396
+ :param rule_ids: List of parent rules
397
+ :param params: Dictionary of rules parameters to be used by the algorithm
398
+ """
399
+ selected_rses = {}
400
+ for rule_id in rule_ids:
401
+ rule = get_rule(rule_id)
402
+ logging.debug("In select_algorithm, %s", str(rule))
403
+ rse = rule["rse_expression"]
404
+ vo = rule["account"].vo
405
+ if rse_exists(rse, vo=vo):
406
+ rse_id = get_rse_id(rse, vo=vo)
407
+ rse_attributes = list_rse_attributes(rse_id)
408
+ if algorithm == "associated_site":
409
+ associated_sites = rse_attributes.get(RseAttr.ASSOCIATED_SITES, None)
410
+ associated_site_idx = params.get("associated_site_idx", None)
411
+ if not associated_site_idx:
412
+ raise SubscriptionWrongParameter(
413
+ "Missing parameter associated_site_idx"
414
+ )
415
+ if associated_sites:
416
+ associated_sites = associated_sites.split(",")
417
+ if associated_site_idx > len(associated_sites) + 1:
418
+ raise SubscriptionWrongParameter(
419
+ "Parameter associated_site_idx is out of range"
420
+ )
421
+ associated_site = associated_sites[associated_site_idx - 1]
422
+ selected_rses[associated_site] = {
423
+ "source_replica_expression": rse,
424
+ "weight": None,
425
+ }
426
+ if algorithm == "exclude_site":
427
+ site = rse_attributes.get(RseAttr.SITE, None)
428
+ rse_expression = params['rse_expression'] + '\\site=%s' % site
429
+ (
430
+ selected_rses,
431
+ create_rule,
432
+ wont_reevaluate,
433
+ ) = __split_rule_select_rses(
434
+ subscription_id=params["subscription_id"],
435
+ subscription_name=params["subscription_name"],
436
+ scope=rule["scope"],
437
+ name=rule["name"],
438
+ account=rule.get("account"),
439
+ weight=rule.get("weight"),
440
+ rse_expression=rse_expression,
441
+ copies=rule.get('copies'),
442
+ blocklisted_rse_id=params['blocklisted_rse_id'],
443
+ logger=logger,
444
+ )
445
+ dict_selected_rses = {}
446
+ for entry in selected_rses:
447
+ dict_selected_rses[entry] = {
448
+ "source_replica_expression": rse,
449
+ "weight": None,
450
+ }
451
+ selected_rses = dict_selected_rses
452
+ else:
453
+ raise SubscriptionWrongParameter(
454
+ "Algorithm %s only works with split_rule" % algorithm
455
+ )
456
+ if rule["copies"] != 1:
457
+ raise SubscriptionWrongParameter(
458
+ "Algorithm %s only works with split_rule" % algorithm
459
+ )
460
+ return selected_rses
461
+
462
+
463
+ def transmogrifier(bulk: int = 5, once: bool = False, sleep_time: int = 60) -> None:
464
+ """
465
+ Creates a Transmogrifier Worker that gets a list of new DIDs for a given hash,
466
+ identifies the subscriptions matching the DIDs and
467
+ submit a replication rule for each DID matching a subscription.
468
+
469
+ :param bulk: The number of requests to process.
470
+ :param once: Run only once.
471
+ :param sleep_time: Time between two cycles.
472
+ """
473
+ run_daemon(
474
+ once=once,
475
+ graceful_stop=graceful_stop,
476
+ executable=DAEMON_NAME,
477
+ partition_wait_time=1,
478
+ sleep_time=sleep_time,
479
+ run_once_fnc=functools.partial(
480
+ run_once,
481
+ bulk=bulk,
482
+ ),
483
+ )
484
+
485
+
486
+ def run_once(heartbeat_handler: "HeartbeatHandler", bulk: int, **_kwargs) -> bool:
487
+
488
+ worker_number, total_workers, logger = heartbeat_handler.live()
489
+ stopwatch = Stopwatch()
490
+ blocklisted_rse_id = [rse["id"] for rse in list_rses({"availability_write": False})]
491
+ identifiers = []
492
+ # List all the active subscriptions
493
+ subscriptions = get_subscriptions(logger=logger)
494
+
495
+ # Loop over all the new dids
496
+ # Get the new DIDs based on the is_new flag
497
+ logger(logging.DEBUG, "Listing new dids")
498
+ for did in list_new_dids(
499
+ thread=worker_number,
500
+ total_threads=total_workers,
501
+ chunk_size=bulk,
502
+ did_type=None,
503
+ ):
504
+ _, _, logger = heartbeat_handler.live()
505
+ did_success = True
506
+ if not (
507
+ did["did_type"] == DIDType.DATASET or did["did_type"] == DIDType.CONTAINER
508
+ ):
509
+ identifiers.append(
510
+ {
511
+ "scope": did["scope"],
512
+ "name": did["name"],
513
+ "did_type": did["did_type"],
514
+ }
515
+ )
516
+ continue
517
+ metadata = get_metadata(did["scope"], did["name"])
518
+
519
+ # Loop over all the subscriptions
520
+ for subscription in subscriptions:
521
+ # Check if the DID match the subscription
522
+ if __is_matching_subscription(subscription, did, metadata) is True:
523
+ filter_string = loads(subscription["filter"])
524
+ split_rule = filter_string.get("split_rule", False)
525
+ stime = time.time()
526
+ logger(
527
+ logging.INFO,
528
+ "%s:%s matches subscription %s"
529
+ % (did["scope"], did["name"], subscription["name"]),
530
+ )
531
+ rules = loads(subscription["replication_rules"])
532
+ created_rules = {}
533
+ for cnt, rule_dict in enumerate(rules):
534
+ created_rules[cnt + 1] = []
535
+ # Get all the rule and subscription parameters
536
+ rule_dict = __get_rule_dict(rule_dict, subscription)
537
+ weight = rule_dict.get("weight", None)
538
+ source_replica_expression = rule_dict.get(
539
+ "source_replica_expression", None
540
+ )
541
+ copies = rule_dict["copies"]
542
+ success = False
543
+
544
+ chained_idx = rule_dict.get("chained_idx", None)
545
+ # By default selected_rses contains only the rse_expression
546
+ # It is overwritten in 2 cases : Chained subscription and split_rule
547
+ selected_rses = [rule_dict.get("rse_expression")]
548
+ if chained_idx:
549
+ # In the case of chained subscription, don't use rseselector but use the rses returned by the algorithm
550
+ params = {}
551
+ params['rse_expression'] = rule_dict.get("rse_expression")
552
+ params['subscription_id'] = subscription["id"]
553
+ params['subscription_name'] = subscription["name"]
554
+ params['blocklisted_rse_id'] = blocklisted_rse_id
555
+ if rule_dict.get("associated_site_idx", None):
556
+ params["associated_site_idx"] = rule_dict.get(
557
+ "associated_site_idx", None
558
+ )
559
+ logger(
560
+ logging.DEBUG,
561
+ "Chained subscription identified. Will use %s",
562
+ str(created_rules[chained_idx]),
563
+ )
564
+ algorithm = rule_dict.get("algorithm", None)
565
+ selected_rses = select_algorithm(
566
+ algorithm,
567
+ created_rules[chained_idx],
568
+ params,
569
+ logger
570
+ )
571
+ copies = 1
572
+ elif split_rule:
573
+ (
574
+ selected_rses,
575
+ create_rule,
576
+ wont_reevaluate,
577
+ ) = __split_rule_select_rses(
578
+ subscription_id=subscription["id"],
579
+ subscription_name=subscription["name"],
580
+ scope=did["scope"],
581
+ name=did["name"],
582
+ account=rule_dict.get("account"),
583
+ weight=weight,
584
+ rse_expression=rule_dict.get("rse_expression"),
585
+ copies=copies,
586
+ blocklisted_rse_id=blocklisted_rse_id,
587
+ logger=logger,
588
+ )
589
+ copies = 1
590
+ if not create_rule:
591
+ continue
592
+ # The DID won't be reevaluated at the next cycle
593
+ did_success = did_success and wont_reevaluate
594
+
595
+ nb_rule = 0
596
+ # Try to create the rule
597
+ logger(logging.DEBUG, 'selected_rses : %s' % selected_rses)
598
+ try:
599
+ for rse in selected_rses:
600
+ if isinstance(selected_rses, dict):
601
+ # selected_rses is a dictionary only when split_rule is True or for chained subscriptions
602
+ source_replica_expression = selected_rses[rse].get(
603
+ "source_replica_expression",
604
+ None,
605
+ )
606
+ weight = selected_rses[rse].get("weight", None)
607
+ logger(
608
+ logging.INFO,
609
+ "Will insert one rule for %s:%s on %s"
610
+ % (did["scope"], did["name"], rse),
611
+ )
612
+ rule_ids = add_rule(
613
+ dids=[
614
+ {
615
+ "scope": did["scope"],
616
+ "name": did["name"],
617
+ }
618
+ ],
619
+ account=rule_dict.get("account"),
620
+ copies=copies,
621
+ rse_expression=rse,
622
+ grouping=rule_dict.get("grouping", "DATASET"),
623
+ weight=weight,
624
+ lifetime=rule_dict.get("lifetime", None),
625
+ locked=rule_dict.get("locked", None),
626
+ subscription_id=subscription["id"],
627
+ source_replica_expression=source_replica_expression,
628
+ activity=rule_dict.get("activity"),
629
+ purge_replicas=rule_dict.get("purge_replicas", False),
630
+ ignore_availability=rule_dict.get(
631
+ "ignore_availability", None
632
+ ),
633
+ comment=rule_dict.get("comment"),
634
+ delay_injection=rule_dict.get("delay_injection"),
635
+ )
636
+ created_rules[cnt + 1].append(rule_ids[0])
637
+ nb_rule += 1
638
+ if nb_rule == copies:
639
+ success = True
640
+ if split_rule:
641
+ success = True
642
+
643
+ METRICS.counter("addnewrule.done").inc(nb_rule)
644
+ METRICS.counter("addnewrule.activity.{activity}").labels(activity="".join(rule_dict.get("activity").split())).inc(nb_rule)
645
+ success = True
646
+ except (
647
+ InvalidReplicationRule,
648
+ InvalidRuleWeight,
649
+ InvalidRSEExpression,
650
+ StagingAreaRuleRequiresLifetime,
651
+ DuplicateRule,
652
+ ) as error:
653
+ # Errors that won't be retried
654
+ success = True
655
+ logger(logging.ERROR, str(error))
656
+ METRICS.counter("addnewrule.errortype.{exception}").labels(exception=str(error.__class__.__name__)).inc()
657
+ except Exception:
658
+ # Errors that will be retried
659
+ METRICS.counter("addnewrule.errortype.{exception}").labels(exception="unknown").inc()
660
+ logger(logging.ERROR, "Unexpected error", exc_info=True)
661
+
662
+ did_success = did_success and success
663
+ if not success:
664
+ logger(
665
+ logging.ERROR,
666
+ "Rule for %s:%s on %s cannot be inserted"
667
+ % (
668
+ did["scope"],
669
+ did["name"],
670
+ rule_dict.get("rse_expression"),
671
+ ),
672
+ )
673
+ else:
674
+ logger(
675
+ logging.INFO,
676
+ "%s rule(s) inserted in %f seconds"
677
+ % (str(nb_rule), time.time() - stime),
678
+ )
679
+
680
+ if did_success:
681
+ if did["did_type"] == str(DIDType.FILE):
682
+ METRICS.counter(name="files_processed").inc()
683
+ elif did["did_type"] == str(DIDType.DATASET):
684
+ METRICS.counter(name="datasets_processed").inc()
685
+ elif did["did_type"] == str(DIDType.CONTAINER):
686
+ METRICS.counter(name="containers_processed").inc()
687
+ METRICS.counter(name="dids_processed").inc()
688
+ identifiers.append(
689
+ {
690
+ "scope": did["scope"],
691
+ "name": did["name"],
692
+ "did_type": did["did_type"],
693
+ }
694
+ )
695
+
696
+ # Mark the DIDs as processed
697
+ flag_stopwatch = Stopwatch()
698
+ for identifier in chunks(identifiers, 100):
699
+ set_new_dids(identifier, None)
700
+ logger(logging.DEBUG, "Time to set the new flag : %f" % flag_stopwatch.elapsed)
701
+
702
+ stopwatch.stop()
703
+
704
+ for sub in subscriptions:
705
+ update_subscription(
706
+ name=sub["name"],
707
+ account=sub["account"],
708
+ metadata={"last_processed": datetime.utcnow()},
709
+ )
710
+ logger(
711
+ logging.INFO,
712
+ "It took %f seconds to process %i DIDs" % (stopwatch.elapsed, len(identifiers)),
713
+ )
714
+ logger(logging.DEBUG, "DIDs processed : %s" % (str(identifiers)))
715
+ METRICS.counter(name="transmogrifier.job.done").inc(1)
716
+ METRICS.timer("job.duration").observe(stopwatch.elapsed)
717
+ must_sleep = True
718
+ return must_sleep
719
+
720
+
721
+ def run(
722
+ threads: int = 1,
723
+ bulk: int = 100,
724
+ once: bool = False,
725
+ sleep_time: int = 60
726
+ ) -> None:
727
+ """
728
+ Starts up the transmogrifier threads.
729
+ """
730
+ setup_logging(process_name=DAEMON_NAME)
731
+
732
+ if rucio.db.sqla.util.is_old_db():
733
+ raise DatabaseException("Database was not updated, daemon won't start")
734
+
735
+ if once:
736
+ logging.info("Will run only one iteration in a single threaded mode")
737
+ transmogrifier(bulk=bulk, once=once)
738
+ else:
739
+ logging.info("starting transmogrifier threads")
740
+ thread_list = [
741
+ threading.Thread(
742
+ target=transmogrifier,
743
+ kwargs={"once": once, "sleep_time": sleep_time, "bulk": bulk},
744
+ )
745
+ for _ in range(0, threads)
746
+ ]
747
+ [thread.start() for thread in thread_list]
748
+ logging.info("waiting for interrupts")
749
+ # Interruptible joins require a timeout.
750
+ while thread_list:
751
+ thread_list = [
752
+ thread.join(timeout=3.14)
753
+ for thread in thread_list
754
+ if thread and thread.is_alive()
755
+ ]
756
+
757
+
758
+ def stop(signum: Optional[int] = None, frame: Optional["FrameType"] = None) -> None:
759
+ """
760
+ Graceful exit.
761
+ """
762
+ graceful_stop.set()