rucio 37.0.0rc1__py3-none-any.whl

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

Potentially problematic release.


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

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