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
rucio/core/monitor.py ADDED
@@ -0,0 +1,448 @@
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
+ Graphite and prometheus metrics
17
+ """
18
+
19
+ import atexit
20
+ import logging
21
+ import os
22
+ import string
23
+ from abc import abstractmethod
24
+ from datetime import datetime, timedelta
25
+ from functools import wraps
26
+ from pathlib import Path
27
+ from threading import Lock
28
+ from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
29
+
30
+ from prometheus_client import REGISTRY, CollectorRegistry, Counter, Gauge, Histogram, generate_latest, multiprocess, push_to_gateway, start_http_server, values
31
+ from statsd import StatsClient
32
+
33
+ import __main__ as main
34
+ from rucio.common.config import config_get, config_get_bool, config_get_int
35
+ from rucio.common.stopwatch import Stopwatch
36
+ from rucio.common.utils import retrying
37
+
38
+ if TYPE_CHECKING:
39
+ from collections.abc import Callable, Iterable, Sequence
40
+
41
+ from rucio.common.types import LoggerFunction
42
+
43
+ _T = TypeVar('_T')
44
+ _M = TypeVar('_M', bound="_MultiMetric")
45
+
46
+ PROMETHEUS_MULTIPROC_DIR = os.environ.get('PROMETHEUS_MULTIPROC_DIR', os.environ.get('prometheus_multiproc_dir', None))
47
+
48
+
49
+ def cleanup_prometheus_files_at_exit() -> None:
50
+ if PROMETHEUS_MULTIPROC_DIR:
51
+ multiprocess.mark_process_dead(os.getpid())
52
+
53
+
54
+ class MultiprocessMutexValue(values.MultiProcessValue()):
55
+ """
56
+ MultiprocessValue protected by mutex
57
+
58
+ Rucio usually is deployed using the apache MPM module, which means that it both uses multiple
59
+ subprocesses, and multiple threads per subprocess.
60
+ """
61
+ def __init__(self, *args, **kwargs):
62
+ super().__init__(*args, **kwargs)
63
+ self._lock = Lock()
64
+
65
+ def inc(self, amount: float) -> None:
66
+ with self._lock:
67
+ return super().inc(amount)
68
+
69
+ def set(self, value: float) -> None:
70
+ with self._lock:
71
+ return super().set(value)
72
+
73
+ def get(self) -> float:
74
+ with self._lock:
75
+ return super().get()
76
+
77
+
78
+ if PROMETHEUS_MULTIPROC_DIR:
79
+ os.makedirs(PROMETHEUS_MULTIPROC_DIR, exist_ok=True)
80
+ values.ValueClass = MultiprocessMutexValue
81
+
82
+ atexit.register(cleanup_prometheus_files_at_exit)
83
+
84
+
85
+ SERVER = config_get('monitor', 'carbon_server', raise_exception=False, default=None)
86
+ PORT = config_get_int('monitor', 'carbon_port', raise_exception=False, default=8125)
87
+ SCOPE = config_get('monitor', 'user_scope', raise_exception=False, default='rucio')
88
+ STATSD_CLIENT = None
89
+ if SERVER is not None:
90
+ STATSD_CLIENT = StatsClient(host=SERVER, port=PORT, prefix=SCOPE)
91
+
92
+ ENABLE_METRICS = config_get_bool('monitor', 'enable_metrics', raise_exception=False, default=False)
93
+ if ENABLE_METRICS:
94
+ METRICS_PORT = config_get_int('monitor', 'metrics_port', raise_exception=False, default=8080)
95
+ start_http_server(METRICS_PORT, registry=REGISTRY)
96
+
97
+ COUNTERS = {}
98
+ GAUGES = {}
99
+ TIMINGS = {}
100
+ METRICS_LOCK = Lock()
101
+
102
+
103
+ _HISTOGRAM_DEFAULT_BUCKETS = Histogram.DEFAULT_BUCKETS
104
+
105
+
106
+ def _cleanup_old_prometheus_files(
107
+ path: str,
108
+ file_pattern: str,
109
+ cleanup_delay: float,
110
+ logger: "LoggerFunction"
111
+ ) -> None:
112
+ """cleanup behind processes which didn't finish gracefully."""
113
+
114
+ oldest_accepted_mtime = datetime.now() - timedelta(seconds=cleanup_delay)
115
+ for file in Path(path).glob(file_pattern):
116
+ if not file.is_file():
117
+ continue
118
+
119
+ file_mtime = datetime.fromtimestamp(file.stat().st_mtime)
120
+
121
+ if file_mtime < oldest_accepted_mtime:
122
+ logger(logging.INFO, 'Cleaning up prometheus db file %s', file)
123
+ try:
124
+ os.remove(file)
125
+ except FileNotFoundError:
126
+ # Probably file already removed by another concurrent process
127
+ pass
128
+
129
+
130
+ def cleanup_old_prometheus_files(logger: "LoggerFunction" = logging.log) -> None:
131
+ path = PROMETHEUS_MULTIPROC_DIR
132
+ if path:
133
+ _cleanup_old_prometheus_files(path, file_pattern='gauge_live*.db', cleanup_delay=timedelta(hours=1).total_seconds(), logger=logger)
134
+ _cleanup_old_prometheus_files(path, file_pattern='*.db', cleanup_delay=timedelta(days=7).total_seconds(), logger=logger)
135
+
136
+
137
+ @retrying(retry_on_exception=lambda _: True,
138
+ wait_fixed=500,
139
+ stop_max_attempt_number=2)
140
+ def generate_prometheus_metrics() -> bytes:
141
+ cleanup_old_prometheus_files()
142
+
143
+ registry = CollectorRegistry()
144
+ multiprocess.MultiProcessCollector(registry)
145
+ return generate_latest(registry)
146
+
147
+
148
+ class _MultiMetric:
149
+ """
150
+ Thin wrapper class allowing to record both prometheus and statsd metrics.
151
+
152
+ Inspired by the prometheus metric behavior: uses labels to parametrize metrics.
153
+ In case of statsd, metrics are formatted using str.format(**labels). The prometheus
154
+ ones using metric.labels(**labels) calls.
155
+
156
+ If the prometheus metric string is not provided, it is derived from the statsd one.
157
+
158
+ If `labelnames` is not provided, tries to extract them from the metric name by parsing
159
+ it as a format string.
160
+ """
161
+
162
+ def __init__(
163
+ self,
164
+ statsd: str,
165
+ prom: Optional[Union[str, Counter, Gauge, Histogram]] = None,
166
+ documentation: Optional[str] = None,
167
+ labelnames: Optional['Sequence[str]'] = None,
168
+ registry: Optional[CollectorRegistry] = None
169
+ ):
170
+ """
171
+ :param statsd: a string, eventually with keyword placeholders for the str.format(**labels) call
172
+ :param prom: a string prometheus metric name; or an instantiated prometheus metric object
173
+ """
174
+ self._registry = registry or REGISTRY
175
+ self._documentation = documentation or ''
176
+ self._statsd = statsd
177
+ if not prom:
178
+ parsed_format = list(string.Formatter().parse(statsd))
179
+ # automatically generate a prometheus metric name
180
+ #
181
+ # remove '.{label}' from the string for each `label`
182
+ # substituted dots with underscores
183
+ if labelnames is None:
184
+ labelnames = tuple(field_name for _, field_name, _, _ in parsed_format if field_name)
185
+ prom = ''.join(literal_text.rstrip('.').replace('.', '_') for literal_text, *_ in parsed_format)
186
+ labelnames = labelnames or ()
187
+ if isinstance(prom, str):
188
+ self._prom = self.init_prometheus_metric(prom, self._documentation, labelnames=labelnames)
189
+ else:
190
+ self._prom = prom
191
+
192
+ self._labelnames = labelnames
193
+
194
+ @abstractmethod
195
+ def init_prometheus_metric(self, name: str, documentation: Optional[str], labelnames: 'Sequence[str]' = ()):
196
+ pass
197
+
198
+ def labels(self: _M, **labelkwargs) -> _M:
199
+ if not labelkwargs:
200
+ return self
201
+
202
+ return self.__class__(
203
+ prom=self._prom.labels(**labelkwargs), # type: ignore
204
+ statsd=self._statsd.format(**labelkwargs),
205
+ documentation=self._documentation,
206
+ labelnames=self._labelnames,
207
+ registry=self._registry,
208
+ )
209
+
210
+
211
+ class _MultiCounter(_MultiMetric):
212
+
213
+ def inc(self, delta: int = 1) -> None:
214
+ delta = abs(delta)
215
+ self._prom.inc(delta) # type: ignore
216
+ if STATSD_CLIENT:
217
+ STATSD_CLIENT.incr(self._statsd, delta)
218
+
219
+ def init_prometheus_metric(self, name: str, documentation: str, labelnames: 'Sequence[str]' = ()) -> Counter:
220
+ return Counter(name, documentation, labelnames=labelnames, registry=self._registry)
221
+
222
+
223
+ class _MultiGauge(_MultiMetric):
224
+
225
+ def set(self, value) -> None:
226
+ self._prom.set(value) # type: ignore
227
+ if STATSD_CLIENT:
228
+ STATSD_CLIENT.gauge(self._statsd, value)
229
+
230
+ def init_prometheus_metric(self, name: str, documentation: str, labelnames: 'Sequence[str]' = ()) -> Gauge:
231
+ return Gauge(name, documentation, labelnames=labelnames, registry=self._registry)
232
+
233
+
234
+ class _MultiTiming(_MultiMetric):
235
+
236
+ def __init__(
237
+ self,
238
+ statsd: str,
239
+ prom: Optional[str] = None,
240
+ documentation: Optional[str] = None,
241
+ labelnames: Optional['Sequence[str]'] = None,
242
+ registry: Optional[CollectorRegistry] = None,
243
+ buckets: 'Iterable[float]' = _HISTOGRAM_DEFAULT_BUCKETS,
244
+ ) -> None:
245
+ self._stopwatch = None
246
+ self._histogram_buckets = tuple(buckets)
247
+ super().__init__(statsd, prom, documentation, labelnames, registry)
248
+
249
+ def observe(self, value: float) -> None:
250
+ self._prom.observe(value) # type: ignore
251
+ if STATSD_CLIENT:
252
+ STATSD_CLIENT.timing(self._statsd, value * 1000)
253
+
254
+ def init_prometheus_metric(self, name: str, documentation: str, labelnames: 'Sequence[str]' = ()) -> Histogram:
255
+ return Histogram(name, documentation, labelnames=labelnames, registry=self._registry, buckets=self._histogram_buckets)
256
+
257
+ def __enter__(self):
258
+ self._stopwatch = Stopwatch()
259
+ return self
260
+
261
+ def __exit__(self, typ, value, tb):
262
+ if self._stopwatch:
263
+ self._stopwatch.stop()
264
+ self.observe(self._stopwatch.elapsed)
265
+
266
+
267
+ def _fetch_or_create_metric(
268
+ name: str,
269
+ labelnames: Optional['Sequence[str]'],
270
+ container: dict[str, _T],
271
+ factory: 'Callable[[str, Optional[Sequence[str]]], _T]'
272
+ ) -> "_T":
273
+ metric = container.get(name)
274
+ if not metric:
275
+ with METRICS_LOCK:
276
+ metric = container.get(name)
277
+ if not metric:
278
+ container[name] = metric = factory(name, labelnames)
279
+ return metric
280
+
281
+
282
+ def _fetch_or_create_counter(
283
+ name: str,
284
+ labelnames: Optional['Sequence[str]'] = None,
285
+ documentation: Optional[str] = None,
286
+ registry: Optional[CollectorRegistry] = None,
287
+ ) -> _MultiCounter:
288
+ return _fetch_or_create_metric(
289
+ name=name,
290
+ labelnames=labelnames,
291
+ container=COUNTERS,
292
+ factory=lambda _name, _labelnames: _MultiCounter(statsd=_name, labelnames=_labelnames,
293
+ documentation=documentation, registry=registry)
294
+ )
295
+
296
+
297
+ def _fetch_or_create_gauge(
298
+ name: str,
299
+ labelnames: Optional['Sequence[str]'] = None,
300
+ documentation: Optional[str] = None,
301
+ registry: Optional[CollectorRegistry] = None,
302
+ ) -> _MultiGauge:
303
+ return _fetch_or_create_metric(
304
+ name=name,
305
+ labelnames=labelnames,
306
+ container=GAUGES,
307
+ factory=lambda _name, _labelnames: _MultiGauge(statsd=_name, labelnames=_labelnames,
308
+ documentation=documentation, registry=registry)
309
+ )
310
+
311
+
312
+ def _fetch_or_create_timer(
313
+ name: str,
314
+ labelnames: Optional['Sequence[str]'] = None,
315
+ documentation: Optional[str] = None,
316
+ registry: Optional[CollectorRegistry] = None,
317
+ buckets: 'Iterable[float]' = _HISTOGRAM_DEFAULT_BUCKETS
318
+ ) -> _MultiTiming:
319
+ return _fetch_or_create_metric(
320
+ name=name,
321
+ labelnames=labelnames,
322
+ container=TIMINGS,
323
+ factory=lambda _name, _labels: _MultiTiming(statsd=_name, labelnames=_labels, documentation=documentation,
324
+ registry=registry, buckets=buckets)
325
+ )
326
+
327
+
328
+ class MetricManager:
329
+
330
+ """
331
+ Wrapper for metrics which prefixes them automatically with the given prefix or,
332
+ alternatively, with the path of the module.
333
+ """
334
+
335
+ def __init__(self, prefix: Optional[str] = None, module: Optional[str] = None,
336
+ registry: Optional[CollectorRegistry] = None, push_gateways: Optional['Sequence[str]'] = None):
337
+ if prefix:
338
+ self.prefix = prefix
339
+ elif module:
340
+ self.prefix = module
341
+ else:
342
+ self.prefix = None
343
+ self.registry = registry or REGISTRY
344
+ self.push_gateways = push_gateways or []
345
+
346
+ def full_name(self, name: str) -> str:
347
+ if self.prefix:
348
+ return f'{self.prefix}.{name}'
349
+ return name
350
+
351
+ def get_registry(self) -> CollectorRegistry:
352
+ return self.registry
353
+
354
+ def counter(
355
+ self,
356
+ name: str,
357
+ *,
358
+ labelnames: Optional['Sequence[str]'] = None,
359
+ documentation: Optional[str] = None,
360
+ ) -> _MultiCounter:
361
+ """
362
+ Log a counter.
363
+
364
+ :param name: The name (suffix) of the counter to be retrieved
365
+ :param labelnames: optional labels used to parametrize the metric
366
+ :param documentation: optional prometheus documentation for this metric
367
+ """
368
+ return _fetch_or_create_counter(name=self.full_name(name), labelnames=labelnames, documentation=documentation)
369
+
370
+ def gauge(
371
+ self,
372
+ name: str,
373
+ *,
374
+ labelnames: Optional['Sequence[str]'] = None,
375
+ documentation: Optional[str] = None,
376
+ ) -> _MultiGauge:
377
+ """
378
+ Log gauge information for a single stat
379
+
380
+ :param name: The name (suffix) of the counter to be retrieved
381
+ :param labelnames: optional labels used to parametrize the metric
382
+ :param documentation: optional prometheus documentation for this metric
383
+ """
384
+ return _fetch_or_create_gauge(name=self.full_name(name), labelnames=labelnames, documentation=documentation)
385
+
386
+ def timer(
387
+ self,
388
+ name: str,
389
+ *,
390
+ labelnames: Optional['Sequence[str]'] = None,
391
+ documentation: Optional[str] = None,
392
+ buckets: 'Iterable[float]' = _HISTOGRAM_DEFAULT_BUCKETS
393
+ ) -> _MultiTiming:
394
+ """
395
+ Log a time measurement.
396
+
397
+ :param name: The name (suffix) of the counter to be retrieved
398
+ :param labelnames: optional labels used to parametrize the metric
399
+ :param documentation: optional prometheus documentation for this metric
400
+ :param buckets: Optional iterable of histogram bucket separators.
401
+ """
402
+ return _fetch_or_create_timer(name=self.full_name(name), labelnames=labelnames, documentation=documentation, buckets=buckets)
403
+
404
+ def time_it(self, original_function=None, *, buckets=_HISTOGRAM_DEFAULT_BUCKETS):
405
+ """
406
+ Function decorator which records a timer: the amount of time spent in the function.
407
+ """
408
+ def _decorator(func):
409
+ @wraps(func)
410
+ def _wrapper(*args, **kwargs):
411
+ with self.timer(name=func.__name__, buckets=buckets):
412
+ return func(*args, **kwargs)
413
+ return _wrapper
414
+ if original_function:
415
+ return _decorator(original_function)
416
+ return _decorator
417
+
418
+ def count_it(self, original_function=None):
419
+ """
420
+ Function decorator which records a counter: how many times the function was executed.
421
+ """
422
+ def _decorator(func):
423
+ @wraps(func)
424
+ def _wrapper(*args, **kwargs):
425
+ try:
426
+ return func(*args, **kwargs)
427
+ finally:
428
+ _fetch_or_create_counter(name=self.full_name(func.__name__) + '_cnt').inc()
429
+ return _wrapper
430
+ if original_function:
431
+ return _decorator(original_function)
432
+ return _decorator
433
+
434
+ def push_metrics_to_gw(self, job: Optional[str] = None, grouping_key: Optional[dict[str, Any]] = None) -> None:
435
+ """
436
+ Push the metrics out to the prometheus push gateways. This is useful for short-running programs which don't
437
+ live long enough to be reliably scraped in the prometheus pull model.
438
+ """
439
+
440
+ if not job:
441
+ job = Path(main.__file__).stem
442
+ grouping_key = grouping_key or {}
443
+
444
+ for server in self.push_gateways:
445
+ try:
446
+ push_to_gateway(server.strip(), job=job, registry=self.registry, grouping_key=grouping_key)
447
+ except:
448
+ continue
@@ -0,0 +1,195 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from re import compile, error, match
16
+ from traceback import format_exc
17
+ from typing import TYPE_CHECKING, Any, Optional, cast
18
+
19
+ from dogpile.cache.api import NO_VALUE
20
+ from sqlalchemy import and_, delete, select
21
+ from sqlalchemy.exc import IntegrityError
22
+
23
+ from rucio.common.cache import MemcacheRegion
24
+ from rucio.common.exception import Duplicate, InvalidObject, RucioException
25
+ from rucio.db.sqla import models
26
+ from rucio.db.sqla.constants import KeyType
27
+ from rucio.db.sqla.session import read_session, transactional_session
28
+
29
+ if TYPE_CHECKING:
30
+ from typing import TypedDict
31
+
32
+ from sqlalchemy.orm import Session
33
+
34
+ from rucio.common.types import InternalScope
35
+
36
+ class NamingConventionDict(TypedDict):
37
+ scope: InternalScope
38
+ regexp: str
39
+
40
+ REGION = MemcacheRegion(expiration_time=900)
41
+
42
+
43
+ @transactional_session
44
+ def add_naming_convention(
45
+ scope: "InternalScope",
46
+ regexp: str,
47
+ convention_type: KeyType,
48
+ *,
49
+ session: "Session"
50
+ ) -> None:
51
+ """
52
+ add a naming convention for a given scope
53
+
54
+ :param scope: the name for the scope.
55
+ :param regexp: the regular expression to validate the name.
56
+ :param convention_type: the did_type on which the regexp should apply.
57
+ :param session: The database session in use.
58
+ """
59
+ # validate the regular expression
60
+ try:
61
+ compile(regexp)
62
+ except error:
63
+ raise RucioException('Invalid regular expression %s!' % regexp)
64
+
65
+ new_convention = models.NamingConvention(scope=scope,
66
+ regexp=regexp,
67
+ convention_type=convention_type)
68
+ try:
69
+ new_convention.save(session=session)
70
+ except IntegrityError:
71
+ raise Duplicate('Naming convention already exists!')
72
+ except:
73
+ raise RucioException(str(format_exc()))
74
+
75
+
76
+ @read_session
77
+ def get_naming_convention(
78
+ scope: "InternalScope",
79
+ convention_type: KeyType,
80
+ *,
81
+ session: "Session"
82
+ ) -> Optional[str]:
83
+ """
84
+ Get the naming convention for a given scope
85
+
86
+ :param scope: the name for the scope.
87
+ :param convention_type: the did_type on which the regexp should apply.
88
+ :param session: The database session in use.
89
+
90
+ :returns: the regular expression.
91
+ """
92
+ stmt = select(
93
+ models.NamingConvention.regexp
94
+ ).where(
95
+ and_(models.NamingConvention.scope == scope,
96
+ models.NamingConvention.convention_type == convention_type)
97
+ )
98
+ return session.execute(stmt).scalar()
99
+
100
+
101
+ @transactional_session
102
+ def delete_naming_convention(
103
+ scope: "InternalScope",
104
+ convention_type: KeyType,
105
+ *,
106
+ session: "Session"
107
+ ) -> int:
108
+ """
109
+ delete a naming convention for a given scope
110
+
111
+ :param scope: the name for the scope.
112
+ :param regexp: the regular expression to validate the name. (DEPRECATED)
113
+ :param convention_type: the did_type on which the regexp should apply.
114
+ :param session: The database session in use.
115
+ """
116
+ if scope.internal is not None:
117
+ REGION.delete(scope.internal)
118
+ stmt = delete(
119
+ models.NamingConvention
120
+ ).where(
121
+ and_(models.NamingConvention.scope == scope,
122
+ models.NamingConvention.convention_type == convention_type)
123
+ )
124
+ return session.execute(stmt).rowcount
125
+
126
+
127
+ @read_session
128
+ def list_naming_conventions(*, session: "Session") -> list["NamingConventionDict"]:
129
+ """
130
+ List all naming conventions.
131
+
132
+ :param session: The database session in use.
133
+
134
+ :returns: a list of dictionaries.
135
+ """
136
+ stmt = select(
137
+ models.NamingConvention.scope,
138
+ models.NamingConvention.regexp
139
+ )
140
+ return [cast("NamingConventionDict", row._asdict()) for row in session.execute(stmt).all()]
141
+
142
+
143
+ @read_session
144
+ def validate_name(
145
+ scope: "InternalScope",
146
+ name: str,
147
+ did_type: str,
148
+ *,
149
+ session: "Session"
150
+ ) -> Optional[dict[str, Any]]:
151
+ """
152
+ Validate a name according to a naming convention.
153
+
154
+ :param scope: the name for the scope.
155
+ :param name: the name.
156
+ :param did_type: the type of did.
157
+
158
+ :param session: The database session in use.
159
+
160
+ :returns: a dictionary with metadata.
161
+ """
162
+ if scope.external is not None:
163
+ if scope.external.startswith('user'):
164
+ return {'project': 'user'}
165
+ elif scope.external.startswith('group'):
166
+ return {'project': 'group'}
167
+
168
+ # Check if naming convention can be found in cache region
169
+ regexp = REGION.get(scope.internal)
170
+ if regexp is NO_VALUE: # no cached entry found
171
+ regexp = get_naming_convention(scope=scope,
172
+ convention_type=KeyType.DATASET,
173
+ session=session)
174
+ if scope.internal is not None:
175
+ regexp and REGION.set(scope.internal, regexp)
176
+
177
+ if not regexp:
178
+ return
179
+
180
+ # Validate with regexp
181
+ groups = match(regexp, str(name)) # type: ignore
182
+ if groups:
183
+ meta = groups.groupdict()
184
+ # Hack to get task_id from version
185
+ if 'version' in meta and meta['version']:
186
+ matched = match(r'(?P<version>\w+)_tid(?P<task_id>\d+)_\w+$', meta['version'])
187
+ if matched:
188
+ meta['version'] = matched.groupdict()['version']
189
+ meta['task_id'] = int(matched.groupdict()['task_id'])
190
+ if 'run_number' in meta and meta['run_number']:
191
+ meta['run_number'] = int(meta['run_number'])
192
+ return meta
193
+
194
+ print(f"Provided name {name} doesn't match the naming convention {regexp}")
195
+ raise InvalidObject(f"Provided name {name} doesn't match the naming convention {regexp}")