rucio 32.8.6__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (481) hide show
  1. rucio/__init__.py +18 -0
  2. rucio/alembicrevision.py +16 -0
  3. rucio/api/__init__.py +14 -0
  4. rucio/api/account.py +266 -0
  5. rucio/api/account_limit.py +287 -0
  6. rucio/api/authentication.py +302 -0
  7. rucio/api/config.py +218 -0
  8. rucio/api/credential.py +60 -0
  9. rucio/api/did.py +726 -0
  10. rucio/api/dirac.py +71 -0
  11. rucio/api/exporter.py +60 -0
  12. rucio/api/heartbeat.py +62 -0
  13. rucio/api/identity.py +160 -0
  14. rucio/api/importer.py +46 -0
  15. rucio/api/lifetime_exception.py +95 -0
  16. rucio/api/lock.py +131 -0
  17. rucio/api/meta.py +85 -0
  18. rucio/api/permission.py +72 -0
  19. rucio/api/quarantined_replica.py +69 -0
  20. rucio/api/replica.py +528 -0
  21. rucio/api/request.py +220 -0
  22. rucio/api/rse.py +601 -0
  23. rucio/api/rule.py +335 -0
  24. rucio/api/scope.py +89 -0
  25. rucio/api/subscription.py +255 -0
  26. rucio/api/temporary_did.py +49 -0
  27. rucio/api/vo.py +112 -0
  28. rucio/client/__init__.py +16 -0
  29. rucio/client/accountclient.py +413 -0
  30. rucio/client/accountlimitclient.py +155 -0
  31. rucio/client/baseclient.py +929 -0
  32. rucio/client/client.py +77 -0
  33. rucio/client/configclient.py +113 -0
  34. rucio/client/credentialclient.py +54 -0
  35. rucio/client/didclient.py +691 -0
  36. rucio/client/diracclient.py +48 -0
  37. rucio/client/downloadclient.py +1674 -0
  38. rucio/client/exportclient.py +44 -0
  39. rucio/client/fileclient.py +51 -0
  40. rucio/client/importclient.py +42 -0
  41. rucio/client/lifetimeclient.py +74 -0
  42. rucio/client/lockclient.py +99 -0
  43. rucio/client/metaclient.py +137 -0
  44. rucio/client/pingclient.py +45 -0
  45. rucio/client/replicaclient.py +444 -0
  46. rucio/client/requestclient.py +109 -0
  47. rucio/client/rseclient.py +664 -0
  48. rucio/client/ruleclient.py +287 -0
  49. rucio/client/scopeclient.py +88 -0
  50. rucio/client/subscriptionclient.py +161 -0
  51. rucio/client/touchclient.py +78 -0
  52. rucio/client/uploadclient.py +871 -0
  53. rucio/common/__init__.py +14 -0
  54. rucio/common/cache.py +74 -0
  55. rucio/common/config.py +796 -0
  56. rucio/common/constants.py +92 -0
  57. rucio/common/constraints.py +18 -0
  58. rucio/common/didtype.py +187 -0
  59. rucio/common/dumper/__init__.py +306 -0
  60. rucio/common/dumper/consistency.py +449 -0
  61. rucio/common/dumper/data_models.py +325 -0
  62. rucio/common/dumper/path_parsing.py +65 -0
  63. rucio/common/exception.py +1092 -0
  64. rucio/common/extra.py +37 -0
  65. rucio/common/logging.py +404 -0
  66. rucio/common/pcache.py +1387 -0
  67. rucio/common/policy.py +84 -0
  68. rucio/common/schema/__init__.py +143 -0
  69. rucio/common/schema/atlas.py +411 -0
  70. rucio/common/schema/belleii.py +406 -0
  71. rucio/common/schema/cms.py +478 -0
  72. rucio/common/schema/domatpc.py +399 -0
  73. rucio/common/schema/escape.py +424 -0
  74. rucio/common/schema/generic.py +431 -0
  75. rucio/common/schema/generic_multi_vo.py +410 -0
  76. rucio/common/schema/icecube.py +404 -0
  77. rucio/common/schema/lsst.py +423 -0
  78. rucio/common/stomp_utils.py +160 -0
  79. rucio/common/stopwatch.py +56 -0
  80. rucio/common/test_rucio_server.py +148 -0
  81. rucio/common/types.py +158 -0
  82. rucio/common/utils.py +1946 -0
  83. rucio/core/__init__.py +14 -0
  84. rucio/core/account.py +426 -0
  85. rucio/core/account_counter.py +171 -0
  86. rucio/core/account_limit.py +357 -0
  87. rucio/core/authentication.py +563 -0
  88. rucio/core/config.py +386 -0
  89. rucio/core/credential.py +218 -0
  90. rucio/core/did.py +3102 -0
  91. rucio/core/did_meta_plugins/__init__.py +250 -0
  92. rucio/core/did_meta_plugins/did_column_meta.py +326 -0
  93. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +116 -0
  94. rucio/core/did_meta_plugins/filter_engine.py +573 -0
  95. rucio/core/did_meta_plugins/json_meta.py +215 -0
  96. rucio/core/did_meta_plugins/mongo_meta.py +199 -0
  97. rucio/core/did_meta_plugins/postgres_meta.py +317 -0
  98. rucio/core/dirac.py +208 -0
  99. rucio/core/distance.py +164 -0
  100. rucio/core/exporter.py +59 -0
  101. rucio/core/heartbeat.py +263 -0
  102. rucio/core/identity.py +290 -0
  103. rucio/core/importer.py +248 -0
  104. rucio/core/lifetime_exception.py +377 -0
  105. rucio/core/lock.py +474 -0
  106. rucio/core/message.py +241 -0
  107. rucio/core/meta.py +190 -0
  108. rucio/core/monitor.py +441 -0
  109. rucio/core/naming_convention.py +154 -0
  110. rucio/core/nongrid_trace.py +124 -0
  111. rucio/core/oidc.py +1339 -0
  112. rucio/core/permission/__init__.py +107 -0
  113. rucio/core/permission/atlas.py +1333 -0
  114. rucio/core/permission/belleii.py +1076 -0
  115. rucio/core/permission/cms.py +1166 -0
  116. rucio/core/permission/escape.py +1076 -0
  117. rucio/core/permission/generic.py +1128 -0
  118. rucio/core/permission/generic_multi_vo.py +1148 -0
  119. rucio/core/quarantined_replica.py +190 -0
  120. rucio/core/replica.py +3627 -0
  121. rucio/core/replica_sorter.py +368 -0
  122. rucio/core/request.py +2241 -0
  123. rucio/core/rse.py +1835 -0
  124. rucio/core/rse_counter.py +155 -0
  125. rucio/core/rse_expression_parser.py +460 -0
  126. rucio/core/rse_selector.py +277 -0
  127. rucio/core/rule.py +3419 -0
  128. rucio/core/rule_grouping.py +1473 -0
  129. rucio/core/scope.py +152 -0
  130. rucio/core/subscription.py +316 -0
  131. rucio/core/temporary_did.py +188 -0
  132. rucio/core/topology.py +448 -0
  133. rucio/core/trace.py +361 -0
  134. rucio/core/transfer.py +1233 -0
  135. rucio/core/vo.py +151 -0
  136. rucio/core/volatile_replica.py +123 -0
  137. rucio/daemons/__init__.py +14 -0
  138. rucio/daemons/abacus/__init__.py +14 -0
  139. rucio/daemons/abacus/account.py +106 -0
  140. rucio/daemons/abacus/collection_replica.py +113 -0
  141. rucio/daemons/abacus/rse.py +107 -0
  142. rucio/daemons/atropos/__init__.py +14 -0
  143. rucio/daemons/atropos/atropos.py +243 -0
  144. rucio/daemons/auditor/__init__.py +261 -0
  145. rucio/daemons/auditor/hdfs.py +86 -0
  146. rucio/daemons/auditor/srmdumps.py +284 -0
  147. rucio/daemons/automatix/__init__.py +14 -0
  148. rucio/daemons/automatix/automatix.py +281 -0
  149. rucio/daemons/badreplicas/__init__.py +14 -0
  150. rucio/daemons/badreplicas/minos.py +311 -0
  151. rucio/daemons/badreplicas/minos_temporary_expiration.py +173 -0
  152. rucio/daemons/badreplicas/necromancer.py +200 -0
  153. rucio/daemons/bb8/__init__.py +14 -0
  154. rucio/daemons/bb8/bb8.py +356 -0
  155. rucio/daemons/bb8/common.py +762 -0
  156. rucio/daemons/bb8/nuclei_background_rebalance.py +147 -0
  157. rucio/daemons/bb8/t2_background_rebalance.py +146 -0
  158. rucio/daemons/c3po/__init__.py +14 -0
  159. rucio/daemons/c3po/algorithms/__init__.py +14 -0
  160. rucio/daemons/c3po/algorithms/simple.py +131 -0
  161. rucio/daemons/c3po/algorithms/t2_free_space.py +125 -0
  162. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +127 -0
  163. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +279 -0
  164. rucio/daemons/c3po/c3po.py +342 -0
  165. rucio/daemons/c3po/collectors/__init__.py +14 -0
  166. rucio/daemons/c3po/collectors/agis.py +108 -0
  167. rucio/daemons/c3po/collectors/free_space.py +62 -0
  168. rucio/daemons/c3po/collectors/jedi_did.py +48 -0
  169. rucio/daemons/c3po/collectors/mock_did.py +46 -0
  170. rucio/daemons/c3po/collectors/network_metrics.py +63 -0
  171. rucio/daemons/c3po/collectors/workload.py +110 -0
  172. rucio/daemons/c3po/utils/__init__.py +14 -0
  173. rucio/daemons/c3po/utils/dataset_cache.py +40 -0
  174. rucio/daemons/c3po/utils/expiring_dataset_cache.py +45 -0
  175. rucio/daemons/c3po/utils/expiring_list.py +63 -0
  176. rucio/daemons/c3po/utils/popularity.py +82 -0
  177. rucio/daemons/c3po/utils/timeseries.py +76 -0
  178. rucio/daemons/cache/__init__.py +14 -0
  179. rucio/daemons/cache/consumer.py +191 -0
  180. rucio/daemons/common.py +391 -0
  181. rucio/daemons/conveyor/__init__.py +14 -0
  182. rucio/daemons/conveyor/common.py +530 -0
  183. rucio/daemons/conveyor/finisher.py +492 -0
  184. rucio/daemons/conveyor/poller.py +372 -0
  185. rucio/daemons/conveyor/preparer.py +198 -0
  186. rucio/daemons/conveyor/receiver.py +206 -0
  187. rucio/daemons/conveyor/stager.py +127 -0
  188. rucio/daemons/conveyor/submitter.py +379 -0
  189. rucio/daemons/conveyor/throttler.py +468 -0
  190. rucio/daemons/follower/__init__.py +14 -0
  191. rucio/daemons/follower/follower.py +97 -0
  192. rucio/daemons/hermes/__init__.py +14 -0
  193. rucio/daemons/hermes/hermes.py +738 -0
  194. rucio/daemons/judge/__init__.py +14 -0
  195. rucio/daemons/judge/cleaner.py +149 -0
  196. rucio/daemons/judge/evaluator.py +172 -0
  197. rucio/daemons/judge/injector.py +154 -0
  198. rucio/daemons/judge/repairer.py +144 -0
  199. rucio/daemons/oauthmanager/__init__.py +14 -0
  200. rucio/daemons/oauthmanager/oauthmanager.py +199 -0
  201. rucio/daemons/reaper/__init__.py +14 -0
  202. rucio/daemons/reaper/dark_reaper.py +272 -0
  203. rucio/daemons/reaper/light_reaper.py +255 -0
  204. rucio/daemons/reaper/reaper.py +701 -0
  205. rucio/daemons/replicarecoverer/__init__.py +14 -0
  206. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +487 -0
  207. rucio/daemons/storage/__init__.py +14 -0
  208. rucio/daemons/storage/consistency/__init__.py +14 -0
  209. rucio/daemons/storage/consistency/actions.py +753 -0
  210. rucio/daemons/tracer/__init__.py +14 -0
  211. rucio/daemons/tracer/kronos.py +513 -0
  212. rucio/daemons/transmogrifier/__init__.py +14 -0
  213. rucio/daemons/transmogrifier/transmogrifier.py +753 -0
  214. rucio/daemons/undertaker/__init__.py +14 -0
  215. rucio/daemons/undertaker/undertaker.py +137 -0
  216. rucio/db/__init__.py +14 -0
  217. rucio/db/sqla/__init__.py +38 -0
  218. rucio/db/sqla/constants.py +192 -0
  219. rucio/db/sqla/migrate_repo/__init__.py +14 -0
  220. rucio/db/sqla/migrate_repo/env.py +111 -0
  221. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +71 -0
  222. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +50 -0
  223. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +61 -0
  224. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +46 -0
  225. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +93 -0
  226. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +78 -0
  227. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +46 -0
  228. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +53 -0
  229. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +69 -0
  230. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +42 -0
  231. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +46 -0
  232. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +61 -0
  233. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +42 -0
  234. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +141 -0
  235. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +75 -0
  236. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +75 -0
  237. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +46 -0
  238. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +51 -0
  239. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +135 -0
  240. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +65 -0
  241. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +42 -0
  242. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +66 -0
  243. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +54 -0
  244. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +43 -0
  245. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +46 -0
  246. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +47 -0
  247. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +54 -0
  248. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +39 -0
  249. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +48 -0
  250. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +47 -0
  251. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +48 -0
  252. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +47 -0
  254. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +72 -0
  255. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +46 -0
  256. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +45 -0
  257. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +48 -0
  258. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +48 -0
  259. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +42 -0
  260. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +69 -0
  261. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +46 -0
  262. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +78 -0
  263. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +62 -0
  264. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +74 -0
  265. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +44 -0
  266. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +67 -0
  267. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +134 -0
  268. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +58 -0
  269. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +79 -0
  270. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +61 -0
  271. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +46 -0
  273. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +65 -0
  274. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +42 -0
  275. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +46 -0
  276. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +46 -0
  277. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +80 -0
  278. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +43 -0
  279. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +61 -0
  280. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +47 -0
  281. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +46 -0
  282. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +52 -0
  283. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +42 -0
  284. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +65 -0
  285. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +46 -0
  286. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +47 -0
  287. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +45 -0
  288. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +46 -0
  289. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +48 -0
  290. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +50 -0
  291. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +59 -0
  292. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +48 -0
  293. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +108 -0
  294. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +51 -0
  296. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +50 -0
  297. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +46 -0
  298. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +42 -0
  299. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +93 -0
  300. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +73 -0
  301. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +52 -0
  302. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +45 -0
  303. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +46 -0
  304. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +54 -0
  305. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +48 -0
  306. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +70 -0
  307. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +48 -0
  308. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +95 -0
  309. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +74 -0
  311. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +78 -0
  312. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +49 -0
  313. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +124 -0
  314. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +60 -0
  315. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +53 -0
  316. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +56 -0
  317. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +67 -0
  318. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +50 -0
  319. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +46 -0
  320. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +92 -0
  321. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +42 -0
  322. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +46 -0
  323. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +147 -0
  324. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +78 -0
  325. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +53 -0
  326. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +74 -0
  327. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +56 -0
  328. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +46 -0
  329. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +68 -0
  330. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +48 -0
  331. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +149 -0
  332. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +106 -0
  333. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +47 -0
  334. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +45 -0
  335. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +105 -0
  336. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +52 -0
  337. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +106 -0
  338. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +30 -0
  339. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +75 -0
  340. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +49 -0
  341. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +45 -0
  342. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +38 -0
  343. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +44 -0
  344. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +46 -0
  345. rucio/db/sqla/models.py +1834 -0
  346. rucio/db/sqla/sautils.py +48 -0
  347. rucio/db/sqla/session.py +470 -0
  348. rucio/db/sqla/types.py +207 -0
  349. rucio/db/sqla/util.py +521 -0
  350. rucio/rse/__init__.py +97 -0
  351. rucio/rse/protocols/__init__.py +14 -0
  352. rucio/rse/protocols/cache.py +123 -0
  353. rucio/rse/protocols/dummy.py +112 -0
  354. rucio/rse/protocols/gfal.py +701 -0
  355. rucio/rse/protocols/globus.py +243 -0
  356. rucio/rse/protocols/gsiftp.py +93 -0
  357. rucio/rse/protocols/http_cache.py +83 -0
  358. rucio/rse/protocols/mock.py +124 -0
  359. rucio/rse/protocols/ngarc.py +210 -0
  360. rucio/rse/protocols/posix.py +251 -0
  361. rucio/rse/protocols/protocol.py +530 -0
  362. rucio/rse/protocols/rclone.py +365 -0
  363. rucio/rse/protocols/rfio.py +137 -0
  364. rucio/rse/protocols/srm.py +339 -0
  365. rucio/rse/protocols/ssh.py +414 -0
  366. rucio/rse/protocols/storm.py +207 -0
  367. rucio/rse/protocols/webdav.py +547 -0
  368. rucio/rse/protocols/xrootd.py +295 -0
  369. rucio/rse/rsemanager.py +752 -0
  370. rucio/tests/__init__.py +14 -0
  371. rucio/tests/common.py +244 -0
  372. rucio/tests/common_server.py +132 -0
  373. rucio/transfertool/__init__.py +14 -0
  374. rucio/transfertool/fts3.py +1484 -0
  375. rucio/transfertool/globus.py +200 -0
  376. rucio/transfertool/globus_library.py +182 -0
  377. rucio/transfertool/mock.py +81 -0
  378. rucio/transfertool/transfertool.py +212 -0
  379. rucio/vcsversion.py +11 -0
  380. rucio/version.py +46 -0
  381. rucio/web/__init__.py +14 -0
  382. rucio/web/rest/__init__.py +14 -0
  383. rucio/web/rest/flaskapi/__init__.py +14 -0
  384. rucio/web/rest/flaskapi/authenticated_bp.py +28 -0
  385. rucio/web/rest/flaskapi/v1/__init__.py +14 -0
  386. rucio/web/rest/flaskapi/v1/accountlimits.py +234 -0
  387. rucio/web/rest/flaskapi/v1/accounts.py +1088 -0
  388. rucio/web/rest/flaskapi/v1/archives.py +100 -0
  389. rucio/web/rest/flaskapi/v1/auth.py +1642 -0
  390. rucio/web/rest/flaskapi/v1/common.py +385 -0
  391. rucio/web/rest/flaskapi/v1/config.py +305 -0
  392. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  393. rucio/web/rest/flaskapi/v1/dids.py +2204 -0
  394. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  395. rucio/web/rest/flaskapi/v1/export.py +77 -0
  396. rucio/web/rest/flaskapi/v1/heartbeats.py +129 -0
  397. rucio/web/rest/flaskapi/v1/identities.py +263 -0
  398. rucio/web/rest/flaskapi/v1/import.py +133 -0
  399. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +315 -0
  400. rucio/web/rest/flaskapi/v1/locks.py +360 -0
  401. rucio/web/rest/flaskapi/v1/main.py +83 -0
  402. rucio/web/rest/flaskapi/v1/meta.py +226 -0
  403. rucio/web/rest/flaskapi/v1/metrics.py +37 -0
  404. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  405. rucio/web/rest/flaskapi/v1/ping.py +89 -0
  406. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  407. rucio/web/rest/flaskapi/v1/replicas.py +1866 -0
  408. rucio/web/rest/flaskapi/v1/requests.py +841 -0
  409. rucio/web/rest/flaskapi/v1/rses.py +2204 -0
  410. rucio/web/rest/flaskapi/v1/rules.py +824 -0
  411. rucio/web/rest/flaskapi/v1/scopes.py +161 -0
  412. rucio/web/rest/flaskapi/v1/subscriptions.py +646 -0
  413. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  414. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  415. rucio/web/rest/flaskapi/v1/tmp_dids.py +115 -0
  416. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  417. rucio/web/rest/flaskapi/v1/vos.py +280 -0
  418. rucio/web/rest/main.py +19 -0
  419. rucio/web/rest/metrics.py +28 -0
  420. rucio-32.8.6.data/data/rucio/etc/alembic.ini.template +71 -0
  421. rucio-32.8.6.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  422. rucio-32.8.6.data/data/rucio/etc/globus-config.yml.template +5 -0
  423. rucio-32.8.6.data/data/rucio/etc/ldap.cfg.template +30 -0
  424. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  425. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  426. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  427. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  428. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  429. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  430. rucio-32.8.6.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  431. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  432. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.template +257 -0
  433. rucio-32.8.6.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  434. rucio-32.8.6.data/data/rucio/requirements.txt +55 -0
  435. rucio-32.8.6.data/data/rucio/tools/bootstrap.py +34 -0
  436. rucio-32.8.6.data/data/rucio/tools/merge_rucio_configs.py +147 -0
  437. rucio-32.8.6.data/data/rucio/tools/reset_database.py +40 -0
  438. rucio-32.8.6.data/scripts/rucio +2540 -0
  439. rucio-32.8.6.data/scripts/rucio-abacus-account +75 -0
  440. rucio-32.8.6.data/scripts/rucio-abacus-collection-replica +47 -0
  441. rucio-32.8.6.data/scripts/rucio-abacus-rse +79 -0
  442. rucio-32.8.6.data/scripts/rucio-admin +2434 -0
  443. rucio-32.8.6.data/scripts/rucio-atropos +61 -0
  444. rucio-32.8.6.data/scripts/rucio-auditor +199 -0
  445. rucio-32.8.6.data/scripts/rucio-automatix +51 -0
  446. rucio-32.8.6.data/scripts/rucio-bb8 +58 -0
  447. rucio-32.8.6.data/scripts/rucio-c3po +86 -0
  448. rucio-32.8.6.data/scripts/rucio-cache-client +135 -0
  449. rucio-32.8.6.data/scripts/rucio-cache-consumer +43 -0
  450. rucio-32.8.6.data/scripts/rucio-conveyor-finisher +59 -0
  451. rucio-32.8.6.data/scripts/rucio-conveyor-poller +67 -0
  452. rucio-32.8.6.data/scripts/rucio-conveyor-preparer +38 -0
  453. rucio-32.8.6.data/scripts/rucio-conveyor-receiver +44 -0
  454. rucio-32.8.6.data/scripts/rucio-conveyor-stager +77 -0
  455. rucio-32.8.6.data/scripts/rucio-conveyor-submitter +140 -0
  456. rucio-32.8.6.data/scripts/rucio-conveyor-throttler +105 -0
  457. rucio-32.8.6.data/scripts/rucio-dark-reaper +54 -0
  458. rucio-32.8.6.data/scripts/rucio-dumper +159 -0
  459. rucio-32.8.6.data/scripts/rucio-follower +45 -0
  460. rucio-32.8.6.data/scripts/rucio-hermes +55 -0
  461. rucio-32.8.6.data/scripts/rucio-judge-cleaner +90 -0
  462. rucio-32.8.6.data/scripts/rucio-judge-evaluator +138 -0
  463. rucio-32.8.6.data/scripts/rucio-judge-injector +45 -0
  464. rucio-32.8.6.data/scripts/rucio-judge-repairer +45 -0
  465. rucio-32.8.6.data/scripts/rucio-kronos +45 -0
  466. rucio-32.8.6.data/scripts/rucio-light-reaper +53 -0
  467. rucio-32.8.6.data/scripts/rucio-minos +54 -0
  468. rucio-32.8.6.data/scripts/rucio-minos-temporary-expiration +51 -0
  469. rucio-32.8.6.data/scripts/rucio-necromancer +121 -0
  470. rucio-32.8.6.data/scripts/rucio-oauth-manager +64 -0
  471. rucio-32.8.6.data/scripts/rucio-reaper +84 -0
  472. rucio-32.8.6.data/scripts/rucio-replica-recoverer +249 -0
  473. rucio-32.8.6.data/scripts/rucio-storage-consistency-actions +75 -0
  474. rucio-32.8.6.data/scripts/rucio-transmogrifier +78 -0
  475. rucio-32.8.6.data/scripts/rucio-undertaker +77 -0
  476. rucio-32.8.6.dist-info/METADATA +83 -0
  477. rucio-32.8.6.dist-info/RECORD +481 -0
  478. rucio-32.8.6.dist-info/WHEEL +5 -0
  479. rucio-32.8.6.dist-info/licenses/AUTHORS.rst +94 -0
  480. rucio-32.8.6.dist-info/licenses/LICENSE +201 -0
  481. rucio-32.8.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,385 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import itertools
17
+ import json
18
+ import logging
19
+ import re
20
+ from configparser import NoOptionError, NoSectionError
21
+ from functools import wraps
22
+ from time import time
23
+ from typing import TYPE_CHECKING
24
+
25
+ import flask
26
+ from flask.views import MethodView
27
+ from werkzeug.datastructures import Headers
28
+ from werkzeug.exceptions import HTTPException
29
+ from werkzeug.wrappers import Request, Response
30
+
31
+ from rucio.api.authentication import validate_auth_token
32
+ from rucio.common import config
33
+ from rucio.common.exception import DatabaseException, RucioException, CannotAuthenticate, UnsupportedRequestedContentType
34
+ from rucio.common.schema import get_schema_value
35
+ from rucio.common.utils import generate_uuid, render_json
36
+ from rucio.core.vo import map_vo
37
+
38
+ if TYPE_CHECKING:
39
+ from collections.abc import Callable, Iterable, Sequence
40
+ from typing import Optional, Union, Any
41
+
42
+ HeadersType = Union[Headers, dict[str, str], Sequence[tuple[str, str]]]
43
+
44
+
45
+ class CORSMiddleware(object):
46
+ """
47
+ WebUI 2.0 makes preflight requests to the API, which are not handled by the API.
48
+ This middleware intercepts the preflight OPTIONS requests and returns a 200 OK response.
49
+ """
50
+
51
+ def __init__(self, app: flask.Flask) -> None:
52
+ self.app = app
53
+
54
+ def __call__(self, environ: dict, start_response: 'Callable') -> 'Union[Response, Iterable[bytes]]':
55
+ request: Request = Request(environ)
56
+
57
+ if request.environ.get('REQUEST_METHOD') == 'OPTIONS':
58
+ try:
59
+ webui_urls = config.config_get_list('webui', 'urls')
60
+ except (NoOptionError, NoSectionError, RuntimeError) as error:
61
+ logging.exception('Could not get webui urls from config file')
62
+ return str(error), 500
63
+ if request.origin in webui_urls:
64
+ response: Response = Response(status=200)
65
+ response.headers['Access-Control-Allow-Origin'] = request.origin
66
+ response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
67
+ response.headers['Access-Control-Allow-Headers'] = '*'
68
+ response.headers['Access-Control-Allow-Credentials'] = 'true'
69
+ return response(environ, start_response)
70
+ response: Response = Response(status=403)
71
+ return response(environ, start_response)
72
+
73
+ # bypass this middleware for non-OPTIONS requests
74
+ return self.app(environ, start_response)
75
+
76
+
77
+ class ErrorHandlingMethodView(MethodView):
78
+ """
79
+ Special MethodView that handles generic RucioExceptions and more generic
80
+ Exceptions for all defined methods automatically.
81
+ """
82
+
83
+ def get_headers(self) -> "Optional[HeadersType]":
84
+ """Can be overridden to add headers to generic error responses."""
85
+ return None
86
+
87
+ def dispatch_request(self, *args, **kwargs):
88
+ headers = self.get_headers() or None
89
+ try:
90
+ return super(ErrorHandlingMethodView, self).dispatch_request(*args, **kwargs)
91
+ except HTTPException:
92
+ raise
93
+ except DatabaseException as error:
94
+ if 'QueuePool' in str(error):
95
+ msg = f'DatabaseException in {self.__class__.__module__} {self.__class__.__name__} {flask.request.method}'
96
+ # logged, because this should be the __exception__
97
+ logging.debug(msg, exc_info=True)
98
+ return generate_http_error_flask(
99
+ status_code=503,
100
+ exc=error.__class__.__name__,
101
+ exc_msg=('Currently there are too many requests for the Rucio '
102
+ 'servers to handle. Please try again in a few minutes.'),
103
+ headers=headers
104
+ )
105
+ else:
106
+ msg = f'DatabaseException in {self.__class__.__module__} {self.__class__.__name__} {flask.request.method}'
107
+ logging.debug(msg, exc_info=True)
108
+ return generate_http_error_flask(
109
+ status_code=500,
110
+ exc=error.__class__.__name__,
111
+ exc_msg='An unknown Database Exception has ocurred.',
112
+ headers=headers
113
+ )
114
+
115
+ except RucioException as error:
116
+ # should be caught in the flask view and generate_http_error_flask with a proper HTTP status code returned
117
+ msg = f'Uncaught RucioException in {self.__class__.__module__} {self.__class__.__name__} {flask.request.method}'
118
+ # logged, because this should be the __exception__
119
+ logging.debug(msg, exc_info=True)
120
+ return generate_http_error_flask(
121
+ status_code=500,
122
+ exc=error.__class__.__name__,
123
+ exc_msg=error.args[0],
124
+ headers=headers
125
+ )
126
+ except Exception as error:
127
+ # logged, because this means a programming error
128
+ logging.exception("Internal Error")
129
+ if headers:
130
+ return str(error), 500, headers
131
+ else:
132
+ return str(error), 500
133
+
134
+
135
+ def request_auth_env():
136
+ if flask.request.environ.get('REQUEST_METHOD') == 'OPTIONS':
137
+ return '', 200
138
+
139
+ auth_token = flask.request.headers.get('X-Rucio-Auth-Token', default=None)
140
+
141
+ try:
142
+ auth = validate_auth_token(auth_token)
143
+ except CannotAuthenticate:
144
+ return generate_http_error_flask(401, CannotAuthenticate.__name__, 'Cannot authenticate with given credentials')
145
+ except RucioException as error:
146
+ return generate_http_error_flask(500, error.__class__.__name__, error.args[0])
147
+ except Exception:
148
+ logging.exception('Internal error in validate_auth_token')
149
+ return 'Internal Error', 500
150
+
151
+ flask.request.environ['vo'] = auth.get('vo', 'def')
152
+ flask.request.environ['issuer'] = auth.get('account')
153
+ flask.request.environ['identity'] = auth.get('identity')
154
+ flask.request.environ['request_id'] = generate_uuid()
155
+ flask.request.environ['start_time'] = time()
156
+
157
+
158
+ def response_headers(response):
159
+ response.headers['Access-Control-Allow-Origin'] = flask.request.environ.get('HTTP_ORIGIN')
160
+ response.headers['Access-Control-Allow-Headers'] = flask.request.environ.get('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
161
+ response.headers['Access-Control-Allow-Methods'] = '*'
162
+ response.headers['Access-Control-Allow-Credentials'] = 'true'
163
+
164
+ if flask.request.environ.get('REQUEST_METHOD') == 'GET':
165
+ response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
166
+ response.headers['Cache-Control'] = 'post-check=0, pre-check=0'
167
+ response.headers['Pragma'] = 'no-cache'
168
+
169
+ return response
170
+
171
+
172
+ def check_accept_header_wrapper_flask(supported_content_types):
173
+ """ Decorator to check if an endpoint supports the requested content type. """
174
+
175
+ def wrapper(f):
176
+ @wraps(f)
177
+ def decorated(*args, **kwargs):
178
+ if not flask.request.accept_mimetypes.provided:
179
+ # accept anything, if Accept header is not provided
180
+ return f(*args, **kwargs)
181
+
182
+ for supported in supported_content_types:
183
+ if supported in flask.request.accept_mimetypes:
184
+ return f(*args, **kwargs)
185
+
186
+ # none matched..
187
+ return generate_http_error_flask(
188
+ status_code=406,
189
+ exc=UnsupportedRequestedContentType.__name__,
190
+ exc_msg=f'The requested content type {flask.request.environ.get("HTTP_ACCEPT")} is not supported. Use {supported_content_types}.'
191
+ )
192
+
193
+ return decorated
194
+
195
+ return wrapper
196
+
197
+
198
+ def parse_scope_name(scope_name, vo):
199
+ """
200
+ Parses the given scope_name according to the schema's
201
+ SCOPE_NAME_REGEXP and returns a (scope, name) tuple.
202
+
203
+ :param scope_name: the scope_name string to be parsed.
204
+ :param vo: the vo currently in use.
205
+ :raises ValueError: when scope_name could not be parsed.
206
+ :returns: a (scope, name) tuple.
207
+ """
208
+ # why again does that regex start with a slash?
209
+ scope_name = re.match(get_schema_value('SCOPE_NAME_REGEXP', vo), '/' + scope_name)
210
+ if scope_name is None:
211
+ raise ValueError('cannot parse scope and name')
212
+ return scope_name.group(1, 2)
213
+
214
+
215
+ def try_stream(generator, content_type=None) -> "flask.Response":
216
+ """
217
+ Peeks at the first element of the passed generator and raises
218
+ an error, if yielding raises. Otherwise returns
219
+ a flask.Response object.
220
+
221
+ :param generator: a generator function or an iterator.
222
+ :param content_type: the response's Content-Type.
223
+ 'application/x-json-stream' by default.
224
+ :returns: a response object with the specified Content-Type.
225
+ """
226
+ if not content_type:
227
+ content_type = 'application/x-json-stream'
228
+
229
+ it = iter(generator)
230
+ try:
231
+ peek = next(it)
232
+ return flask.Response(flask.stream_with_context(itertools.chain((peek,), it)), content_type=content_type)
233
+ except StopIteration:
234
+ return flask.Response('', content_type=content_type)
235
+
236
+
237
+ def error_headers(exc_cls: str, exc_msg):
238
+ def strip_newlines(msg):
239
+ if msg is None:
240
+ return None
241
+
242
+ return msg.replace('\n', ' ').replace('\r', ' ')
243
+
244
+ exc_msg = strip_newlines(exc_msg)
245
+ if exc_msg:
246
+ # Truncate too long exc_msg
247
+ oldlen = len(exc_msg)
248
+ exc_msg = exc_msg[:min(oldlen, 125)]
249
+ if len(exc_msg) != oldlen:
250
+ exc_msg = exc_msg + '...'
251
+ return {
252
+ 'ExceptionClass': strip_newlines(exc_cls),
253
+ 'ExceptionMessage': exc_msg
254
+ }
255
+
256
+
257
+ def _error_response(exc_cls, exc_msg):
258
+ data = {'ExceptionClass': exc_cls,
259
+ 'ExceptionMessage': exc_msg}
260
+ headers = {'Content-Type': 'application/octet-stream'}
261
+ headers.update(error_headers(exc_cls=exc_cls, exc_msg=exc_msg))
262
+ return data, headers
263
+
264
+
265
+ def generate_http_error_flask(
266
+ status_code: "int",
267
+ exc: "Union[str, BaseException]",
268
+ exc_msg: "Optional[str]" = None,
269
+ headers: "Optional[HeadersType]" = None,
270
+ ) -> "flask.Response":
271
+ """Utitily function to generate a complete HTTP error response.
272
+
273
+ :param status_code: The HTTP status code to generate a response for.
274
+ :param exc: The name of the exception class or a RucioException object.
275
+ :param exc_msg: The error message.
276
+ :param headers: any default headers to send along.
277
+ :returns: a response object representing the error.
278
+ """
279
+ if isinstance(exc, BaseException):
280
+ if not exc_msg and exc.args and exc.args[0]:
281
+ exc_msg = exc.args[0]
282
+ exc_cls = exc.__class__.__name__
283
+ else:
284
+ exc_cls = str(exc)
285
+ exc_msg = str(exc_msg)
286
+
287
+ data, prioheaders = _error_response(exc_cls, exc_msg)
288
+ headers = Headers(headers)
289
+ headers.extend(prioheaders)
290
+ try:
291
+ return flask.Response(
292
+ status=status_code,
293
+ headers=headers,
294
+ content_type=prioheaders['Content-Type'],
295
+ response=render_json(**data),
296
+ )
297
+ except Exception:
298
+ logging.exception(f'Cannot create generate_http_error_flask response with {data}')
299
+ raise
300
+
301
+
302
+ def json_parameters(json_loads: "Callable[[str], Any]" = json.loads, optional: bool = False) -> dict:
303
+ """
304
+ Returns the JSON parameters from the current request's body as dict.
305
+ """
306
+ if optional:
307
+ kwargs = {'default': {}}
308
+ else:
309
+ kwargs = {}
310
+ return json_parse(types=(dict, ), json_loads=json_loads, **kwargs)
311
+
312
+
313
+ def json_list(json_loads: "Callable[[str], Any]" = json.loads, optional: bool = False) -> list:
314
+ """
315
+ Returns the JSON array from the current request's body as list.
316
+ """
317
+ if optional:
318
+ kwargs = {'default': []}
319
+ else:
320
+ kwargs = {}
321
+ return json_parse(types=(list, ), json_loads=json_loads, **kwargs)
322
+
323
+
324
+ def json_parse(types: tuple, json_loads: "Callable[[str], Any]" = json.loads, **kwargs):
325
+ def clstostr(cls):
326
+ if cls.__name__ == "dict":
327
+ return "dictionary"
328
+ else:
329
+ return cls.__name__
330
+
331
+ def typestostr(_types: tuple):
332
+ return " or ".join(map(clstostr, _types))
333
+
334
+ data = flask.request.get_data(as_text=True)
335
+ if 'default' in kwargs and not data:
336
+ return kwargs['default']
337
+ try:
338
+ body = json_loads(data)
339
+ if not isinstance(body, types):
340
+ flask.abort(
341
+ generate_http_error_flask(
342
+ status_code=400,
343
+ exc=TypeError.__name__,
344
+ exc_msg='body must be a json ' + typestostr(types)
345
+ )
346
+ )
347
+ return body
348
+ except json.JSONDecodeError:
349
+ flask.abort(
350
+ generate_http_error_flask(
351
+ status_code=400,
352
+ exc=ValueError.__name__,
353
+ exc_msg='cannot decode json parameter ' + typestostr(types)
354
+ )
355
+ )
356
+
357
+
358
+ def param_get(parameters: dict, name: str, **kwargs):
359
+ if 'default' in kwargs:
360
+ return parameters.get(name, kwargs['default'])
361
+ else:
362
+ if name not in parameters:
363
+ flask.abort(
364
+ generate_http_error_flask(
365
+ status_code=400,
366
+ exc=KeyError.__name__,
367
+ exc_msg=f"'{name}' not defined"
368
+ )
369
+ )
370
+ return parameters[name]
371
+
372
+
373
+ def extract_vo(headers: "HeadersType") -> "str":
374
+ """ Extract the VO name from the given request.headers object and
375
+ does any name mapping. Returns the short VO name or raise a
376
+ flask.abort if the VO name doesn't meet the name specification.
377
+
378
+ :papam headers: The request.headers object for the current request.
379
+ :returns: a string containing the short VO name.
380
+ """
381
+ try:
382
+ return map_vo(headers.get('X-Rucio-VO', default='def'))
383
+ except RucioException as err:
384
+ # VO Name doesn't match allowed spec
385
+ flask.abort(generate_http_error_flask(status_code=400, exc=err))
@@ -0,0 +1,305 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from flask import Flask, request as request, jsonify
17
+
18
+ from rucio.api import config
19
+ from rucio.common.exception import ConfigurationError, AccessDenied, ConfigNotFound
20
+ from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
21
+ from rucio.web.rest.flaskapi.v1.common import response_headers, check_accept_header_wrapper_flask, \
22
+ generate_http_error_flask, ErrorHandlingMethodView, json_parameters
23
+
24
+
25
+ class Config(ErrorHandlingMethodView):
26
+ """ REST API for full configuration. """
27
+
28
+ @check_accept_header_wrapper_flask(['application/json'])
29
+ def get(self):
30
+ """
31
+ ---
32
+ summary: List
33
+ description: List the full configuration.
34
+ tags:
35
+ - Config
36
+ responses:
37
+ 200:
38
+ description: OK
39
+ content:
40
+ application/json:
41
+ schema:
42
+ description: A dict with the sections as keys and a dict with the configuration as value.
43
+ type: object
44
+ 401:
45
+ description: Invalid Auth Token
46
+ 406:
47
+ description: Not acceptable
48
+ """
49
+ res = {}
50
+ for section in config.sections(issuer=request.environ.get('issuer'), vo=request.environ.get('vo')):
51
+ res[section] = {}
52
+ for item in config.items(section, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')):
53
+ res[section][item[0]] = item[1]
54
+
55
+ return jsonify(res), 200
56
+
57
+ def post(self):
58
+ """
59
+ ---
60
+ summary: Create
61
+ description: Create or set the configuration option in the requested section.
62
+ tags:
63
+ - Config
64
+ requestBody:
65
+ content:
66
+ 'application/json':
67
+ schema:
68
+ description: "The request body is expected to contain a json {'section': {'option': 'value'}}."
69
+ type: object
70
+ responses:
71
+ 201:
72
+ description: OK
73
+ content:
74
+ application/json:
75
+ schema:
76
+ type: string
77
+ enum: ['Created']
78
+ 401:
79
+ description: Invalid Auth Token
80
+ 400:
81
+ description: The input data was incomplete or invalid
82
+ 500:
83
+ description: Configuration error
84
+ """
85
+ parameters = json_parameters()
86
+ for section, section_config in parameters.items():
87
+ if not isinstance(section_config, dict):
88
+ return generate_http_error_flask(400, ValueError.__name__, '')
89
+ for option, value in section_config.items():
90
+ try:
91
+ config.set(section=section, option=option, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
92
+ except ConfigurationError:
93
+ return generate_http_error_flask(400, 'ConfigurationError', f"Could not set value '{value}' for section '{section}' option '{option}'")
94
+ return 'Created', 201
95
+
96
+
97
+ class Section(ErrorHandlingMethodView):
98
+ """ REST API for the sections in the configuration. """
99
+
100
+ @check_accept_header_wrapper_flask(['application/json'])
101
+ def get(self, section):
102
+ """
103
+ ---
104
+ summary: List Sections
105
+ tags:
106
+ - Config
107
+ parameters:
108
+ - name: section
109
+ in: path
110
+ description: The section to return.
111
+ schema:
112
+ type: string
113
+ style: simple
114
+ requestBody:
115
+ content:
116
+ 'application/json':
117
+ schema:
118
+ type: object
119
+ required:
120
+ - bytes
121
+ properties:
122
+ bytes:
123
+ description: The new limit in bytes.
124
+ type: integer
125
+ responses:
126
+ 200:
127
+ description: OK
128
+ content:
129
+ application/json:
130
+ schema:
131
+ description: Dictionary of section options.
132
+ type: object
133
+ 401:
134
+ description: Invalid Auth Token
135
+ 404:
136
+ description: Config not found
137
+ 406:
138
+ description: Not acceptable
139
+ """
140
+ res = {}
141
+ for item in config.items(section, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')):
142
+ res[item[0]] = item[1]
143
+
144
+ if res == {}:
145
+ return generate_http_error_flask(
146
+ status_code=404,
147
+ exc=ConfigNotFound.__name__,
148
+ exc_msg=f"No configuration found for section '{section}'"
149
+ )
150
+
151
+ return jsonify(res), 200
152
+
153
+
154
+ class OptionGetDel(ErrorHandlingMethodView):
155
+ """ REST API for reading or deleting the options in the configuration. """
156
+
157
+ @check_accept_header_wrapper_flask(['application/json'])
158
+ def get(self, section, option):
159
+ """
160
+ ---
161
+ summary: Get option
162
+ description: Returns the value of an option
163
+ tags:
164
+ - Config
165
+ parameters:
166
+ - name: section
167
+ in: path
168
+ description: The section.
169
+ schema:
170
+ type: string
171
+ style: simple
172
+ - name: option
173
+ in: path
174
+ description: The option of the section.
175
+ schema:
176
+ type: string
177
+ style: simple
178
+ responses:
179
+ 200:
180
+ description: OK
181
+ content:
182
+ application/json:
183
+ schema:
184
+ description: The value of the option
185
+ type: string
186
+ 401:
187
+ description: Invalid Auth Token
188
+ 404:
189
+ description: Config not found
190
+ 406:
191
+ description: Not acceptable
192
+ """
193
+ try:
194
+ result = config.get(section=section, option=option, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
195
+ return jsonify(result), 200
196
+ except AccessDenied as error:
197
+ return generate_http_error_flask(401, error, f"Access to '{section}' option '{option}' denied")
198
+ except ConfigNotFound as error:
199
+ return generate_http_error_flask(404, error, f"No configuration found for section '{section}' option '{option}'")
200
+
201
+ def delete(self, section, option):
202
+ """
203
+ ---
204
+ summary: Delete option
205
+ description: Delete an option of a section.
206
+ tags:
207
+ - Config
208
+ parameters:
209
+ - name: section
210
+ in: path
211
+ description: The section.
212
+ schema:
213
+ type: string
214
+ style: simple
215
+ - name: option
216
+ in: path
217
+ description: The option of the section.
218
+ schema:
219
+ type: string
220
+ style: simple
221
+ responses:
222
+ 200:
223
+ description: OK
224
+ 401:
225
+ description: Invalid Auth Token
226
+ """
227
+ config.remove_option(section=section, option=option, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
228
+ return '', 200
229
+
230
+
231
+ class OptionSet(ErrorHandlingMethodView):
232
+ """ REST API for setting the options in the configuration. """
233
+
234
+ def put(self, section, option, value):
235
+ """
236
+ ---
237
+ summary: Create value
238
+ description: Create or set the value of an option.
239
+ tags:
240
+ - Config
241
+ parameters:
242
+ - name: section
243
+ in: path
244
+ description: The section.
245
+ schema:
246
+ type: string
247
+ style: simple
248
+ - name: option
249
+ in: path
250
+ description: The option of the section.
251
+ schema:
252
+ type: string
253
+ style: simple
254
+ - name: value
255
+ in: path
256
+ description: The value to set.
257
+ schema:
258
+ type: string
259
+ style: simple
260
+ responses:
261
+ 201:
262
+ description: OK
263
+ content:
264
+ application/json:
265
+ schema:
266
+ type: string
267
+ enum: ['Created']
268
+ 401:
269
+ description: Invalid Auth Token
270
+ 500:
271
+ description: Value could not be set
272
+ content:
273
+ application/json:
274
+ schema:
275
+ type: string
276
+ enum: ['Could not set value {} for section {} option {}']
277
+ """
278
+ try:
279
+ config.set(section=section, option=option, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
280
+ return 'Created', 201
281
+ except ConfigurationError as error:
282
+ return generate_http_error_flask(500, error, f"Could not set value '{value}' for section '{section}' option '{option}'")
283
+
284
+
285
+ def blueprint():
286
+ bp = AuthenticatedBlueprint('config', __name__, url_prefix='/config')
287
+
288
+ option_set_view = OptionSet.as_view('option_set')
289
+ bp.add_url_rule('/<section>/<option>/<value>', view_func=option_set_view, methods=['put', ])
290
+ option_get_del_view = OptionGetDel.as_view('option_get_del')
291
+ bp.add_url_rule('/<section>/<option>', view_func=option_get_del_view, methods=['get', 'delete'])
292
+ section_view = Section.as_view('section')
293
+ bp.add_url_rule('/<section>', view_func=section_view, methods=['get', ])
294
+ config_view = Config.as_view('config')
295
+ bp.add_url_rule('', view_func=config_view, methods=['get', 'post'])
296
+
297
+ bp.after_request(response_headers)
298
+ return bp
299
+
300
+
301
+ def make_doc():
302
+ """ Only used for sphinx documentation """
303
+ doc_app = Flask(__name__)
304
+ doc_app.register_blueprint(blueprint())
305
+ return doc_app