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,352 @@
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 json
16
+ import operator
17
+ from typing import TYPE_CHECKING
18
+
19
+ import psycopg
20
+ from psycopg import sql
21
+ from psycopg.rows import dict_row
22
+
23
+ from rucio.common import config, exception
24
+ from rucio.common.types import InternalScope
25
+ from rucio.core.did_meta_plugins.did_meta_plugin_interface import DidMetaPlugin
26
+ from rucio.core.did_meta_plugins.filter_engine import FilterEngine
27
+
28
+ if TYPE_CHECKING:
29
+ from typing import Optional
30
+
31
+ from sqlalchemy.orm import Session
32
+
33
+
34
+ class ExternalPostgresDidMeta(DidMetaPlugin):
35
+ # TODO: column-based plugin? mixed-mode (json & columns)?
36
+ pass
37
+
38
+
39
+ class ExternalPostgresJSONDidMeta(DidMetaPlugin):
40
+ def __init__(self, host=None, port=None, db=None, user=None, password=None, db_schema=None, table=None,
41
+ table_is_managed=None, table_column_vo=None, table_column_scope=None, table_column_name=None,
42
+ table_column_data=None):
43
+ super(ExternalPostgresJSONDidMeta, self).__init__()
44
+ if host is None:
45
+ host = config.config_get('metadata', 'postgres_service_host')
46
+ if port is None:
47
+ port = config.config_get_int('metadata', 'postgres_service_port')
48
+ if db is None:
49
+ db = config.config_get('metadata', 'postgres_db')
50
+ if user is None:
51
+ user = config.config_get('metadata', 'postgres_user')
52
+ if password is None:
53
+ password = config.config_get('metadata', 'postgres_password')
54
+ if db_schema is None:
55
+ db_schema = config.config_get('metadata', 'postgres_db_schema', default='public')
56
+ if table is None:
57
+ table = config.config_get('metadata', 'postgres_table', default='dids')
58
+ if table_is_managed is None:
59
+ table_is_managed = config.config_get_bool('metadata', 'postgres_table_is_managed', default=False)
60
+ if table_column_vo is None:
61
+ table_column_vo = config.config_get('metadata', 'postgres_table_column_vo', default='vo')
62
+ if table_column_scope is None:
63
+ table_column_scope = config.config_get('metadata', 'postgres_table_column_scope', default='scope')
64
+ if table_column_name is None:
65
+ table_column_name = config.config_get('metadata', 'postgres_table_column_name', default='name')
66
+ if table_column_data is None:
67
+ table_column_data = config.config_get('metadata', 'postgres_table_column_data', default='data')
68
+
69
+ self.fixed_table_columns = {
70
+ 'vo': table_column_vo,
71
+ 'scope': table_column_scope,
72
+ 'name': table_column_name
73
+ }
74
+ self.jsonb_column = table_column_data
75
+
76
+ self.table = table
77
+ self.client = psycopg.connect(
78
+ host=host,
79
+ port=port,
80
+ dbname=db,
81
+ user=user,
82
+ password=password)
83
+
84
+ # set search_path to include database schema by default
85
+ cur = self.client.cursor()
86
+ cur.execute(
87
+ sql.SQL("SET search_path TO {}").format(
88
+ sql.Identifier(db_schema)
89
+ )
90
+ )
91
+ cur.close()
92
+
93
+ if not table_is_managed: # not managed by Rucio, so just verify table schema
94
+ self._verify_table_schema(table_column_vo, table_column_scope, table_column_name, table_column_data)
95
+ else: # managed by Rucio, create a metadata table if it doesn't exist
96
+ self._try_create_metadata_table()
97
+
98
+ self.plugin_name = "POSTGRES_JSON"
99
+
100
+ def _try_create_metadata_table(self):
101
+ """
102
+ Try to create a metadata table.
103
+ """
104
+ table_clauses = [
105
+ sql.SQL("id bigint NOT NULL GENERATED ALWAYS AS IDENTITY"),
106
+ sql.SQL("vo varchar NOT NULL"),
107
+ sql.SQL("scope varchar NOT NULL"),
108
+ sql.SQL("name varchar NOT NULL"),
109
+ sql.SQL("data jsonb DEFAULT '{}'::jsonb"),
110
+ sql.SQL("UNIQUE (scope, name)") # unique scope+name table constraint, required for ON CONFLICT
111
+ ]
112
+ statement = sql.SQL("CREATE TABLE IF NOT EXISTS {} ({})").format(
113
+ sql.Identifier(self.table),
114
+ sql.SQL(', ').join(table_clauses)
115
+ )
116
+
117
+ cur = self.client.cursor()
118
+ cur.execute(statement)
119
+ cur.close()
120
+ self.client.commit()
121
+
122
+ def _verify_table_schema(self, table_column_vo, table_column_scope, table_column_name, table_column_data):
123
+ """
124
+ Rudimentary verification that the metadata table schema meets the requirements for the plugin.
125
+
126
+ Should be called when using externally managed database tables as a sanity check.
127
+
128
+ :param table_column_vo: The table column used for the vo
129
+ :param table_column_scope: The table column used for the scope
130
+ :param table_column_name: The table column used for the name
131
+ :param table_column_data: The table column used for the data
132
+ :raises: MetadataSchemaMismatchError
133
+ """
134
+ # Check mandatory columns are of right data type and have the right nullable qualifier.
135
+ statement = sql.SQL(
136
+ "SELECT column_name, data_type, is_nullable FROM INFORMATION_SCHEMA.COLUMNS where table_name = {}").format(
137
+ sql.Literal(self.table)
138
+ )
139
+
140
+ cur = self.client.cursor()
141
+ cur.execute(statement)
142
+ existing_table_columns = cur.fetchall()
143
+ cur.close()
144
+
145
+ mandatory_column_specifications = [
146
+ (table_column_vo, "character varying", "NO"),
147
+ (table_column_scope, "character varying", "NO"),
148
+ (table_column_name, "character varying", "NO"),
149
+ (table_column_data, "jsonb", "YES")
150
+ ]
151
+ for specification in mandatory_column_specifications:
152
+ if specification not in existing_table_columns:
153
+ raise exception.MetadataSchemaMismatchError(
154
+ "mandatory table column {} does not match that defined in the required table schema {}".format(
155
+ specification, existing_table_columns))
156
+
157
+ # Check required table constraints exist.
158
+ statement = sql.SQL(
159
+ "SELECT con.contype AS constraint_type, " # type: ignore
160
+ "(SELECT array_agg(att.attname) FROM pg_attribute att "
161
+ " INNER JOIN unnest(con.conkey) unnest(conkey) ON unnest.conkey = att.attnum "
162
+ " WHERE att.attrelid = con.conrelid) AS columns "
163
+ "FROM pg_constraint con "
164
+ "INNER JOIN pg_class rel ON rel.oid = con.conrelid "
165
+ "INNER JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace "
166
+ "WHERE rel.relname = {}"
167
+ ).format(
168
+ sql.Literal(self.table)
169
+ )
170
+
171
+ cur = self.client.cursor()
172
+ cur.execute(statement)
173
+ existing_table_constraints = cur.fetchall() # list of (constraint_type, [columns])
174
+ cur.close()
175
+
176
+ mandatory_table_constraints = [
177
+ ("u", [table_column_scope, table_column_name]), # unique scope+name table constraint
178
+ ]
179
+ for constraint in mandatory_table_constraints:
180
+ if constraint not in existing_table_constraints:
181
+ raise exception.MetadataSchemaMismatchError(
182
+ "mandatory table constraint {} does not match that defined in the required table schema {}".format(
183
+ constraint, len(existing_table_constraints)))
184
+
185
+ def _drop_metadata_table(self):
186
+ cur = self.client.cursor()
187
+ cur.execute(
188
+ sql.SQL("DROP TABLE IF EXISTS {}").format(
189
+ sql.Identifier(self.table)
190
+ )
191
+ )
192
+ cur.close()
193
+ self.client.commit()
194
+
195
+ def get_metadata(self, scope, name, *, session: "Optional[Session]" = None):
196
+ """
197
+ Get data identifier metadata.
198
+
199
+ :param scope: The scope name
200
+ :param name: The data identifier name
201
+ :param session: The database session in use
202
+ :returns: the metadata for the did
203
+ """
204
+ statement = sql.SQL("SELECT data from {} WHERE scope = {} AND name = {}").format(
205
+ sql.Identifier(self.table),
206
+ sql.Literal(scope.internal),
207
+ sql.Literal(name)
208
+ )
209
+
210
+ cur = self.client.cursor()
211
+ cur.execute(statement)
212
+ metadata = cur.fetchone()
213
+ cur.close()
214
+
215
+ if not metadata:
216
+ raise exception.DataIdentifierNotFound("No metadata found for did '{}:{}".format(scope, name))
217
+
218
+ return metadata[0]
219
+
220
+ def set_metadata(self, scope, name, key, value, recursive=False, *, session: "Optional[Session]" = None):
221
+ """
222
+ Set single metadata key.
223
+
224
+ :param scope: the scope of did
225
+ :param name: the name of the did
226
+ :param key: the key to be added
227
+ :param value: the value of the key to be added
228
+ :param recursive: recurse into DIDs (not supported)
229
+ :param session: The database session in use
230
+ """
231
+ self.set_metadata_bulk(scope=scope, name=name, metadata={key: value}, recursive=recursive, session=session)
232
+
233
+ def set_metadata_bulk(self, scope, name, metadata, recursive=False, *, session: "Optional[Session]" = None):
234
+ """
235
+ Bulk set metadata keys.
236
+
237
+ :param scope: the scope of did
238
+ :param name: the name of the did
239
+ :param metadata: dictionary of metadata keypairs to be added
240
+ :param recursive: recurse into DIDs (not supported)
241
+ :param session: The database session in use
242
+ """
243
+ # upsert metadata
244
+ statement = sql.SQL(
245
+ "INSERT INTO {} (scope, name, vo, data) VALUES ({}, {}, {}, {}) " # type: ignore
246
+ "ON CONFLICT (scope, name) DO UPDATE set data = {}.data || EXCLUDED.data"
247
+ ).format(
248
+ sql.Identifier(self.table),
249
+ sql.Literal(scope.external),
250
+ sql.Literal(name),
251
+ sql.Literal(scope.vo),
252
+ sql.Literal(json.dumps(metadata)),
253
+ sql.Identifier(self.table)
254
+ )
255
+
256
+ cur = self.client.cursor()
257
+ cur.execute(statement)
258
+ cur.close()
259
+ self.client.commit()
260
+
261
+ def delete_metadata(self, scope, name, key, *, session: "Optional[Session]" = None):
262
+ """
263
+ Delete a key from metadata.
264
+
265
+ :param scope: the scope of did
266
+ :param name: the name of the did
267
+ :param key: the key to be deleted
268
+ :param session: the database session in use
269
+ """
270
+ statement = sql.SQL("UPDATE {} SET data = {}.data - {}").format(
271
+ sql.Identifier(self.table),
272
+ sql.Identifier(self.table),
273
+ sql.Literal(key)
274
+ )
275
+
276
+ cur = self.client.cursor()
277
+ cur.execute(statement)
278
+ cur.close()
279
+ self.client.commit()
280
+
281
+ def list_dids(self, scope, filters, did_type='collection', ignore_case=False, limit=None,
282
+ offset=None, long=False, recursive=False, ignore_dids=None, *, session: "Optional[Session]" = None):
283
+
284
+ if not ignore_dids:
285
+ ignore_dids = set()
286
+
287
+ # backwards compatibility for filters as single {}.
288
+ if isinstance(filters, dict):
289
+ filters = [filters]
290
+
291
+ try:
292
+ # instantiate fe and create postgres query
293
+ fe = FilterEngine(filters, model_class=None, strict_coerce=False)
294
+ postgres_query_str = fe.create_postgres_query(
295
+ additional_filters=[
296
+ ('scope', operator.eq, scope.internal),
297
+ ('vo', operator.eq, scope.vo)
298
+ ],
299
+ fixed_table_columns=self.fixed_table_columns,
300
+ jsonb_column=self.jsonb_column
301
+ )
302
+ except Exception as e:
303
+ raise exception.DataIdentifierNotFound(e)
304
+
305
+ if recursive:
306
+ # TODO: possible, but requires retrieving the results of a concurrent sqla query to call list_content
307
+ # on for datasets and containers
308
+ raise exception.UnsupportedOperation(
309
+ "'{}' metadata module does not currently support recursive searches".format(self.plugin_name.lower())
310
+ )
311
+
312
+ statement = sql.SQL("SELECT * FROM {} WHERE {} {}").format(
313
+ sql.Identifier(self.table),
314
+ sql.SQL(postgres_query_str), # type: ignore
315
+ sql.SQL("LIMIT {}").format(sql.Literal(limit)) if limit else sql.SQL("")
316
+ )
317
+
318
+ cur = self.client.cursor(row_factory=dict_row)
319
+ cur.execute(statement)
320
+ query_result = cur.fetchall()
321
+ cur.close()
322
+
323
+ if long:
324
+ for row in query_result:
325
+ did = "{}:{}".format(row['scope'], row['name'])
326
+ if did not in ignore_dids: # aggregating recursive queries may contain duplicate DIDs
327
+ ignore_dids.add(did)
328
+ yield {
329
+ 'scope': InternalScope(row['scope']),
330
+ 'name': row['name'],
331
+ 'did_type': "N/A",
332
+ 'bytes': "N/A",
333
+ 'length': "N/A"
334
+ }
335
+ else:
336
+ for row in query_result:
337
+ did = "{}:{}".format(row['scope'], row['name'])
338
+ if did not in ignore_dids: # aggregating recursive queries may contain duplicate DIDs
339
+ ignore_dids.add(did)
340
+ yield row['name']
341
+
342
+ def manages_key(self, key, *, session: "Optional[Session]" = None):
343
+ return True
344
+
345
+ def get_plugin_name(self):
346
+ """
347
+ Returns a unique identifier for this plugin. This can be later used for filtering down results to this
348
+ plugin only.
349
+
350
+ :returns: The name of the plugin
351
+ """
352
+ return self.plugin_name
rucio/core/dirac.py ADDED
@@ -0,0 +1,237 @@
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 re
16
+ from json import loads
17
+ from json.decoder import JSONDecodeError
18
+ from typing import TYPE_CHECKING, Any, Optional
19
+
20
+ from sqlalchemy import and_, select
21
+ from sqlalchemy.exc import NoResultFound
22
+
23
+ from rucio.common.config import config_get
24
+ from rucio.common.exception import ConfigNotFound, InvalidType, RucioException, UnsupportedOperation
25
+ from rucio.common.types import InternalAccount, InternalScope
26
+ from rucio.common.utils import extract_scope
27
+ from rucio.core.did import add_did, attach_dids_to_dids
28
+ from rucio.core.replica import add_replicas
29
+ from rucio.core.rule import add_rule, list_rules, update_rule
30
+ from rucio.core.scope import list_scopes
31
+ from rucio.db.sqla import models
32
+ from rucio.db.sqla.constants import DIDType
33
+ from rucio.db.sqla.session import read_session, transactional_session
34
+
35
+ if TYPE_CHECKING:
36
+ from collections.abc import Iterable
37
+
38
+ from sqlalchemy.orm import Session
39
+
40
+
41
+ @read_session
42
+ def _exists(
43
+ scope: str,
44
+ name: str,
45
+ *,
46
+ session: "Session"
47
+ ) -> tuple[bool, Optional[DIDType]]:
48
+ """
49
+ Check if the did exists
50
+
51
+ :scope: The scope
52
+ :name: The name
53
+ :session: The session used
54
+ """
55
+ try:
56
+ stmt = select(
57
+ models.DataIdentifier.did_type
58
+ ).with_hint(
59
+ models.DataIdentifier,
60
+ "INDEX(DIDS DIDS_PK)",
61
+ 'oracle'
62
+ ).where(
63
+ and_(models.DataIdentifier.scope == scope,
64
+ models.DataIdentifier.name == name)
65
+ )
66
+ return True, session.execute(stmt).scalar_one()
67
+ except NoResultFound:
68
+ return False, None
69
+
70
+
71
+ @transactional_session
72
+ def add_files(
73
+ lfns: "Iterable[dict[str, Any]]",
74
+ account: str,
75
+ ignore_availability: bool,
76
+ parents_metadata: Optional[dict[str, Any]] = None,
77
+ vo: str = 'def',
78
+ *,
79
+ session: "Session"
80
+ ) -> None:
81
+ """
82
+ Bulk add files :
83
+ - Create the file and replica.
84
+ - If doesn't exist create the dataset containing the file as well as a rule on the dataset on ANY sites.
85
+ - Create all the ascendants of the dataset if they do not exist
86
+
87
+ :param lfns: List of lfn (dictionary {'lfn': <lfn>, 'rse': <rse>, 'bytes': <bytes>, 'adler32': <adler32>, 'guid': <guid>, 'pfn': <pfn>}
88
+ :param issuer: The issuer account.
89
+ :param ignore_availability: A boolean to ignore blocklisted sites.
90
+ :param parents_metadata: Metadata for selected hierarchy DIDs. (dictionary {'lpn': {key : value}})
91
+ :param vo: The VO to act on
92
+ :param session: The session used
93
+ """
94
+ rule_extension_list = []
95
+ attachments = []
96
+ if not parents_metadata:
97
+ parents_metadata = {}
98
+ # The list of scopes is necessary for the extract_scope
99
+ filter_ = {'scope': InternalScope(scope='*', vo=vo)}
100
+ scopes = list_scopes(filter_=filter_, session=session)
101
+ scopes = [scope.external for scope in scopes]
102
+ exist_lfn = []
103
+ try:
104
+ config_lifetime: str = config_get(section='dirac', option='lifetime', default='{}', session=session)
105
+ lifetime_dict: dict = loads(config_lifetime)
106
+ except ConfigNotFound:
107
+ lifetime_dict = {}
108
+ except JSONDecodeError as err:
109
+ raise InvalidType('Problem parsing lifetime option in dirac section : %s' % str(err))
110
+ except Exception as err:
111
+ raise RucioException(str(err))
112
+
113
+ for lfn in lfns:
114
+ # First check if the file exists
115
+ filename = lfn['lfn']
116
+ lfn_scope, _ = extract_scope(filename, scopes)
117
+ lfn_scope = InternalScope(lfn_scope, vo=vo)
118
+
119
+ exists, did_type = _exists(lfn_scope, filename)
120
+ if exists:
121
+ continue
122
+
123
+ # Get all the ascendants of the file
124
+ lfn_split = filename.split('/')
125
+ lpns = ["/".join(lfn_split[:idx]) for idx in range(2, len(lfn_split))]
126
+ lpns.reverse()
127
+ print(lpns)
128
+
129
+ # The parent must be a dataset. Register it as well as the rule
130
+ dsn_name = lpns[0]
131
+ dsn_scope, _ = extract_scope(dsn_name, scopes)
132
+ dsn_scope = InternalScope(dsn_scope, vo=vo)
133
+ dsn_meta = parents_metadata.get(dsn_name, {})
134
+
135
+ # Compute lifetime
136
+ lifetime = None
137
+ if dsn_scope in lifetime_dict:
138
+ lifetime = lifetime_dict[dsn_scope.external]
139
+ else:
140
+ for pattern in lifetime_dict:
141
+ if dsn_scope.external and re.match(pattern, str(dsn_scope.external)):
142
+ lifetime = lifetime_dict[pattern]
143
+ break
144
+
145
+ exists, did_type = _exists(dsn_scope, dsn_name)
146
+ if exists and did_type == DIDType.CONTAINER:
147
+ raise UnsupportedOperation('Cannot create %s as dataset' % dsn_name)
148
+ if (dsn_name not in exist_lfn) and not exists:
149
+ print('Will create %s' % dsn_name)
150
+ # to maintain a compatibility between master and LTS-1.26 branches remove keywords for first 3 arguments
151
+ add_did(dsn_scope,
152
+ dsn_name,
153
+ DIDType.DATASET,
154
+ account=InternalAccount(account, vo=vo),
155
+ statuses=None,
156
+ meta=dsn_meta,
157
+ rules=[{'copies': 1, 'rse_expression': 'ANY=true', 'weight': None, 'account': InternalAccount(account, vo=vo), 'lifetime': lifetime, 'grouping': 'NONE'}],
158
+ lifetime=None,
159
+ dids=None,
160
+ rse_id=None,
161
+ session=session)
162
+ exist_lfn.append(dsn_name)
163
+ parent_name = lpns[1]
164
+ parent_scope, _ = extract_scope(parent_name, scopes)
165
+ parent_scope = InternalScope(parent_scope, vo=vo)
166
+ attachments.append({'scope': parent_scope, 'name': parent_name, 'dids': [{'scope': dsn_scope, 'name': dsn_name}]})
167
+ rule_extension_list.append((dsn_scope, dsn_name))
168
+ if lifetime and (dsn_scope, dsn_name) not in rule_extension_list:
169
+ # Reset the lifetime of the rule to the configured value
170
+ rule = [rul for rul in list_rules({'scope': dsn_scope, 'name': dsn_name, 'account': InternalAccount(account, vo=vo)}, session=session) if rul['rse_expression'] == 'ANY=true']
171
+ if rule:
172
+ update_rule(rule[0]['id'], options={'lifetime': lifetime}, session=session)
173
+ rule_extension_list.append((dsn_scope, dsn_name))
174
+
175
+ # Register the file
176
+ rse_id = lfn.get('rse_id', None)
177
+ if not rse_id:
178
+ raise InvalidType('Missing rse_id')
179
+ bytes_ = lfn.get('bytes', None)
180
+ guid = lfn.get('guid', None)
181
+ adler32 = lfn.get('adler32', None)
182
+ pfn = lfn.get('pfn', None)
183
+ meta = lfn.get('meta', {})
184
+ files = {'scope': lfn_scope, 'name': filename, 'bytes': bytes_, 'adler32': adler32, 'meta': meta}
185
+ if pfn:
186
+ files['pfn'] = str(pfn)
187
+ if guid:
188
+ files['meta']['guid'] = guid
189
+ add_replicas(rse_id=rse_id,
190
+ files=[files],
191
+ dataset_meta=None,
192
+ account=InternalAccount(account, vo=vo),
193
+ ignore_availability=ignore_availability,
194
+ session=session)
195
+ add_rule(dids=[{'scope': lfn_scope, 'name': filename}],
196
+ account=InternalAccount(account, vo=vo),
197
+ copies=1,
198
+ rse_expression=lfn['rse'],
199
+ grouping=None,
200
+ weight=None,
201
+ lifetime=86400,
202
+ locked=None,
203
+ subscription_id=None,
204
+ session=session)
205
+ attachments.append({'scope': dsn_scope, 'name': dsn_name, 'dids': [{'scope': lfn_scope, 'name': filename}]})
206
+
207
+ # Now loop over the ascendants of the dataset and created them
208
+ for lpn in lpns[1:]:
209
+ child_scope, _ = extract_scope(lpn, scopes)
210
+ child_scope = InternalScope(child_scope, vo=vo)
211
+ exists, did_type = _exists(child_scope, lpn)
212
+ child_meta = parents_metadata.get(lpn, {})
213
+ if exists and did_type == DIDType.DATASET:
214
+ raise UnsupportedOperation('Cannot create %s as container' % lpn)
215
+ if (lpn not in exist_lfn) and not exists:
216
+ print('Will create %s' % lpn)
217
+ add_did(child_scope,
218
+ lpn,
219
+ DIDType.CONTAINER,
220
+ account=InternalAccount(account, vo=vo),
221
+ statuses=None,
222
+ meta=child_meta,
223
+ rules=None,
224
+ lifetime=None,
225
+ dids=None,
226
+ rse_id=None,
227
+ session=session)
228
+ exist_lfn.append(lpn)
229
+ parent_name = lpns[lpns.index(lpn) + 1]
230
+ parent_scope, _ = extract_scope(parent_name, scopes)
231
+ parent_scope = InternalScope(parent_scope, vo=vo)
232
+ attachments.append({'scope': parent_scope, 'name': parent_name, 'dids': [{'scope': child_scope, 'name': lpn}]})
233
+ # Finally attach everything
234
+ attach_dids_to_dids(attachments,
235
+ account=InternalAccount(account, vo=vo),
236
+ ignore_duplicate=True,
237
+ session=session)