rucio 35.7.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (493) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/client/__init__.py +15 -0
  4. rucio/client/accountclient.py +433 -0
  5. rucio/client/accountlimitclient.py +183 -0
  6. rucio/client/baseclient.py +974 -0
  7. rucio/client/client.py +76 -0
  8. rucio/client/configclient.py +126 -0
  9. rucio/client/credentialclient.py +59 -0
  10. rucio/client/didclient.py +866 -0
  11. rucio/client/diracclient.py +56 -0
  12. rucio/client/downloadclient.py +1785 -0
  13. rucio/client/exportclient.py +44 -0
  14. rucio/client/fileclient.py +50 -0
  15. rucio/client/importclient.py +42 -0
  16. rucio/client/lifetimeclient.py +90 -0
  17. rucio/client/lockclient.py +109 -0
  18. rucio/client/metaconventionsclient.py +140 -0
  19. rucio/client/pingclient.py +44 -0
  20. rucio/client/replicaclient.py +454 -0
  21. rucio/client/requestclient.py +125 -0
  22. rucio/client/rseclient.py +746 -0
  23. rucio/client/ruleclient.py +294 -0
  24. rucio/client/scopeclient.py +90 -0
  25. rucio/client/subscriptionclient.py +173 -0
  26. rucio/client/touchclient.py +82 -0
  27. rucio/client/uploadclient.py +955 -0
  28. rucio/common/__init__.py +13 -0
  29. rucio/common/cache.py +74 -0
  30. rucio/common/config.py +801 -0
  31. rucio/common/constants.py +159 -0
  32. rucio/common/constraints.py +17 -0
  33. rucio/common/didtype.py +189 -0
  34. rucio/common/dumper/__init__.py +335 -0
  35. rucio/common/dumper/consistency.py +452 -0
  36. rucio/common/dumper/data_models.py +318 -0
  37. rucio/common/dumper/path_parsing.py +64 -0
  38. rucio/common/exception.py +1151 -0
  39. rucio/common/extra.py +36 -0
  40. rucio/common/logging.py +420 -0
  41. rucio/common/pcache.py +1408 -0
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +84 -0
  44. rucio/common/schema/__init__.py +150 -0
  45. rucio/common/schema/atlas.py +413 -0
  46. rucio/common/schema/belleii.py +408 -0
  47. rucio/common/schema/domatpc.py +401 -0
  48. rucio/common/schema/escape.py +426 -0
  49. rucio/common/schema/generic.py +433 -0
  50. rucio/common/schema/generic_multi_vo.py +412 -0
  51. rucio/common/schema/icecube.py +406 -0
  52. rucio/common/stomp_utils.py +159 -0
  53. rucio/common/stopwatch.py +55 -0
  54. rucio/common/test_rucio_server.py +148 -0
  55. rucio/common/types.py +403 -0
  56. rucio/common/utils.py +2238 -0
  57. rucio/core/__init__.py +13 -0
  58. rucio/core/account.py +496 -0
  59. rucio/core/account_counter.py +236 -0
  60. rucio/core/account_limit.py +423 -0
  61. rucio/core/authentication.py +620 -0
  62. rucio/core/config.py +456 -0
  63. rucio/core/credential.py +225 -0
  64. rucio/core/did.py +3000 -0
  65. rucio/core/did_meta_plugins/__init__.py +252 -0
  66. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  68. rucio/core/did_meta_plugins/filter_engine.py +613 -0
  69. rucio/core/did_meta_plugins/json_meta.py +240 -0
  70. rucio/core/did_meta_plugins/mongo_meta.py +216 -0
  71. rucio/core/did_meta_plugins/postgres_meta.py +316 -0
  72. rucio/core/dirac.py +237 -0
  73. rucio/core/distance.py +187 -0
  74. rucio/core/exporter.py +59 -0
  75. rucio/core/heartbeat.py +363 -0
  76. rucio/core/identity.py +300 -0
  77. rucio/core/importer.py +259 -0
  78. rucio/core/lifetime_exception.py +377 -0
  79. rucio/core/lock.py +576 -0
  80. rucio/core/message.py +282 -0
  81. rucio/core/meta_conventions.py +203 -0
  82. rucio/core/monitor.py +447 -0
  83. rucio/core/naming_convention.py +195 -0
  84. rucio/core/nongrid_trace.py +136 -0
  85. rucio/core/oidc.py +1461 -0
  86. rucio/core/permission/__init__.py +119 -0
  87. rucio/core/permission/atlas.py +1348 -0
  88. rucio/core/permission/belleii.py +1077 -0
  89. rucio/core/permission/escape.py +1078 -0
  90. rucio/core/permission/generic.py +1130 -0
  91. rucio/core/permission/generic_multi_vo.py +1150 -0
  92. rucio/core/quarantined_replica.py +223 -0
  93. rucio/core/replica.py +4158 -0
  94. rucio/core/replica_sorter.py +366 -0
  95. rucio/core/request.py +3089 -0
  96. rucio/core/rse.py +1875 -0
  97. rucio/core/rse_counter.py +186 -0
  98. rucio/core/rse_expression_parser.py +459 -0
  99. rucio/core/rse_selector.py +302 -0
  100. rucio/core/rule.py +4483 -0
  101. rucio/core/rule_grouping.py +1618 -0
  102. rucio/core/scope.py +180 -0
  103. rucio/core/subscription.py +364 -0
  104. rucio/core/topology.py +490 -0
  105. rucio/core/trace.py +375 -0
  106. rucio/core/transfer.py +1517 -0
  107. rucio/core/vo.py +169 -0
  108. rucio/core/volatile_replica.py +150 -0
  109. rucio/daemons/__init__.py +13 -0
  110. rucio/daemons/abacus/__init__.py +13 -0
  111. rucio/daemons/abacus/account.py +116 -0
  112. rucio/daemons/abacus/collection_replica.py +124 -0
  113. rucio/daemons/abacus/rse.py +117 -0
  114. rucio/daemons/atropos/__init__.py +13 -0
  115. rucio/daemons/atropos/atropos.py +242 -0
  116. rucio/daemons/auditor/__init__.py +289 -0
  117. rucio/daemons/auditor/hdfs.py +97 -0
  118. rucio/daemons/auditor/srmdumps.py +355 -0
  119. rucio/daemons/automatix/__init__.py +13 -0
  120. rucio/daemons/automatix/automatix.py +293 -0
  121. rucio/daemons/badreplicas/__init__.py +13 -0
  122. rucio/daemons/badreplicas/minos.py +322 -0
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  124. rucio/daemons/badreplicas/necromancer.py +196 -0
  125. rucio/daemons/bb8/__init__.py +13 -0
  126. rucio/daemons/bb8/bb8.py +353 -0
  127. rucio/daemons/bb8/common.py +759 -0
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  129. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  130. rucio/daemons/c3po/__init__.py +13 -0
  131. rucio/daemons/c3po/algorithms/__init__.py +13 -0
  132. rucio/daemons/c3po/algorithms/simple.py +134 -0
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +128 -0
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +130 -0
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +294 -0
  136. rucio/daemons/c3po/c3po.py +371 -0
  137. rucio/daemons/c3po/collectors/__init__.py +13 -0
  138. rucio/daemons/c3po/collectors/agis.py +108 -0
  139. rucio/daemons/c3po/collectors/free_space.py +81 -0
  140. rucio/daemons/c3po/collectors/jedi_did.py +57 -0
  141. rucio/daemons/c3po/collectors/mock_did.py +51 -0
  142. rucio/daemons/c3po/collectors/network_metrics.py +71 -0
  143. rucio/daemons/c3po/collectors/workload.py +112 -0
  144. rucio/daemons/c3po/utils/__init__.py +13 -0
  145. rucio/daemons/c3po/utils/dataset_cache.py +50 -0
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +56 -0
  147. rucio/daemons/c3po/utils/expiring_list.py +62 -0
  148. rucio/daemons/c3po/utils/popularity.py +85 -0
  149. rucio/daemons/c3po/utils/timeseries.py +89 -0
  150. rucio/daemons/cache/__init__.py +13 -0
  151. rucio/daemons/cache/consumer.py +197 -0
  152. rucio/daemons/common.py +415 -0
  153. rucio/daemons/conveyor/__init__.py +13 -0
  154. rucio/daemons/conveyor/common.py +562 -0
  155. rucio/daemons/conveyor/finisher.py +529 -0
  156. rucio/daemons/conveyor/poller.py +404 -0
  157. rucio/daemons/conveyor/preparer.py +205 -0
  158. rucio/daemons/conveyor/receiver.py +249 -0
  159. rucio/daemons/conveyor/stager.py +132 -0
  160. rucio/daemons/conveyor/submitter.py +403 -0
  161. rucio/daemons/conveyor/throttler.py +532 -0
  162. rucio/daemons/follower/__init__.py +13 -0
  163. rucio/daemons/follower/follower.py +101 -0
  164. rucio/daemons/hermes/__init__.py +13 -0
  165. rucio/daemons/hermes/hermes.py +774 -0
  166. rucio/daemons/judge/__init__.py +13 -0
  167. rucio/daemons/judge/cleaner.py +159 -0
  168. rucio/daemons/judge/evaluator.py +185 -0
  169. rucio/daemons/judge/injector.py +162 -0
  170. rucio/daemons/judge/repairer.py +154 -0
  171. rucio/daemons/oauthmanager/__init__.py +13 -0
  172. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  173. rucio/daemons/reaper/__init__.py +13 -0
  174. rucio/daemons/reaper/dark_reaper.py +278 -0
  175. rucio/daemons/reaper/reaper.py +743 -0
  176. rucio/daemons/replicarecoverer/__init__.py +13 -0
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  178. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +13 -0
  186. rucio/daemons/storage/consistency/__init__.py +13 -0
  187. rucio/daemons/storage/consistency/actions.py +846 -0
  188. rucio/daemons/tracer/__init__.py +13 -0
  189. rucio/daemons/tracer/kronos.py +536 -0
  190. rucio/daemons/transmogrifier/__init__.py +13 -0
  191. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  192. rucio/daemons/undertaker/__init__.py +13 -0
  193. rucio/daemons/undertaker/undertaker.py +137 -0
  194. rucio/db/__init__.py +13 -0
  195. rucio/db/sqla/__init__.py +52 -0
  196. rucio/db/sqla/constants.py +201 -0
  197. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  198. rucio/db/sqla/migrate_repo/env.py +110 -0
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  327. rucio/db/sqla/models.py +1740 -0
  328. rucio/db/sqla/sautils.py +55 -0
  329. rucio/db/sqla/session.py +498 -0
  330. rucio/db/sqla/types.py +206 -0
  331. rucio/db/sqla/util.py +543 -0
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/gateway/account.py +339 -0
  334. rucio/gateway/account_limit.py +286 -0
  335. rucio/gateway/authentication.py +375 -0
  336. rucio/gateway/config.py +217 -0
  337. rucio/gateway/credential.py +71 -0
  338. rucio/gateway/did.py +970 -0
  339. rucio/gateway/dirac.py +81 -0
  340. rucio/gateway/exporter.py +59 -0
  341. rucio/gateway/heartbeat.py +74 -0
  342. rucio/gateway/identity.py +204 -0
  343. rucio/gateway/importer.py +45 -0
  344. rucio/gateway/lifetime_exception.py +120 -0
  345. rucio/gateway/lock.py +153 -0
  346. rucio/gateway/meta_conventions.py +87 -0
  347. rucio/gateway/permission.py +71 -0
  348. rucio/gateway/quarantined_replica.py +78 -0
  349. rucio/gateway/replica.py +529 -0
  350. rucio/gateway/request.py +321 -0
  351. rucio/gateway/rse.py +600 -0
  352. rucio/gateway/rule.py +417 -0
  353. rucio/gateway/scope.py +99 -0
  354. rucio/gateway/subscription.py +277 -0
  355. rucio/gateway/vo.py +122 -0
  356. rucio/rse/__init__.py +96 -0
  357. rucio/rse/protocols/__init__.py +13 -0
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +122 -0
  360. rucio/rse/protocols/dummy.py +111 -0
  361. rucio/rse/protocols/gfal.py +703 -0
  362. rucio/rse/protocols/globus.py +243 -0
  363. rucio/rse/protocols/gsiftp.py +92 -0
  364. rucio/rse/protocols/http_cache.py +82 -0
  365. rucio/rse/protocols/mock.py +123 -0
  366. rucio/rse/protocols/ngarc.py +209 -0
  367. rucio/rse/protocols/posix.py +250 -0
  368. rucio/rse/protocols/protocol.py +594 -0
  369. rucio/rse/protocols/rclone.py +364 -0
  370. rucio/rse/protocols/rfio.py +136 -0
  371. rucio/rse/protocols/srm.py +338 -0
  372. rucio/rse/protocols/ssh.py +413 -0
  373. rucio/rse/protocols/storm.py +206 -0
  374. rucio/rse/protocols/webdav.py +550 -0
  375. rucio/rse/protocols/xrootd.py +301 -0
  376. rucio/rse/rsemanager.py +764 -0
  377. rucio/tests/__init__.py +13 -0
  378. rucio/tests/common.py +270 -0
  379. rucio/tests/common_server.py +132 -0
  380. rucio/transfertool/__init__.py +13 -0
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +1596 -0
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +201 -0
  387. rucio/transfertool/globus_library.py +181 -0
  388. rucio/transfertool/mock.py +90 -0
  389. rucio/transfertool/transfertool.py +221 -0
  390. rucio/vcsversion.py +11 -0
  391. rucio/version.py +38 -0
  392. rucio/web/__init__.py +13 -0
  393. rucio/web/rest/__init__.py +13 -0
  394. rucio/web/rest/flaskapi/__init__.py +13 -0
  395. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  396. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  398. rucio/web/rest/flaskapi/v1/accounts.py +1089 -0
  399. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  400. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  401. rucio/web/rest/flaskapi/v1/common.py +426 -0
  402. rucio/web/rest/flaskapi/v1/config.py +304 -0
  403. rucio/web/rest/flaskapi/v1/credentials.py +212 -0
  404. rucio/web/rest/flaskapi/v1/dids.py +2334 -0
  405. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  406. rucio/web/rest/flaskapi/v1/export.py +75 -0
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  408. rucio/web/rest/flaskapi/v1/identities.py +261 -0
  409. rucio/web/rest/flaskapi/v1/import.py +132 -0
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  411. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  412. rucio/web/rest/flaskapi/v1/main.py +91 -0
  413. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  414. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  416. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  417. rucio/web/rest/flaskapi/v1/redirect.py +365 -0
  418. rucio/web/rest/flaskapi/v1/replicas.py +1890 -0
  419. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  420. rucio/web/rest/flaskapi/v1/rses.py +2239 -0
  421. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  422. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  424. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  425. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  426. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  427. rucio/web/rest/flaskapi/v1/types.py +20 -0
  428. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  429. rucio/web/rest/main.py +18 -0
  430. rucio/web/rest/metrics.py +27 -0
  431. rucio/web/rest/ping.py +27 -0
  432. rucio-35.7.0.data/data/rucio/etc/alembic.ini.template +71 -0
  433. rucio-35.7.0.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  434. rucio-35.7.0.data/data/rucio/etc/globus-config.yml.template +5 -0
  435. rucio-35.7.0.data/data/rucio/etc/ldap.cfg.template +30 -0
  436. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  437. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  438. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  439. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  440. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  441. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  442. rucio-35.7.0.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  443. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  444. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.template +257 -0
  445. rucio-35.7.0.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  446. rucio-35.7.0.data/data/rucio/requirements.server.txt +268 -0
  447. rucio-35.7.0.data/data/rucio/tools/bootstrap.py +34 -0
  448. rucio-35.7.0.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  449. rucio-35.7.0.data/data/rucio/tools/reset_database.py +40 -0
  450. rucio-35.7.0.data/scripts/rucio +2542 -0
  451. rucio-35.7.0.data/scripts/rucio-abacus-account +74 -0
  452. rucio-35.7.0.data/scripts/rucio-abacus-collection-replica +46 -0
  453. rucio-35.7.0.data/scripts/rucio-abacus-rse +78 -0
  454. rucio-35.7.0.data/scripts/rucio-admin +2447 -0
  455. rucio-35.7.0.data/scripts/rucio-atropos +60 -0
  456. rucio-35.7.0.data/scripts/rucio-auditor +205 -0
  457. rucio-35.7.0.data/scripts/rucio-automatix +50 -0
  458. rucio-35.7.0.data/scripts/rucio-bb8 +57 -0
  459. rucio-35.7.0.data/scripts/rucio-c3po +85 -0
  460. rucio-35.7.0.data/scripts/rucio-cache-client +134 -0
  461. rucio-35.7.0.data/scripts/rucio-cache-consumer +42 -0
  462. rucio-35.7.0.data/scripts/rucio-conveyor-finisher +58 -0
  463. rucio-35.7.0.data/scripts/rucio-conveyor-poller +66 -0
  464. rucio-35.7.0.data/scripts/rucio-conveyor-preparer +37 -0
  465. rucio-35.7.0.data/scripts/rucio-conveyor-receiver +43 -0
  466. rucio-35.7.0.data/scripts/rucio-conveyor-stager +76 -0
  467. rucio-35.7.0.data/scripts/rucio-conveyor-submitter +139 -0
  468. rucio-35.7.0.data/scripts/rucio-conveyor-throttler +104 -0
  469. rucio-35.7.0.data/scripts/rucio-dark-reaper +53 -0
  470. rucio-35.7.0.data/scripts/rucio-dumper +160 -0
  471. rucio-35.7.0.data/scripts/rucio-follower +44 -0
  472. rucio-35.7.0.data/scripts/rucio-hermes +54 -0
  473. rucio-35.7.0.data/scripts/rucio-judge-cleaner +89 -0
  474. rucio-35.7.0.data/scripts/rucio-judge-evaluator +137 -0
  475. rucio-35.7.0.data/scripts/rucio-judge-injector +44 -0
  476. rucio-35.7.0.data/scripts/rucio-judge-repairer +44 -0
  477. rucio-35.7.0.data/scripts/rucio-kronos +43 -0
  478. rucio-35.7.0.data/scripts/rucio-minos +53 -0
  479. rucio-35.7.0.data/scripts/rucio-minos-temporary-expiration +50 -0
  480. rucio-35.7.0.data/scripts/rucio-necromancer +120 -0
  481. rucio-35.7.0.data/scripts/rucio-oauth-manager +63 -0
  482. rucio-35.7.0.data/scripts/rucio-reaper +83 -0
  483. rucio-35.7.0.data/scripts/rucio-replica-recoverer +248 -0
  484. rucio-35.7.0.data/scripts/rucio-rse-decommissioner +66 -0
  485. rucio-35.7.0.data/scripts/rucio-storage-consistency-actions +74 -0
  486. rucio-35.7.0.data/scripts/rucio-transmogrifier +77 -0
  487. rucio-35.7.0.data/scripts/rucio-undertaker +76 -0
  488. rucio-35.7.0.dist-info/METADATA +72 -0
  489. rucio-35.7.0.dist-info/RECORD +493 -0
  490. rucio-35.7.0.dist-info/WHEEL +5 -0
  491. rucio-35.7.0.dist-info/licenses/AUTHORS.rst +97 -0
  492. rucio-35.7.0.dist-info/licenses/LICENSE +201 -0
  493. rucio-35.7.0.dist-info/top_level.txt +1 -0
rucio/gateway/did.py ADDED
@@ -0,0 +1,970 @@
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
+ from collections.abc import Iterable, Iterator, Mapping, Sequence
16
+ from copy import deepcopy
17
+ from typing import TYPE_CHECKING, Any, Optional
18
+
19
+ import rucio.gateway.permission
20
+ from rucio.common.constants import RESERVED_KEYS
21
+ from rucio.common.exception import AccessDenied, InvalidObject, RucioException
22
+ from rucio.common.schema import validate_schema
23
+ from rucio.common.types import InternalAccount, InternalScope
24
+ from rucio.common.utils import gateway_update_return_dict
25
+ from rucio.core import did, naming_convention
26
+ from rucio.core import meta_conventions as meta_convention_core
27
+ from rucio.core.rse import get_rse_id
28
+ from rucio.db.sqla.constants import DIDType
29
+ from rucio.db.sqla.session import read_session, stream_session, transactional_session
30
+
31
+ if TYPE_CHECKING:
32
+ from sqlalchemy.orm import Session
33
+
34
+
35
+ @stream_session
36
+ def list_dids(
37
+ scope: str,
38
+ filters: Mapping[Any, Any],
39
+ did_type: str = 'collection',
40
+ ignore_case: bool = False,
41
+ limit: Optional[int] = None,
42
+ offset: Optional[int] = None,
43
+ long: bool = False,
44
+ recursive: bool = False,
45
+ vo: str = 'def',
46
+ *,
47
+ session: "Session"
48
+ ) -> Iterator[dict[str, Any]]:
49
+ """
50
+ List dids in a scope.
51
+
52
+ :param scope: The scope name.
53
+ :param filters: Filter arguments in form supported by the filter engine.
54
+ :param did_type: The type of the did: all(container, dataset, file), collection(dataset or container), dataset, container
55
+ :param ignore_case: Ignore case distinctions.
56
+ :param limit: The maximum number of DIDs returned.
57
+ :param offset: Offset number.
58
+ :param long: Long format option to display more information for each DID.
59
+ :param recursive: Recursively list DIDs content.
60
+ :param vo: The VO to act on.
61
+ :param session: The database session in use.
62
+ """
63
+ internal_scope = InternalScope(scope, vo=vo)
64
+
65
+ # replace account and scope in filters with internal representation
66
+ for or_group in filters:
67
+ if 'account' in or_group:
68
+ or_group['account'] = InternalAccount(or_group['account'], vo=vo)
69
+ if 'scope' in or_group:
70
+ or_group['account'] = InternalScope(or_group['scope'], vo=vo)
71
+
72
+ result = did.list_dids(scope=internal_scope, filters=filters, did_type=did_type, ignore_case=ignore_case,
73
+ limit=limit, offset=offset, long=long, recursive=recursive, session=session)
74
+
75
+ for d in result:
76
+ yield gateway_update_return_dict(d, session=session)
77
+
78
+
79
+ @transactional_session
80
+ def add_did(
81
+ scope: str,
82
+ name: str,
83
+ did_type: str,
84
+ issuer: str,
85
+ account: Optional[str] = None,
86
+ statuses: Optional[dict[str, str]] = None,
87
+ meta: Optional[dict[str, str]] = None,
88
+ rules: Optional[Sequence[dict[str, Any]]] = None,
89
+ lifetime: Optional[str] = None,
90
+ dids: Optional[Sequence[dict[str, Any]]] = None,
91
+ rse: Optional[str] = None,
92
+ vo: str = 'def',
93
+ *,
94
+ session: "Session"
95
+ ) -> None:
96
+ """
97
+ Add data did.
98
+
99
+ :param scope: The scope name.
100
+ :param name: The data identifier name.
101
+ :param did_type: The data identifier type.
102
+ :param issuer: The issuer account.
103
+ :param account: The account owner. If None, then issuer is selected as owner.
104
+ :param statuses: Dictionary with statuses, e.g.g {'monotonic':True}.
105
+ :meta: Meta-data associated with the data identifier is represented using key/value pairs in a dictionary.
106
+ :rules: Replication rules associated with the data did. A list of dictionaries, e.g., [{'copies': 2, 'rse_expression': 'TIERS1'}, ].
107
+ :param lifetime: DID's lifetime (in seconds).
108
+ :param dids: The content.
109
+ :param rse: The RSE name when registering replicas.
110
+ :param vo: The VO to act on.
111
+ :param session: The database session in use.
112
+ """
113
+ statuses = statuses or {}
114
+ meta = meta or {}
115
+ rules = rules or []
116
+ dids = dids or []
117
+ v_did = {'name': name, 'type': did_type.upper(), 'scope': scope}
118
+ validate_schema(name='did', obj=v_did, vo=vo)
119
+ validate_schema(name='dids', obj=dids, vo=vo)
120
+ validate_schema(name='rse', obj=rse, vo=vo)
121
+ kwargs = {'scope': scope, 'name': name, 'type': did_type, 'issuer': issuer, 'account': account, 'statuses': statuses, 'meta': meta, 'rules': rules, 'lifetime': lifetime}
122
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='add_did', kwargs=kwargs, session=session):
123
+ raise AccessDenied('Account %s can not add data identifier to scope %s' % (issuer, scope))
124
+
125
+ owner_account = None if account is None else InternalAccount(account, vo=vo)
126
+ issuer_account = InternalAccount(issuer, vo=vo)
127
+ internal_scope = InternalScope(scope, vo=vo)
128
+ for d in dids:
129
+ d['scope'] = InternalScope(d['scope'], vo=vo)
130
+ for r in rules:
131
+ r['account'] = InternalAccount(r['account'], vo=vo)
132
+
133
+ rse_id = None
134
+ if rse is not None:
135
+ rse_id = get_rse_id(rse=rse, vo=vo, session=session)
136
+
137
+ if did_type == 'DATASET':
138
+ # naming_convention validation
139
+ extra_meta = naming_convention.validate_name(scope=internal_scope, name=name, did_type='D', session=session)
140
+
141
+ # merge extra_meta with meta
142
+ for k in extra_meta or {}:
143
+ if k not in meta:
144
+ meta[k] = extra_meta[k]
145
+ elif meta[k] != extra_meta[k]:
146
+ print("Provided metadata %s doesn't match the naming convention: %s != %s" % (k, meta[k], extra_meta[k]))
147
+ raise InvalidObject("Provided metadata %s doesn't match the naming convention: %s != %s" % (k, meta[k], extra_meta[k]))
148
+
149
+ # Validate metadata
150
+ meta_convention_core.validate_meta(meta=meta, did_type=DIDType[did_type.upper()], session=session)
151
+
152
+ return did.add_did(scope=internal_scope, name=name, did_type=DIDType[did_type.upper()], account=owner_account or issuer_account,
153
+ statuses=statuses, meta=meta, rules=rules, lifetime=lifetime,
154
+ dids=dids, rse_id=rse_id, session=session)
155
+
156
+
157
+ @transactional_session
158
+ def add_dids(
159
+ dids: Sequence[dict[str, Any]],
160
+ issuer: str,
161
+ vo: str = 'def',
162
+ *,
163
+ session: "Session"
164
+ ) -> None:
165
+ """
166
+ Bulk Add did.
167
+
168
+ :param dids: A list of dids.
169
+ :param issuer: The issuer account.
170
+ :param vo: The VO to act on.
171
+ :param session: The database session in use.
172
+ """
173
+ for d in dids:
174
+ if 'rse' in d:
175
+ rse_id = None
176
+ if d['rse'] is not None:
177
+ rse_id = get_rse_id(rse=d['rse'], vo=vo, session=session)
178
+ d['rse_id'] = rse_id
179
+
180
+ kwargs = {'issuer': issuer, 'dids': dids}
181
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='add_dids', kwargs=kwargs, session=session):
182
+ raise AccessDenied('Account %s can not bulk add data identifier' % (issuer))
183
+
184
+ issuer_account = InternalAccount(issuer, vo=vo)
185
+ for d in dids:
186
+ d['scope'] = InternalScope(d['scope'], vo=vo)
187
+ if 'account' in d.keys():
188
+ d['account'] = InternalAccount(d['account'], vo=vo)
189
+ if 'dids' in d.keys():
190
+ for child in d['dids']:
191
+ child['scope'] = InternalScope(child['scope'], vo=vo)
192
+ return did.add_dids(dids, account=issuer_account, session=session)
193
+
194
+
195
+ @transactional_session
196
+ def attach_dids(
197
+ scope: str,
198
+ name: str,
199
+ attachment: dict[str, Any],
200
+ issuer: str,
201
+ vo='def',
202
+ *,
203
+ session: "Session"
204
+ ) -> None:
205
+ """
206
+ Append content to data did.
207
+
208
+ :param attachment: The attachment.
209
+ :param issuer: The issuer account.
210
+ :param vo: The VO to act on.
211
+ :param session: The database session in use.
212
+ """
213
+ validate_schema(name='attachment', obj=attachment, vo=vo)
214
+
215
+ rse_id = None
216
+ if 'rse' in attachment:
217
+ if attachment['rse'] is not None:
218
+ rse_id = get_rse_id(rse=attachment['rse'], vo=vo, session=session)
219
+ attachment['rse_id'] = rse_id
220
+
221
+ kwargs = {'scope': scope, 'name': name, 'attachment': attachment}
222
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='attach_dids', kwargs=kwargs, session=session):
223
+ raise AccessDenied('Account %s can not add data identifiers to %s:%s' % (issuer, scope, name))
224
+
225
+ internal_scope = InternalScope(scope, vo=vo)
226
+ issuer_account = InternalAccount(issuer, vo=vo)
227
+ if 'account' in attachment.keys():
228
+ attachment['account'] = InternalAccount(attachment['account'], vo=vo)
229
+ for d in attachment['dids']:
230
+ d['scope'] = InternalScope(d['scope'], vo=vo)
231
+ if 'account' in d.keys():
232
+ d['account'] = InternalAccount(d['account'], vo=vo)
233
+
234
+ if rse_id is not None:
235
+ dids = did.attach_dids(scope=internal_scope, name=name, dids=attachment['dids'],
236
+ account=attachment.get('account', issuer_account), rse_id=rse_id, session=session)
237
+ else:
238
+ dids = did.attach_dids(scope=internal_scope, name=name, dids=attachment['dids'],
239
+ account=attachment.get('account', issuer_account), session=session)
240
+
241
+ return dids
242
+
243
+
244
+ @transactional_session
245
+ def attach_dids_to_dids(
246
+ attachments: Sequence[dict[str, Any]],
247
+ issuer: str,
248
+ ignore_duplicate: bool = False,
249
+ vo: str = 'def',
250
+ *,
251
+ session: "Session"
252
+ ) -> None:
253
+ """
254
+ Append content to dids.
255
+
256
+ :param attachments: The contents.
257
+ :param issuer: The issuer account.
258
+ :param ignore_duplicate: If True, ignore duplicate entries.
259
+ :param vo: The VO to act on.
260
+ :param session: The database session in use.
261
+ """
262
+ validate_schema(name='attachments', obj=attachments, vo=vo)
263
+
264
+ for a in attachments:
265
+ if 'rse' in a:
266
+ rse_id = None
267
+ if a['rse'] is not None:
268
+ rse_id = get_rse_id(rse=a['rse'], vo=vo, session=session)
269
+ a['rse_id'] = rse_id
270
+
271
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='attach_dids_to_dids', kwargs={'attachments': attachments}, session=session):
272
+ raise AccessDenied('Account %s can not add data identifiers' % (issuer))
273
+
274
+ issuer_account = InternalAccount(issuer, vo=vo)
275
+ for attachment in attachments:
276
+ attachment['scope'] = InternalScope(attachment['scope'], vo=vo)
277
+ for d in attachment['dids']:
278
+ d['scope'] = InternalScope(d['scope'], vo=vo)
279
+ if 'account' in d.keys():
280
+ d['account'] = InternalAccount(d['account'], vo=vo)
281
+
282
+ return did.attach_dids_to_dids(attachments=attachments, account=issuer_account,
283
+ ignore_duplicate=ignore_duplicate, session=session)
284
+
285
+
286
+ @transactional_session
287
+ def detach_dids(
288
+ scope: str,
289
+ name: str,
290
+ dids: Sequence[dict[str, Any]],
291
+ issuer: str,
292
+ vo: str = 'def',
293
+ *,
294
+ session: "Session"
295
+ ) -> None:
296
+ """
297
+ Detach data identifier
298
+
299
+ :param scope: The scope name.
300
+ :param name: The data identifier name.
301
+ :param dids: The content.
302
+ :param issuer: The issuer account.
303
+ :param vo: The VO to act on.
304
+ :param session: The database session in use.
305
+ """
306
+ kwargs = {'scope': scope, 'name': name, 'dids': dids, 'issuer': issuer}
307
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='detach_dids', kwargs=kwargs, session=session):
308
+ raise AccessDenied('Account %s can not detach data identifiers from %s:%s' % (issuer, scope, name))
309
+
310
+ internal_scope = InternalScope(scope, vo=vo)
311
+ for d in dids:
312
+ d['scope'] = InternalScope(d['scope'], vo=vo)
313
+
314
+ return did.detach_dids(scope=internal_scope, name=name, dids=dids, session=session)
315
+
316
+
317
+ @stream_session
318
+ def list_new_dids(
319
+ did_type: Optional[str] = None,
320
+ thread: Optional[int] = None,
321
+ total_threads: Optional[int] = None,
322
+ chunk_size: int = 1000,
323
+ vo: str = 'def',
324
+ *,
325
+ session: "Session"
326
+ ) -> Iterator[dict[str, Any]]:
327
+ """
328
+ List recent identifiers.
329
+
330
+ :param did_type : The DID type.
331
+ :param thread: The assigned thread for this necromancer.
332
+ :param total_threads: The total number of threads of all necromancers.
333
+ :param chunk_size: Number of requests to return per yield.
334
+ :param vo: The VO to act on.
335
+ :param session: The database session in use.
336
+ """
337
+ dids = did.list_new_dids(did_type=did_type and DIDType[did_type.upper()], thread=thread, total_threads=total_threads, chunk_size=chunk_size, session=session)
338
+ for d in dids:
339
+ if d['scope'].vo == vo:
340
+ yield gateway_update_return_dict(d, session=session)
341
+
342
+
343
+ @transactional_session
344
+ def set_new_dids(
345
+ dids: Sequence[dict[str, Any]],
346
+ new_flag: bool = True,
347
+ vo: str = 'def',
348
+ *,
349
+ session: "Session"
350
+ ) -> bool:
351
+ """
352
+ Set/reset the flag new
353
+
354
+ :param scope: The scope name.
355
+ :param name: The data identifier name.
356
+ :param new_flag: A boolean to flag new DIDs.
357
+ :param vo: The VO to act on.
358
+ :param session: The database session in use.
359
+ """
360
+ for d in dids:
361
+ d['scope'] = InternalScope(d['scope'], vo=vo)
362
+
363
+ return did.set_new_dids(dids, new_flag, session=session)
364
+
365
+
366
+ @stream_session
367
+ def list_content(
368
+ scope: str,
369
+ name: str,
370
+ vo: str = 'def',
371
+ *,
372
+ session: "Session"
373
+ ) -> Iterator[dict[str, Any]]:
374
+ """
375
+ List data identifier contents.
376
+
377
+ :param scope: The scope name.
378
+ :param name: The data identifier name.
379
+ :param vo: The VO to act on.
380
+ :param session: The database session in use.
381
+ """
382
+
383
+ internal_scope = InternalScope(scope, vo=vo)
384
+
385
+ dids = did.list_content(scope=internal_scope, name=name, session=session)
386
+ for d in dids:
387
+ yield gateway_update_return_dict(d, session=session)
388
+
389
+
390
+ @stream_session
391
+ def list_content_history(
392
+ scope: str,
393
+ name: str,
394
+ vo='def',
395
+ *,
396
+ session: "Session"
397
+ ) -> Iterator[dict[str, Any]]:
398
+ """
399
+ List data identifier contents history.
400
+
401
+ :param scope: The scope name.
402
+ :param name: The data identifier name.
403
+ :param vo: The VO to act on.
404
+ :param session: The database session in use.
405
+ """
406
+
407
+ internal_scope = InternalScope(scope, vo=vo)
408
+
409
+ dids = did.list_content_history(scope=internal_scope, name=name, session=session)
410
+
411
+ for d in dids:
412
+ yield gateway_update_return_dict(d, session=session)
413
+
414
+
415
+ @stream_session
416
+ def bulk_list_files(
417
+ dids: Iterable[dict[str, Any]],
418
+ long: bool = False,
419
+ vo: str = 'def',
420
+ *,
421
+ session: "Session"
422
+ ) -> Iterator[dict[str, Any]]:
423
+ """
424
+ List file contents of a list of data identifiers.
425
+
426
+ :param dids: A list of DIDs.
427
+ :param long: A boolean to choose if more metadata are returned or not.
428
+ :param vo: The VO to act on.
429
+ :param session: The database session in use.
430
+ """
431
+
432
+ for did_ in dids:
433
+ did_['scope'] = InternalScope(did_['scope'], vo=vo)
434
+
435
+ for file_ in did.bulk_list_files(dids=dids, long=long, session=session):
436
+ yield gateway_update_return_dict(file_, session=session)
437
+
438
+
439
+ @stream_session
440
+ def list_files(
441
+ scope: str,
442
+ name: str,
443
+ long: bool,
444
+ vo: str = 'def',
445
+ *,
446
+ session: "Session"
447
+ ) -> Iterator[dict[str, Any]]:
448
+ """
449
+ List data identifier file contents.
450
+
451
+ :param scope: The scope name.
452
+ :param name: The data identifier name.
453
+ :param long: A boolean to choose if GUID is returned or not.
454
+ :param vo: The VO to act on.
455
+ :param session: The database session in use.
456
+ """
457
+
458
+ internal_scope = InternalScope(scope, vo=vo)
459
+
460
+ dids = did.list_files(scope=internal_scope, name=name, long=long, session=session)
461
+
462
+ for d in dids:
463
+ yield gateway_update_return_dict(d, session=session)
464
+
465
+
466
+ @stream_session
467
+ def scope_list(
468
+ scope: str,
469
+ name: Optional[str] = None,
470
+ recursive: bool = False,
471
+ vo: str = 'def',
472
+ *,
473
+ session: "Session"
474
+ ) -> Iterator[dict[str, Any]]:
475
+ """
476
+ List data identifiers in a scope.
477
+
478
+ :param scope: The scope name.
479
+ :param name: The data identifier name.
480
+ :param recursive: boolean, True or False.
481
+ :param vo: The VO to act on.
482
+ :param session: The database session in use.
483
+ """
484
+
485
+ internal_scope = InternalScope(scope, vo=vo)
486
+
487
+ dids = did.scope_list(internal_scope, name=name, recursive=recursive, session=session)
488
+
489
+ for d in dids:
490
+ ret_did = deepcopy(d)
491
+ ret_did['scope'] = ret_did['scope'].external
492
+ if ret_did['parent'] is not None:
493
+ ret_did['parent']['scope'] = ret_did['parent']['scope'].external
494
+ yield ret_did
495
+
496
+
497
+ @read_session
498
+ def get_did(scope: str, name: str, dynamic_depth: Optional[DIDType] = None, vo: str = 'def', *, session: "Session") -> "dict[str, Any]":
499
+ """
500
+ Retrieve a single data did.
501
+
502
+ :param scope: The scope name.
503
+ :param name: The data identifier name.
504
+ :param dynamic_depth: the DID type to use as source for estimation of this DIDs length/bytes.
505
+ If set to None, or to a value which doesn't make sense (ex: requesting depth = CONTAINER for a did of type DATASET)
506
+ will not compute the size dynamically.
507
+ :param vo: The VO to act on.
508
+ :return did: Dictionary containing {'name', 'scope', 'type'}, Exception otherwise
509
+ :param session: The database session in use.
510
+ """
511
+
512
+ internal_scope = InternalScope(scope, vo=vo)
513
+
514
+ d = did.get_did(scope=internal_scope, name=name, dynamic_depth=dynamic_depth, session=session)
515
+ return gateway_update_return_dict(d, session=session)
516
+
517
+
518
+ @transactional_session
519
+ def set_metadata(
520
+ scope: str,
521
+ name: str,
522
+ key: str,
523
+ value: Any,
524
+ issuer: str,
525
+ recursive: bool = False,
526
+ vo: str = 'def',
527
+ *,
528
+ session: "Session"
529
+ ) -> None:
530
+ """
531
+ Add metadata to data did.
532
+
533
+ :param scope: The scope name.
534
+ :param name: The data identifier name.
535
+ :param key: the key.
536
+ :param value: the value.
537
+ :param issuer: The issuer account.
538
+ :param recursive: Option to propagate the metadata update to content.
539
+ :param vo: The VO to act on.
540
+ :param session: The database session in use.
541
+ """
542
+ kwargs = {'scope': scope, 'name': name, 'key': key, 'value': value, 'issuer': issuer}
543
+
544
+ if key in RESERVED_KEYS:
545
+ raise AccessDenied('Account %s can not change this metadata value to data identifier %s:%s' % (issuer, scope, name))
546
+
547
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='set_metadata', kwargs=kwargs, session=session):
548
+ raise AccessDenied('Account %s can not add metadata to data identifier %s:%s' % (issuer, scope, name))
549
+
550
+ internal_scope = InternalScope(scope, vo=vo)
551
+ return did.set_metadata(scope=internal_scope, name=name, key=key, value=value, recursive=recursive, session=session)
552
+
553
+
554
+ @transactional_session
555
+ def set_metadata_bulk(
556
+ scope: str,
557
+ name: str,
558
+ meta: dict[str, Any],
559
+ issuer: str,
560
+ recursive: bool = False,
561
+ vo: str = 'def',
562
+ *,
563
+ session: "Session"
564
+ ) -> None:
565
+ """
566
+ Add metadata to data did.
567
+
568
+ :param scope: The scope name.
569
+ :param name: The data identifier name.
570
+ :param meta: the key-values.
571
+ :param issuer: The issuer account.
572
+ :param recursive: Option to propagate the metadata update to content.
573
+ :param vo: The VO to act on.
574
+ :param session: The database session in use.
575
+ """
576
+ kwargs = {'scope': scope, 'name': name, 'meta': meta, 'issuer': issuer}
577
+
578
+ for key in meta:
579
+ if key in RESERVED_KEYS:
580
+ raise AccessDenied('Account %s can not change the value of the metadata key %s to data identifier %s:%s' % (issuer, key, scope, name))
581
+
582
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='set_metadata_bulk', kwargs=kwargs, session=session):
583
+ raise AccessDenied('Account %s can not add metadata to data identifier %s:%s' % (issuer, scope, name))
584
+
585
+ internal_scope = InternalScope(scope, vo=vo)
586
+ return did.set_metadata_bulk(scope=internal_scope, name=name, meta=meta, recursive=recursive, session=session)
587
+
588
+
589
+ @transactional_session
590
+ def set_dids_metadata_bulk(
591
+ dids: Iterable[dict[str, Any]],
592
+ issuer: str,
593
+ recursive: bool = False,
594
+ vo: str = 'def',
595
+ *,
596
+ session: "Session"
597
+ ) -> None:
598
+ """
599
+ Add metadata to a list of data identifiers.
600
+
601
+ :param issuer: The issuer account.
602
+ :param dids: A list of dids including metadata.
603
+ :param recursive: Option to propagate the metadata update to content.
604
+ :param vo: The VO to act on.
605
+ :param session: The database session in use.
606
+ """
607
+
608
+ for entry in dids:
609
+ kwargs = {'scope': entry['scope'], 'name': entry['name'], 'meta': entry['meta'], 'issuer': issuer}
610
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='set_metadata_bulk', kwargs=kwargs, session=session):
611
+ raise AccessDenied('Account %s can not add metadata to data identifier %s:%s' % (issuer, entry['scope'], entry['name']))
612
+ entry['scope'] = InternalScope(entry['scope'], vo=vo)
613
+ meta = entry['meta']
614
+ for key in meta:
615
+ if key in RESERVED_KEYS:
616
+ raise AccessDenied('Account %s can not change the value of the metadata key %s to data identifier %s:%s' % (issuer, key, entry['scope'], entry['name']))
617
+
618
+ return did.set_dids_metadata_bulk(dids=dids, recursive=recursive, session=session)
619
+
620
+
621
+ @read_session
622
+ def get_metadata(
623
+ scope: str,
624
+ name: str,
625
+ plugin: str = 'DID_COLUMN',
626
+ vo: str = 'def',
627
+ *,
628
+ session: "Session"
629
+ ) -> dict[str, Any]:
630
+ """
631
+ Get data identifier metadata
632
+
633
+ :param scope: The scope name.
634
+ :param name: The data identifier name.
635
+ :param vo: The VO to act on.
636
+ :param session: The database session in use.
637
+ """
638
+
639
+ internal_scope = InternalScope(scope, vo=vo)
640
+
641
+ d = did.get_metadata(scope=internal_scope, name=name, plugin=plugin, session=session)
642
+ return gateway_update_return_dict(d, session=session)
643
+
644
+
645
+ @stream_session
646
+ def get_metadata_bulk(
647
+ dids: Iterable[dict[str, Any]],
648
+ inherit: bool = False,
649
+ vo: str = 'def',
650
+ *,
651
+ session: "Session"
652
+ ) -> Iterator[dict[str, Any]]:
653
+ """
654
+ Get metadata for a list of dids
655
+ :param dids: A list of dids.
656
+ :param inherit: A boolean. If set to true, the metadata of the parent are concatenated.
657
+ :param vo: The VO to act on.
658
+ :param session: The database session in use.
659
+ """
660
+
661
+ validate_schema(name='dids', obj=dids, vo=vo)
662
+ for entry in dids:
663
+ entry['scope'] = InternalScope(entry['scope'], vo=vo)
664
+ meta = did.get_metadata_bulk(dids, inherit=inherit, session=session)
665
+ for met in meta:
666
+ yield gateway_update_return_dict(met, session=session)
667
+
668
+
669
+ @transactional_session
670
+ def delete_metadata(
671
+ scope: str,
672
+ name: str,
673
+ key: str,
674
+ vo: str = 'def',
675
+ *,
676
+ session: "Session"
677
+ ) -> None:
678
+ """
679
+ Delete a key from the metadata column
680
+
681
+ :param scope: the scope of did
682
+ :param name: the name of the did
683
+ :param key: the key to be deleted
684
+ :param vo: The VO to act on.
685
+ :param session: The database session in use.
686
+ """
687
+
688
+ internal_scope = InternalScope(scope, vo=vo)
689
+ return did.delete_metadata(scope=internal_scope, name=name, key=key, session=session)
690
+
691
+
692
+ @transactional_session
693
+ def set_status(
694
+ scope: str,
695
+ name: str,
696
+ issuer: str,
697
+ vo: str = 'def',
698
+ *,
699
+ session: "Session",
700
+ **kwargs
701
+ ) -> None:
702
+ """
703
+ Set data identifier status
704
+
705
+ :param scope: The scope name.
706
+ :param name: The data identifier name.
707
+ :param issuer: The issuer account.
708
+ :param kwargs: Keyword arguments of the form status_name=value.
709
+ :param vo: The VO to act on.
710
+ :param session: The database session in use.
711
+ """
712
+
713
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='set_status', kwargs={'scope': scope, 'name': name, 'issuer': issuer}, session=session):
714
+ raise AccessDenied('Account %s can not set status on data identifier %s:%s' % (issuer, scope, name))
715
+
716
+ internal_scope = InternalScope(scope, vo=vo)
717
+
718
+ return did.set_status(scope=internal_scope, name=name, session=session, **kwargs)
719
+
720
+
721
+ @stream_session
722
+ def get_dataset_by_guid(
723
+ guid: str,
724
+ vo: str = 'def',
725
+ *,
726
+ session: "Session"
727
+ ) -> Iterator[dict[str, Any]]:
728
+ """
729
+ Get the parent datasets for a given GUID.
730
+ :param guid: The GUID.
731
+ :param vo: The VO to act on.
732
+ :param session: The database session in use.
733
+
734
+ :returns: A did
735
+ """
736
+ dids = did.get_dataset_by_guid(guid=guid, session=session)
737
+
738
+ for d in dids:
739
+ if d['scope'].vo != vo:
740
+ raise RucioException('GUID unavailable on VO {}'.format(vo))
741
+ yield gateway_update_return_dict(d, session=session)
742
+
743
+
744
+ @stream_session
745
+ def list_parent_dids(
746
+ scope: str,
747
+ name: str,
748
+ vo: str = 'def',
749
+ *,
750
+ session: "Session"
751
+ ) -> Iterator[dict[str, Any]]:
752
+ """
753
+ List parent datasets and containers of a did.
754
+
755
+ :param scope: The scope.
756
+ :param name: The name.
757
+ :param vo: The VO to act on.
758
+ :param session: The database session in use.
759
+ """
760
+
761
+ internal_scope = InternalScope(scope, vo=vo)
762
+
763
+ dids = did.list_parent_dids(scope=internal_scope, name=name, session=session)
764
+
765
+ for d in dids:
766
+ yield gateway_update_return_dict(d, session=session)
767
+
768
+
769
+ @transactional_session
770
+ def create_did_sample(
771
+ input_scope: str,
772
+ input_name: str,
773
+ output_scope: str,
774
+ output_name: str,
775
+ issuer: str,
776
+ nbfiles: str,
777
+ vo: str = 'def',
778
+ *,
779
+ session: "Session"
780
+ ):
781
+ """
782
+ Create a sample from an input collection.
783
+
784
+ :param input_scope: The scope of the input DID.
785
+ :param input_name: The name of the input DID.
786
+ :param output_scope: The scope of the output dataset.
787
+ :param output_name: The name of the output dataset.
788
+ :param account: The account.
789
+ :param nbfiles: The number of files to register in the output dataset.
790
+ :param issuer: The issuer account.
791
+ :param vo: The VO to act on.
792
+ :param session: The database session in use.
793
+ """
794
+ kwargs = {'issuer': issuer, 'scope': output_scope}
795
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='create_did_sample', kwargs=kwargs, session=session):
796
+ raise AccessDenied('Account %s can not bulk add data identifier' % (issuer))
797
+
798
+ input_internal_scope = InternalScope(input_scope, vo=vo)
799
+ output_internal_scope = InternalScope(output_scope, vo=vo)
800
+
801
+ issuer_account = InternalAccount(issuer, vo=vo)
802
+
803
+ return did.create_did_sample(input_scope=input_internal_scope, input_name=input_name, output_scope=output_internal_scope, output_name=output_name,
804
+ account=issuer_account, nbfiles=nbfiles, session=session)
805
+
806
+
807
+ @transactional_session
808
+ def resurrect(
809
+ dids: Iterable[dict[str, Any]],
810
+ issuer: str,
811
+ vo: str = 'def',
812
+ *,
813
+ session: "Session"
814
+ ) -> None:
815
+ """
816
+ Resurrect DIDs.
817
+
818
+ :param dids: A list of dids.
819
+ :param issuer: The issuer account.
820
+ :param vo: The VO to act on.
821
+ :param session: The database session in use.
822
+ """
823
+ kwargs = {'issuer': issuer}
824
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='resurrect', kwargs=kwargs, session=session):
825
+ raise AccessDenied('Account %s can not resurrect data identifiers' % (issuer))
826
+ validate_schema(name='dids', obj=dids, vo=vo)
827
+
828
+ for d in dids:
829
+ d['scope'] = InternalScope(d['scope'], vo=vo)
830
+
831
+ return did.resurrect(dids=dids, session=session)
832
+
833
+
834
+ @stream_session
835
+ def list_archive_content(
836
+ scope: str,
837
+ name: str,
838
+ vo: str = 'def',
839
+ *,
840
+ session: "Session"
841
+ ) -> Iterator[dict[str, Any]]:
842
+ """
843
+ List archive contents.
844
+
845
+ :param scope: The archive scope name.
846
+ :param name: The archive data identifier name.
847
+ :param vo: The VO to act on.
848
+ :param session: The database session in use.
849
+ """
850
+
851
+ internal_scope = InternalScope(scope, vo=vo)
852
+
853
+ dids = did.list_archive_content(scope=internal_scope, name=name, session=session)
854
+ for d in dids:
855
+ yield gateway_update_return_dict(d, session=session)
856
+
857
+
858
+ @transactional_session
859
+ def add_did_to_followed(
860
+ scope: str,
861
+ name: str,
862
+ account: str,
863
+ *,
864
+ session: "Session",
865
+ vo: str = 'def'
866
+ ) -> None:
867
+ """
868
+ Mark a did as followed by the given account
869
+
870
+ :param scope: The scope name.
871
+ :param name: The data identifier name.
872
+ :param account: The account owner.
873
+ :param session: The database session in use.
874
+ """
875
+ internal_scope = InternalScope(scope, vo=vo)
876
+ internal_account = InternalAccount(account, vo=vo)
877
+ return did.add_did_to_followed(scope=internal_scope, name=name, account=internal_account, session=session)
878
+
879
+
880
+ @transactional_session
881
+ def add_dids_to_followed(
882
+ dids: Iterable[Mapping[str, Any]],
883
+ account: str,
884
+ *,
885
+ session: "Session",
886
+ vo: str = 'def'
887
+ ) -> None:
888
+ """
889
+ Bulk mark datasets as followed
890
+
891
+ :param dids: A list of dids.
892
+ :param account: The account owner.
893
+ :param session: The database session in use.
894
+ """
895
+ internal_account = InternalAccount(account, vo=vo)
896
+ return did.add_dids_to_followed(dids=dids, account=internal_account, session=session)
897
+
898
+
899
+ @stream_session
900
+ def get_users_following_did(
901
+ name: str,
902
+ scope: str,
903
+ *,
904
+ session: "Session",
905
+ vo: str = 'def'
906
+ ) -> Iterator[dict[str, str]]:
907
+ """
908
+ Return list of users following a did
909
+
910
+ :param scope: The scope name.
911
+ :param name: The data identifier name.
912
+ :param session: The database session in use.
913
+ """
914
+ internal_scope = InternalScope(scope, vo=vo)
915
+ users = did.get_users_following_did(name=name, scope=internal_scope, session=session)
916
+ for user in users:
917
+ user['user'] = user['user'].external
918
+ yield user
919
+
920
+
921
+ @transactional_session
922
+ def remove_did_from_followed(
923
+ scope: str,
924
+ name: str,
925
+ account: str,
926
+ issuer: str,
927
+ *,
928
+ session: "Session",
929
+ vo: str = 'def'
930
+ ) -> None:
931
+ """
932
+ Mark a did as not followed
933
+
934
+ :param scope: The scope name.
935
+ :param name: The data identifier name.
936
+ :param account: The account owner.
937
+ :param session: The database session in use.
938
+ :param issuer: The issuer account
939
+ """
940
+ kwargs = {'scope': scope, 'issuer': issuer}
941
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='remove_did_from_followed', kwargs=kwargs, session=session):
942
+ raise AccessDenied('Account %s can not remove data identifiers from followed table' % (issuer))
943
+
944
+ internal_scope = InternalScope(scope, vo=vo)
945
+ internal_account = InternalAccount(account, vo=vo)
946
+ return did.remove_did_from_followed(scope=internal_scope, name=name, account=internal_account, session=session)
947
+
948
+
949
+ @transactional_session
950
+ def remove_dids_from_followed(
951
+ dids: Iterable[Mapping[str, Any]],
952
+ account: str,
953
+ issuer: str,
954
+ *,
955
+ session: "Session",
956
+ vo: str = 'def'
957
+ ) -> None:
958
+ """
959
+ Bulk mark datasets as not followed
960
+
961
+ :param dids: A list of dids.
962
+ :param account: The account owner.
963
+ :param session: The database session in use.
964
+ """
965
+ kwargs = {'dids': dids, 'issuer': issuer}
966
+ if not rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='remove_dids_from_followed', kwargs=kwargs, session=session):
967
+ raise AccessDenied('Account %s can not bulk remove data identifiers from followed table' % (issuer))
968
+
969
+ internal_account = InternalAccount(account, vo=vo)
970
+ return did.remove_dids_from_followed(dids=dids, account=internal_account, session=session)