rucio 35.7.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (493) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/client/__init__.py +15 -0
  4. rucio/client/accountclient.py +433 -0
  5. rucio/client/accountlimitclient.py +183 -0
  6. rucio/client/baseclient.py +974 -0
  7. rucio/client/client.py +76 -0
  8. rucio/client/configclient.py +126 -0
  9. rucio/client/credentialclient.py +59 -0
  10. rucio/client/didclient.py +866 -0
  11. rucio/client/diracclient.py +56 -0
  12. rucio/client/downloadclient.py +1785 -0
  13. rucio/client/exportclient.py +44 -0
  14. rucio/client/fileclient.py +50 -0
  15. rucio/client/importclient.py +42 -0
  16. rucio/client/lifetimeclient.py +90 -0
  17. rucio/client/lockclient.py +109 -0
  18. rucio/client/metaconventionsclient.py +140 -0
  19. rucio/client/pingclient.py +44 -0
  20. rucio/client/replicaclient.py +454 -0
  21. rucio/client/requestclient.py +125 -0
  22. rucio/client/rseclient.py +746 -0
  23. rucio/client/ruleclient.py +294 -0
  24. rucio/client/scopeclient.py +90 -0
  25. rucio/client/subscriptionclient.py +173 -0
  26. rucio/client/touchclient.py +82 -0
  27. rucio/client/uploadclient.py +955 -0
  28. rucio/common/__init__.py +13 -0
  29. rucio/common/cache.py +74 -0
  30. rucio/common/config.py +801 -0
  31. rucio/common/constants.py +159 -0
  32. rucio/common/constraints.py +17 -0
  33. rucio/common/didtype.py +189 -0
  34. rucio/common/dumper/__init__.py +335 -0
  35. rucio/common/dumper/consistency.py +452 -0
  36. rucio/common/dumper/data_models.py +318 -0
  37. rucio/common/dumper/path_parsing.py +64 -0
  38. rucio/common/exception.py +1151 -0
  39. rucio/common/extra.py +36 -0
  40. rucio/common/logging.py +420 -0
  41. rucio/common/pcache.py +1408 -0
  42. rucio/common/plugins.py +153 -0
  43. rucio/common/policy.py +84 -0
  44. rucio/common/schema/__init__.py +150 -0
  45. rucio/common/schema/atlas.py +413 -0
  46. rucio/common/schema/belleii.py +408 -0
  47. rucio/common/schema/domatpc.py +401 -0
  48. rucio/common/schema/escape.py +426 -0
  49. rucio/common/schema/generic.py +433 -0
  50. rucio/common/schema/generic_multi_vo.py +412 -0
  51. rucio/common/schema/icecube.py +406 -0
  52. rucio/common/stomp_utils.py +159 -0
  53. rucio/common/stopwatch.py +55 -0
  54. rucio/common/test_rucio_server.py +148 -0
  55. rucio/common/types.py +403 -0
  56. rucio/common/utils.py +2238 -0
  57. rucio/core/__init__.py +13 -0
  58. rucio/core/account.py +496 -0
  59. rucio/core/account_counter.py +236 -0
  60. rucio/core/account_limit.py +423 -0
  61. rucio/core/authentication.py +620 -0
  62. rucio/core/config.py +456 -0
  63. rucio/core/credential.py +225 -0
  64. rucio/core/did.py +3000 -0
  65. rucio/core/did_meta_plugins/__init__.py +252 -0
  66. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  67. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  68. rucio/core/did_meta_plugins/filter_engine.py +613 -0
  69. rucio/core/did_meta_plugins/json_meta.py +240 -0
  70. rucio/core/did_meta_plugins/mongo_meta.py +216 -0
  71. rucio/core/did_meta_plugins/postgres_meta.py +316 -0
  72. rucio/core/dirac.py +237 -0
  73. rucio/core/distance.py +187 -0
  74. rucio/core/exporter.py +59 -0
  75. rucio/core/heartbeat.py +363 -0
  76. rucio/core/identity.py +300 -0
  77. rucio/core/importer.py +259 -0
  78. rucio/core/lifetime_exception.py +377 -0
  79. rucio/core/lock.py +576 -0
  80. rucio/core/message.py +282 -0
  81. rucio/core/meta_conventions.py +203 -0
  82. rucio/core/monitor.py +447 -0
  83. rucio/core/naming_convention.py +195 -0
  84. rucio/core/nongrid_trace.py +136 -0
  85. rucio/core/oidc.py +1461 -0
  86. rucio/core/permission/__init__.py +119 -0
  87. rucio/core/permission/atlas.py +1348 -0
  88. rucio/core/permission/belleii.py +1077 -0
  89. rucio/core/permission/escape.py +1078 -0
  90. rucio/core/permission/generic.py +1130 -0
  91. rucio/core/permission/generic_multi_vo.py +1150 -0
  92. rucio/core/quarantined_replica.py +223 -0
  93. rucio/core/replica.py +4158 -0
  94. rucio/core/replica_sorter.py +366 -0
  95. rucio/core/request.py +3089 -0
  96. rucio/core/rse.py +1875 -0
  97. rucio/core/rse_counter.py +186 -0
  98. rucio/core/rse_expression_parser.py +459 -0
  99. rucio/core/rse_selector.py +302 -0
  100. rucio/core/rule.py +4483 -0
  101. rucio/core/rule_grouping.py +1618 -0
  102. rucio/core/scope.py +180 -0
  103. rucio/core/subscription.py +364 -0
  104. rucio/core/topology.py +490 -0
  105. rucio/core/trace.py +375 -0
  106. rucio/core/transfer.py +1517 -0
  107. rucio/core/vo.py +169 -0
  108. rucio/core/volatile_replica.py +150 -0
  109. rucio/daemons/__init__.py +13 -0
  110. rucio/daemons/abacus/__init__.py +13 -0
  111. rucio/daemons/abacus/account.py +116 -0
  112. rucio/daemons/abacus/collection_replica.py +124 -0
  113. rucio/daemons/abacus/rse.py +117 -0
  114. rucio/daemons/atropos/__init__.py +13 -0
  115. rucio/daemons/atropos/atropos.py +242 -0
  116. rucio/daemons/auditor/__init__.py +289 -0
  117. rucio/daemons/auditor/hdfs.py +97 -0
  118. rucio/daemons/auditor/srmdumps.py +355 -0
  119. rucio/daemons/automatix/__init__.py +13 -0
  120. rucio/daemons/automatix/automatix.py +293 -0
  121. rucio/daemons/badreplicas/__init__.py +13 -0
  122. rucio/daemons/badreplicas/minos.py +322 -0
  123. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  124. rucio/daemons/badreplicas/necromancer.py +196 -0
  125. rucio/daemons/bb8/__init__.py +13 -0
  126. rucio/daemons/bb8/bb8.py +353 -0
  127. rucio/daemons/bb8/common.py +759 -0
  128. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  129. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  130. rucio/daemons/c3po/__init__.py +13 -0
  131. rucio/daemons/c3po/algorithms/__init__.py +13 -0
  132. rucio/daemons/c3po/algorithms/simple.py +134 -0
  133. rucio/daemons/c3po/algorithms/t2_free_space.py +128 -0
  134. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +130 -0
  135. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +294 -0
  136. rucio/daemons/c3po/c3po.py +371 -0
  137. rucio/daemons/c3po/collectors/__init__.py +13 -0
  138. rucio/daemons/c3po/collectors/agis.py +108 -0
  139. rucio/daemons/c3po/collectors/free_space.py +81 -0
  140. rucio/daemons/c3po/collectors/jedi_did.py +57 -0
  141. rucio/daemons/c3po/collectors/mock_did.py +51 -0
  142. rucio/daemons/c3po/collectors/network_metrics.py +71 -0
  143. rucio/daemons/c3po/collectors/workload.py +112 -0
  144. rucio/daemons/c3po/utils/__init__.py +13 -0
  145. rucio/daemons/c3po/utils/dataset_cache.py +50 -0
  146. rucio/daemons/c3po/utils/expiring_dataset_cache.py +56 -0
  147. rucio/daemons/c3po/utils/expiring_list.py +62 -0
  148. rucio/daemons/c3po/utils/popularity.py +85 -0
  149. rucio/daemons/c3po/utils/timeseries.py +89 -0
  150. rucio/daemons/cache/__init__.py +13 -0
  151. rucio/daemons/cache/consumer.py +197 -0
  152. rucio/daemons/common.py +415 -0
  153. rucio/daemons/conveyor/__init__.py +13 -0
  154. rucio/daemons/conveyor/common.py +562 -0
  155. rucio/daemons/conveyor/finisher.py +529 -0
  156. rucio/daemons/conveyor/poller.py +404 -0
  157. rucio/daemons/conveyor/preparer.py +205 -0
  158. rucio/daemons/conveyor/receiver.py +249 -0
  159. rucio/daemons/conveyor/stager.py +132 -0
  160. rucio/daemons/conveyor/submitter.py +403 -0
  161. rucio/daemons/conveyor/throttler.py +532 -0
  162. rucio/daemons/follower/__init__.py +13 -0
  163. rucio/daemons/follower/follower.py +101 -0
  164. rucio/daemons/hermes/__init__.py +13 -0
  165. rucio/daemons/hermes/hermes.py +774 -0
  166. rucio/daemons/judge/__init__.py +13 -0
  167. rucio/daemons/judge/cleaner.py +159 -0
  168. rucio/daemons/judge/evaluator.py +185 -0
  169. rucio/daemons/judge/injector.py +162 -0
  170. rucio/daemons/judge/repairer.py +154 -0
  171. rucio/daemons/oauthmanager/__init__.py +13 -0
  172. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  173. rucio/daemons/reaper/__init__.py +13 -0
  174. rucio/daemons/reaper/dark_reaper.py +278 -0
  175. rucio/daemons/reaper/reaper.py +743 -0
  176. rucio/daemons/replicarecoverer/__init__.py +13 -0
  177. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  178. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  179. rucio/daemons/rsedecommissioner/config.py +81 -0
  180. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  181. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  182. rucio/daemons/rsedecommissioner/profiles/generic.py +451 -0
  183. rucio/daemons/rsedecommissioner/profiles/types.py +92 -0
  184. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  185. rucio/daemons/storage/__init__.py +13 -0
  186. rucio/daemons/storage/consistency/__init__.py +13 -0
  187. rucio/daemons/storage/consistency/actions.py +846 -0
  188. rucio/daemons/tracer/__init__.py +13 -0
  189. rucio/daemons/tracer/kronos.py +536 -0
  190. rucio/daemons/transmogrifier/__init__.py +13 -0
  191. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  192. rucio/daemons/undertaker/__init__.py +13 -0
  193. rucio/daemons/undertaker/undertaker.py +137 -0
  194. rucio/db/__init__.py +13 -0
  195. rucio/db/sqla/__init__.py +52 -0
  196. rucio/db/sqla/constants.py +201 -0
  197. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  198. rucio/db/sqla/migrate_repo/env.py +110 -0
  199. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  200. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  201. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  202. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  203. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  204. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  205. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  206. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  207. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  208. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  209. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  210. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  211. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  212. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  213. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  214. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  215. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  216. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  217. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  218. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  219. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  220. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  221. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  222. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  223. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  224. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  225. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  226. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  227. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  228. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  229. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  230. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  231. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  232. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  233. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  234. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  235. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  236. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  237. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  238. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  239. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  240. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  241. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  242. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  243. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  244. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  245. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  246. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  247. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  248. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  249. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  251. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  252. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  253. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  254. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  255. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  256. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  257. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  258. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  259. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  260. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  261. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  262. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  263. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  264. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  265. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  266. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  267. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  268. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  269. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  270. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  271. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  273. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  274. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  275. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  276. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  278. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  279. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  280. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  281. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  282. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  283. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  284. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  285. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  286. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  287. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  288. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  289. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  290. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  291. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  292. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  293. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  294. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  295. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  296. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  297. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  298. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  299. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  300. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  301. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  302. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  303. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  304. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  305. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  306. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  307. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  308. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  309. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  311. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  312. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  313. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  314. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  316. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  317. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  318. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  319. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  320. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  321. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  322. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  323. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  324. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  325. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  326. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  327. rucio/db/sqla/models.py +1740 -0
  328. rucio/db/sqla/sautils.py +55 -0
  329. rucio/db/sqla/session.py +498 -0
  330. rucio/db/sqla/types.py +206 -0
  331. rucio/db/sqla/util.py +543 -0
  332. rucio/gateway/__init__.py +13 -0
  333. rucio/gateway/account.py +339 -0
  334. rucio/gateway/account_limit.py +286 -0
  335. rucio/gateway/authentication.py +375 -0
  336. rucio/gateway/config.py +217 -0
  337. rucio/gateway/credential.py +71 -0
  338. rucio/gateway/did.py +970 -0
  339. rucio/gateway/dirac.py +81 -0
  340. rucio/gateway/exporter.py +59 -0
  341. rucio/gateway/heartbeat.py +74 -0
  342. rucio/gateway/identity.py +204 -0
  343. rucio/gateway/importer.py +45 -0
  344. rucio/gateway/lifetime_exception.py +120 -0
  345. rucio/gateway/lock.py +153 -0
  346. rucio/gateway/meta_conventions.py +87 -0
  347. rucio/gateway/permission.py +71 -0
  348. rucio/gateway/quarantined_replica.py +78 -0
  349. rucio/gateway/replica.py +529 -0
  350. rucio/gateway/request.py +321 -0
  351. rucio/gateway/rse.py +600 -0
  352. rucio/gateway/rule.py +417 -0
  353. rucio/gateway/scope.py +99 -0
  354. rucio/gateway/subscription.py +277 -0
  355. rucio/gateway/vo.py +122 -0
  356. rucio/rse/__init__.py +96 -0
  357. rucio/rse/protocols/__init__.py +13 -0
  358. rucio/rse/protocols/bittorrent.py +184 -0
  359. rucio/rse/protocols/cache.py +122 -0
  360. rucio/rse/protocols/dummy.py +111 -0
  361. rucio/rse/protocols/gfal.py +703 -0
  362. rucio/rse/protocols/globus.py +243 -0
  363. rucio/rse/protocols/gsiftp.py +92 -0
  364. rucio/rse/protocols/http_cache.py +82 -0
  365. rucio/rse/protocols/mock.py +123 -0
  366. rucio/rse/protocols/ngarc.py +209 -0
  367. rucio/rse/protocols/posix.py +250 -0
  368. rucio/rse/protocols/protocol.py +594 -0
  369. rucio/rse/protocols/rclone.py +364 -0
  370. rucio/rse/protocols/rfio.py +136 -0
  371. rucio/rse/protocols/srm.py +338 -0
  372. rucio/rse/protocols/ssh.py +413 -0
  373. rucio/rse/protocols/storm.py +206 -0
  374. rucio/rse/protocols/webdav.py +550 -0
  375. rucio/rse/protocols/xrootd.py +301 -0
  376. rucio/rse/rsemanager.py +764 -0
  377. rucio/tests/__init__.py +13 -0
  378. rucio/tests/common.py +270 -0
  379. rucio/tests/common_server.py +132 -0
  380. rucio/transfertool/__init__.py +13 -0
  381. rucio/transfertool/bittorrent.py +199 -0
  382. rucio/transfertool/bittorrent_driver.py +52 -0
  383. rucio/transfertool/bittorrent_driver_qbittorrent.py +133 -0
  384. rucio/transfertool/fts3.py +1596 -0
  385. rucio/transfertool/fts3_plugins.py +152 -0
  386. rucio/transfertool/globus.py +201 -0
  387. rucio/transfertool/globus_library.py +181 -0
  388. rucio/transfertool/mock.py +90 -0
  389. rucio/transfertool/transfertool.py +221 -0
  390. rucio/vcsversion.py +11 -0
  391. rucio/version.py +38 -0
  392. rucio/web/__init__.py +13 -0
  393. rucio/web/rest/__init__.py +13 -0
  394. rucio/web/rest/flaskapi/__init__.py +13 -0
  395. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  396. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  397. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  398. rucio/web/rest/flaskapi/v1/accounts.py +1089 -0
  399. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  400. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  401. rucio/web/rest/flaskapi/v1/common.py +426 -0
  402. rucio/web/rest/flaskapi/v1/config.py +304 -0
  403. rucio/web/rest/flaskapi/v1/credentials.py +212 -0
  404. rucio/web/rest/flaskapi/v1/dids.py +2334 -0
  405. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  406. rucio/web/rest/flaskapi/v1/export.py +75 -0
  407. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  408. rucio/web/rest/flaskapi/v1/identities.py +261 -0
  409. rucio/web/rest/flaskapi/v1/import.py +132 -0
  410. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  411. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  412. rucio/web/rest/flaskapi/v1/main.py +91 -0
  413. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  414. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  415. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  416. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  417. rucio/web/rest/flaskapi/v1/redirect.py +365 -0
  418. rucio/web/rest/flaskapi/v1/replicas.py +1890 -0
  419. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  420. rucio/web/rest/flaskapi/v1/rses.py +2239 -0
  421. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  422. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  423. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  424. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  425. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  426. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  427. rucio/web/rest/flaskapi/v1/types.py +20 -0
  428. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  429. rucio/web/rest/main.py +18 -0
  430. rucio/web/rest/metrics.py +27 -0
  431. rucio/web/rest/ping.py +27 -0
  432. rucio-35.7.0.data/data/rucio/etc/alembic.ini.template +71 -0
  433. rucio-35.7.0.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  434. rucio-35.7.0.data/data/rucio/etc/globus-config.yml.template +5 -0
  435. rucio-35.7.0.data/data/rucio/etc/ldap.cfg.template +30 -0
  436. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  437. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  438. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  439. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  440. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  441. rucio-35.7.0.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  442. rucio-35.7.0.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  443. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  444. rucio-35.7.0.data/data/rucio/etc/rucio.cfg.template +257 -0
  445. rucio-35.7.0.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  446. rucio-35.7.0.data/data/rucio/requirements.server.txt +268 -0
  447. rucio-35.7.0.data/data/rucio/tools/bootstrap.py +34 -0
  448. rucio-35.7.0.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  449. rucio-35.7.0.data/data/rucio/tools/reset_database.py +40 -0
  450. rucio-35.7.0.data/scripts/rucio +2542 -0
  451. rucio-35.7.0.data/scripts/rucio-abacus-account +74 -0
  452. rucio-35.7.0.data/scripts/rucio-abacus-collection-replica +46 -0
  453. rucio-35.7.0.data/scripts/rucio-abacus-rse +78 -0
  454. rucio-35.7.0.data/scripts/rucio-admin +2447 -0
  455. rucio-35.7.0.data/scripts/rucio-atropos +60 -0
  456. rucio-35.7.0.data/scripts/rucio-auditor +205 -0
  457. rucio-35.7.0.data/scripts/rucio-automatix +50 -0
  458. rucio-35.7.0.data/scripts/rucio-bb8 +57 -0
  459. rucio-35.7.0.data/scripts/rucio-c3po +85 -0
  460. rucio-35.7.0.data/scripts/rucio-cache-client +134 -0
  461. rucio-35.7.0.data/scripts/rucio-cache-consumer +42 -0
  462. rucio-35.7.0.data/scripts/rucio-conveyor-finisher +58 -0
  463. rucio-35.7.0.data/scripts/rucio-conveyor-poller +66 -0
  464. rucio-35.7.0.data/scripts/rucio-conveyor-preparer +37 -0
  465. rucio-35.7.0.data/scripts/rucio-conveyor-receiver +43 -0
  466. rucio-35.7.0.data/scripts/rucio-conveyor-stager +76 -0
  467. rucio-35.7.0.data/scripts/rucio-conveyor-submitter +139 -0
  468. rucio-35.7.0.data/scripts/rucio-conveyor-throttler +104 -0
  469. rucio-35.7.0.data/scripts/rucio-dark-reaper +53 -0
  470. rucio-35.7.0.data/scripts/rucio-dumper +160 -0
  471. rucio-35.7.0.data/scripts/rucio-follower +44 -0
  472. rucio-35.7.0.data/scripts/rucio-hermes +54 -0
  473. rucio-35.7.0.data/scripts/rucio-judge-cleaner +89 -0
  474. rucio-35.7.0.data/scripts/rucio-judge-evaluator +137 -0
  475. rucio-35.7.0.data/scripts/rucio-judge-injector +44 -0
  476. rucio-35.7.0.data/scripts/rucio-judge-repairer +44 -0
  477. rucio-35.7.0.data/scripts/rucio-kronos +43 -0
  478. rucio-35.7.0.data/scripts/rucio-minos +53 -0
  479. rucio-35.7.0.data/scripts/rucio-minos-temporary-expiration +50 -0
  480. rucio-35.7.0.data/scripts/rucio-necromancer +120 -0
  481. rucio-35.7.0.data/scripts/rucio-oauth-manager +63 -0
  482. rucio-35.7.0.data/scripts/rucio-reaper +83 -0
  483. rucio-35.7.0.data/scripts/rucio-replica-recoverer +248 -0
  484. rucio-35.7.0.data/scripts/rucio-rse-decommissioner +66 -0
  485. rucio-35.7.0.data/scripts/rucio-storage-consistency-actions +74 -0
  486. rucio-35.7.0.data/scripts/rucio-transmogrifier +77 -0
  487. rucio-35.7.0.data/scripts/rucio-undertaker +76 -0
  488. rucio-35.7.0.dist-info/METADATA +72 -0
  489. rucio-35.7.0.dist-info/RECORD +493 -0
  490. rucio-35.7.0.dist-info/WHEEL +5 -0
  491. rucio-35.7.0.dist-info/licenses/AUTHORS.rst +97 -0
  492. rucio-35.7.0.dist-info/licenses/LICENSE +201 -0
  493. rucio-35.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,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,498 @@
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 datetime import datetime, timedelta
20
+ from functools import update_wrapper
21
+ from inspect import getfullargspec, isgeneratorfunction
22
+ from os.path import basename
23
+ from threading import Lock
24
+ from time import sleep
25
+ from typing import TYPE_CHECKING, Any, Union
26
+
27
+ from sqlalchemy import MetaData, create_engine, event, text
28
+ from sqlalchemy.exc import DatabaseError, DisconnectionError, OperationalError, SQLAlchemyError, TimeoutError
29
+ from sqlalchemy.orm import DeclarativeBase, Session, scoped_session, sessionmaker
30
+ from sqlalchemy.pool import NullPool, Pool, QueuePool, SingletonThreadPool
31
+
32
+ from rucio.common.config import config_get
33
+ from rucio.common.exception import DatabaseException, InputValidationError, RucioException
34
+ from rucio.common.extra import import_extras
35
+ from rucio.common.utils import retrying
36
+
37
+ EXTRA_MODULES = import_extras(['MySQLdb', 'pymysql'])
38
+
39
+ if TYPE_CHECKING:
40
+ from collections.abc import Callable
41
+ from typing import Optional, ParamSpec, TypeVar
42
+
43
+ from pymysql import Connection as MySQLConnection
44
+ from sqlalchemy.engine.base import Engine
45
+
46
+ from rucio.common.types import LoggerFunction
47
+
48
+ P = ParamSpec('P')
49
+ R = TypeVar('R')
50
+ CallableTypeVar = TypeVar('CallableTypeVar', bound='Callable[..., Any]')
51
+
52
+
53
+ try:
54
+ main_script = os.path.basename(sys.argv[0])
55
+ CURRENT_COMPONENT = main_script.split('-')[1]
56
+ except:
57
+ CURRENT_COMPONENT = None
58
+
59
+ DATABASE_SECTION = 'database'
60
+ try:
61
+ if CURRENT_COMPONENT:
62
+ sql_connection = config_get('%s-database' % CURRENT_COMPONENT, 'default', check_config_table=False).strip()
63
+ if sql_connection and len(sql_connection):
64
+ DATABASE_SECTION = '%s-database' % CURRENT_COMPONENT
65
+ except:
66
+ pass
67
+
68
+ DEFAULT_SCHEMA_NAME = config_get(DATABASE_SECTION, 'schema',
69
+ raise_exception=False, default=None, check_config_table=False)
70
+ _METADATA = MetaData(schema=DEFAULT_SCHEMA_NAME)
71
+ _MAKER, _ENGINE, _LOCK = None, None, Lock()
72
+
73
+
74
+ class BASE(DeclarativeBase):
75
+ metadata = _METADATA
76
+
77
+
78
+ def _fk_pragma_on_connect(dbapi_con, con_record) -> None:
79
+ # Hack for previous versions of sqlite3
80
+ try:
81
+ dbapi_con.execute('pragma foreign_keys=ON')
82
+ except AttributeError:
83
+ pass
84
+
85
+
86
+ def mysql_ping_listener(
87
+ dbapi_conn: "MySQLConnection",
88
+ connection_rec,
89
+ connection_proxy
90
+ ) -> None:
91
+ """
92
+ Ensures that MySQL connections checked out of the
93
+ pool are alive.
94
+
95
+ Borrowed from:
96
+ http://groups.google.com/group/sqlalchemy/msg/a4ce563d802c929f
97
+
98
+ :param dbapi_conn: DBAPI connection
99
+ :param connection_rec: connection record
100
+ :param connection_proxy: connection proxy
101
+ """
102
+
103
+ try:
104
+ dbapi_conn.cursor().execute('select 1')
105
+ except dbapi_conn.OperationalError as ex:
106
+ if ex.args[0] in (2006, 2013, 2014, 2045, 2055):
107
+ msg = 'Got mysql server has gone away: %s' % ex
108
+ raise DisconnectionError(msg)
109
+ else:
110
+ raise
111
+
112
+
113
+ def mysql_convert_decimal_to_float(
114
+ pymysql: bool = False
115
+ ) -> dict[Union[type[object], int], 'Callable[..., Any]']:
116
+ """
117
+ The default datatype returned by mysql-python for numerics is decimal.Decimal.
118
+ This type cannot be serialised to JSON, therefore we need to autoconvert to floats.
119
+ Even worse, there's two types of decimals created by the MySQLdb driver, so we must
120
+ override both.
121
+
122
+ :return converter: Converter object
123
+ """
124
+
125
+ def pymysql_converter() -> dict[Union[type[object], int], 'Callable[..., Any]']:
126
+ from pymysql.constants import FIELD_TYPE
127
+ from pymysql.converters import conversions as conv
128
+ converter = conv.copy()
129
+ converter[FIELD_TYPE.DECIMAL] = float
130
+ converter[FIELD_TYPE.NEWDECIMAL] = float
131
+ return converter
132
+
133
+ if pymysql:
134
+ if not EXTRA_MODULES['pymysql']:
135
+ raise RucioException('Trying to use pymysql without having it installed!')
136
+ else:
137
+ converter = pymysql_converter()
138
+ elif EXTRA_MODULES['MySQLdb']:
139
+ import MySQLdb.converters # pylint: disable=import-error
140
+ from MySQLdb.constants import FIELD_TYPE # pylint: disable=import-error
141
+ converter = MySQLdb.converters.conversions.copy()
142
+ converter[FIELD_TYPE.DECIMAL] = float
143
+ converter[FIELD_TYPE.NEWDECIMAL] = float
144
+ elif EXTRA_MODULES['pymysql']:
145
+ converter = pymysql_converter()
146
+ else:
147
+ raise RucioException('Trying to use MySQL without mysql-python or pymysql installed!')
148
+
149
+ return converter
150
+
151
+
152
+ def psql_convert_decimal_to_float(dbapi_conn, connection_rec) -> None:
153
+ """
154
+ The default datatype returned by psycopg2 for numerics is decimal.Decimal.
155
+ This type cannot be serialised to JSON, therefore we need to autoconvert to floats.
156
+
157
+ :param dbapi_conn: DBAPI connection
158
+ :param connection_rec: connection record
159
+ """
160
+
161
+ try:
162
+ import psycopg2.extensions # pylint: disable=import-error
163
+ except:
164
+ raise RucioException('Trying to use PostgreSQL without psycopg2 or psycopg2-binary installed!')
165
+
166
+ DEC2FLOAT = psycopg2.extensions.new_type(psycopg2.extensions.DECIMAL.values,
167
+ 'DEC2FLOAT',
168
+ lambda value, curs: float(value) if value is not None else None)
169
+ psycopg2.extensions.register_type(DEC2FLOAT)
170
+
171
+
172
+ def my_on_connect(dbapi_con, connection_record) -> None:
173
+ """ Adds information to track performance and resource by module.
174
+ Info are recorded in the V$SESSION and V$SQLAREA views.
175
+ """
176
+ caller = basename(sys.argv[0])
177
+ dbapi_con.clientinfo = caller
178
+ dbapi_con.client_identifier = caller
179
+ dbapi_con.module = caller
180
+ dbapi_con.action = caller
181
+
182
+
183
+ def _get_engine_poolclass(poolclass: str) -> Pool:
184
+ """Resolve the correct SQLAlchemy Pool type to use from the
185
+ poolclass config option.
186
+
187
+ :param poolclass: User-selected pool class from config file.
188
+ :returns: The corresponding SQLAlchemy Pool class.
189
+ :raises InputValidationError: if config value doesn't correspond to an SQLAlchemy Pool class.
190
+ """
191
+
192
+ SQLA_CONFIG_POOLCLASS_MAPPING = {
193
+ 'queuepool': QueuePool,
194
+ 'singletonthreadpool': SingletonThreadPool,
195
+ 'nullpool': NullPool,
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 a 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
+ # Using sqlAlchemy 2.0 with future=True.
227
+ # if backing up from 2.0, need to remove future=True .
228
+ _ENGINE = create_engine(sql_connection, future=True, **params)
229
+ if 'mysql' in sql_connection:
230
+ event.listen(_ENGINE, 'checkout', mysql_ping_listener)
231
+ elif 'postgresql' in sql_connection:
232
+ event.listen(_ENGINE, 'connect', psql_convert_decimal_to_float)
233
+ elif 'sqlite' in sql_connection:
234
+ event.listen(_ENGINE, 'connect', _fk_pragma_on_connect)
235
+ elif 'oracle' in sql_connection:
236
+ event.listen(_ENGINE, 'connect', my_on_connect)
237
+ if not _ENGINE:
238
+ raise RuntimeError("Could not form database engine.")
239
+ return _ENGINE
240
+
241
+
242
+ def get_dump_engine(
243
+ echo: bool = False
244
+ ) -> 'Engine':
245
+ """ Creates a dump engine to a specific database.
246
+ :returns: engine """
247
+
248
+ statements = list()
249
+
250
+ def dump(sql, *multiparams, **params):
251
+ statement = str(sql.compile(dialect=engine.dialect))
252
+ if statement in statements:
253
+ return
254
+ statements.append(statement)
255
+ if statement.endswith(')\n\n'):
256
+ if engine.dialect.name == 'oracle':
257
+ print(statement.replace(')\n\n', ') PCTFREE 0;\n'))
258
+ else:
259
+ print(statement.replace(')\n\n', ');\n'))
260
+ elif statement.endswith(')'):
261
+ print(statement.replace(')', ');\n'))
262
+ else:
263
+ print(statement)
264
+ sql_connection = config_get(DATABASE_SECTION, 'default', check_config_table=False)
265
+
266
+ engine = create_engine(sql_connection, echo=echo, strategy='mock', executor=dump)
267
+ return engine
268
+
269
+
270
+ def get_maker() -> sessionmaker:
271
+ """
272
+ Return a SQLAlchemy sessionmaker.
273
+ May assign __MAKER if not already assigned.
274
+ """
275
+ global _MAKER, _ENGINE
276
+ if not _ENGINE:
277
+ raise RuntimeError("Could not form database engine.")
278
+ if not _MAKER:
279
+ # turn on sqlAlchemy 2.0 with future=True.
280
+ _MAKER = sessionmaker(bind=_ENGINE, autocommit=False, autoflush=True, expire_on_commit=True, future=True)
281
+ return _MAKER
282
+
283
+
284
+ def get_session() -> scoped_session:
285
+ """ Creates a session to a specific database, assumes that schema already in place.
286
+ :returns: session
287
+ """
288
+ global _MAKER, _LOCK
289
+ if not _MAKER:
290
+ _LOCK.acquire()
291
+ try:
292
+ get_engine()
293
+ get_maker()
294
+ finally:
295
+ _LOCK.release()
296
+ if not _MAKER:
297
+ raise RuntimeError("Session factory is not defined.")
298
+ session = scoped_session(_MAKER)
299
+ return session
300
+
301
+
302
+ def wait_for_database(
303
+ timeout: int = 60,
304
+ interval: int = 2,
305
+ *,
306
+ logger: "LoggerFunction" = logging.log
307
+ ) -> None:
308
+ """ Wait for the database for a specific amount of time """
309
+
310
+ end_time = datetime.utcnow() + timedelta(seconds=timeout)
311
+ while True:
312
+ try:
313
+ session = get_session()
314
+ if session.bind.dialect.name == 'oracle':
315
+ session.execute(text('select 1 from dual'))
316
+ else:
317
+ session.execute(text('select 1'))
318
+ session.close()
319
+ break
320
+ except SQLAlchemyError as e:
321
+ logger(logging.WARNING, 'Still waiting for database: %s', e)
322
+ if datetime.utcnow() >= end_time:
323
+ raise
324
+
325
+ sleep(interval)
326
+
327
+
328
+ def retry_if_db_connection_error(exception: Exception) -> bool:
329
+ """Return True if error in connecting to db."""
330
+ if isinstance(exception, (OperationalError, DatabaseException)):
331
+ conn_err_codes = ('2002', '2003', '2006', # MySQL
332
+ 'ORA-00028', # Oracle session has been killed
333
+ 'ORA-01012', # not logged on
334
+ 'ORA-03113', # end-of-file on communication channel
335
+ 'ORA-03114', # not connected to ORACLE
336
+ 'ORA-03135', # connection lost contact
337
+ 'ORA-25408',) # can not safely replay call
338
+ for err_code in conn_err_codes:
339
+ if exception.args[0].find(err_code) != -1:
340
+ return True
341
+ return False
342
+
343
+
344
+ def _update_session_wrapper(
345
+ wrapper: "CallableTypeVar",
346
+ wrapped: 'Callable'
347
+ ) -> "CallableTypeVar":
348
+ """
349
+ In addition to the work done by functools.update_wrapper, this function also preservers
350
+ the signature of the initial function. With the exception that the 'session' parameter
351
+ is overridden to have a default value of 'None'.
352
+
353
+ wrapper is the function to be updated
354
+ wrapped is the original function
355
+
356
+ To simplify the implementation of this function, we require 'session' be a
357
+ keyword-only argument in the wrapped function.
358
+ """
359
+ try:
360
+ arg_spec = getfullargspec(wrapped)
361
+ arg_spec.kwonlyargs.index('session')
362
+ except ValueError:
363
+ # We require decorated functions to have a 'session' keyword-only attribute.
364
+ # re-raise ValueError if not
365
+ raise
366
+
367
+ update_wrapper(wrapper, wrapped)
368
+
369
+ wrapper.__defaults__ = copy.copy(wrapped.__defaults__)
370
+ wrapper.__kwdefaults__ = copy.copy(wrapped.__kwdefaults__)
371
+ wrapper.__annotations__["session"] = "Optional[Session]"
372
+
373
+ # Set the default of the keyword-only attribute 'session' to None
374
+ if not wrapped.__kwdefaults__:
375
+ wrapper.__kwdefaults__ = {'session': None}
376
+ elif 'session' not in wrapped.__kwdefaults__:
377
+ wrapper.__kwdefaults__['session'] = None
378
+ return wrapper
379
+
380
+
381
+ def read_session(function: "Callable[P, R]"):
382
+ '''
383
+ decorator that set the session variable to use inside a function.
384
+ With that decorator it's possible to use the session variable like if a global variable session is declared.
385
+
386
+ session is a sqlalchemy session, and you can get one calling get_session().
387
+ This is useful if only SELECTs and the like are being done; anything involving
388
+ INSERTs, UPDATEs etc should use transactional_session.
389
+ '''
390
+ @retrying(retry_on_exception=retry_if_db_connection_error,
391
+ wait_fixed=500,
392
+ stop_max_attempt_number=2)
393
+ def new_funct(*args: "P.args", session: "Optional[Session]" = None, **kwargs): # pylint:disable=missing-kwoa
394
+ if isgeneratorfunction(function):
395
+ raise RucioException('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
+ return _update_session_wrapper(new_funct, function)
419
+
420
+
421
+ def stream_session(function: "Callable[P, R]"):
422
+ '''
423
+ decorator that set the session variable to use inside a function.
424
+ With that decorator it's possible to use the session variable like if a global variable session is declared.
425
+
426
+ session is a sqlalchemy session, and you can get one calling get_session().
427
+ This is useful if only SELECTs and the like are being done; anything involving
428
+ INSERTs, UPDATEs etc should use transactional_session.
429
+ '''
430
+ @retrying(retry_on_exception=retry_if_db_connection_error,
431
+ wait_fixed=500,
432
+ stop_max_attempt_number=2)
433
+ def new_funct(*args: "P.args", session: "Optional[Session]" = None, **kwargs): # pylint:disable=missing-kwoa
434
+
435
+ if not isgeneratorfunction(function):
436
+ raise RucioException('stream_session decorator should be used only with generator. Use read_session instead.')
437
+
438
+ if not session:
439
+ session_scoped = get_session()
440
+ session = session_scoped()
441
+ session.begin() # type: ignore
442
+ try:
443
+ for row in function(*args, session=session, **kwargs):
444
+ yield row
445
+ except TimeoutError as error:
446
+ session.rollback() # type: ignore
447
+ raise DatabaseException(str(error))
448
+ except DatabaseError as error:
449
+ session.rollback() # type: ignore
450
+ raise DatabaseException(str(error))
451
+ except:
452
+ session.rollback() # type: ignore
453
+ raise
454
+ finally:
455
+ session_scoped.remove()
456
+ else:
457
+ try:
458
+ for row in function(*args, session=session, **kwargs):
459
+ yield row
460
+ except:
461
+ raise
462
+ return _update_session_wrapper(new_funct, function)
463
+
464
+
465
+ def transactional_session(function: "Callable[P, R]") -> 'Callable':
466
+ '''
467
+ decorator that set the session variable to use inside a function.
468
+ With that decorator it's possible to use the session variable like if a global variable session is declared.
469
+
470
+ session is a sqlalchemy session, and you can get one calling get_session().
471
+ '''
472
+ def new_funct(
473
+ *args: "P.args",
474
+ session: "Optional[Session]" = None,
475
+ **kwargs
476
+ ) -> "R": # pylint:disable=missing-kwoa
477
+ if not session:
478
+ session_scoped = get_session()
479
+ session = session_scoped()
480
+ session.begin() # type: ignore
481
+ try:
482
+ result = function(*args, session=session, **kwargs)
483
+ session.commit() # type: ignore
484
+ except TimeoutError as error:
485
+ session.rollback() # type: ignore
486
+ raise DatabaseException(str(error))
487
+ except DatabaseError as error:
488
+ session.rollback() # type: ignore
489
+ raise DatabaseException(str(error))
490
+ except:
491
+ session.rollback() # type: ignore
492
+ raise
493
+ finally:
494
+ session_scoped.remove() # pylint: disable=maybe-no-member
495
+ else:
496
+ result = function(*args, session=session, **kwargs)
497
+ return result
498
+ return _update_session_wrapper(new_funct, function)