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,1103 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from datetime import datetime
16
+ from json import dumps
17
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
18
+
19
+ from flask import Flask, Response, jsonify, redirect, request
20
+
21
+ from rucio.common.exception import AccessDenied, AccountNotFound, CounterNotFound, Duplicate, IdentityError, InvalidObject, RSENotFound, RuleNotFound, ScopeNotFound
22
+ from rucio.common.utils import APIEncoder, render_json
23
+ from rucio.gateway.account import add_account, add_account_attribute, del_account, del_account_attribute, get_account_info, get_usage_history, list_account_attributes, list_accounts, list_identities, update_account
24
+ from rucio.gateway.account_limit import get_global_account_limit, get_global_account_limits, get_global_account_usage, get_local_account_limit, get_local_account_limits, get_local_account_usage
25
+ from rucio.gateway.identity import add_account_identity, del_account_identity
26
+ from rucio.gateway.rule import list_replication_rules
27
+ from rucio.gateway.scope import add_scope, get_scopes
28
+ from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
29
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, param_get, response_headers, try_stream
30
+
31
+ if TYPE_CHECKING:
32
+ from collections.abc import Iterator
33
+
34
+ from flask.typing import ResponseReturnValue
35
+
36
+
37
+ class Attributes(ErrorHandlingMethodView):
38
+
39
+ @check_accept_header_wrapper_flask(['application/json'])
40
+ def get(self, account: str) -> Response:
41
+ """
42
+ ---
43
+ summary: List attributes
44
+ description: List all attributes for an account.
45
+ tags:
46
+ - Account
47
+ parameters:
48
+ - name: account
49
+ in: path
50
+ description: The account identifier.
51
+ schema:
52
+ type: string
53
+ style: simple
54
+ responses:
55
+ 200:
56
+ description: OK
57
+ content:
58
+ application/json:
59
+ schema:
60
+ type: array
61
+ items:
62
+ type: object
63
+ description: An account attribute.
64
+ properties:
65
+ key:
66
+ description: The key of the account attribute.
67
+ type: string
68
+ value:
69
+ description: The value of the account attribute.
70
+ type: string
71
+ 401:
72
+ description: Invalid Auth Token
73
+ 404:
74
+ description: No account found for the given id.
75
+ 406:
76
+ description: Not acceptable.
77
+ """
78
+ try:
79
+ attribs = list_account_attributes(account, vo=request.environ.get('vo'))
80
+ except AccountNotFound as error:
81
+ return generate_http_error_flask(404, error)
82
+
83
+ return jsonify(attribs)
84
+
85
+ def post(self, account: str, key: str) -> 'ResponseReturnValue':
86
+ """
87
+ ---
88
+ summary: Create attribute
89
+ description: Create an attribute to an account.
90
+ tags:
91
+ - Account
92
+ parameters:
93
+ - name: account
94
+ in: path
95
+ description: The account identifier.
96
+ schema:
97
+ type: string
98
+ style: simple
99
+ - name: key
100
+ in: path
101
+ description: The key of the account attribute.
102
+ schema:
103
+ type: string
104
+ style: simple
105
+ requestBody:
106
+ content:
107
+ 'application/json':
108
+ schema:
109
+ type: object
110
+ required:
111
+ - value
112
+ properties:
113
+ key:
114
+ description: The key of the attribute. This would override the key defined in path.
115
+ type: string
116
+ value:
117
+ description: The value of the attribute.
118
+ type: string
119
+ responses:
120
+ 201:
121
+ description: OK
122
+ content:
123
+ application/json:
124
+ schema:
125
+ type: string
126
+ enum: ["Created"]
127
+ 401:
128
+ description: Invalid Auth Token
129
+ 404:
130
+ description: No account found for the given id.
131
+ 409:
132
+ description: Attribute already exists
133
+ """
134
+ parameters = json_parameters()
135
+ value = param_get(parameters, 'value')
136
+ try:
137
+ add_account_attribute(key=key, value=value, account=account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
138
+ except AccessDenied as error:
139
+ return generate_http_error_flask(401, error)
140
+ except Duplicate as error:
141
+ return generate_http_error_flask(409, error)
142
+ except AccountNotFound as error:
143
+ return generate_http_error_flask(404, error)
144
+
145
+ return 'Created', 201
146
+
147
+ def delete(self, account: str, key: str) -> Union[Response, tuple[Literal[''], Literal[200]]]:
148
+ """
149
+ ---
150
+ summary: Delete attribute
151
+ description: Delete an attribute of an account.
152
+ tags:
153
+ - Account
154
+ parameters:
155
+ - name: account
156
+ in: path
157
+ description: The account identifier.
158
+ schema:
159
+ type: string
160
+ style: simple
161
+ - name: key
162
+ in: path
163
+ description: The key of the account attribute to remove.
164
+ schema:
165
+ type: string
166
+ style: simple
167
+ responses:
168
+ 200:
169
+ description: OK
170
+ 401:
171
+ description: Invalid Auth Token
172
+ 404:
173
+ description: No account found for the given id.
174
+ """
175
+ try:
176
+ del_account_attribute(account=account, key=key, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
177
+ except AccessDenied as error:
178
+ return generate_http_error_flask(401, error)
179
+ except AccountNotFound as error:
180
+ return generate_http_error_flask(404, error)
181
+
182
+ return '', 200
183
+
184
+
185
+ class Scopes(ErrorHandlingMethodView):
186
+ @check_accept_header_wrapper_flask(['application/json'])
187
+ def get(self, account: str) -> Response:
188
+ """
189
+ ---
190
+ summary: List scopes
191
+ description: List all scopse for an account.
192
+ tags:
193
+ - Account
194
+ parameters:
195
+ - name: account
196
+ in: path
197
+ description: The account identifier.
198
+ schema:
199
+ type: string
200
+ style: simple
201
+ responses:
202
+ 200:
203
+ description: OK
204
+ content:
205
+ application/x-json-stream:
206
+ schema:
207
+ description: All scopes for the account.
208
+ type: array
209
+ items:
210
+ description: A scope
211
+ type: string
212
+ 401:
213
+ description: Invalid Auth Token
214
+ 404:
215
+ description: No account or scope found for the given id.
216
+ 406:
217
+ description: Not acceptable
218
+ """
219
+ try:
220
+ scopes = get_scopes(account, vo=request.environ.get('vo'))
221
+ except AccountNotFound as error:
222
+ return generate_http_error_flask(404, error)
223
+
224
+ if not len(scopes):
225
+ return generate_http_error_flask(404, ScopeNotFound.__name__, f"no scopes found for account ID '{account}'")
226
+
227
+ return jsonify(scopes)
228
+
229
+ def post(self, account: str, scope: str) -> 'ResponseReturnValue':
230
+ """
231
+ ---
232
+ summary: Create scope
233
+ description: Creates a scopse with the given name for an account.
234
+ tags:
235
+ - Account
236
+ parameters:
237
+ - name: account
238
+ in: path
239
+ description: The account identifier.
240
+ schema:
241
+ type: string
242
+ style: simple
243
+ - name: scope
244
+ in: path
245
+ description: The scope name.
246
+ schema:
247
+ type: string
248
+ style: simple
249
+ responses:
250
+ 201:
251
+ description: OK
252
+ content:
253
+ application/json:
254
+ schema:
255
+ type: string
256
+ enum: ["Created"]
257
+ 400:
258
+ description: Not acceptable
259
+ 401:
260
+ description: Invalid Auth Token
261
+ 404:
262
+ description: No account found.
263
+ 409:
264
+ description: Scope already exists.
265
+ """
266
+ try:
267
+ add_scope(scope, account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
268
+ except InvalidObject as error:
269
+ return generate_http_error_flask(400, error)
270
+ except AccessDenied as error:
271
+ return generate_http_error_flask(401, error)
272
+ except Duplicate as error:
273
+ return generate_http_error_flask(409, error)
274
+ except AccountNotFound as error:
275
+ return generate_http_error_flask(404, error)
276
+
277
+ return 'Created', 201
278
+
279
+
280
+ class AccountParameter(ErrorHandlingMethodView):
281
+ """ create, update, get and disable rucio accounts. """
282
+
283
+ @check_accept_header_wrapper_flask(['application/json'])
284
+ def get(self, account: str) -> "ResponseReturnValue":
285
+ """
286
+ ---
287
+ summary: List account parameters
288
+ description: Lists all parameters for an account.
289
+ tags:
290
+ - Account
291
+ parameters:
292
+ - name: account
293
+ in: path
294
+ description: The account identifier.
295
+ schema:
296
+ type: string
297
+ style: simple
298
+ responses:
299
+ 201:
300
+ description: OK
301
+ content:
302
+ application/json:
303
+ schema:
304
+ type: object
305
+ properties:
306
+ account:
307
+ description: The account identifier.
308
+ type: string
309
+ account_type:
310
+ description: The account type.
311
+ type: string
312
+ status:
313
+ description: The account status.
314
+ type: string
315
+ email:
316
+ description: The email for the account.
317
+ type: string
318
+ suspended_at:
319
+ description: Datetime if the account was suspended.
320
+ type: string
321
+ deleted_at:
322
+ description: Datetime if the account was deleted.
323
+ type: string
324
+ 401:
325
+ description: Invalid Auth Token
326
+ 404:
327
+ description: No account found.
328
+ 406:
329
+ description: Not acceptable
330
+ """
331
+ if account == 'whoami':
332
+ # Redirect to the account uri
333
+ frontend = request.headers.get('X-Requested-Host', default=None)
334
+ if frontend:
335
+ return redirect(f'{frontend}/accounts/{request.environ.get("issuer")}', code=302)
336
+ return redirect(request.environ.get('issuer'), code=303) # type: ignore (request.environ.get('issuer') might be None)
337
+
338
+ try:
339
+ acc = get_account_info(account, vo=request.environ.get('vo'))
340
+ except AccountNotFound as error:
341
+ return generate_http_error_flask(404, error)
342
+ except AccessDenied as error:
343
+ return generate_http_error_flask(401, error)
344
+
345
+ accdict = acc.to_dict()
346
+
347
+ for key, value in accdict.items():
348
+ if isinstance(value, datetime):
349
+ accdict[key] = value.strftime('%Y-%m-%dT%H:%M:%S')
350
+
351
+ return Response(render_json(**accdict), content_type="application/json")
352
+
353
+ def put(self, account: str) -> Union[Response, tuple[Literal[''], Literal[200]]]:
354
+ """
355
+ ---
356
+ summary: Update
357
+ description: Update a parameter for an account.
358
+ tags:
359
+ - Account
360
+ parameters:
361
+ - name: account
362
+ in: path
363
+ description: The account identifier.
364
+ schema:
365
+ type: string
366
+ style: simple
367
+ requestBody:
368
+ content:
369
+ 'application/json':
370
+ schema:
371
+ description: Json object with key-value pairs corresponding to the new values of the parameters.
372
+ type: object
373
+ responses:
374
+ 200:
375
+ description: OK
376
+ 401:
377
+ description: Invalid Auth Token
378
+ 404:
379
+ description: No account found.
380
+ 400:
381
+ description: Unknown status
382
+ """
383
+ parameters = json_parameters()
384
+ for key, value in parameters.items():
385
+ try:
386
+ update_account(account, key=key, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
387
+ except ValueError:
388
+ return generate_http_error_flask(400, ValueError.__name__, f'Unknown value {value}')
389
+ except AccessDenied as error:
390
+ return generate_http_error_flask(401, error)
391
+ except AccountNotFound as error:
392
+ return generate_http_error_flask(404, error)
393
+
394
+ return '', 200
395
+
396
+ def post(self, account: str) -> 'ResponseReturnValue':
397
+ """
398
+ ---
399
+ summary: Create
400
+ description: Create an account.
401
+ tags:
402
+ - Account
403
+ parameters:
404
+ - name: account
405
+ in: path
406
+ description: The account identifier.
407
+ schema:
408
+ type: string
409
+ style: simple
410
+ requestBody:
411
+ content:
412
+ 'application/json':
413
+ schema:
414
+ type: object
415
+ required:
416
+ - type
417
+ - email
418
+ properties:
419
+ type:
420
+ description: The account type.
421
+ type: string
422
+ enum: ["USER", "GROUP", "SERVICE"]
423
+ email:
424
+ description: The email for the account.
425
+ type: string
426
+ responses:
427
+ 201:
428
+ description: OK
429
+ content:
430
+ application/json:
431
+ schema:
432
+ type: string
433
+ enum: ["Created"]
434
+ 401:
435
+ description: Invalid Auth Token
436
+ 409:
437
+ description: Account already exists
438
+ 400:
439
+ description: Unknown status
440
+ """
441
+ parameters = json_parameters()
442
+ type_param = param_get(parameters, 'type')
443
+ email = param_get(parameters, 'email')
444
+ try:
445
+ add_account(account, type_param, email, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
446
+ except Duplicate as error:
447
+ return generate_http_error_flask(409, error)
448
+ except AccessDenied as error:
449
+ return generate_http_error_flask(401, error)
450
+ except InvalidObject as error:
451
+ return generate_http_error_flask(400, error)
452
+
453
+ return 'Created', 201
454
+
455
+ def delete(self, account: str) -> Union[Response, tuple[Literal[''], Literal[200]]]:
456
+ """
457
+ ---
458
+ summary: Delete
459
+ description: Delete an account.
460
+ tags:
461
+ - Account
462
+ parameters:
463
+ - name: account
464
+ in: path
465
+ description: The account identifier.
466
+ schema:
467
+ type: string
468
+ style: simple
469
+ responses:
470
+ 201:
471
+ description: OK
472
+ 401:
473
+ description: Invalid Auth Token
474
+ 404:
475
+ description: Account not found
476
+ """
477
+ try:
478
+ del_account(account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
479
+ except AccessDenied as error:
480
+ return generate_http_error_flask(401, error)
481
+ except AccountNotFound as error:
482
+ return generate_http_error_flask(404, error)
483
+
484
+ return '', 200
485
+
486
+
487
+ class Account(ErrorHandlingMethodView):
488
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
489
+ def get(self) -> Response:
490
+ """
491
+ ---
492
+ summary: List
493
+ description: List all accounts.
494
+ tags:
495
+ - Account
496
+ responses:
497
+ 200:
498
+ description: OK
499
+ content:
500
+ application/x-json-stream:
501
+ schema:
502
+ type: array
503
+ items:
504
+ type: object
505
+ properties:
506
+ account:
507
+ description: The account identifier.
508
+ type: string
509
+ type:
510
+ description: The type.
511
+ type: string
512
+ email:
513
+ description: The email.
514
+ type: string
515
+ 401:
516
+ description: Invalid Auth Token
517
+ """
518
+
519
+ def generate(_filter: dict[str, Any], vo: str) -> "Iterator[str]":
520
+ for account in list_accounts(filter_=_filter, vo=vo):
521
+ yield render_json(**account) + "\n"
522
+
523
+ return try_stream(generate(_filter=dict(request.args.items(multi=False)), vo=request.environ.get('vo', 'def')))
524
+
525
+
526
+ class LocalAccountLimits(ErrorHandlingMethodView):
527
+ @check_accept_header_wrapper_flask(['application/json'])
528
+ def get(self, account: str, rse: Optional[str] = None) -> Response:
529
+ """
530
+ ---
531
+ summary: Get local limit
532
+ description: Get the current local limits for an account on a specific RSE.
533
+ tags:
534
+ - Account
535
+ parameters:
536
+ - name: account
537
+ in: path
538
+ description: The account identifier.
539
+ schema:
540
+ type: string
541
+ style: simple
542
+ - name: rse
543
+ in: path
544
+ description: The rse identifier.
545
+ schema:
546
+ type: string
547
+ style: simple
548
+ responses:
549
+ 200:
550
+ description: OK
551
+ content:
552
+ application/json:
553
+ schema:
554
+ description: Json object with rse identifiers as keys and account limits in bytes as values.
555
+ type: object
556
+ 401:
557
+ description: Invalid Auth Token
558
+ 404:
559
+ description: RSE not found
560
+ 406:
561
+ description: Not Acceptable
562
+ """
563
+ try:
564
+ if rse:
565
+ limits = get_local_account_limit(account=account, rse=rse, vo=request.environ.get('vo'))
566
+ else:
567
+ limits = get_local_account_limits(account=account, vo=request.environ.get('vo'))
568
+ except RSENotFound as error:
569
+ return generate_http_error_flask(404, error)
570
+
571
+ return Response(render_json(**limits), content_type="application/json")
572
+
573
+
574
+ class GlobalAccountLimits(ErrorHandlingMethodView):
575
+ @check_accept_header_wrapper_flask(['application/json'])
576
+ def get(self, account: str, rse_expression: Optional[str] = None) -> Response:
577
+ """
578
+ ---
579
+ summary: Get global limit
580
+ description: Get the current global limits for an account on a specific RSE expression.
581
+ tags:
582
+ - Account
583
+ parameters:
584
+ - name: account
585
+ in: path
586
+ description: The account identifier.
587
+ schema:
588
+ type: string
589
+ style: simple
590
+ - name: rse_expression
591
+ in: path
592
+ description: The rse identifier.
593
+ schema:
594
+ type: string
595
+ style: simple
596
+ responses:
597
+ 200:
598
+ description: OK
599
+ content:
600
+ application/json:
601
+ schema:
602
+ description: Json object with rse expression as keys and limits in bytes as values.
603
+ type: object
604
+ 401:
605
+ description: Invalid Auth Token
606
+ 404:
607
+ description: RSE not found
608
+ 406:
609
+ description: Not Acceptable
610
+ """
611
+ try:
612
+ if rse_expression:
613
+ limits = get_global_account_limit(account=account, rse_expression=rse_expression, vo=request.environ.get('vo'))
614
+ else:
615
+ limits = get_global_account_limits(account=account, vo=request.environ.get('vo'))
616
+ except RSENotFound as error:
617
+ return generate_http_error_flask(404, error)
618
+
619
+ return Response(render_json(**limits), content_type="application/json")
620
+
621
+
622
+ class Identities(ErrorHandlingMethodView):
623
+ def post(self, account: str) -> 'ResponseReturnValue':
624
+ """
625
+ ---
626
+ summary: Create identity
627
+ description: Grant an account identity access to an account.
628
+ tags:
629
+ - Account
630
+ parameters:
631
+ - name: account
632
+ in: path
633
+ description: The account identifier.
634
+ schema:
635
+ type: string
636
+ style: simple
637
+ requestBody:
638
+ content:
639
+ 'application/json':
640
+ schema:
641
+ type: object
642
+ required:
643
+ - identity
644
+ - authtype
645
+ - email
646
+ properties:
647
+ identity:
648
+ description: The identity.
649
+ type: string
650
+ authtype:
651
+ description: The authtype.
652
+ type: string
653
+ email:
654
+ description: The email.
655
+ type: string
656
+ password:
657
+ description: The password.
658
+ type: string
659
+ default: none
660
+ default:
661
+ description: Should this be the default account?
662
+ type: string
663
+ default: false
664
+ responses:
665
+ 201:
666
+ description: OK
667
+ content:
668
+ application/json:
669
+ schema:
670
+ type: string
671
+ enum: ["Created"]
672
+ 401:
673
+ description: Invalid Auth Token
674
+ 404:
675
+ description: Account not found
676
+ 409:
677
+ description: Already exists
678
+ 400:
679
+ description: Parameter missing
680
+ """
681
+ parameters = json_parameters()
682
+ identity = param_get(parameters, 'identity')
683
+ authtype = param_get(parameters, 'authtype')
684
+ email = param_get(parameters, 'email')
685
+
686
+ issuer = request.environ.get('issuer')
687
+ vo = request.environ.get('vo')
688
+
689
+ if not issuer or not vo:
690
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
691
+
692
+ try:
693
+ add_account_identity(
694
+ identity_key=identity,
695
+ id_type=authtype,
696
+ account=account,
697
+ email=email,
698
+ password=param_get(parameters, 'password', default=None),
699
+ issuer=issuer,
700
+ default=param_get(parameters, 'default', default=False),
701
+ vo=vo,
702
+ )
703
+ except AccessDenied as error:
704
+ return generate_http_error_flask(401, error)
705
+ except Duplicate as error:
706
+ return generate_http_error_flask(409, error)
707
+ except AccountNotFound as error:
708
+ return generate_http_error_flask(404, error)
709
+ except IdentityError as error:
710
+ return generate_http_error_flask(400, error)
711
+
712
+ return 'Created', 201
713
+
714
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
715
+ def get(self, account: str) -> Response:
716
+ """
717
+ ---
718
+ summary: List identities
719
+ description: Lists all identities for an account.
720
+ tags:
721
+ - Account
722
+ parameters:
723
+ - name: account
724
+ in: path
725
+ description: The account identifier.
726
+ schema:
727
+ type: string
728
+ style: simple
729
+ responses:
730
+ 200:
731
+ description: OK
732
+ content:
733
+ application/x-json-stream:
734
+ schema:
735
+ type: array
736
+ items:
737
+ type: array
738
+ minItems: 2
739
+ maxItems: 2
740
+ items:
741
+ type: string
742
+ 401:
743
+ description: Invalid Auth Token
744
+ 404:
745
+ description: Account not found
746
+ 406:
747
+ description: Not acceptable
748
+ """
749
+ try:
750
+ def generate(vo: str) -> "Iterator[str]":
751
+ for identity in list_identities(account, vo=vo):
752
+ yield render_json(**identity) + "\n"
753
+
754
+ return try_stream(generate(request.environ.get('vo', 'def')))
755
+ except AccountNotFound as error:
756
+ return generate_http_error_flask(404, error)
757
+
758
+ def delete(self, account: str) -> 'ResponseReturnValue':
759
+ """
760
+ ---
761
+ summary: Delete identity
762
+ description: Delete an account identity.
763
+ tags:
764
+ - Account
765
+ parameters:
766
+ - name: account
767
+ in: path
768
+ description: The account identifier.
769
+ schema:
770
+ type: string
771
+ style: simple
772
+ requestBody:
773
+ content:
774
+ 'application/json':
775
+ schema:
776
+ type: object
777
+ required:
778
+ - identity
779
+ - authtype
780
+ properties:
781
+ identity:
782
+ description: The identity.
783
+ type: string
784
+ authtype:
785
+ description: The authtype.
786
+ type: string
787
+ responses:
788
+ 200:
789
+ description: OK
790
+ 401:
791
+ description: Invalid Auth Token
792
+ 404:
793
+ description: Account or identity not found
794
+ """
795
+ parameters = json_parameters()
796
+ identity = param_get(parameters, 'identity')
797
+ authtype = param_get(parameters, 'authtype')
798
+
799
+ issuer = request.environ.get('issuer')
800
+ vo = request.environ.get('vo')
801
+
802
+ if not issuer or not vo:
803
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
804
+
805
+ try:
806
+ del_account_identity(identity, authtype, account, issuer, vo=vo)
807
+ except AccessDenied as error:
808
+ return generate_http_error_flask(401, error)
809
+ except (AccountNotFound, IdentityError) as error:
810
+ return generate_http_error_flask(404, error)
811
+
812
+ return '', 200
813
+
814
+
815
+ class Rules(ErrorHandlingMethodView):
816
+
817
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
818
+ def get(self, account: str) -> Response:
819
+ """
820
+ ---
821
+ summary: List rules
822
+ description: Lists all rules for an account.
823
+ tags:
824
+ - Account
825
+ parameters:
826
+ - name: account
827
+ in: path
828
+ description: The account identifier.
829
+ schema:
830
+ type: string
831
+ style: simple
832
+ responses:
833
+ 200:
834
+ description: OK
835
+ content:
836
+ application/x-json-stream:
837
+ schema:
838
+ type: array
839
+ items:
840
+ type: string
841
+ 401:
842
+ description: Invalid Auth Token
843
+ 404:
844
+ description: Account or rule not found
845
+ 406:
846
+ description: Not acceptable
847
+ """
848
+ filters = {'account': account}
849
+ filters.update(request.args)
850
+ try:
851
+ def generate(vo: str) -> "Iterator[str]":
852
+ for rule in list_replication_rules(filters=filters, vo=vo):
853
+ yield dumps(rule, cls=APIEncoder) + '\n'
854
+
855
+ return try_stream(generate(vo=request.environ.get('vo', 'def')))
856
+ except RuleNotFound as error:
857
+ return generate_http_error_flask(404, error)
858
+
859
+
860
+ class UsageHistory(ErrorHandlingMethodView):
861
+
862
+ @check_accept_header_wrapper_flask(['application/json'])
863
+ def get(self, account: str, rse: str) -> Response:
864
+ """
865
+ ---
866
+ summary: Get account usage history
867
+ description: Returns the account usage history.
868
+ tags:
869
+ - Account
870
+ parameters:
871
+ - name: account
872
+ in: path
873
+ description: The account identifier.
874
+ schema:
875
+ type: string
876
+ style: simple
877
+ - name: rse
878
+ in: path
879
+ description: The rse identifier.
880
+ schema:
881
+ type: string
882
+ style: simple
883
+ responses:
884
+ 200:
885
+ description: OK
886
+ content:
887
+ application/json:
888
+ schema:
889
+ type: array
890
+ items:
891
+ type: object
892
+ properties:
893
+ bytes:
894
+ description: The number of bytes used.
895
+ type: integer
896
+ files:
897
+ description: The files.
898
+ type: string
899
+ updated_at:
900
+ description: When the data was provided.
901
+ type: string
902
+ 401:
903
+ description: Invalid Auth Token
904
+ 404:
905
+ description: Account not found
906
+ 406:
907
+ description: Not acceptable
908
+ """
909
+ try:
910
+ usage = get_usage_history(account=account, rse=rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
911
+ except (AccountNotFound, CounterNotFound) as error:
912
+ return generate_http_error_flask(404, error)
913
+ except AccessDenied as error:
914
+ return generate_http_error_flask(401, error)
915
+
916
+ for entry in usage:
917
+ for key, value in entry.items():
918
+ if isinstance(value, datetime):
919
+ entry[key] = value.strftime('%Y-%m-%dT%H:%M:%S')
920
+
921
+ return jsonify(usage)
922
+
923
+
924
+ class LocalUsage(ErrorHandlingMethodView):
925
+
926
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
927
+ def get(self, account: str, rse: Optional[str] = None) -> Response:
928
+ """
929
+ ---
930
+ summary: Get local account usage
931
+ description: Returns the local account usage.
932
+ tags:
933
+ - Account
934
+ parameters:
935
+ - name: account
936
+ in: path
937
+ description: The account identifier.
938
+ schema:
939
+ type: string
940
+ style: simple
941
+ - name: rse
942
+ in: path
943
+ description: The rse identifier.
944
+ schema:
945
+ type: string
946
+ style: simple
947
+ responses:
948
+ 200:
949
+ description: OK
950
+ content:
951
+ application/x-json-stream:
952
+ schema:
953
+ type: array
954
+ items:
955
+ type: object
956
+ properties:
957
+ rse_id:
958
+ description: The rse id.
959
+ type: string
960
+ bytes:
961
+ description: The number of bytes used.
962
+ type: integer
963
+ bytes_limit:
964
+ description: The maximum number of bytes.
965
+ type: integer
966
+ bytes_remaining:
967
+ description: The remaining number of bytes.
968
+ type: integer
969
+ 401:
970
+ description: Invalid Auth Token
971
+ 404:
972
+ description: Account or rse not found
973
+ 406:
974
+ description: Not acceptable
975
+ """
976
+ try:
977
+ def generate(issuer: str, vo: str) -> "Iterator[str]":
978
+ for usage in get_local_account_usage(account=account, rse=rse, issuer=issuer, vo=vo):
979
+ yield dumps(usage, cls=APIEncoder) + '\n'
980
+
981
+ return try_stream(generate(issuer=request.environ.get('issuer'), vo=request.environ.get('vo', 'def'))) # type: ignore (request.environ.get('issuer') could be None)
982
+ except (AccountNotFound, RSENotFound) as error:
983
+ return generate_http_error_flask(404, error)
984
+ except AccessDenied as error:
985
+ return generate_http_error_flask(401, error)
986
+
987
+
988
+ class GlobalUsage(ErrorHandlingMethodView):
989
+
990
+ @check_accept_header_wrapper_flask(['application/x-json-stream'])
991
+ def get(self, account: str, rse_expression: Optional[str] = None) -> Response:
992
+ """
993
+ ---
994
+ summary: Get local account usage
995
+ description: Returns the local account usage.
996
+ tags:
997
+ - Account
998
+ parameters:
999
+ - name: account
1000
+ in: path
1001
+ description: The account identifier.
1002
+ schema:
1003
+ type: string
1004
+ style: simple
1005
+ - name: rse_expression
1006
+ in: path
1007
+ description: The rse expression.
1008
+ schema:
1009
+ type: string
1010
+ style: simple
1011
+ responses:
1012
+ 200:
1013
+ description: OK
1014
+ content:
1015
+ application/x-json-stream:
1016
+ schema:
1017
+ type: array
1018
+ items:
1019
+ type: object
1020
+ properties:
1021
+ rse_expression:
1022
+ description: The rse expression.
1023
+ type: string
1024
+ bytes:
1025
+ description: The number of bytes used.
1026
+ type: integer
1027
+ bytes_limit:
1028
+ description: The maximum number of bytes.
1029
+ type: integer
1030
+ bytes_remaining:
1031
+ description: The remaining number of bytes.
1032
+ type: integer
1033
+ 401:
1034
+ description: Invalid Auth Token
1035
+ 404:
1036
+ description: Account or rse not found
1037
+ 406:
1038
+ description: Not acceptable
1039
+ """
1040
+ try:
1041
+ def generate(vo: str, issuer: str) -> "Iterator[str]":
1042
+ for usage in get_global_account_usage(account=account, rse_expression=rse_expression, issuer=issuer, vo=vo):
1043
+ yield dumps(usage, cls=APIEncoder) + '\n'
1044
+
1045
+ return try_stream(generate(vo=request.environ.get('vo', 'def'), issuer=request.environ.get('issuer'))) # type: ignore (request.environ.get('issuer') could be None)
1046
+ except (AccountNotFound, RSENotFound) as error:
1047
+ return generate_http_error_flask(404, error)
1048
+ except AccessDenied as error:
1049
+ return generate_http_error_flask(401, error)
1050
+
1051
+
1052
+ def blueprint(with_doc: bool = False) -> AuthenticatedBlueprint:
1053
+ bp = AuthenticatedBlueprint('accounts', __name__, url_prefix='/accounts')
1054
+
1055
+ attributes_view = Attributes.as_view('attributes')
1056
+ bp.add_url_rule('/<account>/attr/', view_func=attributes_view, methods=['get', ])
1057
+ bp.add_url_rule('/<account>/attr/<key>', view_func=attributes_view, methods=['post', 'delete'])
1058
+ scopes_view = Scopes.as_view('scopes')
1059
+ bp.add_url_rule('/<account>/scopes/', view_func=scopes_view, methods=['get', ])
1060
+ bp.add_url_rule('/<account>/scopes/<scope>', view_func=scopes_view, methods=['post', ])
1061
+ local_account_limits_view = LocalAccountLimits.as_view('local_account_limit')
1062
+ bp.add_url_rule('/<account>/limits/local', view_func=local_account_limits_view, methods=['get', ])
1063
+ bp.add_url_rule('/<account>/limits', view_func=local_account_limits_view, methods=['get', ])
1064
+ bp.add_url_rule('/<account>/limits/local/<rse>', view_func=local_account_limits_view, methods=['get', ])
1065
+ bp.add_url_rule('/<account>/limits/<rse>', view_func=local_account_limits_view, methods=['get', ])
1066
+ global_account_limits_view = GlobalAccountLimits.as_view('global_account_limit')
1067
+ bp.add_url_rule('/<account>/limits/global', view_func=global_account_limits_view, methods=['get', ])
1068
+ bp.add_url_rule('/<account>/limits/global/<rse_expression>', view_func=global_account_limits_view, methods=['get', ])
1069
+ identities_view = Identities.as_view('identities')
1070
+ bp.add_url_rule('/<account>/identities', view_func=identities_view, methods=['get', 'post', 'delete'])
1071
+ rules_view = Rules.as_view('rules')
1072
+ bp.add_url_rule('/<account>/rules', view_func=rules_view, methods=['get', ])
1073
+ usagehistory_view = UsageHistory.as_view('usagehistory')
1074
+ bp.add_url_rule('/<account>/usage/history/<rse>', view_func=usagehistory_view, methods=['get', ])
1075
+ usage_view = LocalUsage.as_view('usage')
1076
+ bp.add_url_rule('/<account>/usage/local', view_func=usage_view, methods=['get', ])
1077
+ bp.add_url_rule('/<account>/usage', view_func=usage_view, methods=['get', ])
1078
+ if not with_doc:
1079
+ # for backwards-compatibility
1080
+ # rule without trailing slash needs to be added before rule with trailing slash
1081
+ bp.add_url_rule('/<account>/usage/', view_func=usage_view, methods=['get', ])
1082
+ bp.add_url_rule('/<account>/usage/local/<rse>', view_func=usage_view, methods=['get', ])
1083
+ bp.add_url_rule('/<account>/usage/<rse>', view_func=usage_view, methods=['get', ])
1084
+ global_usage_view = GlobalUsage.as_view('global_usage')
1085
+ bp.add_url_rule('/<account>/usage/global', view_func=global_usage_view, methods=['get', ])
1086
+ bp.add_url_rule('/<account>/usage/global/<rse_expression>', view_func=global_usage_view, methods=['get', ])
1087
+ account_parameter_view = AccountParameter.as_view('account_parameter')
1088
+ bp.add_url_rule('/<account>', view_func=account_parameter_view, methods=['get', 'put', 'post', 'delete'])
1089
+ account_view = Account.as_view('account')
1090
+ if not with_doc:
1091
+ # rule without trailing slash needs to be added before rule with trailing slash
1092
+ bp.add_url_rule('', view_func=account_view, methods=['get', ])
1093
+ bp.add_url_rule('/', view_func=account_view, methods=['get', ])
1094
+
1095
+ bp.after_request(response_headers)
1096
+ return bp
1097
+
1098
+
1099
+ def make_doc() -> Flask:
1100
+ """ Only used for sphinx documentation """
1101
+ doc_app = Flask(__name__)
1102
+ doc_app.register_blueprint(blueprint(with_doc=True))
1103
+ return doc_app