rucio 32.8.6__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 (481) hide show
  1. rucio/__init__.py +18 -0
  2. rucio/alembicrevision.py +16 -0
  3. rucio/api/__init__.py +14 -0
  4. rucio/api/account.py +266 -0
  5. rucio/api/account_limit.py +287 -0
  6. rucio/api/authentication.py +302 -0
  7. rucio/api/config.py +218 -0
  8. rucio/api/credential.py +60 -0
  9. rucio/api/did.py +726 -0
  10. rucio/api/dirac.py +71 -0
  11. rucio/api/exporter.py +60 -0
  12. rucio/api/heartbeat.py +62 -0
  13. rucio/api/identity.py +160 -0
  14. rucio/api/importer.py +46 -0
  15. rucio/api/lifetime_exception.py +95 -0
  16. rucio/api/lock.py +131 -0
  17. rucio/api/meta.py +85 -0
  18. rucio/api/permission.py +72 -0
  19. rucio/api/quarantined_replica.py +69 -0
  20. rucio/api/replica.py +528 -0
  21. rucio/api/request.py +220 -0
  22. rucio/api/rse.py +601 -0
  23. rucio/api/rule.py +335 -0
  24. rucio/api/scope.py +89 -0
  25. rucio/api/subscription.py +255 -0
  26. rucio/api/temporary_did.py +49 -0
  27. rucio/api/vo.py +112 -0
  28. rucio/client/__init__.py +16 -0
  29. rucio/client/accountclient.py +413 -0
  30. rucio/client/accountlimitclient.py +155 -0
  31. rucio/client/baseclient.py +929 -0
  32. rucio/client/client.py +77 -0
  33. rucio/client/configclient.py +113 -0
  34. rucio/client/credentialclient.py +54 -0
  35. rucio/client/didclient.py +691 -0
  36. rucio/client/diracclient.py +48 -0
  37. rucio/client/downloadclient.py +1674 -0
  38. rucio/client/exportclient.py +44 -0
  39. rucio/client/fileclient.py +51 -0
  40. rucio/client/importclient.py +42 -0
  41. rucio/client/lifetimeclient.py +74 -0
  42. rucio/client/lockclient.py +99 -0
  43. rucio/client/metaclient.py +137 -0
  44. rucio/client/pingclient.py +45 -0
  45. rucio/client/replicaclient.py +444 -0
  46. rucio/client/requestclient.py +109 -0
  47. rucio/client/rseclient.py +664 -0
  48. rucio/client/ruleclient.py +287 -0
  49. rucio/client/scopeclient.py +88 -0
  50. rucio/client/subscriptionclient.py +161 -0
  51. rucio/client/touchclient.py +78 -0
  52. rucio/client/uploadclient.py +871 -0
  53. rucio/common/__init__.py +14 -0
  54. rucio/common/cache.py +74 -0
  55. rucio/common/config.py +796 -0
  56. rucio/common/constants.py +92 -0
  57. rucio/common/constraints.py +18 -0
  58. rucio/common/didtype.py +187 -0
  59. rucio/common/dumper/__init__.py +306 -0
  60. rucio/common/dumper/consistency.py +449 -0
  61. rucio/common/dumper/data_models.py +325 -0
  62. rucio/common/dumper/path_parsing.py +65 -0
  63. rucio/common/exception.py +1092 -0
  64. rucio/common/extra.py +37 -0
  65. rucio/common/logging.py +404 -0
  66. rucio/common/pcache.py +1387 -0
  67. rucio/common/policy.py +84 -0
  68. rucio/common/schema/__init__.py +143 -0
  69. rucio/common/schema/atlas.py +411 -0
  70. rucio/common/schema/belleii.py +406 -0
  71. rucio/common/schema/cms.py +478 -0
  72. rucio/common/schema/domatpc.py +399 -0
  73. rucio/common/schema/escape.py +424 -0
  74. rucio/common/schema/generic.py +431 -0
  75. rucio/common/schema/generic_multi_vo.py +410 -0
  76. rucio/common/schema/icecube.py +404 -0
  77. rucio/common/schema/lsst.py +423 -0
  78. rucio/common/stomp_utils.py +160 -0
  79. rucio/common/stopwatch.py +56 -0
  80. rucio/common/test_rucio_server.py +148 -0
  81. rucio/common/types.py +158 -0
  82. rucio/common/utils.py +1946 -0
  83. rucio/core/__init__.py +14 -0
  84. rucio/core/account.py +426 -0
  85. rucio/core/account_counter.py +171 -0
  86. rucio/core/account_limit.py +357 -0
  87. rucio/core/authentication.py +563 -0
  88. rucio/core/config.py +386 -0
  89. rucio/core/credential.py +218 -0
  90. rucio/core/did.py +3102 -0
  91. rucio/core/did_meta_plugins/__init__.py +250 -0
  92. rucio/core/did_meta_plugins/did_column_meta.py +326 -0
  93. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +116 -0
  94. rucio/core/did_meta_plugins/filter_engine.py +573 -0
  95. rucio/core/did_meta_plugins/json_meta.py +215 -0
  96. rucio/core/did_meta_plugins/mongo_meta.py +199 -0
  97. rucio/core/did_meta_plugins/postgres_meta.py +317 -0
  98. rucio/core/dirac.py +208 -0
  99. rucio/core/distance.py +164 -0
  100. rucio/core/exporter.py +59 -0
  101. rucio/core/heartbeat.py +263 -0
  102. rucio/core/identity.py +290 -0
  103. rucio/core/importer.py +248 -0
  104. rucio/core/lifetime_exception.py +377 -0
  105. rucio/core/lock.py +474 -0
  106. rucio/core/message.py +241 -0
  107. rucio/core/meta.py +190 -0
  108. rucio/core/monitor.py +441 -0
  109. rucio/core/naming_convention.py +154 -0
  110. rucio/core/nongrid_trace.py +124 -0
  111. rucio/core/oidc.py +1339 -0
  112. rucio/core/permission/__init__.py +107 -0
  113. rucio/core/permission/atlas.py +1333 -0
  114. rucio/core/permission/belleii.py +1076 -0
  115. rucio/core/permission/cms.py +1166 -0
  116. rucio/core/permission/escape.py +1076 -0
  117. rucio/core/permission/generic.py +1128 -0
  118. rucio/core/permission/generic_multi_vo.py +1148 -0
  119. rucio/core/quarantined_replica.py +190 -0
  120. rucio/core/replica.py +3627 -0
  121. rucio/core/replica_sorter.py +368 -0
  122. rucio/core/request.py +2241 -0
  123. rucio/core/rse.py +1835 -0
  124. rucio/core/rse_counter.py +155 -0
  125. rucio/core/rse_expression_parser.py +460 -0
  126. rucio/core/rse_selector.py +277 -0
  127. rucio/core/rule.py +3419 -0
  128. rucio/core/rule_grouping.py +1473 -0
  129. rucio/core/scope.py +152 -0
  130. rucio/core/subscription.py +316 -0
  131. rucio/core/temporary_did.py +188 -0
  132. rucio/core/topology.py +448 -0
  133. rucio/core/trace.py +361 -0
  134. rucio/core/transfer.py +1233 -0
  135. rucio/core/vo.py +151 -0
  136. rucio/core/volatile_replica.py +123 -0
  137. rucio/daemons/__init__.py +14 -0
  138. rucio/daemons/abacus/__init__.py +14 -0
  139. rucio/daemons/abacus/account.py +106 -0
  140. rucio/daemons/abacus/collection_replica.py +113 -0
  141. rucio/daemons/abacus/rse.py +107 -0
  142. rucio/daemons/atropos/__init__.py +14 -0
  143. rucio/daemons/atropos/atropos.py +243 -0
  144. rucio/daemons/auditor/__init__.py +261 -0
  145. rucio/daemons/auditor/hdfs.py +86 -0
  146. rucio/daemons/auditor/srmdumps.py +284 -0
  147. rucio/daemons/automatix/__init__.py +14 -0
  148. rucio/daemons/automatix/automatix.py +281 -0
  149. rucio/daemons/badreplicas/__init__.py +14 -0
  150. rucio/daemons/badreplicas/minos.py +311 -0
  151. rucio/daemons/badreplicas/minos_temporary_expiration.py +173 -0
  152. rucio/daemons/badreplicas/necromancer.py +200 -0
  153. rucio/daemons/bb8/__init__.py +14 -0
  154. rucio/daemons/bb8/bb8.py +356 -0
  155. rucio/daemons/bb8/common.py +762 -0
  156. rucio/daemons/bb8/nuclei_background_rebalance.py +147 -0
  157. rucio/daemons/bb8/t2_background_rebalance.py +146 -0
  158. rucio/daemons/c3po/__init__.py +14 -0
  159. rucio/daemons/c3po/algorithms/__init__.py +14 -0
  160. rucio/daemons/c3po/algorithms/simple.py +131 -0
  161. rucio/daemons/c3po/algorithms/t2_free_space.py +125 -0
  162. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +127 -0
  163. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +279 -0
  164. rucio/daemons/c3po/c3po.py +342 -0
  165. rucio/daemons/c3po/collectors/__init__.py +14 -0
  166. rucio/daemons/c3po/collectors/agis.py +108 -0
  167. rucio/daemons/c3po/collectors/free_space.py +62 -0
  168. rucio/daemons/c3po/collectors/jedi_did.py +48 -0
  169. rucio/daemons/c3po/collectors/mock_did.py +46 -0
  170. rucio/daemons/c3po/collectors/network_metrics.py +63 -0
  171. rucio/daemons/c3po/collectors/workload.py +110 -0
  172. rucio/daemons/c3po/utils/__init__.py +14 -0
  173. rucio/daemons/c3po/utils/dataset_cache.py +40 -0
  174. rucio/daemons/c3po/utils/expiring_dataset_cache.py +45 -0
  175. rucio/daemons/c3po/utils/expiring_list.py +63 -0
  176. rucio/daemons/c3po/utils/popularity.py +82 -0
  177. rucio/daemons/c3po/utils/timeseries.py +76 -0
  178. rucio/daemons/cache/__init__.py +14 -0
  179. rucio/daemons/cache/consumer.py +191 -0
  180. rucio/daemons/common.py +391 -0
  181. rucio/daemons/conveyor/__init__.py +14 -0
  182. rucio/daemons/conveyor/common.py +530 -0
  183. rucio/daemons/conveyor/finisher.py +492 -0
  184. rucio/daemons/conveyor/poller.py +372 -0
  185. rucio/daemons/conveyor/preparer.py +198 -0
  186. rucio/daemons/conveyor/receiver.py +206 -0
  187. rucio/daemons/conveyor/stager.py +127 -0
  188. rucio/daemons/conveyor/submitter.py +379 -0
  189. rucio/daemons/conveyor/throttler.py +468 -0
  190. rucio/daemons/follower/__init__.py +14 -0
  191. rucio/daemons/follower/follower.py +97 -0
  192. rucio/daemons/hermes/__init__.py +14 -0
  193. rucio/daemons/hermes/hermes.py +738 -0
  194. rucio/daemons/judge/__init__.py +14 -0
  195. rucio/daemons/judge/cleaner.py +149 -0
  196. rucio/daemons/judge/evaluator.py +172 -0
  197. rucio/daemons/judge/injector.py +154 -0
  198. rucio/daemons/judge/repairer.py +144 -0
  199. rucio/daemons/oauthmanager/__init__.py +14 -0
  200. rucio/daemons/oauthmanager/oauthmanager.py +199 -0
  201. rucio/daemons/reaper/__init__.py +14 -0
  202. rucio/daemons/reaper/dark_reaper.py +272 -0
  203. rucio/daemons/reaper/light_reaper.py +255 -0
  204. rucio/daemons/reaper/reaper.py +701 -0
  205. rucio/daemons/replicarecoverer/__init__.py +14 -0
  206. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +487 -0
  207. rucio/daemons/storage/__init__.py +14 -0
  208. rucio/daemons/storage/consistency/__init__.py +14 -0
  209. rucio/daemons/storage/consistency/actions.py +753 -0
  210. rucio/daemons/tracer/__init__.py +14 -0
  211. rucio/daemons/tracer/kronos.py +513 -0
  212. rucio/daemons/transmogrifier/__init__.py +14 -0
  213. rucio/daemons/transmogrifier/transmogrifier.py +753 -0
  214. rucio/daemons/undertaker/__init__.py +14 -0
  215. rucio/daemons/undertaker/undertaker.py +137 -0
  216. rucio/db/__init__.py +14 -0
  217. rucio/db/sqla/__init__.py +38 -0
  218. rucio/db/sqla/constants.py +192 -0
  219. rucio/db/sqla/migrate_repo/__init__.py +14 -0
  220. rucio/db/sqla/migrate_repo/env.py +111 -0
  221. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +71 -0
  222. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +50 -0
  223. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +61 -0
  224. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +46 -0
  225. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +93 -0
  226. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +78 -0
  227. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +46 -0
  228. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +53 -0
  229. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +69 -0
  230. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +42 -0
  231. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +46 -0
  232. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +61 -0
  233. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +42 -0
  234. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +141 -0
  235. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +75 -0
  236. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +75 -0
  237. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +46 -0
  238. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +51 -0
  239. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +135 -0
  240. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +65 -0
  241. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +42 -0
  242. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +66 -0
  243. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +54 -0
  244. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +43 -0
  245. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +46 -0
  246. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +47 -0
  247. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +54 -0
  248. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +39 -0
  249. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +48 -0
  250. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +47 -0
  251. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +48 -0
  252. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +47 -0
  254. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +72 -0
  255. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +46 -0
  256. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +45 -0
  257. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +48 -0
  258. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +48 -0
  259. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +42 -0
  260. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +69 -0
  261. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +46 -0
  262. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +78 -0
  263. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +62 -0
  264. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +74 -0
  265. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +44 -0
  266. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +67 -0
  267. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +134 -0
  268. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +58 -0
  269. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +79 -0
  270. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +61 -0
  271. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +46 -0
  273. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +65 -0
  274. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +42 -0
  275. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +46 -0
  276. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +46 -0
  277. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +80 -0
  278. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +43 -0
  279. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +61 -0
  280. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +47 -0
  281. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +46 -0
  282. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +52 -0
  283. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +42 -0
  284. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +65 -0
  285. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +46 -0
  286. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +47 -0
  287. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +45 -0
  288. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +46 -0
  289. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +48 -0
  290. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +50 -0
  291. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +59 -0
  292. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +48 -0
  293. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +108 -0
  294. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +51 -0
  296. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +50 -0
  297. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +46 -0
  298. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +42 -0
  299. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +93 -0
  300. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +73 -0
  301. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +52 -0
  302. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +45 -0
  303. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +46 -0
  304. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +54 -0
  305. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +48 -0
  306. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +70 -0
  307. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +48 -0
  308. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +95 -0
  309. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +74 -0
  311. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +78 -0
  312. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +49 -0
  313. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +124 -0
  314. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +60 -0
  315. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +53 -0
  316. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +56 -0
  317. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +67 -0
  318. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +50 -0
  319. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +46 -0
  320. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +92 -0
  321. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +42 -0
  322. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +46 -0
  323. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +147 -0
  324. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +78 -0
  325. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +53 -0
  326. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +74 -0
  327. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +56 -0
  328. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +46 -0
  329. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +68 -0
  330. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +48 -0
  331. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +149 -0
  332. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +106 -0
  333. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +47 -0
  334. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +45 -0
  335. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +105 -0
  336. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +52 -0
  337. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +106 -0
  338. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +30 -0
  339. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +75 -0
  340. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +49 -0
  341. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +45 -0
  342. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +38 -0
  343. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +44 -0
  344. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +46 -0
  345. rucio/db/sqla/models.py +1834 -0
  346. rucio/db/sqla/sautils.py +48 -0
  347. rucio/db/sqla/session.py +470 -0
  348. rucio/db/sqla/types.py +207 -0
  349. rucio/db/sqla/util.py +521 -0
  350. rucio/rse/__init__.py +97 -0
  351. rucio/rse/protocols/__init__.py +14 -0
  352. rucio/rse/protocols/cache.py +123 -0
  353. rucio/rse/protocols/dummy.py +112 -0
  354. rucio/rse/protocols/gfal.py +701 -0
  355. rucio/rse/protocols/globus.py +243 -0
  356. rucio/rse/protocols/gsiftp.py +93 -0
  357. rucio/rse/protocols/http_cache.py +83 -0
  358. rucio/rse/protocols/mock.py +124 -0
  359. rucio/rse/protocols/ngarc.py +210 -0
  360. rucio/rse/protocols/posix.py +251 -0
  361. rucio/rse/protocols/protocol.py +530 -0
  362. rucio/rse/protocols/rclone.py +365 -0
  363. rucio/rse/protocols/rfio.py +137 -0
  364. rucio/rse/protocols/srm.py +339 -0
  365. rucio/rse/protocols/ssh.py +414 -0
  366. rucio/rse/protocols/storm.py +207 -0
  367. rucio/rse/protocols/webdav.py +547 -0
  368. rucio/rse/protocols/xrootd.py +295 -0
  369. rucio/rse/rsemanager.py +752 -0
  370. rucio/tests/__init__.py +14 -0
  371. rucio/tests/common.py +244 -0
  372. rucio/tests/common_server.py +132 -0
  373. rucio/transfertool/__init__.py +14 -0
  374. rucio/transfertool/fts3.py +1484 -0
  375. rucio/transfertool/globus.py +200 -0
  376. rucio/transfertool/globus_library.py +182 -0
  377. rucio/transfertool/mock.py +81 -0
  378. rucio/transfertool/transfertool.py +212 -0
  379. rucio/vcsversion.py +11 -0
  380. rucio/version.py +46 -0
  381. rucio/web/__init__.py +14 -0
  382. rucio/web/rest/__init__.py +14 -0
  383. rucio/web/rest/flaskapi/__init__.py +14 -0
  384. rucio/web/rest/flaskapi/authenticated_bp.py +28 -0
  385. rucio/web/rest/flaskapi/v1/__init__.py +14 -0
  386. rucio/web/rest/flaskapi/v1/accountlimits.py +234 -0
  387. rucio/web/rest/flaskapi/v1/accounts.py +1088 -0
  388. rucio/web/rest/flaskapi/v1/archives.py +100 -0
  389. rucio/web/rest/flaskapi/v1/auth.py +1642 -0
  390. rucio/web/rest/flaskapi/v1/common.py +385 -0
  391. rucio/web/rest/flaskapi/v1/config.py +305 -0
  392. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  393. rucio/web/rest/flaskapi/v1/dids.py +2204 -0
  394. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  395. rucio/web/rest/flaskapi/v1/export.py +77 -0
  396. rucio/web/rest/flaskapi/v1/heartbeats.py +129 -0
  397. rucio/web/rest/flaskapi/v1/identities.py +263 -0
  398. rucio/web/rest/flaskapi/v1/import.py +133 -0
  399. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +315 -0
  400. rucio/web/rest/flaskapi/v1/locks.py +360 -0
  401. rucio/web/rest/flaskapi/v1/main.py +83 -0
  402. rucio/web/rest/flaskapi/v1/meta.py +226 -0
  403. rucio/web/rest/flaskapi/v1/metrics.py +37 -0
  404. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  405. rucio/web/rest/flaskapi/v1/ping.py +89 -0
  406. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  407. rucio/web/rest/flaskapi/v1/replicas.py +1866 -0
  408. rucio/web/rest/flaskapi/v1/requests.py +841 -0
  409. rucio/web/rest/flaskapi/v1/rses.py +2204 -0
  410. rucio/web/rest/flaskapi/v1/rules.py +824 -0
  411. rucio/web/rest/flaskapi/v1/scopes.py +161 -0
  412. rucio/web/rest/flaskapi/v1/subscriptions.py +646 -0
  413. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  414. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  415. rucio/web/rest/flaskapi/v1/tmp_dids.py +115 -0
  416. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  417. rucio/web/rest/flaskapi/v1/vos.py +280 -0
  418. rucio/web/rest/main.py +19 -0
  419. rucio/web/rest/metrics.py +28 -0
  420. rucio-32.8.6.data/data/rucio/etc/alembic.ini.template +71 -0
  421. rucio-32.8.6.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  422. rucio-32.8.6.data/data/rucio/etc/globus-config.yml.template +5 -0
  423. rucio-32.8.6.data/data/rucio/etc/ldap.cfg.template +30 -0
  424. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  425. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  426. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  427. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  428. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  429. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  430. rucio-32.8.6.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  431. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  432. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.template +257 -0
  433. rucio-32.8.6.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  434. rucio-32.8.6.data/data/rucio/requirements.txt +55 -0
  435. rucio-32.8.6.data/data/rucio/tools/bootstrap.py +34 -0
  436. rucio-32.8.6.data/data/rucio/tools/merge_rucio_configs.py +147 -0
  437. rucio-32.8.6.data/data/rucio/tools/reset_database.py +40 -0
  438. rucio-32.8.6.data/scripts/rucio +2540 -0
  439. rucio-32.8.6.data/scripts/rucio-abacus-account +75 -0
  440. rucio-32.8.6.data/scripts/rucio-abacus-collection-replica +47 -0
  441. rucio-32.8.6.data/scripts/rucio-abacus-rse +79 -0
  442. rucio-32.8.6.data/scripts/rucio-admin +2434 -0
  443. rucio-32.8.6.data/scripts/rucio-atropos +61 -0
  444. rucio-32.8.6.data/scripts/rucio-auditor +199 -0
  445. rucio-32.8.6.data/scripts/rucio-automatix +51 -0
  446. rucio-32.8.6.data/scripts/rucio-bb8 +58 -0
  447. rucio-32.8.6.data/scripts/rucio-c3po +86 -0
  448. rucio-32.8.6.data/scripts/rucio-cache-client +135 -0
  449. rucio-32.8.6.data/scripts/rucio-cache-consumer +43 -0
  450. rucio-32.8.6.data/scripts/rucio-conveyor-finisher +59 -0
  451. rucio-32.8.6.data/scripts/rucio-conveyor-poller +67 -0
  452. rucio-32.8.6.data/scripts/rucio-conveyor-preparer +38 -0
  453. rucio-32.8.6.data/scripts/rucio-conveyor-receiver +44 -0
  454. rucio-32.8.6.data/scripts/rucio-conveyor-stager +77 -0
  455. rucio-32.8.6.data/scripts/rucio-conveyor-submitter +140 -0
  456. rucio-32.8.6.data/scripts/rucio-conveyor-throttler +105 -0
  457. rucio-32.8.6.data/scripts/rucio-dark-reaper +54 -0
  458. rucio-32.8.6.data/scripts/rucio-dumper +159 -0
  459. rucio-32.8.6.data/scripts/rucio-follower +45 -0
  460. rucio-32.8.6.data/scripts/rucio-hermes +55 -0
  461. rucio-32.8.6.data/scripts/rucio-judge-cleaner +90 -0
  462. rucio-32.8.6.data/scripts/rucio-judge-evaluator +138 -0
  463. rucio-32.8.6.data/scripts/rucio-judge-injector +45 -0
  464. rucio-32.8.6.data/scripts/rucio-judge-repairer +45 -0
  465. rucio-32.8.6.data/scripts/rucio-kronos +45 -0
  466. rucio-32.8.6.data/scripts/rucio-light-reaper +53 -0
  467. rucio-32.8.6.data/scripts/rucio-minos +54 -0
  468. rucio-32.8.6.data/scripts/rucio-minos-temporary-expiration +51 -0
  469. rucio-32.8.6.data/scripts/rucio-necromancer +121 -0
  470. rucio-32.8.6.data/scripts/rucio-oauth-manager +64 -0
  471. rucio-32.8.6.data/scripts/rucio-reaper +84 -0
  472. rucio-32.8.6.data/scripts/rucio-replica-recoverer +249 -0
  473. rucio-32.8.6.data/scripts/rucio-storage-consistency-actions +75 -0
  474. rucio-32.8.6.data/scripts/rucio-transmogrifier +78 -0
  475. rucio-32.8.6.data/scripts/rucio-undertaker +77 -0
  476. rucio-32.8.6.dist-info/METADATA +83 -0
  477. rucio-32.8.6.dist-info/RECORD +481 -0
  478. rucio-32.8.6.dist-info/WHEEL +5 -0
  479. rucio-32.8.6.dist-info/licenses/AUTHORS.rst +94 -0
  480. rucio-32.8.6.dist-info/licenses/LICENSE +201 -0
  481. rucio-32.8.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,250 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import importlib
17
+ from configparser import NoOptionError, NoSectionError
18
+ from typing import TYPE_CHECKING
19
+
20
+ from rucio.common import config, exception
21
+ from rucio.db.sqla.session import read_session, transactional_session
22
+
23
+ if TYPE_CHECKING:
24
+ from sqlalchemy.orm import Session
25
+
26
+ # Set default modules.
27
+ #
28
+ # - "base" metadata is tied to column fields in the "dids" table. As such, types are restricted to column types and
29
+ # additional fields cannot be created.
30
+ # - "custom" metadata refers to any plugin that has been written to interface with Rucio's metadata plugin system.
31
+ #
32
+ # By default, the default custom metadata plugin is set to the prepackaged json module. This utilises a separate table,
33
+ # "did_meta", allowing users to add custom metadata key/value pairs of any type.
34
+ #
35
+ DEFAULT_BASE_METADATA_PLUGIN_MODULE_PATH = "rucio.core.did_meta_plugins.did_column_meta.DidColumnMeta"
36
+ DEFAULT_CUSTOM_METADATA_PLUGIN_MODULE_PATH = "rucio.core.did_meta_plugins.json_meta.JSONDidMeta"
37
+
38
+ # Overwrite these defaults if plugins are set in the configuration file.
39
+ #
40
+ if config.config_has_section('metadata'):
41
+ try:
42
+ CUSTOM_METADATA_PLUGIN_MODULE_PATHS = config.config_get('metadata', 'plugins')
43
+ except (NoOptionError, NoSectionError):
44
+ CUSTOM_METADATA_PLUGIN_MODULE_PATHS = DEFAULT_CUSTOM_METADATA_PLUGIN_MODULE_PATH
45
+ else:
46
+ CUSTOM_METADATA_PLUGIN_MODULE_PATHS = DEFAULT_CUSTOM_METADATA_PLUGIN_MODULE_PATH
47
+ METADATA_PLUGIN_MODULE_PATHS = [DEFAULT_BASE_METADATA_PLUGIN_MODULE_PATH] + CUSTOM_METADATA_PLUGIN_MODULE_PATHS.split(",")
48
+
49
+ # Import plugin modules.
50
+ #
51
+ # Note that the order of this list is important. As the base metadata plugin module is always first, base key
52
+ # retrieval and setting will always take precedence over custom plugins, i.e. it is not possible to set a custom key with
53
+ # the same name as those in the base list.
54
+ #
55
+ # Another consequence of this is that if set_metadata() is called with multiple plugins specified, the first to return
56
+ # True to manages_key() will be used.
57
+ #
58
+ METADATA_PLUGIN_MODULES = []
59
+ for meta_module_path in METADATA_PLUGIN_MODULE_PATHS:
60
+ try:
61
+ base_module = ".".join(meta_module_path.split(".")[:-1])
62
+ base_class = meta_module_path.split(".")[-1]
63
+ metadata_plugin_module = getattr(importlib.import_module(base_module), base_class)()
64
+ METADATA_PLUGIN_MODULES.append(metadata_plugin_module)
65
+ except ImportError:
66
+ raise exception.PolicyPackageNotFound('Module ' + meta_module_path + ' not found')
67
+
68
+ # Set restricted character set for metadata in form character: reason
69
+ #
70
+ RESTRICTED_CHARACTERS = {
71
+ '.': "Used as a delimiter for key and operator (<key>.<operator>) in filtering engine."
72
+ }
73
+
74
+
75
+ @read_session
76
+ def get_metadata(scope, name, plugin="DID_COLUMN", *, session: "Session"):
77
+ """
78
+ Gets the metadata for a given did from a specified plugin.
79
+
80
+ If [plugin] is set to "all", metadata from all available metadata plugins will be returned,
81
+ else [plugin] can be used to only return the metadata using a specific plugin.
82
+
83
+ :param scope: The scope of the did.
84
+ :param name: The data identifier name.
85
+ :param plugin: (optional) Filter specific metadata plugins.
86
+ :returns: List of metadata for did.
87
+ :raises: NotImplementedError
88
+ """
89
+ if plugin.lower() == "all":
90
+ all_metadata = {}
91
+ for metadata_plugin in METADATA_PLUGIN_MODULES:
92
+ metadata = metadata_plugin.get_metadata(scope, name, session=session)
93
+ all_metadata.update(metadata)
94
+ return all_metadata
95
+ else:
96
+ for metadata_plugin in METADATA_PLUGIN_MODULES:
97
+ if metadata_plugin.get_plugin_name().lower() == plugin.lower():
98
+ return metadata_plugin.get_metadata(scope, name, session=session)
99
+ raise NotImplementedError('Metadata plugin "%s" is not enabled on the server.' % plugin)
100
+
101
+
102
+ @transactional_session
103
+ def set_metadata(scope, name, key, value, recursive=False, *, session: "Session"):
104
+ """
105
+ Sets metadata for a given did.
106
+
107
+ :param scope: The scope of the did.
108
+ :param name: The data identifier name.
109
+ :param key: Metadata key.
110
+ :param value: Metadata value.
111
+ :param recursive: (optional) Propagate the metadata change recursively to content.
112
+ :param session: (optional) The database session in use.
113
+ :raises: InvalidMetadata
114
+ """
115
+ # Check for forbidden characters in key.
116
+ for char in RESTRICTED_CHARACTERS:
117
+ if char in key:
118
+ raise exception.InvalidMetadata('Restricted character "{}" found in metadata key. Reason: {}'.format(
119
+ char,
120
+ RESTRICTED_CHARACTERS[char]
121
+ ))
122
+
123
+ # Sequentially check if each metadata plugin manages this key. Note that the order of [METADATA_PLUGIN_MODULES]
124
+ # means that the key is always checked for existence in the base list first.
125
+ metadata_was_set = False
126
+ for metadata_plugin in METADATA_PLUGIN_MODULES:
127
+ if metadata_plugin.manages_key(key, session=session):
128
+ metadata_plugin.set_metadata(scope, name, key, value, recursive, session=session)
129
+ metadata_was_set = True
130
+ break
131
+
132
+ if not metadata_was_set:
133
+ raise exception.InvalidMetadata('No plugin manages metadata key %s for DID %s:%s' % (key, scope, name))
134
+
135
+
136
+ @transactional_session
137
+ def set_metadata_bulk(scope, name, meta, recursive=False, *, session: "Session"):
138
+ """
139
+ Bulk sets metadata for a given did.
140
+
141
+ :param scope: The scope name.
142
+ :param name: The data identifier name.
143
+ :param meta: The key-value mapping of metadata to set.
144
+ :param recursive: (optional) Propagate the metadata change recursively to content.
145
+ :param session: (optional) The database session in use.
146
+ :raises: InvalidMetadata
147
+ """
148
+ metadata = meta
149
+
150
+ unmanaged_keys = list()
151
+ if not isinstance(metadata, dict):
152
+ metadata = dict(metadata)
153
+ metadata_plugin_keys = {metadata_plugin: [] for metadata_plugin in METADATA_PLUGIN_MODULES}
154
+
155
+ # Iterate through all keys, sequentially checking if each metadata plugin manages the considered key. If it
156
+ # does, add it to the list in the plugin's entry in {metadata_plugin_keys}. Note that the order of
157
+ # [METADATA_PLUGIN_MODULES] means that the key is always checked for existence in the base list first.
158
+ for key in metadata.keys():
159
+ # Check for forbidden characters in key.
160
+ for char in RESTRICTED_CHARACTERS:
161
+ if char in key:
162
+ raise exception.InvalidMetadata('Restricted character "{}" found in metadata key. Reason: {}'.format(
163
+ char,
164
+ RESTRICTED_CHARACTERS[char]
165
+ ))
166
+ metadata_is_included = False
167
+ for metadata_plugin in METADATA_PLUGIN_MODULES:
168
+ if metadata_plugin.manages_key(key, session=session):
169
+ metadata_plugin_keys[metadata_plugin].append(key)
170
+ metadata_is_included = True
171
+ break
172
+ if not metadata_is_included:
173
+ unmanaged_keys.append(key)
174
+ if unmanaged_keys:
175
+ raise exception.InvalidMetadata('No plugin manages metadata keys %s on DID %s:%s' % (unmanaged_keys, scope, name))
176
+
177
+ # For each plugin, set the metadata.
178
+ for metadata_plugin, keys_managed_by_this_plugin in metadata_plugin_keys.items():
179
+ if keys_managed_by_this_plugin:
180
+ this_plugin_metadata = {key: metadata[key] for key in keys_managed_by_this_plugin}
181
+ metadata_plugin.set_metadata_bulk(scope, name, metadata=this_plugin_metadata, recursive=recursive, session=session)
182
+
183
+
184
+ @transactional_session
185
+ def delete_metadata(scope, name, key, *, session: "Session"):
186
+ """
187
+ Deletes metadata stored for a given key.
188
+
189
+ :param scope: The scope of the did.
190
+ :param name: The name of the did.
191
+ :param key: Key of the metadata.
192
+ """
193
+ for metadata_plugin in METADATA_PLUGIN_MODULES:
194
+ if metadata_plugin.manages_key(key, session=session):
195
+ metadata_plugin.delete_metadata(scope, name, key, session=session)
196
+
197
+
198
+ @read_session
199
+ def list_dids(scope=None, filters=None, did_type='collection', ignore_case=False, limit=None,
200
+ offset=None, long=False, recursive=False, ignore_dids=None, *, session: "Session"):
201
+ """
202
+ Search data identifiers.
203
+
204
+ All filter keys should belong to a single plugin. Queries across plugins are not currently supported.
205
+
206
+ :param scope: the scope name.
207
+ :param filters: dictionary of attributes by which the results should be filtered.
208
+ :param did_type: the type of the did: all(container, dataset, file), collection(dataset or container), dataset, container, file.
209
+ :param ignore_case: ignore case distinctions.
210
+ :param limit: limit number.
211
+ :param offset: offset number.
212
+ :param long: Long format option to display more information for each DID.
213
+ :param recursive: Recursively list DIDs content.
214
+ :param ignore_dids: List of DIDs to refrain from yielding.
215
+ :param session: The database session in use.
216
+ :returns: List of dids satisfying metadata criteria.
217
+ :raises: InvalidMetadata
218
+ """
219
+ # backwards compatability for filters as single {}.
220
+ if isinstance(filters, dict):
221
+ filters = [filters]
222
+
223
+ required_unique_plugins = set() # keep track of which plugins are required
224
+ for or_group in filters:
225
+ for key in or_group.keys():
226
+ if key == 'name': # [name] is always passed through, and needs to be in schema of all plugins
227
+ continue
228
+ key_nooperator = key.split('.')[0] # remove operator attribute from key if suffixed
229
+
230
+ # Iterate through the list of metadata plugins, checking which (if any) manages this particular key
231
+ # and appending the corresponding plugin to the set, required_unique_plugins.
232
+ is_this_key_managed = False
233
+ for metadata_plugin in METADATA_PLUGIN_MODULES:
234
+ if metadata_plugin.manages_key(key_nooperator, session=session):
235
+ required_unique_plugins.add(metadata_plugin)
236
+ is_this_key_managed = True
237
+ break
238
+ if not is_this_key_managed:
239
+ raise exception.InvalidMetadata('There is no metadata plugin that manages the filter key(s) you requested.')
240
+
241
+ if not required_unique_plugins: # if no metadata keys were specified, fall back to using the base plugin
242
+ required_unique_plugins = [METADATA_PLUGIN_MODULES[0]]
243
+ elif len(required_unique_plugins) > 1: # check that only a single plugin is required for the query, otherwise not supported
244
+ raise exception.InvalidMetadata('Filter keys used do not all belong to the same metadata plugin.')
245
+ selected_plugin_to_use = list(required_unique_plugins)[0]
246
+
247
+ return selected_plugin_to_use.list_dids(scope=scope, filters=filters, did_type=did_type,
248
+ ignore_case=ignore_case, limit=limit,
249
+ offset=offset, long=long, recursive=recursive,
250
+ ignore_dids=ignore_dids, session=session)
@@ -0,0 +1,326 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import operator
17
+ from datetime import datetime, timedelta
18
+ from typing import TYPE_CHECKING
19
+
20
+ from sqlalchemy import update, inspect
21
+ from sqlalchemy.exc import CompileError, InvalidRequestError
22
+ from sqlalchemy.orm.exc import NoResultFound
23
+ from sqlalchemy.sql import func
24
+ from sqlalchemy.sql.expression import true
25
+
26
+ from rucio.common import exception
27
+ from rucio.core import account_counter, rse_counter
28
+ from rucio.core.did_meta_plugins.did_meta_plugin_interface import DidMetaPlugin
29
+ from rucio.core.did_meta_plugins.filter_engine import FilterEngine
30
+ from rucio.db.sqla import models
31
+ from rucio.db.sqla.constants import DIDType
32
+ from rucio.db.sqla.session import stream_session, read_session, transactional_session
33
+
34
+ if TYPE_CHECKING:
35
+ from typing import Optional
36
+ from sqlalchemy.orm import Session
37
+
38
+
39
+ class DidColumnMeta(DidMetaPlugin):
40
+ """
41
+ A metadata plugin to interact with the base did table metadata.
42
+ """
43
+ def __init__(self):
44
+ super(DidColumnMeta, self).__init__()
45
+ self.plugin_name = "DID_COLUMN"
46
+
47
+ @read_session
48
+ def get_metadata(self, scope, name, *, session: "Session"):
49
+ """
50
+ Get data identifier metadata.
51
+
52
+ :param scope: The scope name.
53
+ :param name: The data identifier name.
54
+ :param session: The database session in use.
55
+ """
56
+ try:
57
+ row = session.query(models.DataIdentifier).filter_by(scope=scope, name=name).\
58
+ with_hint(models.DataIdentifier, "INDEX(DIDS DIDS_PK)", 'oracle').one()
59
+ return row.to_dict()
60
+ except NoResultFound:
61
+ raise exception.DataIdentifierNotFound(f"Data identifier '{scope}:{name}' not found")
62
+
63
+ @transactional_session
64
+ def set_metadata(self, scope, name, key, value, recursive=False, *, session: "Session"):
65
+ self.set_metadata_bulk(scope=scope, name=name, metadata={key: value}, recursive=recursive, session=session)
66
+
67
+ @transactional_session
68
+ def set_metadata_bulk(self, scope, name, metadata, recursive=False, *, session: "Session"):
69
+ did_query = session.query(models.DataIdentifier).with_hint(models.DataIdentifier, "INDEX(DIDS DIDS_PK)", 'oracle').filter_by(scope=scope, name=name)
70
+ if did_query.one_or_none() is None:
71
+ raise exception.DataIdentifierNotFound("Data identifier '%s:%s' not found" % (scope, name))
72
+
73
+ remainder = {}
74
+ for key, value in metadata.items():
75
+ if key == 'eol_at' and isinstance(value, str):
76
+ try:
77
+ eol_at = datetime.strptime(value, '%Y-%M-%d')
78
+ rowcount = did_query.update({'eol_at': eol_at}, synchronize_session='fetch')
79
+ except TypeError as error:
80
+ raise exception.InvalidValueForKey(error)
81
+ if not rowcount:
82
+ # check for did presence
83
+ raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
84
+ elif key == 'lifetime':
85
+ try:
86
+ expired_at = None
87
+ if value is not None:
88
+ expired_at = datetime.utcnow() + timedelta(seconds=float(value))
89
+ rowcount = did_query.update({'expired_at': expired_at}, synchronize_session='fetch')
90
+ except TypeError as error:
91
+ raise exception.InvalidValueForKey(error)
92
+ if not rowcount:
93
+ # check for did presence
94
+ raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
95
+ elif key in ['guid', 'events']:
96
+ rowcount = did_query.filter_by(did_type=DIDType.FILE).update({key: value}, synchronize_session=False)
97
+ if not rowcount:
98
+ # check for did presence
99
+ raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
100
+
101
+ session.query(models.DataIdentifierAssociation).filter_by(child_scope=scope, child_name=name, child_type=DIDType.FILE).update({key: value}, synchronize_session=False)
102
+ if key == 'events':
103
+ for parent_scope, parent_name in session.query(models.DataIdentifierAssociation.scope, models.DataIdentifierAssociation.name).filter_by(child_scope=scope, child_name=name):
104
+ events = session.query(func.sum(models.DataIdentifierAssociation.events)).filter_by(scope=parent_scope, name=parent_name).one()[0]
105
+ session.query(models.DataIdentifier).filter_by(scope=parent_scope, name=parent_name).update({'events': events}, synchronize_session=False)
106
+ elif key == 'adler32':
107
+ rowcount = did_query.filter_by(did_type=DIDType.FILE).update({key: value}, synchronize_session=False)
108
+ if not rowcount:
109
+ # check for did presence
110
+ raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
111
+
112
+ session.query(models.DataIdentifierAssociation).filter_by(child_scope=scope, child_name=name, child_type=DIDType.FILE).update({key: value}, synchronize_session=False)
113
+ session.query(models.Request).filter_by(scope=scope, name=name).update({key: value}, synchronize_session=False)
114
+ session.query(models.RSEFileAssociation).filter_by(scope=scope, name=name).update({key: value}, synchronize_session=False)
115
+ elif key == 'bytes':
116
+ rowcount = did_query.filter_by(did_type=DIDType.FILE).update({key: value}, synchronize_session=False)
117
+ if not rowcount:
118
+ # check for did presence
119
+ raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
120
+
121
+ session.query(models.DataIdentifierAssociation).filter_by(child_scope=scope, child_name=name, child_type=DIDType.FILE).update({key: value}, synchronize_session=False)
122
+ session.query(models.Request).filter_by(scope=scope, name=name).update({key: value}, synchronize_session=False)
123
+
124
+ for account, bytes_, rse_id, rule_id in session.query(models.ReplicaLock.account, models.ReplicaLock.bytes, models.ReplicaLock.rse_id, models.ReplicaLock.rule_id).filter_by(scope=scope, name=name):
125
+ session.query(models.ReplicaLock).filter_by(scope=scope, name=name, rule_id=rule_id, rse_id=rse_id).update({key: value}, synchronize_session=False)
126
+ account_counter.decrease(rse_id=rse_id, account=account, files=1, bytes_=bytes_, session=session)
127
+ account_counter.increase(rse_id=rse_id, account=account, files=1, bytes_=value, session=session)
128
+
129
+ for bytes_, rse_id in session.query(models.RSEFileAssociation.bytes, models.RSEFileAssociation.rse_id).filter_by(scope=scope, name=name):
130
+ session.query(models.RSEFileAssociation).filter_by(scope=scope, name=name, rse_id=rse_id).update({key: value}, synchronize_session=False)
131
+ rse_counter.decrease(rse_id=rse_id, files=1, bytes_=bytes_, session=session)
132
+ rse_counter.increase(rse_id=rse_id, files=1, bytes_=value, session=session)
133
+
134
+ for parent_scope, parent_name in session.query(models.DataIdentifierAssociation.scope, models.DataIdentifierAssociation.name).filter_by(child_scope=scope, child_name=name):
135
+ values = {}
136
+ values['length'], values['bytes'], values['events'] = session.query(func.count(models.DataIdentifierAssociation.scope),
137
+ func.sum(models.DataIdentifierAssociation.bytes),
138
+ func.sum(models.DataIdentifierAssociation.events)).filter_by(scope=parent_scope, name=parent_name).one()
139
+ session.query(models.DataIdentifier).filter_by(scope=parent_scope, name=parent_name).update(values, synchronize_session=False)
140
+ session.query(models.DatasetLock).filter_by(scope=parent_scope, name=parent_name).update({'length': values['length'], 'bytes': values['bytes']}, synchronize_session=False)
141
+ else:
142
+ remainder[key] = value
143
+
144
+ if remainder:
145
+ try:
146
+ rowcount = did_query.update(remainder, synchronize_session='fetch')
147
+ except CompileError as error:
148
+ raise exception.InvalidMetadata(error)
149
+ except InvalidRequestError:
150
+ raise exception.InvalidMetadata("Some of the keys are not accepted: " + str(list(remainder.keys())))
151
+ if not rowcount:
152
+ raise exception.UnsupportedOperation('Some of the keys for %s:%s cannot be updated: %s' % (scope, name, str(list(remainder.keys()))))
153
+
154
+ # propagate metadata updates to child content
155
+ if recursive:
156
+ content_query = session.query(models.DataIdentifierAssociation.child_scope, models.DataIdentifierAssociation.child_name)
157
+ content_query = content_query.with_hint(models.DataIdentifierAssociation, "INDEX(CONTENTS CONTENTS_PK)", 'oracle').filter_by(scope=scope, name=name)
158
+
159
+ for child_scope, child_name in content_query:
160
+ try:
161
+ stmt = update(models.DataIdentifier)\
162
+ .prefix_with("/*+ INDEX(DIDS DIDS_PK) */", dialect='oracle')\
163
+ .filter_by(scope=child_scope, name=child_name)\
164
+ .execution_options(synchronize_session='fetch')\
165
+ .values(remainder)
166
+ session.execute(stmt)
167
+ except CompileError as error:
168
+ raise exception.InvalidMetadata(error)
169
+ except InvalidRequestError:
170
+ raise exception.InvalidMetadata("Some of the keys are not accepted recursively: " + str(list(remainder.keys())))
171
+
172
+ @stream_session
173
+ def list_dids(self, scope, filters, did_type='collection', ignore_case=False, limit=None,
174
+ offset=None, long=False, recursive=False, ignore_dids=None, *, session: "Session"):
175
+ """
176
+ Search data identifiers.
177
+
178
+ :param scope: the scope name.
179
+ :param filters: dictionary of attributes by which the results should be filtered.
180
+ :param did_type: the type of the did: all(container, dataset, file), collection(dataset or container), dataset, container, file.
181
+ :param ignore_case: ignore case distinctions.
182
+ :param limit: limit number.
183
+ :param offset: offset number.
184
+ :param long: Long format option to display more information for each DID.
185
+ :param session: The database session in use.
186
+ :param recursive: Recursively list DIDs content.
187
+ :param ignore_dids: List of DIDs to refrain from yielding.
188
+ """
189
+ if not ignore_dids:
190
+ ignore_dids = set()
191
+
192
+ # mapping for semantic <type> to a (set of) recognised DIDType(s).
193
+ type_to_did_type_mapping = {
194
+ 'all': [DIDType.CONTAINER, DIDType.DATASET, DIDType.FILE],
195
+ 'collection': [DIDType.CONTAINER, DIDType.DATASET],
196
+ 'container': [DIDType.CONTAINER],
197
+ 'dataset': [DIDType.DATASET],
198
+ 'file': [DIDType.FILE]
199
+ }
200
+
201
+ # backwards compatability for filters as single {}.
202
+ if isinstance(filters, dict):
203
+ filters = [filters]
204
+
205
+ # for each or_group, make sure there is a mapped "did_type" filter.
206
+ # if type maps to many DIDTypes, the corresponding or_group will be copied the required number of times to satisfy all the logical possibilities.
207
+ filters_tmp = []
208
+ for or_group in filters:
209
+ if 'type' not in or_group:
210
+ or_group_type = did_type.lower()
211
+ else:
212
+ or_group_type = or_group.pop('type').lower()
213
+ if or_group_type not in type_to_did_type_mapping.keys():
214
+ raise exception.UnsupportedOperation('{} is not a valid type. Valid types are {}'.format(or_group_type, type_to_did_type_mapping.keys()))
215
+
216
+ for mapped_did_type in type_to_did_type_mapping[or_group_type]:
217
+ or_group['did_type'] = mapped_did_type
218
+ filters_tmp.append(or_group.copy())
219
+ filters = filters_tmp
220
+
221
+ # instantiate fe and create sqla query
222
+ fe = FilterEngine(filters, model_class=models.DataIdentifier)
223
+ query = fe.create_sqla_query(
224
+ additional_model_attributes=[
225
+ models.DataIdentifier.scope,
226
+ models.DataIdentifier.name,
227
+ models.DataIdentifier.did_type,
228
+ models.DataIdentifier.bytes,
229
+ models.DataIdentifier.length
230
+ ], additional_filters=[
231
+ (models.DataIdentifier.scope, operator.eq, scope),
232
+ (models.DataIdentifier.suppressed, operator.ne, true())
233
+ ],
234
+ session=session
235
+ )
236
+ query.with_hint(models.DataIdentifier, 'NO_EXPAND', 'oracle')
237
+
238
+ if limit:
239
+ query = query.limit(limit)
240
+ if recursive:
241
+ from rucio.core.did import list_content
242
+
243
+ # Get attached DIDs and save in list because query has to be finished before starting a new one in the recursion
244
+ collections_content = []
245
+ for did in query.yield_per(100):
246
+ if (did.did_type == DIDType.CONTAINER or did.did_type == DIDType.DATASET):
247
+ collections_content += [d for d in list_content(scope=did.scope, name=did.name)]
248
+
249
+ # Replace any name filtering with recursed DID names.
250
+ for did in collections_content:
251
+ for or_group in filters:
252
+ or_group['name'] = did['name']
253
+ for result in self.list_dids(scope=did['scope'], filters=filters, recursive=True, did_type=did_type, limit=limit, offset=offset,
254
+ long=long, ignore_dids=ignore_dids, session=session):
255
+ yield result
256
+
257
+ for did in query.yield_per(5): # don't unpack this as it makes it dependent on query return order!
258
+ if long:
259
+ did_full = "{}:{}".format(did.scope, did.name)
260
+ if did_full not in ignore_dids: # concatenating results of OR clauses may contain duplicate DIDs if query result sets not mutually exclusive.
261
+ ignore_dids.add(did_full)
262
+ yield {
263
+ 'scope': did.scope,
264
+ 'name': did.name,
265
+ 'did_type': str(did.did_type),
266
+ 'bytes': did.bytes,
267
+ 'length': did.length
268
+ }
269
+ else:
270
+ did_full = "{}:{}".format(did.scope, did.name)
271
+ if did_full not in ignore_dids: # concatenating results of OR clauses may contain duplicate DIDs if query result sets not mutually exclusive.
272
+ ignore_dids.add(did_full)
273
+ yield did.name
274
+
275
+ def delete_metadata(self, scope, name, key, *, session: "Optional[Session]" = None):
276
+ """
277
+ Deletes the metadata stored for the given key.
278
+
279
+ :param scope: The scope of the did.
280
+ :param name: The name of the did.
281
+ :param key: Key of the metadata.
282
+ """
283
+ raise NotImplementedError('The DidColumnMeta plugin does not currently support deleting metadata.')
284
+
285
+ def manages_key(self, key, *, session: "Optional[Session]" = None):
286
+ # Build list of which keys are managed by this plugin.
287
+ #
288
+ all_did_table_columns = []
289
+ for column in inspect(models.DataIdentifier).attrs:
290
+ all_did_table_columns.append(column.key)
291
+
292
+ exclude_did_table_columns = [
293
+ 'account',
294
+ 'availability',
295
+ 'complete',
296
+ 'created_at',
297
+ 'did_type',
298
+ 'is_open',
299
+ 'monotonic',
300
+ 'obsolete',
301
+ 'scope',
302
+ 'suppressed',
303
+ 'updated_at'
304
+ ]
305
+
306
+ additional_keys = [
307
+ 'lifetime',
308
+ 'created_before',
309
+ 'created_after',
310
+ 'length.gt',
311
+ 'length.lt',
312
+ 'length.gte',
313
+ 'length.lte',
314
+ 'type'
315
+ ]
316
+
317
+ hardcoded_keys = list(set(all_did_table_columns) - set(exclude_did_table_columns)) + additional_keys
318
+
319
+ return key in hardcoded_keys
320
+
321
+ def get_plugin_name(self):
322
+ """
323
+ Returns a unique identifier for this plugin. This can be later used for filtering down results to this plugin only.
324
+ :returns: The name of the plugin.
325
+ """
326
+ return self.plugin_name