rucio 35.7.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (493) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/client/__init__.py +15 -0
  4. rucio/client/accountclient.py +433 -0
  5. rucio/client/accountlimitclient.py +183 -0
  6. rucio/client/baseclient.py +974 -0
  7. rucio/client/client.py +76 -0
  8. rucio/client/configclient.py +126 -0
  9. rucio/client/credentialclient.py +59 -0
  10. rucio/client/didclient.py +866 -0
  11. rucio/client/diracclient.py +56 -0
  12. rucio/client/downloadclient.py +1785 -0
  13. rucio/client/exportclient.py +44 -0
  14. rucio/client/fileclient.py +50 -0
  15. rucio/client/importclient.py +42 -0
  16. rucio/client/lifetimeclient.py +90 -0
  17. rucio/client/lockclient.py +109 -0
  18. rucio/client/metaconventionsclient.py +140 -0
  19. rucio/client/pingclient.py +44 -0
  20. rucio/client/replicaclient.py +454 -0
  21. rucio/client/requestclient.py +125 -0
  22. rucio/client/rseclient.py +746 -0
  23. rucio/client/ruleclient.py +294 -0
  24. rucio/client/scopeclient.py +90 -0
  25. rucio/client/subscriptionclient.py +173 -0
  26. rucio/client/touchclient.py +82 -0
  27. rucio/client/uploadclient.py +955 -0
  28. rucio/common/__init__.py +13 -0
  29. rucio/common/cache.py +74 -0
  30. rucio/common/config.py +801 -0
  31. rucio/common/constants.py +159 -0
  32. rucio/common/constraints.py +17 -0
  33. rucio/common/didtype.py +189 -0
  34. rucio/common/dumper/__init__.py +335 -0
  35. rucio/common/dumper/consistency.py +452 -0
  36. rucio/common/dumper/data_models.py +318 -0
  37. rucio/common/dumper/path_parsing.py +64 -0
  38. rucio/common/exception.py +1151 -0
  39. rucio/common/extra.py +36 -0
  40. rucio/common/logging.py +420 -0
  41. rucio/common/pcache.py +1408 -0
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +84 -0
  44. rucio/common/schema/__init__.py +150 -0
  45. rucio/common/schema/atlas.py +413 -0
  46. rucio/common/schema/belleii.py +408 -0
  47. rucio/common/schema/domatpc.py +401 -0
  48. rucio/common/schema/escape.py +426 -0
  49. rucio/common/schema/generic.py +433 -0
  50. rucio/common/schema/generic_multi_vo.py +412 -0
  51. rucio/common/schema/icecube.py +406 -0
  52. rucio/common/stomp_utils.py +159 -0
  53. rucio/common/stopwatch.py +55 -0
  54. rucio/common/test_rucio_server.py +148 -0
  55. rucio/common/types.py +403 -0
  56. rucio/common/utils.py +2238 -0
  57. rucio/core/__init__.py +13 -0
  58. rucio/core/account.py +496 -0
  59. rucio/core/account_counter.py +236 -0
  60. rucio/core/account_limit.py +423 -0
  61. rucio/core/authentication.py +620 -0
  62. rucio/core/config.py +456 -0
  63. rucio/core/credential.py +225 -0
  64. rucio/core/did.py +3000 -0
  65. rucio/core/did_meta_plugins/__init__.py +252 -0
  66. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  68. rucio/core/did_meta_plugins/filter_engine.py +613 -0
  69. rucio/core/did_meta_plugins/json_meta.py +240 -0
  70. rucio/core/did_meta_plugins/mongo_meta.py +216 -0
  71. rucio/core/did_meta_plugins/postgres_meta.py +316 -0
  72. rucio/core/dirac.py +237 -0
  73. rucio/core/distance.py +187 -0
  74. rucio/core/exporter.py +59 -0
  75. rucio/core/heartbeat.py +363 -0
  76. rucio/core/identity.py +300 -0
  77. rucio/core/importer.py +259 -0
  78. rucio/core/lifetime_exception.py +377 -0
  79. rucio/core/lock.py +576 -0
  80. rucio/core/message.py +282 -0
  81. rucio/core/meta_conventions.py +203 -0
  82. rucio/core/monitor.py +447 -0
  83. rucio/core/naming_convention.py +195 -0
  84. rucio/core/nongrid_trace.py +136 -0
  85. rucio/core/oidc.py +1461 -0
  86. rucio/core/permission/__init__.py +119 -0
  87. rucio/core/permission/atlas.py +1348 -0
  88. rucio/core/permission/belleii.py +1077 -0
  89. rucio/core/permission/escape.py +1078 -0
  90. rucio/core/permission/generic.py +1130 -0
  91. rucio/core/permission/generic_multi_vo.py +1150 -0
  92. rucio/core/quarantined_replica.py +223 -0
  93. rucio/core/replica.py +4158 -0
  94. rucio/core/replica_sorter.py +366 -0
  95. rucio/core/request.py +3089 -0
  96. rucio/core/rse.py +1875 -0
  97. rucio/core/rse_counter.py +186 -0
  98. rucio/core/rse_expression_parser.py +459 -0
  99. rucio/core/rse_selector.py +302 -0
  100. rucio/core/rule.py +4483 -0
  101. rucio/core/rule_grouping.py +1618 -0
  102. rucio/core/scope.py +180 -0
  103. rucio/core/subscription.py +364 -0
  104. rucio/core/topology.py +490 -0
  105. rucio/core/trace.py +375 -0
  106. rucio/core/transfer.py +1517 -0
  107. rucio/core/vo.py +169 -0
  108. rucio/core/volatile_replica.py +150 -0
  109. rucio/daemons/__init__.py +13 -0
  110. rucio/daemons/abacus/__init__.py +13 -0
  111. rucio/daemons/abacus/account.py +116 -0
  112. rucio/daemons/abacus/collection_replica.py +124 -0
  113. rucio/daemons/abacus/rse.py +117 -0
  114. rucio/daemons/atropos/__init__.py +13 -0
  115. rucio/daemons/atropos/atropos.py +242 -0
  116. rucio/daemons/auditor/__init__.py +289 -0
  117. rucio/daemons/auditor/hdfs.py +97 -0
  118. rucio/daemons/auditor/srmdumps.py +355 -0
  119. rucio/daemons/automatix/__init__.py +13 -0
  120. rucio/daemons/automatix/automatix.py +293 -0
  121. rucio/daemons/badreplicas/__init__.py +13 -0
  122. rucio/daemons/badreplicas/minos.py +322 -0
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  124. rucio/daemons/badreplicas/necromancer.py +196 -0
  125. rucio/daemons/bb8/__init__.py +13 -0
  126. rucio/daemons/bb8/bb8.py +353 -0
  127. rucio/daemons/bb8/common.py +759 -0
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  129. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  130. rucio/daemons/c3po/__init__.py +13 -0
  131. rucio/daemons/c3po/algorithms/__init__.py +13 -0
  132. rucio/daemons/c3po/algorithms/simple.py +134 -0
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +128 -0
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +130 -0
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +294 -0
  136. rucio/daemons/c3po/c3po.py +371 -0
  137. rucio/daemons/c3po/collectors/__init__.py +13 -0
  138. rucio/daemons/c3po/collectors/agis.py +108 -0
  139. rucio/daemons/c3po/collectors/free_space.py +81 -0
  140. rucio/daemons/c3po/collectors/jedi_did.py +57 -0
  141. rucio/daemons/c3po/collectors/mock_did.py +51 -0
  142. rucio/daemons/c3po/collectors/network_metrics.py +71 -0
  143. rucio/daemons/c3po/collectors/workload.py +112 -0
  144. rucio/daemons/c3po/utils/__init__.py +13 -0
  145. rucio/daemons/c3po/utils/dataset_cache.py +50 -0
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +56 -0
  147. rucio/daemons/c3po/utils/expiring_list.py +62 -0
  148. rucio/daemons/c3po/utils/popularity.py +85 -0
  149. rucio/daemons/c3po/utils/timeseries.py +89 -0
  150. rucio/daemons/cache/__init__.py +13 -0
  151. rucio/daemons/cache/consumer.py +197 -0
  152. rucio/daemons/common.py +415 -0
  153. rucio/daemons/conveyor/__init__.py +13 -0
  154. rucio/daemons/conveyor/common.py +562 -0
  155. rucio/daemons/conveyor/finisher.py +529 -0
  156. rucio/daemons/conveyor/poller.py +404 -0
  157. rucio/daemons/conveyor/preparer.py +205 -0
  158. rucio/daemons/conveyor/receiver.py +249 -0
  159. rucio/daemons/conveyor/stager.py +132 -0
  160. rucio/daemons/conveyor/submitter.py +403 -0
  161. rucio/daemons/conveyor/throttler.py +532 -0
  162. rucio/daemons/follower/__init__.py +13 -0
  163. rucio/daemons/follower/follower.py +101 -0
  164. rucio/daemons/hermes/__init__.py +13 -0
  165. rucio/daemons/hermes/hermes.py +774 -0
  166. rucio/daemons/judge/__init__.py +13 -0
  167. rucio/daemons/judge/cleaner.py +159 -0
  168. rucio/daemons/judge/evaluator.py +185 -0
  169. rucio/daemons/judge/injector.py +162 -0
  170. rucio/daemons/judge/repairer.py +154 -0
  171. rucio/daemons/oauthmanager/__init__.py +13 -0
  172. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  173. rucio/daemons/reaper/__init__.py +13 -0
  174. rucio/daemons/reaper/dark_reaper.py +278 -0
  175. rucio/daemons/reaper/reaper.py +743 -0
  176. rucio/daemons/replicarecoverer/__init__.py +13 -0
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  178. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +13 -0
  186. rucio/daemons/storage/consistency/__init__.py +13 -0
  187. rucio/daemons/storage/consistency/actions.py +846 -0
  188. rucio/daemons/tracer/__init__.py +13 -0
  189. rucio/daemons/tracer/kronos.py +536 -0
  190. rucio/daemons/transmogrifier/__init__.py +13 -0
  191. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  192. rucio/daemons/undertaker/__init__.py +13 -0
  193. rucio/daemons/undertaker/undertaker.py +137 -0
  194. rucio/db/__init__.py +13 -0
  195. rucio/db/sqla/__init__.py +52 -0
  196. rucio/db/sqla/constants.py +201 -0
  197. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  198. rucio/db/sqla/migrate_repo/env.py +110 -0
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  327. rucio/db/sqla/models.py +1740 -0
  328. rucio/db/sqla/sautils.py +55 -0
  329. rucio/db/sqla/session.py +498 -0
  330. rucio/db/sqla/types.py +206 -0
  331. rucio/db/sqla/util.py +543 -0
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/gateway/account.py +339 -0
  334. rucio/gateway/account_limit.py +286 -0
  335. rucio/gateway/authentication.py +375 -0
  336. rucio/gateway/config.py +217 -0
  337. rucio/gateway/credential.py +71 -0
  338. rucio/gateway/did.py +970 -0
  339. rucio/gateway/dirac.py +81 -0
  340. rucio/gateway/exporter.py +59 -0
  341. rucio/gateway/heartbeat.py +74 -0
  342. rucio/gateway/identity.py +204 -0
  343. rucio/gateway/importer.py +45 -0
  344. rucio/gateway/lifetime_exception.py +120 -0
  345. rucio/gateway/lock.py +153 -0
  346. rucio/gateway/meta_conventions.py +87 -0
  347. rucio/gateway/permission.py +71 -0
  348. rucio/gateway/quarantined_replica.py +78 -0
  349. rucio/gateway/replica.py +529 -0
  350. rucio/gateway/request.py +321 -0
  351. rucio/gateway/rse.py +600 -0
  352. rucio/gateway/rule.py +417 -0
  353. rucio/gateway/scope.py +99 -0
  354. rucio/gateway/subscription.py +277 -0
  355. rucio/gateway/vo.py +122 -0
  356. rucio/rse/__init__.py +96 -0
  357. rucio/rse/protocols/__init__.py +13 -0
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +122 -0
  360. rucio/rse/protocols/dummy.py +111 -0
  361. rucio/rse/protocols/gfal.py +703 -0
  362. rucio/rse/protocols/globus.py +243 -0
  363. rucio/rse/protocols/gsiftp.py +92 -0
  364. rucio/rse/protocols/http_cache.py +82 -0
  365. rucio/rse/protocols/mock.py +123 -0
  366. rucio/rse/protocols/ngarc.py +209 -0
  367. rucio/rse/protocols/posix.py +250 -0
  368. rucio/rse/protocols/protocol.py +594 -0
  369. rucio/rse/protocols/rclone.py +364 -0
  370. rucio/rse/protocols/rfio.py +136 -0
  371. rucio/rse/protocols/srm.py +338 -0
  372. rucio/rse/protocols/ssh.py +413 -0
  373. rucio/rse/protocols/storm.py +206 -0
  374. rucio/rse/protocols/webdav.py +550 -0
  375. rucio/rse/protocols/xrootd.py +301 -0
  376. rucio/rse/rsemanager.py +764 -0
  377. rucio/tests/__init__.py +13 -0
  378. rucio/tests/common.py +270 -0
  379. rucio/tests/common_server.py +132 -0
  380. rucio/transfertool/__init__.py +13 -0
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +1596 -0
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +201 -0
  387. rucio/transfertool/globus_library.py +181 -0
  388. rucio/transfertool/mock.py +90 -0
  389. rucio/transfertool/transfertool.py +221 -0
  390. rucio/vcsversion.py +11 -0
  391. rucio/version.py +38 -0
  392. rucio/web/__init__.py +13 -0
  393. rucio/web/rest/__init__.py +13 -0
  394. rucio/web/rest/flaskapi/__init__.py +13 -0
  395. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  396. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  398. rucio/web/rest/flaskapi/v1/accounts.py +1089 -0
  399. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  400. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  401. rucio/web/rest/flaskapi/v1/common.py +426 -0
  402. rucio/web/rest/flaskapi/v1/config.py +304 -0
  403. rucio/web/rest/flaskapi/v1/credentials.py +212 -0
  404. rucio/web/rest/flaskapi/v1/dids.py +2334 -0
  405. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  406. rucio/web/rest/flaskapi/v1/export.py +75 -0
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  408. rucio/web/rest/flaskapi/v1/identities.py +261 -0
  409. rucio/web/rest/flaskapi/v1/import.py +132 -0
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  411. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  412. rucio/web/rest/flaskapi/v1/main.py +91 -0
  413. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  414. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  416. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  417. rucio/web/rest/flaskapi/v1/redirect.py +365 -0
  418. rucio/web/rest/flaskapi/v1/replicas.py +1890 -0
  419. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  420. rucio/web/rest/flaskapi/v1/rses.py +2239 -0
  421. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  422. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  424. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  425. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  426. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  427. rucio/web/rest/flaskapi/v1/types.py +20 -0
  428. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  429. rucio/web/rest/main.py +18 -0
  430. rucio/web/rest/metrics.py +27 -0
  431. rucio/web/rest/ping.py +27 -0
  432. rucio-35.7.0.data/data/rucio/etc/alembic.ini.template +71 -0
  433. rucio-35.7.0.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  434. rucio-35.7.0.data/data/rucio/etc/globus-config.yml.template +5 -0
  435. rucio-35.7.0.data/data/rucio/etc/ldap.cfg.template +30 -0
  436. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  437. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  438. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  439. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  440. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  441. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  442. rucio-35.7.0.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  443. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  444. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.template +257 -0
  445. rucio-35.7.0.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  446. rucio-35.7.0.data/data/rucio/requirements.server.txt +268 -0
  447. rucio-35.7.0.data/data/rucio/tools/bootstrap.py +34 -0
  448. rucio-35.7.0.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  449. rucio-35.7.0.data/data/rucio/tools/reset_database.py +40 -0
  450. rucio-35.7.0.data/scripts/rucio +2542 -0
  451. rucio-35.7.0.data/scripts/rucio-abacus-account +74 -0
  452. rucio-35.7.0.data/scripts/rucio-abacus-collection-replica +46 -0
  453. rucio-35.7.0.data/scripts/rucio-abacus-rse +78 -0
  454. rucio-35.7.0.data/scripts/rucio-admin +2447 -0
  455. rucio-35.7.0.data/scripts/rucio-atropos +60 -0
  456. rucio-35.7.0.data/scripts/rucio-auditor +205 -0
  457. rucio-35.7.0.data/scripts/rucio-automatix +50 -0
  458. rucio-35.7.0.data/scripts/rucio-bb8 +57 -0
  459. rucio-35.7.0.data/scripts/rucio-c3po +85 -0
  460. rucio-35.7.0.data/scripts/rucio-cache-client +134 -0
  461. rucio-35.7.0.data/scripts/rucio-cache-consumer +42 -0
  462. rucio-35.7.0.data/scripts/rucio-conveyor-finisher +58 -0
  463. rucio-35.7.0.data/scripts/rucio-conveyor-poller +66 -0
  464. rucio-35.7.0.data/scripts/rucio-conveyor-preparer +37 -0
  465. rucio-35.7.0.data/scripts/rucio-conveyor-receiver +43 -0
  466. rucio-35.7.0.data/scripts/rucio-conveyor-stager +76 -0
  467. rucio-35.7.0.data/scripts/rucio-conveyor-submitter +139 -0
  468. rucio-35.7.0.data/scripts/rucio-conveyor-throttler +104 -0
  469. rucio-35.7.0.data/scripts/rucio-dark-reaper +53 -0
  470. rucio-35.7.0.data/scripts/rucio-dumper +160 -0
  471. rucio-35.7.0.data/scripts/rucio-follower +44 -0
  472. rucio-35.7.0.data/scripts/rucio-hermes +54 -0
  473. rucio-35.7.0.data/scripts/rucio-judge-cleaner +89 -0
  474. rucio-35.7.0.data/scripts/rucio-judge-evaluator +137 -0
  475. rucio-35.7.0.data/scripts/rucio-judge-injector +44 -0
  476. rucio-35.7.0.data/scripts/rucio-judge-repairer +44 -0
  477. rucio-35.7.0.data/scripts/rucio-kronos +43 -0
  478. rucio-35.7.0.data/scripts/rucio-minos +53 -0
  479. rucio-35.7.0.data/scripts/rucio-minos-temporary-expiration +50 -0
  480. rucio-35.7.0.data/scripts/rucio-necromancer +120 -0
  481. rucio-35.7.0.data/scripts/rucio-oauth-manager +63 -0
  482. rucio-35.7.0.data/scripts/rucio-reaper +83 -0
  483. rucio-35.7.0.data/scripts/rucio-replica-recoverer +248 -0
  484. rucio-35.7.0.data/scripts/rucio-rse-decommissioner +66 -0
  485. rucio-35.7.0.data/scripts/rucio-storage-consistency-actions +74 -0
  486. rucio-35.7.0.data/scripts/rucio-transmogrifier +77 -0
  487. rucio-35.7.0.data/scripts/rucio-undertaker +76 -0
  488. rucio-35.7.0.dist-info/METADATA +72 -0
  489. rucio-35.7.0.dist-info/RECORD +493 -0
  490. rucio-35.7.0.dist-info/WHEEL +5 -0
  491. rucio-35.7.0.dist-info/licenses/AUTHORS.rst +97 -0
  492. rucio-35.7.0.dist-info/licenses/LICENSE +201 -0
  493. rucio-35.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2334 @@
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 ast
16
+ from json import dumps
17
+
18
+ from flask import Flask, Response, request
19
+
20
+ from rucio.common.exception import (
21
+ AccessDenied,
22
+ DatabaseException,
23
+ DataIdentifierAlreadyExists,
24
+ DataIdentifierNotFound,
25
+ Duplicate,
26
+ DuplicateContent,
27
+ FileAlreadyExists,
28
+ FileConsistencyMismatch,
29
+ InvalidMetadata,
30
+ InvalidObject,
31
+ InvalidPath,
32
+ InvalidValueForKey,
33
+ KeyNotFound,
34
+ RSENotFound,
35
+ RuleNotFound,
36
+ ScopeNotFound,
37
+ UnsupportedMetadataPlugin,
38
+ UnsupportedOperation,
39
+ UnsupportedStatus,
40
+ )
41
+ from rucio.common.utils import APIEncoder, parse_response, render_json
42
+ from rucio.db.sqla.constants import DIDType
43
+ from rucio.gateway.did import (
44
+ add_did,
45
+ add_did_to_followed,
46
+ add_dids,
47
+ attach_dids,
48
+ attach_dids_to_dids,
49
+ bulk_list_files,
50
+ create_did_sample,
51
+ delete_metadata,
52
+ detach_dids,
53
+ get_dataset_by_guid,
54
+ get_did,
55
+ get_metadata,
56
+ get_metadata_bulk,
57
+ get_users_following_did,
58
+ list_content,
59
+ list_content_history,
60
+ list_dids,
61
+ list_files,
62
+ list_new_dids,
63
+ list_parent_dids,
64
+ remove_did_from_followed,
65
+ resurrect,
66
+ scope_list,
67
+ set_dids_metadata_bulk,
68
+ set_metadata,
69
+ set_metadata_bulk,
70
+ set_status,
71
+ )
72
+ from rucio.gateway.rule import list_associated_replication_rules_for_file, list_replication_rules
73
+ from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
74
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_list, json_parameters, json_parse, param_get, parse_scope_name, response_headers, try_stream
75
+
76
+
77
+ class Scope(ErrorHandlingMethodView):
78
+
79
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
80
+ def get(self, scope):
81
+ """
82
+ ---
83
+ summary: Get Data Identifier
84
+ description: Return all data identifiers in the given scope.
85
+ tags:
86
+ - Data Identifiers
87
+ parameters:
88
+ - name: scope
89
+ in: path
90
+ description: The scope.
91
+ schema:
92
+ type: string
93
+ style: simple
94
+ requestBody:
95
+ content:
96
+ application/json:
97
+ schema:
98
+ type: object
99
+ properties:
100
+ name:
101
+ description: The name of the did.
102
+ type: string
103
+ recursive:
104
+ description: If specified, also returns the child ids recursively.
105
+ type: boolean
106
+ responses:
107
+ 200:
108
+ description: OK
109
+ content:
110
+ application/x-json-stream:
111
+ schema:
112
+ description: Line separated dictionary of dids.
113
+ type: array
114
+ items:
115
+ type: object
116
+ description: Data identifier
117
+ properties:
118
+ scope:
119
+ type: string
120
+ description: The scope of the did.
121
+ name:
122
+ type: string
123
+ description: The name of the did.
124
+ type:
125
+ type: string
126
+ description: The type of the did.
127
+ enum: ['F', 'D', 'C', 'A', 'X', 'Y', 'Z']
128
+ parent:
129
+ type: string
130
+ description: The parent of the did.
131
+ level:
132
+ type: integer
133
+ description: The level of the did.
134
+ 401:
135
+ description: Invalid Auth Token
136
+ 404:
137
+ description: No Dids found
138
+ 406:
139
+ description: Not acceptable
140
+ """
141
+ try:
142
+ def generate(name, recursive, vo):
143
+ for did in scope_list(scope=scope, name=name, recursive=recursive, vo=vo):
144
+ yield render_json(**did) + '\n'
145
+
146
+ recursive = 'recursive' in request.args
147
+
148
+ return try_stream(
149
+ generate(
150
+ name=request.args.get('name', default=None),
151
+ recursive=recursive,
152
+ vo=request.environ.get('vo')
153
+ )
154
+ )
155
+ except DataIdentifierNotFound as error:
156
+ return generate_http_error_flask(404, error)
157
+
158
+
159
+ class Search(ErrorHandlingMethodView):
160
+
161
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
162
+ def get(self, scope):
163
+ """
164
+ ---
165
+ summary: List Data identifier
166
+ description: List all data identifiers in a scope which match a given metadata.
167
+ tags:
168
+ - Data Identifiers
169
+ parameters:
170
+ - name: scope
171
+ in: path
172
+ description: The scope of the data identifiers.
173
+ schema:
174
+ type: string
175
+ style: simple
176
+ - name: type
177
+ in: query
178
+ description: The did type to search for.
179
+ schema:
180
+ type: string
181
+ enum: ['all', 'collection', 'container', 'dataset', 'file']
182
+ default: 'collection'
183
+ - name: limit
184
+ in: query
185
+ description: The maximum number od dids returned.
186
+ schema:
187
+ type: integer
188
+ - name: long
189
+ in: query
190
+ description: Provides a longer output, otherwise just prints names.
191
+ schema:
192
+ type: boolean
193
+ default: false
194
+ - name: recursive
195
+ in: query
196
+ description: Recursively list chilred.
197
+ schema:
198
+ type: boolean
199
+ - name: created_before
200
+ in: query
201
+ description: Date string in RFC-1123 format where the creation date was earlier.
202
+ schema:
203
+ type: string
204
+ - name: created_after
205
+ in: query
206
+ description: Date string in RFC-1123 format where the creation date was later.
207
+ schema:
208
+ type: string
209
+ - name: length
210
+ in: query
211
+ description: Exact number of attached DIDs.
212
+ schema:
213
+ type: integer
214
+ - name: length.gt
215
+ in: query
216
+ description: Number of attached DIDs greater than.
217
+ schema:
218
+ type: integer
219
+ - name: length.lt
220
+ in: query
221
+ description: Number of attached DIDs less than.
222
+ schema:
223
+ type: integer
224
+ - name: length.gte
225
+ in: query
226
+ description: Number of attached DIDs greater than or equal to
227
+ schema:
228
+ type: integer
229
+ - name: length.lte
230
+ in: query
231
+ description: Number of attached DIDs less than or equal to.
232
+ schema:
233
+ type: integer
234
+ - name: name
235
+ in: query
236
+ description: Name or pattern of a did.
237
+ schema:
238
+ type: string
239
+ responses:
240
+ 200:
241
+ description: OK
242
+ content:
243
+ application/x-json-stream:
244
+ schema:
245
+ description: Line separated name of DIDs or dictionaries of DIDs for long option.
246
+ type: array
247
+ items:
248
+ type: object
249
+ description: the name of a DID or a dictionarie of a DID for long option.
250
+ 401:
251
+ description: Invalid Auth Token
252
+ 404:
253
+ description: Invalid key in filter.
254
+ 406:
255
+ description: Not acceptable
256
+ 409:
257
+ description: Wrong did type
258
+ """
259
+ filters = request.args.get('filters', default=None)
260
+ if filters is not None:
261
+ filters = ast.literal_eval(filters)
262
+ else:
263
+ # backwards compatibility for created*, length* and name filters passed through as request args
264
+ filters = {}
265
+ for arg, value in request.args.copy().items():
266
+ if arg not in ['type', 'limit', 'long', 'recursive']:
267
+ filters[arg] = value
268
+ filters = [filters]
269
+
270
+ did_type = request.args.get('type', default=None)
271
+ limit = request.args.get('limit', default=None)
272
+ long = request.args.get('long', type=['True', '1'].__contains__, default=False)
273
+ recursive = request.args.get('recursive', type='True'.__eq__, default=False)
274
+ try:
275
+ def generate(vo):
276
+ for did in list_dids(scope=scope, filters=filters, did_type=did_type, limit=limit, long=long, recursive=recursive, vo=vo):
277
+ yield dumps(did) + '\n'
278
+ return try_stream(generate(vo=request.environ.get('vo')))
279
+ except UnsupportedOperation as error:
280
+ return generate_http_error_flask(409, error)
281
+ except KeyNotFound as error:
282
+ return generate_http_error_flask(404, error)
283
+
284
+
285
+ class BulkDIDS(ErrorHandlingMethodView):
286
+
287
+ @check_accept_header_wrapper_flask(['application/json'])
288
+ def post(self):
289
+ """
290
+ ---
291
+ summary: Add Dids bulk
292
+ description: Add new Dids in bulk.
293
+ tags:
294
+ - Data Identifiers
295
+ requestBody:
296
+ content:
297
+ application/json:
298
+ schema:
299
+ type: array
300
+ items:
301
+ description: One did to add.
302
+ type: object
303
+ required:
304
+ - scope
305
+ - name
306
+ - type
307
+ properties:
308
+ scope:
309
+ description: The did scope.
310
+ type: string
311
+ name:
312
+ description: The did name.
313
+ type: string
314
+ type:
315
+ description: The type of the did.
316
+ type: string
317
+ enum: ["F", "D", "C", "A", "X", "Y", "Z"]
318
+ account:
319
+ description: The account associated with the did.
320
+ type: string
321
+ statuses:
322
+ description: The monotonic status
323
+ type: string
324
+ responses:
325
+ 201:
326
+ description: OK
327
+ content:
328
+ application/json:
329
+ schema:
330
+ type: string
331
+ enum: ["Created"]
332
+ 401:
333
+ description: Invalid Auth Token
334
+ 406:
335
+ description: Not acceptable
336
+ 409:
337
+ description: Did already exists
338
+ """
339
+ dids = json_list()
340
+ try:
341
+ add_dids(dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
342
+ except DataIdentifierNotFound as error:
343
+ return generate_http_error_flask(404, error)
344
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
345
+ return generate_http_error_flask(409, error)
346
+ except AccessDenied as error:
347
+ return generate_http_error_flask(401, error)
348
+ return 'Created', 201
349
+
350
+
351
+ class Attachments(ErrorHandlingMethodView):
352
+
353
+ def post(self):
354
+ """
355
+ ---
356
+ summary: Attach did to did
357
+ description: Attaches a did to another did
358
+ tags:
359
+ - Data Identifiers
360
+ requestBody:
361
+ content:
362
+ 'application/json':
363
+ schema:
364
+ oneOf:
365
+ - description: An array containing all dids. Duplicates are not ignored.
366
+ type: array
367
+ required:
368
+ - scope
369
+ - name
370
+ - dids
371
+ properties:
372
+ scope:
373
+ description: The scope of the did.
374
+ type: string
375
+ name:
376
+ description: The name of the did.
377
+ type: string
378
+ dids:
379
+ description: The dids associated to the did.
380
+ type: array
381
+ items:
382
+ type: object
383
+ description: A did.
384
+ required:
385
+ - scope
386
+ - name
387
+ properties:
388
+ scope:
389
+ description: The scope of the did.
390
+ type: string
391
+ name:
392
+ description: The name of the did.
393
+ type: string
394
+ rse_id:
395
+ description: The rse id of the did.
396
+ type: string
397
+ - type: object
398
+ required:
399
+ - attachments
400
+ properties:
401
+ ignore_duplicates:
402
+ description: If duplicates should be ignored.
403
+ type: boolean
404
+ default: false
405
+ attachments:
406
+ description: An array containing all dids. Duplicates are not ignored.
407
+ type: array
408
+ required:
409
+ - scope
410
+ - name
411
+ - dids
412
+ properties:
413
+ scope:
414
+ description: The scope of the did.
415
+ type: string
416
+ name:
417
+ description: The name of the did.
418
+ type: string
419
+ dids:
420
+ description: The dids associated to the did.
421
+ type: array
422
+ items:
423
+ type: object
424
+ description: A did.
425
+ required:
426
+ - scope
427
+ - name
428
+ properties:
429
+ scope:
430
+ description: The scope of the did.
431
+ type: string
432
+ name:
433
+ description: The name of the did.
434
+ type: string
435
+ rse_id:
436
+ description: The rse id of the did.
437
+ type: string
438
+ responses:
439
+ 200:
440
+ description: OK
441
+ content:
442
+ application/json:
443
+ schema:
444
+ type: string
445
+ enum: ["Created"]
446
+ 401:
447
+ description: Invalid Auth Token
448
+ 404:
449
+ description: Not found
450
+ 406:
451
+ description: Not acceptable
452
+ """
453
+ parameters = json_parse((dict, list))
454
+ if isinstance(parameters, list):
455
+ attachments = parameters
456
+ ignore_duplicate = False
457
+ elif isinstance(parameters, dict):
458
+ attachments = param_get(parameters, 'attachments')
459
+ ignore_duplicate = param_get(parameters, 'ignore_duplicate', default=False)
460
+ else:
461
+ return generate_http_error_flask(406, exc="Invalid attachment format.")
462
+
463
+ try:
464
+ attach_dids_to_dids(attachments=attachments, ignore_duplicate=ignore_duplicate, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
465
+ except DataIdentifierNotFound as error:
466
+ return generate_http_error_flask(404, error)
467
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation, FileAlreadyExists) as error:
468
+ return generate_http_error_flask(409, error)
469
+ except AccessDenied as error:
470
+ return generate_http_error_flask(401, error)
471
+ except FileConsistencyMismatch as error:
472
+ return generate_http_error_flask(412, error)
473
+
474
+ return 'Created', 201
475
+
476
+
477
+ class DIDs(ErrorHandlingMethodView):
478
+
479
+ @check_accept_header_wrapper_flask(['application/json'])
480
+ def get(self, scope_name):
481
+ """
482
+ ---
483
+ summary: Get did
484
+ description: Get a single data identifier.
485
+ tags:
486
+ - Data identifiers
487
+ parameters:
488
+ - name: scope_name
489
+ in: path
490
+ description: The scope and the name of the did.
491
+ schema:
492
+ type: string
493
+ style: simple
494
+ - name: dynamic_depth
495
+ in: query
496
+ description: The DID type at which to stop the dynamic length/size estimation
497
+ schema:
498
+ type: string
499
+ enum: ["FILE", "DATASET"]
500
+ - name: dynamic
501
+ in: query
502
+ description: Same as dynamic_depth = "FILE"
503
+ deprecated: true
504
+ schema:
505
+ type: string
506
+ responses:
507
+ 200:
508
+ description: OK
509
+ content:
510
+ application/json:
511
+ schema:
512
+ oneOf:
513
+ - description: A single file did.
514
+ type: object
515
+ properties:
516
+ scope:
517
+ description: The scope of the did.
518
+ type: string
519
+ name:
520
+ description: The name of the did.
521
+ type: string
522
+ type:
523
+ description: The type of the string.
524
+ type: string
525
+ account:
526
+ description: The associated account.
527
+ type: string
528
+ bytes:
529
+ description: The size in bytes.
530
+ type: integer
531
+ length:
532
+ description: The number of files. Corresponses to 1.
533
+ type: integer
534
+ enum: [1]
535
+ md5:
536
+ description: md5 checksum.
537
+ type: string
538
+ adler32:
539
+ description: adler32 checksum.
540
+ type: string
541
+ - description: A single file did.
542
+ type: object
543
+ properties:
544
+ scope:
545
+ description: The scope of the did.
546
+ type: string
547
+ name:
548
+ description: The name of the did.
549
+ type: string
550
+ type:
551
+ description: The type of the string.
552
+ type: string
553
+ account:
554
+ description: The associated account.
555
+ type: string
556
+ open:
557
+ description: If the did is write open.
558
+ type: boolean
559
+ monotonic:
560
+ description: If the did is monotonic.
561
+ type: boolean
562
+ expired_at:
563
+ description: When the did expired.
564
+ type: string
565
+ length:
566
+ description: The number of associated dids.
567
+ type: number
568
+ bytes:
569
+ description: The size in bytes.
570
+ type: number
571
+ 401:
572
+ description: Invalid Auth Token
573
+ 404:
574
+ description: Scope not found
575
+ 406:
576
+ description: Not acceptable
577
+ """
578
+ try:
579
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
580
+ dynamic_depth = None
581
+ if 'dynamic_depth' in request.args:
582
+ orig = request.args['dynamic_depth'].upper()
583
+ if orig == 'DATASET':
584
+ dynamic_depth = DIDType.DATASET
585
+ elif orig == 'FILE':
586
+ dynamic_depth = DIDType.FILE
587
+ else:
588
+ dynamic_depth = None
589
+ elif 'dynamic' in request.args:
590
+ dynamic_depth = DIDType.FILE
591
+ did = get_did(scope=scope, name=name, dynamic_depth=dynamic_depth, vo=request.environ.get('vo'))
592
+ return Response(render_json(**did), content_type='application/json')
593
+ except ValueError as error:
594
+ return generate_http_error_flask(400, error)
595
+ except (ScopeNotFound, DataIdentifierNotFound) as error:
596
+ return generate_http_error_flask(404, error)
597
+
598
+ def post(self, scope_name):
599
+ """
600
+ ---
601
+ summary: Create did
602
+ description: Create a new data identifier.
603
+ tags:
604
+ - Data Identifiers
605
+ parameters:
606
+ - name: scope_name
607
+ in: path
608
+ description: The scope and the name of the did.
609
+ schema:
610
+ type: string
611
+ style: simple
612
+ requestBody:
613
+ content:
614
+ 'application/json':
615
+ schema:
616
+ type: object
617
+ required:
618
+ - type
619
+ properties:
620
+ type:
621
+ description: The type of the did.
622
+ type: string
623
+ statuses:
624
+ description: The statuses of the did.
625
+ type: string
626
+ meta:
627
+ description: The meta of the did.
628
+ type: string
629
+ rules:
630
+ description: The rules associated with the did.
631
+ type: array
632
+ items:
633
+ type: object
634
+ description: A rule.
635
+ lifetime:
636
+ description: The lifetime of the did.
637
+ type: string
638
+ dids:
639
+ description: The dids associated with the did.
640
+ type: array
641
+ items:
642
+ type: object
643
+ description: The did associated with a did.
644
+ properties:
645
+ scope:
646
+ description: The scope of the did.
647
+ type: string
648
+ name:
649
+ description: The name of the did.
650
+ type: string
651
+ rse:
652
+ description: The rse associated with the did.
653
+ type: string
654
+ responses:
655
+ 201:
656
+ description: OK
657
+ content:
658
+ application/json:
659
+ schema:
660
+ type: string
661
+ enum: ['Created']
662
+ 401:
663
+ description: Invalid Auth Token
664
+ 404:
665
+ description: Did or scope not found
666
+ 409:
667
+ description: Did already exists
668
+ """
669
+ try:
670
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
671
+ except ValueError as error:
672
+ return generate_http_error_flask(400, error)
673
+
674
+ parameters = json_parameters()
675
+ type_param = param_get(parameters, 'type')
676
+
677
+ try:
678
+ add_did(
679
+ scope=scope,
680
+ name=name,
681
+ did_type=type_param,
682
+ statuses=param_get(parameters, 'statuses', default={}),
683
+ meta=param_get(parameters, 'meta', default={}),
684
+ rules=param_get(parameters, 'rules', default=[]),
685
+ lifetime=param_get(parameters, 'lifetime', default=None),
686
+ dids=param_get(parameters, 'dids', default=[]),
687
+ rse=param_get(parameters, 'rse', default=None),
688
+ issuer=request.environ.get('issuer'),
689
+ vo=request.environ.get('vo'),
690
+ )
691
+ except (InvalidObject, InvalidPath) as error:
692
+ return generate_http_error_flask(400, error)
693
+ except (DataIdentifierNotFound, ScopeNotFound) as error:
694
+ return generate_http_error_flask(404, error)
695
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
696
+ return generate_http_error_flask(409, error)
697
+ except AccessDenied as error:
698
+ return generate_http_error_flask(401, error)
699
+ except DatabaseException as error:
700
+ if 'DELETED_DIDS_PK violated' in str(error):
701
+ return generate_http_error_flask(
702
+ status_code=406,
703
+ exc=error.__class__.__name__,
704
+ exc_msg=str('A deleted DID {} with scope {} is reused'.format(name, scope))
705
+ )
706
+ else:
707
+ return generate_http_error_flask(406, error)
708
+
709
+ return 'Created', 201
710
+
711
+ def put(self, scope_name):
712
+ """
713
+ ---
714
+ summary: Update did
715
+ description: Update a did.
716
+ tags:
717
+ - Data Identifiers
718
+ parameters:
719
+ - name: scope_name
720
+ in: path
721
+ description: The scope and the name of the did.
722
+ schema:
723
+ type: string
724
+ style: simple
725
+ requestBody:
726
+ content:
727
+ 'application/json':
728
+ schema:
729
+ type: object
730
+ properties:
731
+ open:
732
+ description: The open status
733
+ type: boolean
734
+ responses:
735
+ 200:
736
+ description: OK
737
+ 401:
738
+ description: Invalid Auth Token
739
+ 404:
740
+ description: Did not found
741
+ 409:
742
+ description: Wrong status
743
+ """
744
+ try:
745
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
746
+ except ValueError as error:
747
+ return generate_http_error_flask(400, error)
748
+
749
+ parameters = json_parameters()
750
+
751
+ try:
752
+ set_status(scope=scope, name=name, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), **parameters)
753
+ except DataIdentifierNotFound as error:
754
+ return generate_http_error_flask(404, error)
755
+ except (UnsupportedStatus, UnsupportedOperation) as error:
756
+ return generate_http_error_flask(409, error)
757
+ except AccessDenied as error:
758
+ return generate_http_error_flask(401, error)
759
+
760
+ return '', 200
761
+
762
+
763
+ class Attachment(ErrorHandlingMethodView):
764
+
765
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
766
+ def get(self, scope_name):
767
+ """
768
+ ---
769
+ summary: Get did
770
+ description: Returns the contents of a data identifier.
771
+ tags:
772
+ - Data Identifiers
773
+ parameters:
774
+ - name: scope_name
775
+ in: path
776
+ description: The scope and the name of the did.
777
+ schema:
778
+ type: string
779
+ style: simple
780
+ responses:
781
+ 200:
782
+ description: Did found
783
+ content:
784
+ application/x-json-stream:
785
+ schema:
786
+ description: The contents of a did. Items are line separated.
787
+ type: array
788
+ items:
789
+ type: object
790
+ required:
791
+ - scope
792
+ - name
793
+ - type
794
+ - bytes
795
+ - adler32
796
+ - md5
797
+ properties:
798
+ scope:
799
+ description: The scope of the did.
800
+ type: string
801
+ name:
802
+ description: The name of the did.
803
+ type: string
804
+ type:
805
+ description: The type of the did.
806
+ type: string
807
+ bytes:
808
+ description: The size of the did.
809
+ type: number
810
+ adler32:
811
+ description: The adler32 checksum of the did.
812
+ type: string
813
+ md5:
814
+ description: The md5 checksum of the did.
815
+ type: string
816
+ 401:
817
+ description: Invalid Auth Token
818
+ 404:
819
+ description: Scope not found
820
+ 406:
821
+ description: Not acceptable
822
+ """
823
+ try:
824
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
825
+
826
+ def generate(vo):
827
+ for did in list_content(scope=scope, name=name, vo=vo):
828
+ yield render_json(**did) + '\n'
829
+
830
+ return try_stream(generate(vo=request.environ.get('vo')))
831
+ except ValueError as error:
832
+ return generate_http_error_flask(400, error)
833
+ except DataIdentifierNotFound as error:
834
+ return generate_http_error_flask(404, error)
835
+
836
+ def post(self, scope_name):
837
+ """
838
+ ---
839
+ summary: Add dids to did
840
+ description: Append data identifiers to data identifiers.
841
+ tags:
842
+ - Data Identifiers
843
+ parameters:
844
+ - name: scope_name
845
+ in: path
846
+ description: The scope and the name of the did.
847
+ schema:
848
+ type: string
849
+ style: simple
850
+ requestBody:
851
+ content:
852
+ 'application/json':
853
+ schema:
854
+ type: object
855
+ required:
856
+ - dids
857
+ properties:
858
+ rse:
859
+ description: The name of the rse.
860
+ type: string
861
+ account:
862
+ description: The account which attaches the dids.
863
+ type: string
864
+ dids:
865
+ description: The dids to attach.
866
+ type: object
867
+ properties:
868
+ account:
869
+ description: The account attaching the did.
870
+ type: string
871
+ scope:
872
+ description: The scope of the did.
873
+ type: string
874
+ name:
875
+ description: The name of the did.
876
+ type: string
877
+ responses:
878
+ 201:
879
+ description: OK
880
+ content:
881
+ application/json:
882
+ schema:
883
+ type: string
884
+ enum: ["Created"]
885
+ 401:
886
+ description: Invalid Auth Token
887
+ 404:
888
+ description: Did not found
889
+ 406:
890
+ description: Not acceptable
891
+ 409:
892
+ description: Already attached
893
+ """
894
+ try:
895
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
896
+ except ValueError as error:
897
+ return generate_http_error_flask(400, error)
898
+
899
+ attachments = json_parameters()
900
+
901
+ try:
902
+ attach_dids(scope=scope, name=name, attachment=attachments, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
903
+ except InvalidPath as error:
904
+ return generate_http_error_flask(400, error)
905
+ except (DataIdentifierNotFound, RSENotFound) as error:
906
+ return generate_http_error_flask(404, error)
907
+ except (DuplicateContent, UnsupportedOperation, FileAlreadyExists) as error:
908
+ return generate_http_error_flask(409, error)
909
+ except AccessDenied as error:
910
+ return generate_http_error_flask(401, error)
911
+
912
+ return 'Created', 201
913
+
914
+ def delete(self, scope_name):
915
+ """
916
+ ---
917
+ summary: Detach dids from did
918
+ description: Detach data identifiers from data identifiers.
919
+ tags:
920
+ - Data Identifiers
921
+ parameters:
922
+ - name: scope_name
923
+ in: path
924
+ description: The scope and the name of the did.
925
+ schema:
926
+ type: string
927
+ style: simple
928
+ requestBody:
929
+ content:
930
+ 'application/json':
931
+ schema:
932
+ type: object
933
+ required:
934
+ - dids
935
+ properties:
936
+ dids:
937
+ description: The dids to detach.
938
+ type: array
939
+ items:
940
+ type: object
941
+ properties:
942
+ scope:
943
+ description: The scope of the did.
944
+ type: string
945
+ name:
946
+ description: The name of the did.
947
+ type: string
948
+ responses:
949
+ 200:
950
+ description: OK
951
+ 401:
952
+ description: Invalid Auth Token
953
+ 404:
954
+ description: Did not found
955
+ """
956
+ try:
957
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
958
+ except ValueError as error:
959
+ return generate_http_error_flask(400, error)
960
+
961
+ parameters = json_parameters()
962
+ dids = param_get(parameters, 'dids')
963
+
964
+ try:
965
+ detach_dids(scope=scope, name=name, dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
966
+ except UnsupportedOperation as error:
967
+ return generate_http_error_flask(409, error)
968
+ except DataIdentifierNotFound as error:
969
+ return generate_http_error_flask(404, error)
970
+ except AccessDenied as error:
971
+ return generate_http_error_flask(401, error)
972
+
973
+ return '', 200
974
+
975
+
976
+ class AttachmentHistory(ErrorHandlingMethodView):
977
+
978
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
979
+ def get(self, scope_name):
980
+ """
981
+ ---
982
+ summary: Get history
983
+ description: Returns the content history of a data identifier.
984
+ tags:
985
+ - Data Identifiers
986
+ parameters:
987
+ - name: scope_name
988
+ in: path
989
+ description: The scope and the name of the did.
990
+ schema:
991
+ type: string
992
+ style: simple
993
+ responses:
994
+ 200:
995
+ description: Did found
996
+ content:
997
+ application/x-json-stream:
998
+ schema:
999
+ description: The dids with their information and history. Elements are separated by new line characters.
1000
+ type: array
1001
+ items:
1002
+ type: object
1003
+ description: A single did with history data.
1004
+ properties:
1005
+ scope:
1006
+ description: The scope of the did.
1007
+ type: string
1008
+ name:
1009
+ description: The name of the did.
1010
+ type: string
1011
+ type:
1012
+ description: The type of the did.
1013
+ type: string
1014
+ bytes:
1015
+ description: The size of the did in bytes.
1016
+ type: integer
1017
+ adler32:
1018
+ description: The abler32 sha checksum.
1019
+ type: string
1020
+ md5:
1021
+ description: The md5 checksum.
1022
+ type: string
1023
+ deleted_at:
1024
+ description: The deleted_at date time.
1025
+ type: string
1026
+ created_at:
1027
+ description: The created_at date time.
1028
+ type: string
1029
+ updated_at:
1030
+ description: The last time the did was updated.
1031
+ type: string
1032
+ 401:
1033
+ description: Invalid Auth Token
1034
+ 404:
1035
+ description: Did not found
1036
+ 406:
1037
+ description: Not acceptable
1038
+ """
1039
+ try:
1040
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1041
+
1042
+ def generate(vo):
1043
+ for did in list_content_history(scope=scope, name=name, vo=vo):
1044
+ yield render_json(**did) + '\n'
1045
+
1046
+ return try_stream(generate(vo=request.environ.get('vo')))
1047
+ except ValueError as error:
1048
+ return generate_http_error_flask(400, error)
1049
+ except DataIdentifierNotFound as error:
1050
+ return generate_http_error_flask(404, error)
1051
+
1052
+
1053
+ class Files(ErrorHandlingMethodView):
1054
+
1055
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1056
+ def get(self, scope_name):
1057
+ """
1058
+ ---
1059
+ summary: Get replicas
1060
+ description: List all replicas for a did.
1061
+ tags:
1062
+ - Data Identifiers
1063
+ parameters:
1064
+ - name: scope_name
1065
+ in: path
1066
+ description: The scope and the name of the did.
1067
+ schema:
1068
+ type: string
1069
+ style: simple
1070
+ - name: long
1071
+ in: query
1072
+ description: Flag to trigger long output.
1073
+ schema:
1074
+ type: object
1075
+ required: false
1076
+ responses:
1077
+ 200:
1078
+ description: OK
1079
+ content:
1080
+ application/x-json-stream:
1081
+ schema:
1082
+ oneOf:
1083
+ - description: All replica information if `long` is defined.
1084
+ type: array
1085
+ items:
1086
+ type: object
1087
+ properties:
1088
+ scope:
1089
+ description: The scope of the did.
1090
+ type: string
1091
+ name:
1092
+ description: The name of the did.
1093
+ type: string
1094
+ bytes:
1095
+ description: The size of the did in bytes.
1096
+ type: integer
1097
+ guid:
1098
+ description: The guid of the did.
1099
+ type: string
1100
+ events:
1101
+ description: The number of events of the did.
1102
+ type: integer
1103
+ adler32:
1104
+ description: The adler32 checksum.
1105
+ type: string
1106
+ lumiblocknr:
1107
+ description: The lumi block nr. Only available if `long` is defined in the query.
1108
+ type: integer
1109
+ - description: All replica information.
1110
+ type: array
1111
+ items:
1112
+ type: object
1113
+ properties:
1114
+ scope:
1115
+ description: The scope of the did.
1116
+ type: string
1117
+ name:
1118
+ description: The name of the did.
1119
+ type: string
1120
+ bytes:
1121
+ description: The size of the did in bytes.
1122
+ type: integer
1123
+ guid:
1124
+ description: The guid of the did.
1125
+ type: string
1126
+ events:
1127
+ description: The number of events of the did.
1128
+ type: integer
1129
+ adler32:
1130
+ description: The adler32 checksum.
1131
+ type: string
1132
+ 401:
1133
+ description: Invalid Auth Token
1134
+ 404:
1135
+ description: Did not found
1136
+ 406:
1137
+ description: Not acceptable
1138
+ """
1139
+ long = 'long' in request.args
1140
+
1141
+ try:
1142
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1143
+
1144
+ def generate(vo):
1145
+ for file in list_files(scope=scope, name=name, long=long, vo=vo):
1146
+ yield dumps(file) + '\n'
1147
+
1148
+ return try_stream(generate(vo=request.environ.get('vo')))
1149
+ except ValueError as error:
1150
+ return generate_http_error_flask(400, error)
1151
+ except DataIdentifierNotFound as error:
1152
+ return generate_http_error_flask(404, error)
1153
+
1154
+
1155
+ class BulkFiles(ErrorHandlingMethodView):
1156
+
1157
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1158
+ def post(self):
1159
+ """
1160
+ ---
1161
+ summary: List files bulk
1162
+ description: List files in multiple dids
1163
+ tags:
1164
+ - Data Identifiers
1165
+ requestBody:
1166
+ content:
1167
+ application/json:
1168
+ schema:
1169
+ type: array
1170
+ items:
1171
+ description: One did to list files.
1172
+ type: object
1173
+ required:
1174
+ - scope
1175
+ - name
1176
+ properties:
1177
+ scope:
1178
+ description: The did scope.
1179
+ type: string
1180
+ name:
1181
+ description: The did name.
1182
+ type: string
1183
+ responses:
1184
+ 201:
1185
+ description: OK
1186
+ content:
1187
+ application/x-json-stream:
1188
+ schema:
1189
+ description: All collections file content.
1190
+ type: array
1191
+ items:
1192
+ description: Collections file content.
1193
+ type: object
1194
+ properties:
1195
+ parent_scope:
1196
+ description: The scope of the parent did.
1197
+ type: string
1198
+ parent_name:
1199
+ description: The name of the parent did.
1200
+ type: string
1201
+ scope:
1202
+ description: The scope of the did.
1203
+ type: string
1204
+ name:
1205
+ description: The name of the did.
1206
+ type: string
1207
+ bytes:
1208
+ description: The size of the did in bytes.
1209
+ type: integer
1210
+ guid:
1211
+ description: The guid of the did.
1212
+ type: string
1213
+ events:
1214
+ description: The number of events of the did.
1215
+ type: integer
1216
+ adler32:
1217
+ description: The adler32 checksum.
1218
+ type: string
1219
+ 401:
1220
+ description: Invalid Auth Token
1221
+ """
1222
+ parameters = json_parameters(parse_response)
1223
+ dids = param_get(parameters, 'dids', default=[])
1224
+ try:
1225
+ def generate(vo):
1226
+ for did in bulk_list_files(dids=dids, vo=vo):
1227
+ yield render_json(**did) + '\n'
1228
+
1229
+ return try_stream(generate(vo=request.environ.get('vo')))
1230
+ except AccessDenied as error:
1231
+ return generate_http_error_flask(401, error)
1232
+ return 'Created', 201
1233
+
1234
+
1235
+ class Parents(ErrorHandlingMethodView):
1236
+
1237
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1238
+ def get(self, scope_name):
1239
+ """
1240
+ ---
1241
+ summary: Get Parents
1242
+ description: Lists all parents of the did.
1243
+ tags:
1244
+ - Data Identifiers
1245
+ parameters:
1246
+ - name: scope_name
1247
+ in: path
1248
+ description: The scope and the name of the did.
1249
+ schema:
1250
+ type: string
1251
+ style: simple
1252
+ responses:
1253
+ 200:
1254
+ description: OK
1255
+ content:
1256
+ application/x-json-stream:
1257
+ schema:
1258
+ description: The parents of the did.
1259
+ type: array
1260
+ items:
1261
+ type: object
1262
+ description: A parent of the did.
1263
+ properties:
1264
+ scope:
1265
+ description: The scope of the did.
1266
+ type: string
1267
+ name:
1268
+ description: The name of the did.
1269
+ type: string
1270
+ type:
1271
+ description: The type of the did.
1272
+ type: string
1273
+ 401:
1274
+ description: Invalid Auth Token
1275
+ 404:
1276
+ description: Did not found
1277
+ 406:
1278
+ description: Not acceptable
1279
+ """
1280
+ try:
1281
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1282
+
1283
+ def generate(vo):
1284
+ for dataset in list_parent_dids(scope=scope, name=name, vo=vo):
1285
+ yield render_json(**dataset) + "\n"
1286
+
1287
+ return try_stream(generate(vo=request.environ.get('vo')))
1288
+ except ValueError as error:
1289
+ return generate_http_error_flask(400, error)
1290
+ except DataIdentifierNotFound as error:
1291
+ return generate_http_error_flask(404, error)
1292
+
1293
+
1294
+ class Meta(ErrorHandlingMethodView):
1295
+
1296
+ @check_accept_header_wrapper_flask(['application/json'])
1297
+ def get(self, scope_name):
1298
+ """
1299
+ ---
1300
+ summary: Get metadata
1301
+ description: Get the metadata of a did.
1302
+ tags:
1303
+ - Data Identifiers
1304
+ parameters:
1305
+ - name: scope_name
1306
+ in: path
1307
+ description: The scope and the name of the did.
1308
+ schema:
1309
+ type: string
1310
+ style: simple
1311
+ - name: plugin
1312
+ in: query
1313
+ description: The plugin to use.
1314
+ schema:
1315
+ type: string
1316
+ default: DID_COLUMN
1317
+ responses:
1318
+ 200:
1319
+ description: OK
1320
+ content:
1321
+ application/json:
1322
+ schema:
1323
+ description: A data identifier with all attributes.
1324
+ type: object
1325
+ 400:
1326
+ description: Bad Request - Invalid metadata plugin specified
1327
+ 401:
1328
+ description: Invalid Auth Token
1329
+ 404:
1330
+ description: Did not found
1331
+ 406:
1332
+ description: Not acceptable
1333
+ """
1334
+ try:
1335
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1336
+ except ValueError as error:
1337
+ return generate_http_error_flask(400, error)
1338
+
1339
+ try:
1340
+ plugin = request.args.get('plugin', default='DID_COLUMN')
1341
+ meta = get_metadata(scope=scope, name=name, plugin=plugin, vo=request.environ.get('vo'))
1342
+ return Response(render_json(**meta), content_type='application/json')
1343
+ except DataIdentifierNotFound as error:
1344
+ return generate_http_error_flask(404, error)
1345
+ except UnsupportedMetadataPlugin as error:
1346
+ return generate_http_error_flask(400, error)
1347
+
1348
+ def post(self, scope_name):
1349
+ """
1350
+ ---
1351
+ summary: Add metadata
1352
+ description: Add metadata to a did.
1353
+ tags:
1354
+ - Data Identifiers
1355
+ parameters:
1356
+ - name: scope_name
1357
+ in: path
1358
+ description: The scope and the name of the did.
1359
+ schema:
1360
+ type: string
1361
+ style: simple
1362
+ requestBody:
1363
+ content:
1364
+ 'application/json':
1365
+ schema:
1366
+ type: object
1367
+ required:
1368
+ - meta
1369
+ properties:
1370
+ meta:
1371
+ description: The metadata to add. A dictionary containing the metadata name as key and the value as value.
1372
+ type: object
1373
+ recursive:
1374
+ description: Flag if the metadata should be applied recirsively to children.
1375
+ type: boolean
1376
+ default: false
1377
+ responses:
1378
+ 201:
1379
+ description: Created
1380
+ content:
1381
+ application/json:
1382
+ schema:
1383
+ type: string
1384
+ enum: ["Created"]
1385
+ 401:
1386
+ description: Invalid Auth Token
1387
+ 404:
1388
+ description: Not found
1389
+ 406:
1390
+ description: Not acceptable
1391
+ """
1392
+ try:
1393
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1394
+ except ValueError as error:
1395
+ return generate_http_error_flask(400, error)
1396
+
1397
+ parameters = json_parameters()
1398
+ meta = param_get(parameters, 'meta')
1399
+
1400
+ try:
1401
+ set_metadata_bulk(
1402
+ scope=scope,
1403
+ name=name,
1404
+ meta=meta,
1405
+ issuer=request.environ.get('issuer'),
1406
+ recursive=param_get(parameters, 'recursive', default=False),
1407
+ vo=request.environ.get('vo'),
1408
+ )
1409
+ except DataIdentifierNotFound as error:
1410
+ return generate_http_error_flask(404, error)
1411
+ except Duplicate as error:
1412
+ return generate_http_error_flask(409, error)
1413
+ except (KeyNotFound, InvalidMetadata, InvalidValueForKey) as error:
1414
+ return generate_http_error_flask(400, error)
1415
+
1416
+ return "Created", 201
1417
+
1418
+ def delete(self, scope_name):
1419
+ """
1420
+ ---
1421
+ summary: Delete metadata
1422
+ description: Deletes the specified metadata from the did.
1423
+ tags:
1424
+ - Data Identifiers
1425
+ parameters:
1426
+ - name: scope_name
1427
+ in: path
1428
+ description: The scope and the name of the did.
1429
+ schema:
1430
+ type: string
1431
+ style: simple
1432
+ - name: key
1433
+ in: query
1434
+ description: The key to delete.
1435
+ schema:
1436
+ type: string
1437
+ responses:
1438
+ 200:
1439
+ description: OK
1440
+ 400:
1441
+ description: scope_name could not be parsed.
1442
+ 401:
1443
+ description: Invalid Auth Token
1444
+ 404:
1445
+ description: Did or key not found
1446
+ 406:
1447
+ description: Not acceptable
1448
+ 409:
1449
+ description: Feature is not in current database.
1450
+ """
1451
+ try:
1452
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1453
+ except ValueError as error:
1454
+ return generate_http_error_flask(400, error)
1455
+
1456
+ if 'key' in request.args:
1457
+ key = request.args['key']
1458
+ else:
1459
+ return generate_http_error_flask(404, KeyNotFound.__name__, 'No key provided to remove')
1460
+
1461
+ try:
1462
+ delete_metadata(scope=scope, name=name, key=key, vo=request.environ.get('vo'))
1463
+ except (KeyNotFound, DataIdentifierNotFound) as error:
1464
+ return generate_http_error_flask(404, error)
1465
+ except NotImplementedError as error:
1466
+ return generate_http_error_flask(409, error, 'Feature not in current database')
1467
+
1468
+ return '', 200
1469
+
1470
+
1471
+ class SingleMeta(ErrorHandlingMethodView):
1472
+ def post(self, scope_name, key):
1473
+ """
1474
+ ---
1475
+ summary: Add metadata
1476
+ description: Add metadata to a did.
1477
+ tags:
1478
+ - Data Identifiers
1479
+ parameters:
1480
+ - name: scope_name
1481
+ in: path
1482
+ description: The scope and the name of the did.
1483
+ schema:
1484
+ type: string
1485
+ style: simple
1486
+ - name: key
1487
+ in: path
1488
+ description: The key for the metadata.
1489
+ schema:
1490
+ type: string
1491
+ style: simple
1492
+ requestBody:
1493
+ content:
1494
+ 'application/json':
1495
+ schema:
1496
+ type: object
1497
+ required:
1498
+ - value
1499
+ properties:
1500
+ value:
1501
+ description: The value to set.
1502
+ type: object
1503
+ responses:
1504
+ 201:
1505
+ description: Created
1506
+ content:
1507
+ application/json:
1508
+ schema:
1509
+ type: string
1510
+ enum: ["Created"]
1511
+ 401:
1512
+ description: Invalid Auth Token
1513
+ 404:
1514
+ description: Did not found
1515
+ 406:
1516
+ description: Not acceptable
1517
+ 409:
1518
+ description: Metadata already exists
1519
+ 400:
1520
+ description: Invalid key or value
1521
+ """
1522
+ try:
1523
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1524
+ except ValueError as error:
1525
+ return generate_http_error_flask(400, error)
1526
+
1527
+ parameters = json_parameters()
1528
+ value = param_get(parameters, 'value')
1529
+
1530
+ try:
1531
+ set_metadata(
1532
+ scope=scope,
1533
+ name=name,
1534
+ key=key,
1535
+ value=value,
1536
+ issuer=request.environ.get('issuer'),
1537
+ recursive=param_get(parameters, 'recursive', default=False),
1538
+ vo=request.environ.get('vo'),
1539
+ )
1540
+ except DataIdentifierNotFound as error:
1541
+ return generate_http_error_flask(404, error)
1542
+ except Duplicate as error:
1543
+ return generate_http_error_flask(409, error)
1544
+ except (KeyNotFound, InvalidMetadata, InvalidValueForKey) as error:
1545
+ return generate_http_error_flask(400, error)
1546
+
1547
+ return 'Created', 201
1548
+
1549
+
1550
+ class BulkDIDsMeta(ErrorHandlingMethodView):
1551
+
1552
+ def post(self):
1553
+ """
1554
+ ---
1555
+ summary: Add metadata bulk
1556
+ description: Adds metadata in a bulk.
1557
+ tags:
1558
+ - Data Identifiers
1559
+ requestBody:
1560
+ content:
1561
+ 'application/json':
1562
+ schema:
1563
+ type: object
1564
+ required:
1565
+ - dids
1566
+ properties:
1567
+ dids:
1568
+ description: A list with all the dids and the metadata.
1569
+ type: array
1570
+ items:
1571
+ description: The did and associated metadata.
1572
+ type: object
1573
+ properties:
1574
+ scope:
1575
+ description: The scope of the did.
1576
+ type: string
1577
+ name:
1578
+ description: The name of the did.
1579
+ type: string
1580
+ meta:
1581
+ description: The metadata to add. A dictionary with the meta key as key and the value as value.
1582
+ type: object
1583
+ responses:
1584
+ 200:
1585
+ description: Created
1586
+ content:
1587
+ application/json:
1588
+ schema:
1589
+ type: string
1590
+ enum: ["Created"]
1591
+ 401:
1592
+ description: Invalid Auth Token
1593
+ 404:
1594
+ description: Did not found
1595
+ 406:
1596
+ description: Not acceptable
1597
+ 409:
1598
+ description: Unsupported Operation
1599
+ """
1600
+ parameters = json_parameters()
1601
+ dids = param_get(parameters, 'dids')
1602
+
1603
+ try:
1604
+ set_dids_metadata_bulk(dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
1605
+ except DataIdentifierNotFound as error:
1606
+ return generate_http_error_flask(404, error)
1607
+ except UnsupportedOperation as error:
1608
+ return generate_http_error_flask(409, error)
1609
+ except AccessDenied as error:
1610
+ return generate_http_error_flask(401, error)
1611
+
1612
+ return 'Created', 201
1613
+
1614
+
1615
+ class Rules(ErrorHandlingMethodView):
1616
+
1617
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1618
+ def get(self, scope_name):
1619
+ """
1620
+ ---
1621
+ summary: Get rules
1622
+ description: Lists all rules of a given did.
1623
+ tags:
1624
+ - Data Identifiers
1625
+ parameters:
1626
+ - name: scope_name
1627
+ in: path
1628
+ description: The scope and the name of the did.
1629
+ schema:
1630
+ type: string
1631
+ style: simple
1632
+ responses:
1633
+ 200:
1634
+ description: The rules associated with a did.
1635
+ content:
1636
+ application/x-json-stream:
1637
+ schema:
1638
+ description: The rules associated with a did.
1639
+ type: array
1640
+ items:
1641
+ description: A rule.
1642
+ type: object
1643
+ 401:
1644
+ description: Invalid Auth Token
1645
+ 404:
1646
+ description: Did or rule not found
1647
+ 406:
1648
+ description: Not acceptable
1649
+ """
1650
+ try:
1651
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1652
+
1653
+ def generate(vo):
1654
+ get_did(scope=scope, name=name, vo=vo)
1655
+ for rule in list_replication_rules({'scope': scope, 'name': name}, vo=vo):
1656
+ yield dumps(rule, cls=APIEncoder) + '\n'
1657
+
1658
+ return try_stream(generate(vo=request.environ.get('vo')))
1659
+ except ValueError as error:
1660
+ return generate_http_error_flask(400, error)
1661
+ except RuleNotFound as error:
1662
+ return generate_http_error_flask(404, error)
1663
+ except DataIdentifierNotFound as error:
1664
+ return generate_http_error_flask(404, error)
1665
+
1666
+
1667
+ class BulkMeta(ErrorHandlingMethodView):
1668
+
1669
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1670
+ def post(self):
1671
+ """
1672
+ ---
1673
+ summary: Get metadata bulk
1674
+ description: List all metadata of a list of data identifiers.
1675
+ tags:
1676
+ - Data Identifiers
1677
+ requestBody:
1678
+ content:
1679
+ 'application/x-json-stream':
1680
+ schema:
1681
+ type: object
1682
+ required:
1683
+ - dids
1684
+ properties:
1685
+ dids:
1686
+ description: The dids.
1687
+ type: array
1688
+ items:
1689
+ description: A did.
1690
+ type: object
1691
+ properties:
1692
+ name:
1693
+ description: The name of the did.
1694
+ type: string
1695
+ scope:
1696
+ description: The scope of the did.
1697
+ type: string
1698
+ inherit:
1699
+ description: Concatenated the metadata of the parent if set to true.
1700
+ type: boolean
1701
+ default: false
1702
+ responses:
1703
+ 200:
1704
+ description: OK
1705
+ content:
1706
+ application/json:
1707
+ schema:
1708
+ description: A list of metadata identifiers for the dids. Separated by new lines.
1709
+ type: array
1710
+ items:
1711
+ description: The metadata for one did.
1712
+ type: object
1713
+ 400:
1714
+ description: Cannot decode json parameter list
1715
+ 401:
1716
+ description: Invalid Auth Token
1717
+ 404:
1718
+ description: Did not found
1719
+ 406:
1720
+ description: Not acceptable
1721
+ """
1722
+ parameters = json_parameters()
1723
+ dids = param_get(parameters, 'dids')
1724
+ inherit = param_get(parameters, 'inherit', default=False)
1725
+
1726
+ try:
1727
+ def generate(vo):
1728
+ for meta in get_metadata_bulk(dids, inherit=inherit, vo=vo):
1729
+ yield render_json(**meta) + '\n'
1730
+
1731
+ return try_stream(generate(vo=request.environ.get('vo')))
1732
+ except ValueError as error:
1733
+ return generate_http_error_flask(400, error, 'Cannot decode json parameter list')
1734
+ except DataIdentifierNotFound as error:
1735
+ return generate_http_error_flask(404, error)
1736
+
1737
+
1738
+ class AssociatedRules(ErrorHandlingMethodView):
1739
+
1740
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1741
+ def get(self, scope_name):
1742
+ """
1743
+ ---
1744
+ summary: Get associated rules
1745
+ description: Gets all associated rules for a file.
1746
+ tags:
1747
+ - Data Identifiers
1748
+ parameters:
1749
+ - name: scope_name
1750
+ in: path
1751
+ description: The scope and the name of the did.
1752
+ schema:
1753
+ type: string
1754
+ style: simple
1755
+ responses:
1756
+ 200:
1757
+ description: OK
1758
+ content:
1759
+ application/x-json-stream:
1760
+ schema:
1761
+ description: All associated rules for a file. Items are separated by new line character.
1762
+ type: array
1763
+ items:
1764
+ description: A replication rule associated with the file. Has more fields than listed here.
1765
+ type: object
1766
+ properties:
1767
+ id:
1768
+ description: The id of the rule.
1769
+ type: string
1770
+ subscription_id:
1771
+ description: The subscription id of the rule.
1772
+ type: string
1773
+ account:
1774
+ description: The account associated with the rule.
1775
+ type: string
1776
+ scope:
1777
+ description: The scope associated with the rule.
1778
+ type: string
1779
+ name:
1780
+ description: The name of the rule.
1781
+ type: string
1782
+ state:
1783
+ description: The state of the rule.
1784
+ type: string
1785
+ rse_expression:
1786
+ description: The rse expression of the rule.
1787
+ type: string
1788
+ 401:
1789
+ description: Invalid Auth Token
1790
+ 404:
1791
+ description: Did not found
1792
+ 406:
1793
+ description: Not acceptable
1794
+ """
1795
+ try:
1796
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
1797
+
1798
+ def generate(vo):
1799
+ for rule in list_associated_replication_rules_for_file(scope=scope, name=name, vo=vo):
1800
+ yield dumps(rule, cls=APIEncoder) + '\n'
1801
+
1802
+ return try_stream(generate(vo=request.environ.get('vo')))
1803
+ except ValueError as error:
1804
+ return generate_http_error_flask(400, error)
1805
+ except DataIdentifierNotFound as error:
1806
+ return generate_http_error_flask(404, error)
1807
+
1808
+
1809
+ class GUIDLookup(ErrorHandlingMethodView):
1810
+
1811
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
1812
+ def get(self, guid):
1813
+ """
1814
+ ---
1815
+ summary: Get dataset
1816
+ description: Returns the dataset associated with a GUID.
1817
+ tags:
1818
+ - Data Identifiers
1819
+ parameters:
1820
+ - name: guid
1821
+ in: path
1822
+ description: The GUID to query buy.
1823
+ schema:
1824
+ type: string
1825
+ style: simple
1826
+ responses:
1827
+ 200:
1828
+ description: OK
1829
+ content:
1830
+ application/x-json-stream:
1831
+ schema:
1832
+ description: A list of all datasets associated with the guid. Items are separated by new line character.
1833
+ type: array
1834
+ items:
1835
+ description: A dataset associated with a guid.
1836
+ type: object
1837
+ properties:
1838
+ scope:
1839
+ description: The scope of the dataset.
1840
+ type: string
1841
+ name:
1842
+ description: The name of the dataset.
1843
+ type: string
1844
+ 401:
1845
+ description: Invalid Auth Token
1846
+ 404:
1847
+ description: Did not found
1848
+ 406:
1849
+ description: Not acceptable
1850
+ """
1851
+ try:
1852
+ def generate(vo):
1853
+ for dataset in get_dataset_by_guid(guid, vo=vo):
1854
+ yield dumps(dataset, cls=APIEncoder) + '\n'
1855
+
1856
+ return try_stream(generate(vo=request.environ.get('vo')))
1857
+ except DataIdentifierNotFound as error:
1858
+ return generate_http_error_flask(404, error)
1859
+
1860
+
1861
+ class SampleLegacy(ErrorHandlingMethodView):
1862
+
1863
+ def post(self, input_scope, input_name, output_scope, output_name, nbfiles):
1864
+ """
1865
+ ---
1866
+ summary: Create sample
1867
+ description: Creates a sample from an input collection.
1868
+ tags:
1869
+ - Data Identifiers
1870
+ parameters:
1871
+ - name: input_scope
1872
+ in: path
1873
+ description: The input scope.
1874
+ schema:
1875
+ type: string
1876
+ style: simple
1877
+ - name: input_name
1878
+ in: path
1879
+ description: The input name.
1880
+ schema:
1881
+ type: string
1882
+ style: simple
1883
+ - name: output_scope
1884
+ in: path
1885
+ description: The output scope.
1886
+ schema:
1887
+ type: string
1888
+ style: simple
1889
+ - name: output_name
1890
+ in: path
1891
+ description: The output name.
1892
+ schema:
1893
+ type: string
1894
+ style: simple
1895
+ - name: nbfiles
1896
+ in: path
1897
+ description: The number of files to register in the output dataset.
1898
+ schema:
1899
+ type: string
1900
+ style: simple
1901
+ responses:
1902
+ 201:
1903
+ description: OK
1904
+ content:
1905
+ application/json:
1906
+ schema:
1907
+ type: string
1908
+ enum: ["Created"]
1909
+ 401:
1910
+ description: Invalid Auth Token
1911
+ 404:
1912
+ description: Not found
1913
+ 406:
1914
+ description: Not acceptable
1915
+ 409:
1916
+ description: Duplication
1917
+ """
1918
+ try:
1919
+ create_did_sample(
1920
+ input_scope=input_scope,
1921
+ input_name=input_name,
1922
+ output_scope=output_scope,
1923
+ output_name=output_name,
1924
+ issuer=request.environ.get('issuer'),
1925
+ nbfiles=nbfiles,
1926
+ vo=request.environ.get('vo'),
1927
+ )
1928
+ except DataIdentifierNotFound as error:
1929
+ return generate_http_error_flask(404, error)
1930
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
1931
+ return generate_http_error_flask(409, error)
1932
+ except AccessDenied as error:
1933
+ return generate_http_error_flask(401, error)
1934
+
1935
+ return 'Created', 201
1936
+
1937
+
1938
+ class Sample(ErrorHandlingMethodView):
1939
+
1940
+ def post(self):
1941
+ """
1942
+ ---
1943
+ summary: Create sample
1944
+ description: Creates a sample from an input collection.
1945
+ tags:
1946
+ - Data Identifiers
1947
+ requestBody:
1948
+ description: Parameters (source and destination) for the files in the sample to be created
1949
+ content:
1950
+ 'application/json':
1951
+ schema:
1952
+ type: object
1953
+ required:
1954
+ - input_scope
1955
+ - input_name
1956
+ - output_scope
1957
+ - output_name
1958
+ - nbfiles
1959
+ properties:
1960
+ input_scope:
1961
+ description: The input scope.
1962
+ type: string
1963
+ input_name:
1964
+ description: The input name.
1965
+ type: string
1966
+ output_scope:
1967
+ description: The output scope.
1968
+ type: string
1969
+ output_name:
1970
+ description: The output name.
1971
+ type: string
1972
+ nbfiles:
1973
+ description: The number of files to register in the output dataset.
1974
+ type: string
1975
+ responses:
1976
+ 201:
1977
+ description: OK
1978
+ content:
1979
+ application/json:
1980
+ schema:
1981
+ type: string
1982
+ enum: ["Created"]
1983
+ 401:
1984
+ description: Invalid Auth Token
1985
+ 404:
1986
+ description: Not found
1987
+ 406:
1988
+ description: Not acceptable
1989
+ 409:
1990
+ description: Duplication
1991
+ """
1992
+ parameters = json_parameters()
1993
+ try:
1994
+ create_did_sample(
1995
+ input_scope=parameters['input_scope'],
1996
+ input_name=parameters['input_name'],
1997
+ output_scope=parameters['output_scope'],
1998
+ output_name=parameters['output_name'],
1999
+ issuer=request.environ.get('issuer'),
2000
+ nbfiles=parameters['nbfiles'],
2001
+ vo=request.environ.get('vo'),
2002
+ )
2003
+ except DataIdentifierNotFound as error:
2004
+ return generate_http_error_flask(404, error)
2005
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
2006
+ return generate_http_error_flask(409, error)
2007
+ except AccessDenied as error:
2008
+ return generate_http_error_flask(401, error)
2009
+
2010
+ return 'Created', 201
2011
+
2012
+
2013
+ class NewDIDs(ErrorHandlingMethodView):
2014
+
2015
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
2016
+ def get(self):
2017
+ """
2018
+ ---
2019
+ summary: Get recent identifiers
2020
+ description: Returns a list of recent identifiers.
2021
+ tags:
2022
+ - Data Identifiers
2023
+ parameters:
2024
+ - name: type
2025
+ in: query
2026
+ description: The type of the did.
2027
+ schema:
2028
+ type: string
2029
+ required: false
2030
+ responses:
2031
+ 200:
2032
+ description: OK
2033
+ content:
2034
+ application/x-json-stream:
2035
+ schema:
2036
+ description: A list of the recent dids. Items are separated by new line characters.
2037
+ type: array
2038
+ items:
2039
+ description: A did.
2040
+ type: object
2041
+ properties:
2042
+ scope:
2043
+ description: The scope of the did.
2044
+ type: string
2045
+ name:
2046
+ description: The name of the did.
2047
+ type: string
2048
+ did_type:
2049
+ description: The type of the did.
2050
+ type: string
2051
+ 401:
2052
+ description: Invalid Auth Token
2053
+ 406:
2054
+ description: Not acceptable
2055
+ """
2056
+ def generate(_type, vo):
2057
+ for did in list_new_dids(did_type=_type, vo=vo):
2058
+ yield dumps(did, cls=APIEncoder) + '\n'
2059
+
2060
+ type_param = request.args.get('type', default=None)
2061
+
2062
+ return try_stream(generate(_type=type_param, vo=request.environ.get('vo')))
2063
+
2064
+
2065
+ class Resurrect(ErrorHandlingMethodView):
2066
+
2067
+ def post(self):
2068
+ """
2069
+ ---
2070
+ summary: Resurrect dids
2071
+ description: Resurrect all given dids.
2072
+ tags:
2073
+ - Data Identifiers
2074
+ requestBody:
2075
+ content:
2076
+ 'application/json':
2077
+ schema:
2078
+ description: List of did to resurrect.
2079
+ type: array
2080
+ items:
2081
+ description: A did to resurrect.
2082
+ type: object
2083
+ properties:
2084
+ scope:
2085
+ description: The scope of the did.
2086
+ type: string
2087
+ name:
2088
+ description: The name of the did
2089
+ type: string
2090
+ responses:
2091
+ 201:
2092
+ description: OK
2093
+ content:
2094
+ application/json:
2095
+ schema:
2096
+ type: string
2097
+ enum: ["Created"]
2098
+ 401:
2099
+ description: Invalid Auth Token
2100
+ 404:
2101
+ description: Not found
2102
+ 409:
2103
+ description: Conflict
2104
+ 500:
2105
+ description: Internal error
2106
+ """
2107
+ dids = json_list()
2108
+
2109
+ try:
2110
+ resurrect(dids=dids, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
2111
+ except DataIdentifierNotFound as error:
2112
+ return generate_http_error_flask(404, error)
2113
+ except (DuplicateContent, DataIdentifierAlreadyExists, UnsupportedOperation) as error:
2114
+ return generate_http_error_flask(409, error)
2115
+ except AccessDenied as error:
2116
+ return generate_http_error_flask(401, error)
2117
+ return 'Created', 201
2118
+
2119
+
2120
+ class Follow(ErrorHandlingMethodView):
2121
+
2122
+ @check_accept_header_wrapper_flask(['application/json'])
2123
+ def get(self, scope_name):
2124
+ """
2125
+ ---
2126
+ summary: Get followers
2127
+ description: Get all followers for a specific did.
2128
+ tags:
2129
+ - Data Identifiers
2130
+ parameters:
2131
+ - name: scope_name
2132
+ in: path
2133
+ description: The scope and the name of the did.
2134
+ schema:
2135
+ type: string
2136
+ style: simple
2137
+ responses:
2138
+ 200:
2139
+ description: OK
2140
+ content:
2141
+ application/json:
2142
+ schema:
2143
+ description: A list of all followers of a did.
2144
+ type: array
2145
+ items:
2146
+ description: A follower of a did.
2147
+ type: object
2148
+ properties:
2149
+ user:
2150
+ description: The user which follows the did.
2151
+ type: string
2152
+ 400:
2153
+ description: Value error
2154
+ 401:
2155
+ description: Invalid Auth Token
2156
+ 404:
2157
+ description: Did not found
2158
+ 406:
2159
+ description: Not acceptable
2160
+ """
2161
+ try:
2162
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
2163
+
2164
+ def generate(vo):
2165
+ for user in get_users_following_did(scope=scope, name=name, vo=vo):
2166
+ yield render_json(**user) + '\n'
2167
+
2168
+ return try_stream(generate(vo=request.environ.get('vo')), content_type='application/json')
2169
+ except ValueError as error:
2170
+ return generate_http_error_flask(400, error)
2171
+ except DataIdentifierNotFound as error:
2172
+ return generate_http_error_flask(404, error)
2173
+
2174
+ def post(self, scope_name):
2175
+ """
2176
+ ---
2177
+ summary: Post follow
2178
+ description: Mark the input DID as being followed by the given account.
2179
+ tags:
2180
+ - Data Identifiers
2181
+ parameters:
2182
+ - name: scope_name
2183
+ in: path
2184
+ description: The scope and the name of the did.
2185
+ schema:
2186
+ type: string
2187
+ style: simple
2188
+ requestBody:
2189
+ content:
2190
+ application/json:
2191
+ schema:
2192
+ type: object
2193
+ required:
2194
+ - account
2195
+ properties:
2196
+ account:
2197
+ description: The account to follow the did.
2198
+ type: string
2199
+ responses:
2200
+ 201:
2201
+ description: OK
2202
+ 400:
2203
+ description: Scope or name could not be interpreted
2204
+ 401:
2205
+ description: Invalid Auth Token
2206
+ 404:
2207
+ description: Not found
2208
+ 500:
2209
+ description: Internal error
2210
+ """
2211
+ try:
2212
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
2213
+ except ValueError as error:
2214
+ return generate_http_error_flask(400, error)
2215
+
2216
+ parameters = json_parameters()
2217
+ account = param_get(parameters, 'account')
2218
+
2219
+ try:
2220
+ add_did_to_followed(scope=scope, name=name, account=account, vo=request.environ.get('vo'))
2221
+ except DataIdentifierNotFound as error:
2222
+ return generate_http_error_flask(404, error)
2223
+ except AccessDenied as error:
2224
+ return generate_http_error_flask(401, error)
2225
+
2226
+ def delete(self, scope_name):
2227
+ """
2228
+ ---
2229
+ summary: Delete follow
2230
+ description: Mark the input DID as not followed
2231
+ tags:
2232
+ - Data Identifiers
2233
+ parameters:
2234
+ - name: scope_name
2235
+ in: path
2236
+ description: The scope and the name of the did.
2237
+ schema:
2238
+ type: string
2239
+ style: simple
2240
+ requestBody:
2241
+ content:
2242
+ 'application/json':
2243
+ schema:
2244
+ type: object
2245
+ required:
2246
+ - account
2247
+ properties:
2248
+ account:
2249
+ description: The account to unfollow the did.
2250
+ type: string
2251
+ responses:
2252
+ 200:
2253
+ description: OK
2254
+ 401:
2255
+ description: Invalid Auth Token
2256
+ 404:
2257
+ description: Not found
2258
+ 500:
2259
+ description: Internal error
2260
+ """
2261
+ try:
2262
+ scope, name = parse_scope_name(scope_name, request.environ.get('vo'))
2263
+ except ValueError as error:
2264
+ return generate_http_error_flask(400, error)
2265
+
2266
+ parameters = json_parameters()
2267
+ account = param_get(parameters, 'account')
2268
+
2269
+ try:
2270
+ remove_did_from_followed(scope=scope, name=name, account=account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
2271
+ except DataIdentifierNotFound as error:
2272
+ return generate_http_error_flask(404, error)
2273
+
2274
+ return '', 200
2275
+
2276
+
2277
+ def blueprint():
2278
+ bp = AuthenticatedBlueprint('dids', __name__, url_prefix='/dids')
2279
+
2280
+ scope_view = Scope.as_view('scope')
2281
+ bp.add_url_rule('/<scope>/', view_func=scope_view, methods=['get', ])
2282
+ guid_lookup_view = GUIDLookup.as_view('guid_lookup')
2283
+ bp.add_url_rule('/<guid>/guid', view_func=guid_lookup_view, methods=['get', ])
2284
+ search_view = Search.as_view('search')
2285
+ bp.add_url_rule('/<scope>/dids/search', view_func=search_view, methods=['get', ])
2286
+ dids_view = DIDs.as_view('dids')
2287
+ bp.add_url_rule('/<path:scope_name>/status', view_func=dids_view, methods=['put', 'get'])
2288
+ files_view = Files.as_view('files')
2289
+ bp.add_url_rule('/<path:scope_name>/files', view_func=files_view, methods=['get', ])
2290
+ attachment_history_view = AttachmentHistory.as_view('attachment_history')
2291
+ bp.add_url_rule('/<path:scope_name>/dids/history', view_func=attachment_history_view, methods=['get', ])
2292
+ attachment_view = Attachment.as_view('attachment')
2293
+ bp.add_url_rule('/<path:scope_name>/dids', view_func=attachment_view, methods=['get', 'post', 'delete'])
2294
+ meta_view = Meta.as_view('meta')
2295
+ bp.add_url_rule('/<path:scope_name>/meta', view_func=meta_view, methods=['get', 'post', 'delete'])
2296
+ singlemeta_view = SingleMeta.as_view('singlemeta')
2297
+ bp.add_url_rule('/<path:scope_name>/meta/<key>', view_func=singlemeta_view, methods=['post', ])
2298
+ bulkdidsmeta_view = BulkDIDsMeta.as_view('bulkdidsmeta')
2299
+ bp.add_url_rule('/bulkdidsmeta', view_func=bulkdidsmeta_view, methods=['post', ])
2300
+ rules_view = Rules.as_view('rules')
2301
+ bp.add_url_rule('/<path:scope_name>/rules', view_func=rules_view, methods=['get', ])
2302
+ parents_view = Parents.as_view('parents')
2303
+ bp.add_url_rule('/<path:scope_name>/parents', view_func=parents_view, methods=['get', ])
2304
+ associated_rules_view = AssociatedRules.as_view('associated_rules')
2305
+ bp.add_url_rule('/<path:scope_name>/associated_rules', view_func=associated_rules_view, methods=['get', ])
2306
+ follow_view = Follow.as_view('follow')
2307
+ bp.add_url_rule('/<path:scope_name>/follow', view_func=follow_view, methods=['get', 'post', 'delete'])
2308
+ bp.add_url_rule('/<path:scope_name>', view_func=dids_view, methods=['get', 'post'])
2309
+ bulkdids_view = BulkDIDS.as_view('bulkdids')
2310
+ bp.add_url_rule('', view_func=bulkdids_view, methods=['post', ])
2311
+ sample_view_legacy = SampleLegacy.as_view('sample')
2312
+ bp.add_url_rule('/<input_scope>/<input_name>/<output_scope>/<output_name>/<nbfiles>/sample', view_func=sample_view_legacy, methods=['post', ])
2313
+ sample_view = Sample.as_view('sample_new')
2314
+ bp.add_url_rule('/sample', view_func=sample_view, methods=['post', ])
2315
+ attachements_view = Attachments.as_view('attachments')
2316
+ bp.add_url_rule('/attachments', view_func=attachements_view, methods=['post', ])
2317
+ new_dids_view = NewDIDs.as_view('new_dids')
2318
+ bp.add_url_rule('/new', view_func=new_dids_view, methods=['get', ])
2319
+ resurrect_view = Resurrect.as_view('resurrect')
2320
+ bp.add_url_rule('/resurrect', view_func=resurrect_view, methods=['post', ])
2321
+ bulkmeta_view = BulkMeta.as_view('bulkmeta')
2322
+ bp.add_url_rule('/bulkmeta', view_func=bulkmeta_view, methods=['post', ])
2323
+ files_view = BulkFiles.as_view('bulkfiles')
2324
+ bp.add_url_rule('/bulkfiles', view_func=files_view, methods=['post', ])
2325
+
2326
+ bp.after_request(response_headers)
2327
+ return bp
2328
+
2329
+
2330
+ def make_doc():
2331
+ """ Only used for sphinx documentation """
2332
+ doc_app = Flask(__name__)
2333
+ doc_app.register_blueprint(blueprint())
2334
+ return doc_app