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,55 @@
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
+ '''
16
+ SQLAlchemy utilities
17
+
18
+ '''
19
+
20
+ from typing import TYPE_CHECKING, Any, Union
21
+
22
+ from sqlalchemy.ext.compiler import compiles
23
+ from sqlalchemy.sql.expression import ClauseElement, Executable
24
+
25
+ if TYPE_CHECKING:
26
+ from sqlalchemy import Table
27
+ from sqlalchemy.engine.interfaces import SQLCompiler
28
+ from sqlalchemy.sql.schema import Column
29
+ from sqlalchemy.sql.selectable import Select
30
+
31
+
32
+ class InsertFromSelect(Executable, ClauseElement):
33
+ def __init__(self, insert_spec: "Union[Table, list[Column]]", select: "Select") -> None:
34
+ self.insert_spec = insert_spec
35
+ self.select = select
36
+
37
+
38
+ @compiles(InsertFromSelect)
39
+ def visit_insert_from_select(element: InsertFromSelect, compiler: "SQLCompiler", **kw: Any) -> str:
40
+ if isinstance(element.insert_spec, list):
41
+ columns = []
42
+ for column in element.insert_spec:
43
+ if element.insert_spec[0].table != column.table:
44
+ raise Exception("Insert columns must belong to the same table")
45
+ columns.append(column.name)
46
+
47
+ table = compiler.process(element.insert_spec[0].table, asfrom=True)
48
+ columns = ", ".join(columns)
49
+
50
+ sql = "INSERT INTO %s (%s) %s" % (table, columns, compiler.process(element.select))
51
+
52
+ else:
53
+ sql = "INSERT INTO %s %s" % (compiler.process(element.insert_spec, asfrom=True), compiler.process(element.select))
54
+
55
+ return sql
@@ -0,0 +1,529 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import copy
16
+ import logging
17
+ import os
18
+ import sys
19
+ from contextlib import contextmanager
20
+ from datetime import datetime, timedelta
21
+ from functools import update_wrapper
22
+ from inspect import getfullargspec, isgeneratorfunction
23
+ from os.path import basename
24
+ from threading import Lock
25
+ from time import sleep
26
+ from typing import TYPE_CHECKING, Any, Union
27
+
28
+ from sqlalchemy import MetaData, create_engine, event, text
29
+ from sqlalchemy.exc import DatabaseError, DisconnectionError, OperationalError, SQLAlchemyError, TimeoutError
30
+ from sqlalchemy.orm import DeclarativeBase, Session, scoped_session, sessionmaker
31
+ from sqlalchemy.pool import NullPool, Pool, QueuePool, SingletonThreadPool
32
+
33
+ from rucio.common.config import config_get
34
+ from rucio.common.exception import DatabaseException, InputValidationError, RucioException
35
+ from rucio.common.extra import import_extras
36
+ from rucio.common.utils import retrying
37
+ from rucio.db.sqla.constants import DatabaseOperationType
38
+
39
+ EXTRA_MODULES = import_extras(['MySQLdb', 'pymysql'])
40
+
41
+ if TYPE_CHECKING:
42
+ from collections.abc import Callable, Iterator
43
+ from typing import Optional, ParamSpec, TypeVar
44
+
45
+ from pymysql import Connection as MySQLConnection
46
+ from sqlalchemy.engine.base import Engine
47
+
48
+ from rucio.common.types import LoggerFunction
49
+
50
+ P = ParamSpec('P')
51
+ R = TypeVar('R')
52
+ CallableTypeVar = TypeVar('CallableTypeVar', bound='Callable[..., Any]')
53
+
54
+ try:
55
+ main_script = os.path.basename(sys.argv[0])
56
+ CURRENT_COMPONENT = main_script.split('-')[1]
57
+ except:
58
+ CURRENT_COMPONENT = None
59
+
60
+ DATABASE_SECTION = 'database'
61
+ try:
62
+ if CURRENT_COMPONENT:
63
+ sql_connection = config_get('%s-database' % CURRENT_COMPONENT, 'default', check_config_table=False).strip()
64
+ if sql_connection and len(sql_connection):
65
+ DATABASE_SECTION = '%s-database' % CURRENT_COMPONENT
66
+ except:
67
+ pass
68
+
69
+ DEFAULT_SCHEMA_NAME = config_get(DATABASE_SECTION, 'schema',
70
+ raise_exception=False, default=None, check_config_table=False)
71
+ _METADATA = MetaData(schema=DEFAULT_SCHEMA_NAME)
72
+ _MAKER, _ENGINE, _LOCK = None, None, Lock()
73
+
74
+
75
+ SQLA_CONFIG_POOLCLASS_MAPPING = {
76
+ 'queuepool': QueuePool,
77
+ 'singletonthreadpool': SingletonThreadPool,
78
+ 'nullpool': NullPool,
79
+ }
80
+
81
+
82
+ class BASE(DeclarativeBase):
83
+ metadata = _METADATA
84
+
85
+
86
+ def _fk_pragma_on_connect(dbapi_con, con_record) -> None:
87
+ # Hack for previous versions of sqlite3
88
+ try:
89
+ dbapi_con.execute('pragma foreign_keys=ON')
90
+ except AttributeError:
91
+ pass
92
+
93
+
94
+ def mysql_ping_listener(
95
+ dbapi_conn: "MySQLConnection",
96
+ connection_rec,
97
+ connection_proxy
98
+ ) -> None:
99
+ """
100
+ Ensures that MySQL connections checked out of the
101
+ pool are alive.
102
+
103
+ Borrowed from:
104
+ http://groups.google.com/group/sqlalchemy/msg/a4ce563d802c929f
105
+
106
+ :param dbapi_conn: DBAPI connection
107
+ :param connection_rec: connection record
108
+ :param connection_proxy: connection proxy
109
+ """
110
+
111
+ try:
112
+ dbapi_conn.cursor().execute('select 1')
113
+ except dbapi_conn.OperationalError as ex:
114
+ if ex.args[0] in (2006, 2013, 2014, 2045, 2055):
115
+ msg = 'Got mysql server has gone away: %s' % ex
116
+ raise DisconnectionError(msg)
117
+ else:
118
+ raise
119
+
120
+
121
+ def mysql_convert_decimal_to_float(
122
+ pymysql: bool = False
123
+ ) -> dict[Union[type[object], int], 'Callable[..., Any]']:
124
+ """
125
+ The default datatype returned by mysql-python for numerics is decimal.Decimal.
126
+ This type cannot be serialised to JSON, therefore we need to autoconvert to floats.
127
+ Even worse, there's two types of decimals created by the MySQLdb driver, so we must
128
+ override both.
129
+
130
+ :return converter: Converter object
131
+ """
132
+
133
+ def pymysql_converter() -> dict[Union[type[object], int], 'Callable[..., Any]']:
134
+ from pymysql.constants import FIELD_TYPE
135
+ from pymysql.converters import conversions as conv
136
+ converter = conv.copy()
137
+ converter[FIELD_TYPE.DECIMAL] = float
138
+ converter[FIELD_TYPE.NEWDECIMAL] = float
139
+ return converter
140
+
141
+ if pymysql:
142
+ if not EXTRA_MODULES['pymysql']:
143
+ raise RucioException('Trying to use pymysql without having it installed!')
144
+ else:
145
+ converter = pymysql_converter()
146
+ elif EXTRA_MODULES['MySQLdb']:
147
+ import MySQLdb.converters # pylint: disable=import-error
148
+ from MySQLdb.constants import FIELD_TYPE # pylint: disable=import-error
149
+ converter = MySQLdb.converters.conversions.copy()
150
+ converter[FIELD_TYPE.DECIMAL] = float
151
+ converter[FIELD_TYPE.NEWDECIMAL] = float
152
+ elif EXTRA_MODULES['pymysql']:
153
+ converter = pymysql_converter()
154
+ else:
155
+ raise RucioException('Trying to use MySQL without mysql-python or pymysql installed!')
156
+
157
+ return converter
158
+
159
+
160
+ def psql_convert_decimal_to_float(dbapi_conn, connection_rec) -> None:
161
+ """
162
+ Configure the PostgreSQL connection to return numeric types as float instead of Decimal.
163
+ Psycopg3 provides this functionality through type adapters.
164
+
165
+ :param dbapi_conn: DBAPI connection
166
+ :param connection_rec: connection record
167
+ """
168
+ try:
169
+ import psycopg
170
+ # Register a global loader that converts numeric types to float
171
+ dbapi_conn.adapters.register_loader("numeric", psycopg.types.numeric.FloatLoader)
172
+ except ImportError:
173
+ raise RucioException('Trying to use PostgreSQL without psycopg installed!')
174
+ except Exception as error:
175
+ raise RucioException(f'Error setting up PostgreSQL Decimal to Float conversion: {str(error)}')
176
+
177
+
178
+ def my_on_connect(dbapi_con, connection_record) -> None:
179
+ """ Adds information to track performance and resource by module.
180
+ Info are recorded in the V$SESSION and V$SQLAREA views.
181
+ """
182
+ caller = basename(sys.argv[0])
183
+ dbapi_con.clientinfo = caller
184
+ dbapi_con.client_identifier = caller
185
+ dbapi_con.module = caller
186
+ dbapi_con.action = caller
187
+
188
+
189
+ def _get_engine_poolclass(poolclass: str) -> Pool:
190
+ """Resolve the correct SQLAlchemy Pool type to use from the
191
+ poolclass config option.
192
+
193
+ :param poolclass: User-selected pool class from config file.
194
+ :returns: The corresponding SQLAlchemy Pool class.
195
+ :raises InputValidationError: if config value doesn't correspond to an SQLAlchemy Pool class.
196
+ """
197
+
198
+ poolclass = poolclass.lower()
199
+
200
+ if poolclass not in SQLA_CONFIG_POOLCLASS_MAPPING:
201
+ raise InputValidationError('Unknown poolclass: %s' % poolclass)
202
+
203
+ return SQLA_CONFIG_POOLCLASS_MAPPING[poolclass]
204
+
205
+
206
+ def get_engine() -> 'Engine':
207
+ """ Creates an engine to a specific database.
208
+ :returns: engine
209
+ """
210
+ global _ENGINE
211
+ if not _ENGINE:
212
+ sql_connection = config_get(DATABASE_SECTION, 'default', check_config_table=False)
213
+ config_params = [('pool_size', int), ('max_overflow', int), ('pool_timeout', int),
214
+ ('pool_recycle', int), ('echo', int), ('echo_pool', str),
215
+ ('pool_reset_on_return', str), ('use_threadlocal', int),
216
+ ('poolclass', _get_engine_poolclass)]
217
+ params = {}
218
+ if 'mysql' in sql_connection:
219
+ conv = mysql_convert_decimal_to_float(pymysql=sql_connection.startswith('mysql+pymysql'))
220
+ params['connect_args'] = {'conv': conv}
221
+ for param, param_type in config_params:
222
+ try:
223
+ params[param] = param_type(config_get(DATABASE_SECTION, param, check_config_table=False))
224
+ except:
225
+ pass
226
+ _ENGINE = create_engine(sql_connection, **params)
227
+ if 'mysql' in sql_connection:
228
+ event.listen(_ENGINE, 'checkout', mysql_ping_listener)
229
+ elif 'postgresql' in sql_connection:
230
+ event.listen(_ENGINE, 'connect', psql_convert_decimal_to_float)
231
+ elif 'sqlite' in sql_connection:
232
+ event.listen(_ENGINE, 'connect', _fk_pragma_on_connect)
233
+ elif 'oracle' in sql_connection:
234
+ event.listen(_ENGINE, 'connect', my_on_connect)
235
+ if not _ENGINE:
236
+ raise RuntimeError("Could not form database engine.")
237
+ return _ENGINE
238
+
239
+
240
+ def get_dump_engine(
241
+ echo: bool = False
242
+ ) -> 'Engine':
243
+ """ Creates a dump engine to a specific database.
244
+ :returns: engine """
245
+
246
+ statements = list()
247
+
248
+ def dump(sql, *multiparams, **params):
249
+ statement = str(sql.compile(dialect=engine.dialect))
250
+ if statement in statements:
251
+ return
252
+ statements.append(statement)
253
+ if statement.endswith(')\n\n'):
254
+ if engine.dialect.name == 'oracle':
255
+ print(statement.replace(')\n\n', ') PCTFREE 0;\n'))
256
+ else:
257
+ print(statement.replace(')\n\n', ');\n'))
258
+ elif statement.endswith(')'):
259
+ print(statement.replace(')', ');\n'))
260
+ else:
261
+ print(statement)
262
+
263
+ sql_connection = config_get(DATABASE_SECTION, 'default', check_config_table=False)
264
+
265
+ engine = create_engine(sql_connection, echo=echo, strategy='mock', executor=dump)
266
+ return engine
267
+
268
+
269
+ def get_maker() -> sessionmaker:
270
+ """
271
+ Return a SQLAlchemy sessionmaker.
272
+ May assign __MAKER if not already assigned.
273
+ """
274
+ global _MAKER, _ENGINE
275
+ if not _ENGINE:
276
+ raise RuntimeError("Could not form database engine.")
277
+ if not _MAKER:
278
+ _MAKER = sessionmaker(bind=_ENGINE, autocommit=False, autoflush=True, expire_on_commit=True)
279
+ return _MAKER
280
+
281
+
282
+ def get_session() -> scoped_session:
283
+ """ Creates a session to a specific database, assumes that schema already in place.
284
+ :returns: session
285
+ """
286
+ global _MAKER, _LOCK
287
+ if not _MAKER:
288
+ _LOCK.acquire()
289
+ try:
290
+ get_engine()
291
+ get_maker()
292
+ finally:
293
+ _LOCK.release()
294
+ if not _MAKER:
295
+ raise RuntimeError("Session factory is not defined.")
296
+ session = scoped_session(_MAKER)
297
+ return session
298
+
299
+
300
+ def wait_for_database(
301
+ timeout: int = 60,
302
+ interval: int = 2,
303
+ *,
304
+ logger: "LoggerFunction" = logging.log
305
+ ) -> None:
306
+ """ Wait for the database for a specific amount of time """
307
+
308
+ end_time = datetime.utcnow() + timedelta(seconds=timeout)
309
+ while True:
310
+ try:
311
+ session = get_session()
312
+ if session.bind.dialect.name == 'oracle':
313
+ session.execute(text('select 1 from dual'))
314
+ else:
315
+ session.execute(text('select 1'))
316
+ session.close()
317
+ break
318
+ except SQLAlchemyError as e:
319
+ logger(logging.WARNING, 'Still waiting for database: %s', e)
320
+ if datetime.utcnow() >= end_time:
321
+ raise
322
+
323
+ sleep(interval)
324
+
325
+
326
+ def retry_if_db_connection_error(exception: Exception) -> bool:
327
+ """Return True if error in connecting to db."""
328
+ if isinstance(exception, (OperationalError, DatabaseException)):
329
+ conn_err_codes = ('2002', '2003', '2006', # MySQL
330
+ 'ORA-00028', # Oracle session has been killed
331
+ 'ORA-01012', # not logged on
332
+ 'ORA-03113', # end-of-file on communication channel
333
+ 'ORA-03114', # not connected to ORACLE
334
+ 'ORA-03135', # connection lost contact
335
+ 'ORA-25408',) # can not safely replay call
336
+ for err_code in conn_err_codes:
337
+ if exception.args[0].find(err_code) != -1:
338
+ return True
339
+ return False
340
+
341
+
342
+ def _update_session_wrapper(
343
+ wrapper: "CallableTypeVar",
344
+ wrapped: 'Callable'
345
+ ) -> "CallableTypeVar":
346
+ """
347
+ In addition to the work done by functools.update_wrapper, this function also preservers
348
+ the signature of the initial function. With the exception that the 'session' parameter
349
+ is overridden to have a default value of 'None'.
350
+
351
+ wrapper is the function to be updated
352
+ wrapped is the original function
353
+
354
+ To simplify the implementation of this function, we require 'session' be a
355
+ keyword-only argument in the wrapped function.
356
+ """
357
+ try:
358
+ arg_spec = getfullargspec(wrapped)
359
+ arg_spec.kwonlyargs.index('session')
360
+ except ValueError:
361
+ # We require decorated functions to have a 'session' keyword-only attribute.
362
+ # re-raise ValueError if not
363
+ raise
364
+
365
+ update_wrapper(wrapper, wrapped)
366
+
367
+ wrapper.__defaults__ = copy.copy(wrapped.__defaults__)
368
+ wrapper.__kwdefaults__ = copy.copy(wrapped.__kwdefaults__)
369
+ wrapper.__annotations__["session"] = "Optional[Session]"
370
+
371
+ # Set the default of the keyword-only attribute 'session' to None
372
+ if not wrapped.__kwdefaults__:
373
+ wrapper.__kwdefaults__ = {'session': None}
374
+ elif 'session' not in wrapped.__kwdefaults__:
375
+ wrapper.__kwdefaults__['session'] = None
376
+ return wrapper
377
+
378
+
379
+ def read_session(function: "Callable[P, R]"):
380
+ '''
381
+ decorator that set the session variable to use inside a function.
382
+ With that decorator it's possible to use the session variable like if a global variable session is declared.
383
+
384
+ session is a sqlalchemy session, and you can get one calling get_session().
385
+ This is useful if only SELECTs and the like are being done; anything involving
386
+ INSERTs, UPDATEs etc should use transactional_session.
387
+ '''
388
+
389
+ @retrying(retry_on_exception=retry_if_db_connection_error,
390
+ wait_fixed=500,
391
+ stop_max_attempt_number=2)
392
+ def new_funct(*args: "P.args", session: "Optional[Session]" = None, **kwargs): # pylint:disable=missing-kwoa
393
+ if isgeneratorfunction(function):
394
+ raise RucioException(
395
+ 'read_session decorator should not be used with generator. Use stream_session instead.')
396
+
397
+ if not session:
398
+ session_scoped = get_session()
399
+ session = session_scoped()
400
+ session.begin() # type: ignore
401
+ try:
402
+ return function(*args, session=session, **kwargs)
403
+ except TimeoutError as error:
404
+ session.rollback() # type: ignore
405
+ raise DatabaseException(str(error))
406
+ except DatabaseError as error:
407
+ session.rollback() # type: ignore
408
+ raise DatabaseException(str(error))
409
+ except:
410
+ session.rollback() # type: ignore
411
+ raise
412
+ finally:
413
+ session_scoped.remove()
414
+ try:
415
+ return function(*args, session=session, **kwargs)
416
+ except Exception:
417
+ raise
418
+
419
+ return _update_session_wrapper(new_funct, function)
420
+
421
+
422
+ def stream_session(function: "Callable[P, R]"):
423
+ '''
424
+ decorator that set the session variable to use inside a function.
425
+ With that decorator it's possible to use the session variable like if a global variable session is declared.
426
+
427
+ session is a sqlalchemy session, and you can get one calling get_session().
428
+ This is useful if only SELECTs and the like are being done; anything involving
429
+ INSERTs, UPDATEs etc should use transactional_session.
430
+ '''
431
+
432
+ @retrying(retry_on_exception=retry_if_db_connection_error,
433
+ wait_fixed=500,
434
+ stop_max_attempt_number=2)
435
+ def new_funct(*args: "P.args", session: "Optional[Session]" = None, **kwargs): # pylint:disable=missing-kwoa
436
+
437
+ if not isgeneratorfunction(function):
438
+ raise RucioException(
439
+ 'stream_session decorator should be used only with generator. Use read_session instead.')
440
+
441
+ if not session:
442
+ session_scoped = get_session()
443
+ session = session_scoped()
444
+ session.begin() # type: ignore
445
+ try:
446
+ for row in function(*args, session=session, **kwargs):
447
+ yield row
448
+ except TimeoutError as error:
449
+ session.rollback() # type: ignore
450
+ raise DatabaseException(str(error))
451
+ except DatabaseError as error:
452
+ session.rollback() # type: ignore
453
+ raise DatabaseException(str(error))
454
+ except:
455
+ session.rollback() # type: ignore
456
+ raise
457
+ finally:
458
+ session_scoped.remove()
459
+ else:
460
+ try:
461
+ for row in function(*args, session=session, **kwargs):
462
+ yield row
463
+ except:
464
+ raise
465
+ return _update_session_wrapper(new_funct, function)
466
+
467
+
468
+ def transactional_session(function: "Callable[P, R]") -> 'Callable':
469
+ '''
470
+ decorator that set the session variable to use inside a function.
471
+ With that decorator it's possible to use the session variable like if a global variable session is declared.
472
+
473
+ session is a sqlalchemy session, and you can get one calling get_session().
474
+ '''
475
+
476
+ def new_funct(
477
+ *args: "P.args",
478
+ session: "Optional[Session]" = None,
479
+ **kwargs
480
+ ) -> "R": # pylint:disable=missing-kwoa
481
+ if not session:
482
+ session_scoped = get_session()
483
+ session = session_scoped()
484
+ session.begin() # type: ignore
485
+ try:
486
+ result = function(*args, session=session, **kwargs)
487
+ session.commit() # type: ignore
488
+ except TimeoutError as error:
489
+ session.rollback() # type: ignore
490
+ raise DatabaseException(str(error))
491
+ except DatabaseError as error:
492
+ session.rollback() # type: ignore
493
+ raise DatabaseException(str(error))
494
+ except:
495
+ session.rollback() # type: ignore
496
+ raise
497
+ finally:
498
+ session_scoped.remove() # pylint: disable=maybe-no-member
499
+ else:
500
+ result = function(*args, session=session, **kwargs)
501
+ return result
502
+
503
+ return _update_session_wrapper(new_funct, function)
504
+
505
+
506
+ @retrying(retry_on_exception=retry_if_db_connection_error,
507
+ wait_fixed=500,
508
+ stop_max_attempt_number=2)
509
+ @contextmanager
510
+ def db_session(operation: DatabaseOperationType) -> "Iterator[Session]":
511
+ session_scoped = get_session()
512
+ session = session_scoped()
513
+ session.begin()
514
+
515
+ try:
516
+ yield session
517
+ if operation is DatabaseOperationType.WRITE:
518
+ session.commit()
519
+ except TimeoutError as error:
520
+ session.rollback()
521
+ raise DatabaseException(str(error))
522
+ except DatabaseError as error:
523
+ session.rollback()
524
+ raise DatabaseException(str(error))
525
+ except Exception:
526
+ session.rollback()
527
+ raise
528
+ finally:
529
+ session_scoped.remove()