rucio 37.0.0rc1__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (487) hide show
  1. rucio/__init__.py +17 -0
  2. rucio/alembicrevision.py +15 -0
  3. rucio/cli/__init__.py +14 -0
  4. rucio/cli/account.py +216 -0
  5. rucio/cli/bin_legacy/__init__.py +13 -0
  6. rucio/cli/bin_legacy/rucio.py +2825 -0
  7. rucio/cli/bin_legacy/rucio_admin.py +2500 -0
  8. rucio/cli/command.py +272 -0
  9. rucio/cli/config.py +72 -0
  10. rucio/cli/did.py +191 -0
  11. rucio/cli/download.py +128 -0
  12. rucio/cli/lifetime_exception.py +33 -0
  13. rucio/cli/replica.py +162 -0
  14. rucio/cli/rse.py +293 -0
  15. rucio/cli/rule.py +158 -0
  16. rucio/cli/scope.py +40 -0
  17. rucio/cli/subscription.py +73 -0
  18. rucio/cli/upload.py +60 -0
  19. rucio/cli/utils.py +226 -0
  20. rucio/client/__init__.py +15 -0
  21. rucio/client/accountclient.py +432 -0
  22. rucio/client/accountlimitclient.py +183 -0
  23. rucio/client/baseclient.py +983 -0
  24. rucio/client/client.py +120 -0
  25. rucio/client/configclient.py +126 -0
  26. rucio/client/credentialclient.py +59 -0
  27. rucio/client/didclient.py +868 -0
  28. rucio/client/diracclient.py +56 -0
  29. rucio/client/downloadclient.py +1783 -0
  30. rucio/client/exportclient.py +44 -0
  31. rucio/client/fileclient.py +50 -0
  32. rucio/client/importclient.py +42 -0
  33. rucio/client/lifetimeclient.py +90 -0
  34. rucio/client/lockclient.py +109 -0
  35. rucio/client/metaconventionsclient.py +140 -0
  36. rucio/client/pingclient.py +44 -0
  37. rucio/client/replicaclient.py +452 -0
  38. rucio/client/requestclient.py +125 -0
  39. rucio/client/richclient.py +317 -0
  40. rucio/client/rseclient.py +746 -0
  41. rucio/client/ruleclient.py +294 -0
  42. rucio/client/scopeclient.py +90 -0
  43. rucio/client/subscriptionclient.py +173 -0
  44. rucio/client/touchclient.py +82 -0
  45. rucio/client/uploadclient.py +969 -0
  46. rucio/common/__init__.py +13 -0
  47. rucio/common/bittorrent.py +234 -0
  48. rucio/common/cache.py +111 -0
  49. rucio/common/checksum.py +168 -0
  50. rucio/common/client.py +122 -0
  51. rucio/common/config.py +788 -0
  52. rucio/common/constants.py +217 -0
  53. rucio/common/constraints.py +17 -0
  54. rucio/common/didtype.py +237 -0
  55. rucio/common/dumper/__init__.py +342 -0
  56. rucio/common/dumper/consistency.py +497 -0
  57. rucio/common/dumper/data_models.py +362 -0
  58. rucio/common/dumper/path_parsing.py +75 -0
  59. rucio/common/exception.py +1208 -0
  60. rucio/common/extra.py +31 -0
  61. rucio/common/logging.py +420 -0
  62. rucio/common/pcache.py +1409 -0
  63. rucio/common/plugins.py +185 -0
  64. rucio/common/policy.py +93 -0
  65. rucio/common/schema/__init__.py +200 -0
  66. rucio/common/schema/generic.py +416 -0
  67. rucio/common/schema/generic_multi_vo.py +395 -0
  68. rucio/common/stomp_utils.py +423 -0
  69. rucio/common/stopwatch.py +55 -0
  70. rucio/common/test_rucio_server.py +154 -0
  71. rucio/common/types.py +483 -0
  72. rucio/common/utils.py +1688 -0
  73. rucio/core/__init__.py +13 -0
  74. rucio/core/account.py +496 -0
  75. rucio/core/account_counter.py +236 -0
  76. rucio/core/account_limit.py +425 -0
  77. rucio/core/authentication.py +620 -0
  78. rucio/core/config.py +437 -0
  79. rucio/core/credential.py +224 -0
  80. rucio/core/did.py +3004 -0
  81. rucio/core/did_meta_plugins/__init__.py +252 -0
  82. rucio/core/did_meta_plugins/did_column_meta.py +331 -0
  83. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +165 -0
  84. rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
  85. rucio/core/did_meta_plugins/filter_engine.py +672 -0
  86. rucio/core/did_meta_plugins/json_meta.py +240 -0
  87. rucio/core/did_meta_plugins/mongo_meta.py +229 -0
  88. rucio/core/did_meta_plugins/postgres_meta.py +352 -0
  89. rucio/core/dirac.py +237 -0
  90. rucio/core/distance.py +187 -0
  91. rucio/core/exporter.py +59 -0
  92. rucio/core/heartbeat.py +363 -0
  93. rucio/core/identity.py +301 -0
  94. rucio/core/importer.py +260 -0
  95. rucio/core/lifetime_exception.py +377 -0
  96. rucio/core/lock.py +577 -0
  97. rucio/core/message.py +288 -0
  98. rucio/core/meta_conventions.py +203 -0
  99. rucio/core/monitor.py +448 -0
  100. rucio/core/naming_convention.py +195 -0
  101. rucio/core/nongrid_trace.py +136 -0
  102. rucio/core/oidc.py +1463 -0
  103. rucio/core/permission/__init__.py +161 -0
  104. rucio/core/permission/generic.py +1124 -0
  105. rucio/core/permission/generic_multi_vo.py +1144 -0
  106. rucio/core/quarantined_replica.py +224 -0
  107. rucio/core/replica.py +4483 -0
  108. rucio/core/replica_sorter.py +362 -0
  109. rucio/core/request.py +3091 -0
  110. rucio/core/rse.py +2079 -0
  111. rucio/core/rse_counter.py +185 -0
  112. rucio/core/rse_expression_parser.py +459 -0
  113. rucio/core/rse_selector.py +304 -0
  114. rucio/core/rule.py +4484 -0
  115. rucio/core/rule_grouping.py +1620 -0
  116. rucio/core/scope.py +181 -0
  117. rucio/core/subscription.py +362 -0
  118. rucio/core/topology.py +490 -0
  119. rucio/core/trace.py +375 -0
  120. rucio/core/transfer.py +1531 -0
  121. rucio/core/vo.py +169 -0
  122. rucio/core/volatile_replica.py +151 -0
  123. rucio/daemons/__init__.py +13 -0
  124. rucio/daemons/abacus/__init__.py +13 -0
  125. rucio/daemons/abacus/account.py +116 -0
  126. rucio/daemons/abacus/collection_replica.py +124 -0
  127. rucio/daemons/abacus/rse.py +117 -0
  128. rucio/daemons/atropos/__init__.py +13 -0
  129. rucio/daemons/atropos/atropos.py +242 -0
  130. rucio/daemons/auditor/__init__.py +289 -0
  131. rucio/daemons/auditor/hdfs.py +97 -0
  132. rucio/daemons/auditor/srmdumps.py +355 -0
  133. rucio/daemons/automatix/__init__.py +13 -0
  134. rucio/daemons/automatix/automatix.py +304 -0
  135. rucio/daemons/badreplicas/__init__.py +13 -0
  136. rucio/daemons/badreplicas/minos.py +322 -0
  137. rucio/daemons/badreplicas/minos_temporary_expiration.py +171 -0
  138. rucio/daemons/badreplicas/necromancer.py +196 -0
  139. rucio/daemons/bb8/__init__.py +13 -0
  140. rucio/daemons/bb8/bb8.py +353 -0
  141. rucio/daemons/bb8/common.py +759 -0
  142. rucio/daemons/bb8/nuclei_background_rebalance.py +153 -0
  143. rucio/daemons/bb8/t2_background_rebalance.py +153 -0
  144. rucio/daemons/cache/__init__.py +13 -0
  145. rucio/daemons/cache/consumer.py +133 -0
  146. rucio/daemons/common.py +405 -0
  147. rucio/daemons/conveyor/__init__.py +13 -0
  148. rucio/daemons/conveyor/common.py +562 -0
  149. rucio/daemons/conveyor/finisher.py +529 -0
  150. rucio/daemons/conveyor/poller.py +394 -0
  151. rucio/daemons/conveyor/preparer.py +205 -0
  152. rucio/daemons/conveyor/receiver.py +179 -0
  153. rucio/daemons/conveyor/stager.py +133 -0
  154. rucio/daemons/conveyor/submitter.py +403 -0
  155. rucio/daemons/conveyor/throttler.py +532 -0
  156. rucio/daemons/follower/__init__.py +13 -0
  157. rucio/daemons/follower/follower.py +101 -0
  158. rucio/daemons/hermes/__init__.py +13 -0
  159. rucio/daemons/hermes/hermes.py +534 -0
  160. rucio/daemons/judge/__init__.py +13 -0
  161. rucio/daemons/judge/cleaner.py +159 -0
  162. rucio/daemons/judge/evaluator.py +185 -0
  163. rucio/daemons/judge/injector.py +162 -0
  164. rucio/daemons/judge/repairer.py +154 -0
  165. rucio/daemons/oauthmanager/__init__.py +13 -0
  166. rucio/daemons/oauthmanager/oauthmanager.py +198 -0
  167. rucio/daemons/reaper/__init__.py +13 -0
  168. rucio/daemons/reaper/dark_reaper.py +282 -0
  169. rucio/daemons/reaper/reaper.py +739 -0
  170. rucio/daemons/replicarecoverer/__init__.py +13 -0
  171. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +626 -0
  172. rucio/daemons/rsedecommissioner/__init__.py +13 -0
  173. rucio/daemons/rsedecommissioner/config.py +81 -0
  174. rucio/daemons/rsedecommissioner/profiles/__init__.py +24 -0
  175. rucio/daemons/rsedecommissioner/profiles/atlas.py +60 -0
  176. rucio/daemons/rsedecommissioner/profiles/generic.py +452 -0
  177. rucio/daemons/rsedecommissioner/profiles/types.py +93 -0
  178. rucio/daemons/rsedecommissioner/rse_decommissioner.py +280 -0
  179. rucio/daemons/storage/__init__.py +13 -0
  180. rucio/daemons/storage/consistency/__init__.py +13 -0
  181. rucio/daemons/storage/consistency/actions.py +848 -0
  182. rucio/daemons/tracer/__init__.py +13 -0
  183. rucio/daemons/tracer/kronos.py +511 -0
  184. rucio/daemons/transmogrifier/__init__.py +13 -0
  185. rucio/daemons/transmogrifier/transmogrifier.py +762 -0
  186. rucio/daemons/undertaker/__init__.py +13 -0
  187. rucio/daemons/undertaker/undertaker.py +137 -0
  188. rucio/db/__init__.py +13 -0
  189. rucio/db/sqla/__init__.py +52 -0
  190. rucio/db/sqla/constants.py +206 -0
  191. rucio/db/sqla/migrate_repo/__init__.py +13 -0
  192. rucio/db/sqla/migrate_repo/env.py +110 -0
  193. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +70 -0
  194. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +47 -0
  195. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +59 -0
  196. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +43 -0
  197. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +91 -0
  198. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +76 -0
  199. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +43 -0
  200. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +50 -0
  201. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +68 -0
  202. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +40 -0
  203. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +45 -0
  204. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +60 -0
  205. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +40 -0
  206. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +140 -0
  207. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +73 -0
  208. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +74 -0
  209. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +43 -0
  210. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +50 -0
  211. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +134 -0
  212. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +64 -0
  213. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +39 -0
  214. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +64 -0
  215. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +51 -0
  216. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +41 -0
  217. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +43 -0
  218. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +44 -0
  219. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +53 -0
  220. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +38 -0
  221. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +47 -0
  222. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +45 -0
  223. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +45 -0
  224. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +57 -0
  225. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +45 -0
  226. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +69 -0
  227. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +43 -0
  228. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +42 -0
  229. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +47 -0
  230. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +46 -0
  231. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +40 -0
  232. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +67 -0
  233. rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
  234. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +44 -0
  235. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +77 -0
  236. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +60 -0
  237. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +72 -0
  238. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +42 -0
  239. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +65 -0
  240. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +133 -0
  241. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +55 -0
  242. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +76 -0
  243. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +60 -0
  244. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +44 -0
  245. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +43 -0
  246. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +64 -0
  247. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +40 -0
  248. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +43 -0
  249. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +44 -0
  250. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +78 -0
  251. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +41 -0
  252. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +44 -0
  254. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +43 -0
  255. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +49 -0
  256. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +40 -0
  257. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +63 -0
  258. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +43 -0
  259. rucio/db/sqla/migrate_repo/versions/4df2c5ddabc0_remove_temporary_dids.py +55 -0
  260. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +45 -0
  261. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +43 -0
  262. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +43 -0
  263. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +45 -0
  264. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +47 -0
  265. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +58 -0
  266. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +45 -0
  267. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +106 -0
  268. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +55 -0
  269. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +50 -0
  270. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +47 -0
  271. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +43 -0
  272. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +41 -0
  273. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +91 -0
  274. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +72 -0
  275. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +49 -0
  276. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +43 -0
  277. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +43 -0
  278. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +53 -0
  279. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +45 -0
  280. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +68 -0
  281. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +45 -0
  282. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +94 -0
  283. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +54 -0
  284. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +72 -0
  285. rucio/db/sqla/migrate_repo/versions/a08fa8de1545_transfer_stats_table.py +55 -0
  286. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +76 -0
  287. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +47 -0
  288. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +121 -0
  289. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +59 -0
  290. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +52 -0
  291. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +54 -0
  292. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +64 -0
  293. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +49 -0
  294. rucio/db/sqla/migrate_repo/versions/b0070f3695c8_add_deletedidmeta_table.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +43 -0
  296. rucio/db/sqla/migrate_repo/versions/b5493606bbf5_fix_primary_key_for_subscription_history.py +41 -0
  297. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +91 -0
  298. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +40 -0
  299. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +43 -0
  300. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +143 -0
  301. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +76 -0
  302. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +50 -0
  303. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +72 -0
  304. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +55 -0
  305. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +43 -0
  306. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +65 -0
  307. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +47 -0
  308. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +146 -0
  309. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +104 -0
  310. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +44 -0
  311. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +43 -0
  312. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +103 -0
  313. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +49 -0
  314. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +104 -0
  315. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +29 -0
  316. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +74 -0
  317. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +47 -0
  318. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +43 -0
  319. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +37 -0
  320. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +43 -0
  321. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +43 -0
  322. rucio/db/sqla/models.py +1743 -0
  323. rucio/db/sqla/sautils.py +55 -0
  324. rucio/db/sqla/session.py +529 -0
  325. rucio/db/sqla/types.py +206 -0
  326. rucio/db/sqla/util.py +543 -0
  327. rucio/gateway/__init__.py +13 -0
  328. rucio/gateway/account.py +345 -0
  329. rucio/gateway/account_limit.py +363 -0
  330. rucio/gateway/authentication.py +381 -0
  331. rucio/gateway/config.py +227 -0
  332. rucio/gateway/credential.py +70 -0
  333. rucio/gateway/did.py +987 -0
  334. rucio/gateway/dirac.py +83 -0
  335. rucio/gateway/exporter.py +60 -0
  336. rucio/gateway/heartbeat.py +76 -0
  337. rucio/gateway/identity.py +189 -0
  338. rucio/gateway/importer.py +46 -0
  339. rucio/gateway/lifetime_exception.py +121 -0
  340. rucio/gateway/lock.py +153 -0
  341. rucio/gateway/meta_conventions.py +98 -0
  342. rucio/gateway/permission.py +74 -0
  343. rucio/gateway/quarantined_replica.py +79 -0
  344. rucio/gateway/replica.py +538 -0
  345. rucio/gateway/request.py +330 -0
  346. rucio/gateway/rse.py +632 -0
  347. rucio/gateway/rule.py +437 -0
  348. rucio/gateway/scope.py +100 -0
  349. rucio/gateway/subscription.py +280 -0
  350. rucio/gateway/vo.py +126 -0
  351. rucio/rse/__init__.py +96 -0
  352. rucio/rse/protocols/__init__.py +13 -0
  353. rucio/rse/protocols/bittorrent.py +194 -0
  354. rucio/rse/protocols/cache.py +111 -0
  355. rucio/rse/protocols/dummy.py +100 -0
  356. rucio/rse/protocols/gfal.py +708 -0
  357. rucio/rse/protocols/globus.py +243 -0
  358. rucio/rse/protocols/http_cache.py +82 -0
  359. rucio/rse/protocols/mock.py +123 -0
  360. rucio/rse/protocols/ngarc.py +209 -0
  361. rucio/rse/protocols/posix.py +250 -0
  362. rucio/rse/protocols/protocol.py +361 -0
  363. rucio/rse/protocols/rclone.py +365 -0
  364. rucio/rse/protocols/rfio.py +145 -0
  365. rucio/rse/protocols/srm.py +338 -0
  366. rucio/rse/protocols/ssh.py +414 -0
  367. rucio/rse/protocols/storm.py +195 -0
  368. rucio/rse/protocols/webdav.py +594 -0
  369. rucio/rse/protocols/xrootd.py +302 -0
  370. rucio/rse/rsemanager.py +881 -0
  371. rucio/rse/translation.py +260 -0
  372. rucio/tests/__init__.py +13 -0
  373. rucio/tests/common.py +280 -0
  374. rucio/tests/common_server.py +149 -0
  375. rucio/transfertool/__init__.py +13 -0
  376. rucio/transfertool/bittorrent.py +200 -0
  377. rucio/transfertool/bittorrent_driver.py +50 -0
  378. rucio/transfertool/bittorrent_driver_qbittorrent.py +134 -0
  379. rucio/transfertool/fts3.py +1600 -0
  380. rucio/transfertool/fts3_plugins.py +152 -0
  381. rucio/transfertool/globus.py +201 -0
  382. rucio/transfertool/globus_library.py +181 -0
  383. rucio/transfertool/mock.py +89 -0
  384. rucio/transfertool/transfertool.py +221 -0
  385. rucio/vcsversion.py +11 -0
  386. rucio/version.py +45 -0
  387. rucio/web/__init__.py +13 -0
  388. rucio/web/rest/__init__.py +13 -0
  389. rucio/web/rest/flaskapi/__init__.py +13 -0
  390. rucio/web/rest/flaskapi/authenticated_bp.py +27 -0
  391. rucio/web/rest/flaskapi/v1/__init__.py +13 -0
  392. rucio/web/rest/flaskapi/v1/accountlimits.py +236 -0
  393. rucio/web/rest/flaskapi/v1/accounts.py +1103 -0
  394. rucio/web/rest/flaskapi/v1/archives.py +102 -0
  395. rucio/web/rest/flaskapi/v1/auth.py +1644 -0
  396. rucio/web/rest/flaskapi/v1/common.py +426 -0
  397. rucio/web/rest/flaskapi/v1/config.py +304 -0
  398. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  399. rucio/web/rest/flaskapi/v1/dids.py +2340 -0
  400. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  401. rucio/web/rest/flaskapi/v1/export.py +75 -0
  402. rucio/web/rest/flaskapi/v1/heartbeats.py +127 -0
  403. rucio/web/rest/flaskapi/v1/identities.py +285 -0
  404. rucio/web/rest/flaskapi/v1/import.py +132 -0
  405. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +312 -0
  406. rucio/web/rest/flaskapi/v1/locks.py +358 -0
  407. rucio/web/rest/flaskapi/v1/main.py +91 -0
  408. rucio/web/rest/flaskapi/v1/meta_conventions.py +241 -0
  409. rucio/web/rest/flaskapi/v1/metrics.py +36 -0
  410. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  411. rucio/web/rest/flaskapi/v1/ping.py +88 -0
  412. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  413. rucio/web/rest/flaskapi/v1/replicas.py +1894 -0
  414. rucio/web/rest/flaskapi/v1/requests.py +998 -0
  415. rucio/web/rest/flaskapi/v1/rses.py +2250 -0
  416. rucio/web/rest/flaskapi/v1/rules.py +854 -0
  417. rucio/web/rest/flaskapi/v1/scopes.py +159 -0
  418. rucio/web/rest/flaskapi/v1/subscriptions.py +650 -0
  419. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  420. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  421. rucio/web/rest/flaskapi/v1/traces.py +137 -0
  422. rucio/web/rest/flaskapi/v1/types.py +20 -0
  423. rucio/web/rest/flaskapi/v1/vos.py +278 -0
  424. rucio/web/rest/main.py +18 -0
  425. rucio/web/rest/metrics.py +27 -0
  426. rucio/web/rest/ping.py +27 -0
  427. rucio-37.0.0rc1.data/data/rucio/etc/alembic.ini.template +71 -0
  428. rucio-37.0.0rc1.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  429. rucio-37.0.0rc1.data/data/rucio/etc/globus-config.yml.template +5 -0
  430. rucio-37.0.0rc1.data/data/rucio/etc/ldap.cfg.template +30 -0
  431. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  432. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  433. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  434. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  435. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  436. rucio-37.0.0rc1.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  437. rucio-37.0.0rc1.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  438. rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.atlas.client.template +43 -0
  439. rucio-37.0.0rc1.data/data/rucio/etc/rucio.cfg.template +241 -0
  440. rucio-37.0.0rc1.data/data/rucio/etc/rucio_multi_vo.cfg.template +217 -0
  441. rucio-37.0.0rc1.data/data/rucio/requirements.server.txt +297 -0
  442. rucio-37.0.0rc1.data/data/rucio/tools/bootstrap.py +34 -0
  443. rucio-37.0.0rc1.data/data/rucio/tools/merge_rucio_configs.py +144 -0
  444. rucio-37.0.0rc1.data/data/rucio/tools/reset_database.py +40 -0
  445. rucio-37.0.0rc1.data/scripts/rucio +133 -0
  446. rucio-37.0.0rc1.data/scripts/rucio-abacus-account +74 -0
  447. rucio-37.0.0rc1.data/scripts/rucio-abacus-collection-replica +46 -0
  448. rucio-37.0.0rc1.data/scripts/rucio-abacus-rse +78 -0
  449. rucio-37.0.0rc1.data/scripts/rucio-admin +97 -0
  450. rucio-37.0.0rc1.data/scripts/rucio-atropos +60 -0
  451. rucio-37.0.0rc1.data/scripts/rucio-auditor +206 -0
  452. rucio-37.0.0rc1.data/scripts/rucio-automatix +50 -0
  453. rucio-37.0.0rc1.data/scripts/rucio-bb8 +57 -0
  454. rucio-37.0.0rc1.data/scripts/rucio-cache-client +141 -0
  455. rucio-37.0.0rc1.data/scripts/rucio-cache-consumer +42 -0
  456. rucio-37.0.0rc1.data/scripts/rucio-conveyor-finisher +58 -0
  457. rucio-37.0.0rc1.data/scripts/rucio-conveyor-poller +66 -0
  458. rucio-37.0.0rc1.data/scripts/rucio-conveyor-preparer +37 -0
  459. rucio-37.0.0rc1.data/scripts/rucio-conveyor-receiver +44 -0
  460. rucio-37.0.0rc1.data/scripts/rucio-conveyor-stager +76 -0
  461. rucio-37.0.0rc1.data/scripts/rucio-conveyor-submitter +139 -0
  462. rucio-37.0.0rc1.data/scripts/rucio-conveyor-throttler +104 -0
  463. rucio-37.0.0rc1.data/scripts/rucio-dark-reaper +53 -0
  464. rucio-37.0.0rc1.data/scripts/rucio-dumper +160 -0
  465. rucio-37.0.0rc1.data/scripts/rucio-follower +44 -0
  466. rucio-37.0.0rc1.data/scripts/rucio-hermes +54 -0
  467. rucio-37.0.0rc1.data/scripts/rucio-judge-cleaner +89 -0
  468. rucio-37.0.0rc1.data/scripts/rucio-judge-evaluator +137 -0
  469. rucio-37.0.0rc1.data/scripts/rucio-judge-injector +44 -0
  470. rucio-37.0.0rc1.data/scripts/rucio-judge-repairer +44 -0
  471. rucio-37.0.0rc1.data/scripts/rucio-kronos +44 -0
  472. rucio-37.0.0rc1.data/scripts/rucio-minos +53 -0
  473. rucio-37.0.0rc1.data/scripts/rucio-minos-temporary-expiration +50 -0
  474. rucio-37.0.0rc1.data/scripts/rucio-necromancer +120 -0
  475. rucio-37.0.0rc1.data/scripts/rucio-oauth-manager +63 -0
  476. rucio-37.0.0rc1.data/scripts/rucio-reaper +83 -0
  477. rucio-37.0.0rc1.data/scripts/rucio-replica-recoverer +248 -0
  478. rucio-37.0.0rc1.data/scripts/rucio-rse-decommissioner +66 -0
  479. rucio-37.0.0rc1.data/scripts/rucio-storage-consistency-actions +74 -0
  480. rucio-37.0.0rc1.data/scripts/rucio-transmogrifier +77 -0
  481. rucio-37.0.0rc1.data/scripts/rucio-undertaker +76 -0
  482. rucio-37.0.0rc1.dist-info/METADATA +92 -0
  483. rucio-37.0.0rc1.dist-info/RECORD +487 -0
  484. rucio-37.0.0rc1.dist-info/WHEEL +5 -0
  485. rucio-37.0.0rc1.dist-info/licenses/AUTHORS.rst +100 -0
  486. rucio-37.0.0rc1.dist-info/licenses/LICENSE +201 -0
  487. rucio-37.0.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,304 @@
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 functools
16
+ import logging
17
+ import random
18
+ import secrets
19
+ import tempfile
20
+ import threading
21
+ from configparser import NoOptionError, NoSectionError
22
+ from datetime import datetime
23
+ from json import load
24
+ from os import remove, rmdir
25
+ from typing import TYPE_CHECKING, Any, Optional
26
+
27
+ import rucio.db.sqla.util
28
+ from rucio.client import Client
29
+ from rucio.client.uploadclient import UploadClient
30
+ from rucio.common import exception
31
+ from rucio.common.config import config_get, config_get_bool, config_get_int, config_get_list
32
+ from rucio.common.logging import setup_logging
33
+ from rucio.common.stopwatch import Stopwatch
34
+ from rucio.common.types import InternalScope, LoggerFunction
35
+ from rucio.common.utils import execute, generate_uuid
36
+ from rucio.core.monitor import MetricManager
37
+ from rucio.core.scope import list_scopes
38
+ from rucio.core.vo import map_vo
39
+ from rucio.daemons.common import HeartbeatHandler, run_daemon
40
+
41
+ if TYPE_CHECKING:
42
+ from collections.abc import Mapping
43
+ from types import FrameType
44
+
45
+
46
+ METRICS = MetricManager(module=__name__)
47
+ graceful_stop = threading.Event()
48
+ DAEMON_NAME = "automatix"
49
+
50
+
51
+ def get_data_distribution(
52
+ inputfile: str
53
+ ) -> tuple[dict[str, int], dict[str, Any]]:
54
+ with open(inputfile) as data_file:
55
+ data = load(data_file)
56
+ probabilities = {}
57
+ probability = 0
58
+ for key in data:
59
+ probability += data[key]["probability"]
60
+ probabilities[key] = probability
61
+ for key in list(probabilities):
62
+ probabilities[key] = float(probabilities[key]) / probability
63
+ return probabilities, data
64
+
65
+
66
+ def choose_element(
67
+ probabilities: "Mapping[str, int]",
68
+ data: "Mapping[str, Any]"
69
+ ) -> dict[str, Any]:
70
+ rnd = random.uniform(0, 1) # noqa: S311
71
+ prob = 0
72
+ for key in probabilities:
73
+ prob = probabilities[key]
74
+ if prob >= rnd:
75
+ return data[key]
76
+ return data[key]
77
+
78
+
79
+ def generate_file(
80
+ fname: str,
81
+ size: int,
82
+ logger: LoggerFunction = logging.log
83
+ ) -> int:
84
+ cmd = "/bin/dd if=/dev/urandom of=%s bs=%s count=1" % (fname, size)
85
+ exitcode, out, err = execute(cmd)
86
+ logger(logging.DEBUG, out)
87
+ logger(logging.DEBUG, err)
88
+ return exitcode
89
+
90
+
91
+ def generate_didname(
92
+ metadata: "Mapping[str, str]",
93
+ dsn: Optional[str],
94
+ did_type: str
95
+ ) -> str:
96
+ try:
97
+ did_prefix = config_get("automatix", "did_prefix")
98
+ except (NoOptionError, NoSectionError, RuntimeError):
99
+ did_prefix = ""
100
+ try:
101
+ pattern = config_get("automatix", "%s_pattern" % did_type)
102
+ separator = config_get("automatix", "separator")
103
+ except (NoOptionError, NoSectionError, RuntimeError):
104
+ return generate_uuid()
105
+ fields = pattern.split(separator)
106
+ file_name = ""
107
+ for field in fields:
108
+ if field == 'date':
109
+ field_str = str(datetime.utcnow().date())
110
+ elif field == 'did_prefix':
111
+ field_str = did_prefix
112
+ elif field == "dsn":
113
+ field_str = dsn
114
+ elif field == "uuid":
115
+ field_str = generate_uuid()
116
+ elif field == "randint":
117
+ field_str = str(secrets.choice(range(0, 100001)))
118
+ else:
119
+ field_str = metadata.get(field, None)
120
+ if not field_str:
121
+ field_str = str(secrets.choice(range(0, 100001)))
122
+ file_name = "%s%s%s" % (file_name, separator, field_str)
123
+ len_separator = len(separator)
124
+ return file_name[len_separator:]
125
+
126
+
127
+ def automatix(inputfile: str, sleep_time: int, once: bool = False) -> None:
128
+ """
129
+ Creates an automatix Worker that uploads datasets to a list of rses.
130
+
131
+ :param inputfile: The input file where the parameters of the distribution is set
132
+ :param sleep_time: Thread sleep time after each chunk of work.
133
+ :param once: Run only once.
134
+ """
135
+ run_daemon(
136
+ once=once,
137
+ graceful_stop=graceful_stop,
138
+ executable=DAEMON_NAME,
139
+ partition_wait_time=1,
140
+ sleep_time=sleep_time,
141
+ run_once_fnc=functools.partial(
142
+ run_once,
143
+ inputfile=inputfile,
144
+ ),
145
+ )
146
+
147
+
148
+ def run_once(heartbeat_handler: HeartbeatHandler, inputfile: str, **_kwargs) -> bool:
149
+
150
+ _, _, logger = heartbeat_handler.live()
151
+ try:
152
+ rses = config_get_list("automatix", "rses")
153
+ except (NoOptionError, NoSectionError, RuntimeError):
154
+ logging.log(
155
+ logging.ERROR,
156
+ "Option rses not found in automatix section. Trying the legacy sites option",
157
+ )
158
+ try:
159
+ rses = config_get_list("automatix", "sites")
160
+ logging.log(
161
+ logging.WARNING,
162
+ "Option sites found in automatix section. This option will be deprecated soon. Please update your config to use rses.",
163
+ )
164
+ except (NoOptionError, NoSectionError, RuntimeError):
165
+ logger(logging.ERROR, "Could not load sites from configuration")
166
+ return True
167
+
168
+ set_metadata = config_get_bool(
169
+ "automatix", "set_metadata", raise_exception=False, default=True
170
+ )
171
+ dataset_lifetime = config_get_int(
172
+ "automatix", "dataset_lifetime", raise_exception=False, default=0
173
+ )
174
+ account = config_get("automatix", "account", raise_exception=False, default="root")
175
+ scope = config_get("automatix", "scope", raise_exception=False, default="test")
176
+ client = Client(account=account)
177
+ vo = map_vo(client.vo) # type: ignore
178
+ filters = {"scope": InternalScope("*", vo=vo)}
179
+ scopes = list_scopes(filter_=filters)
180
+ if InternalScope(scope, vo=vo) not in scopes:
181
+ logger(logging.ERROR, "Scope %s does not exist. Exiting", scope)
182
+ return True
183
+
184
+ logger(logging.INFO, "Getting data distribution")
185
+ probabilities, data = get_data_distribution(inputfile)
186
+ logger(logging.DEBUG, "Probabilities %s", probabilities)
187
+
188
+ cycle_stopwatch = Stopwatch()
189
+ successes = []
190
+ failures = []
191
+ for rse in rses:
192
+ stopwatch = Stopwatch()
193
+ _, _, logger = heartbeat_handler.live()
194
+ tmpdir = tempfile.mkdtemp()
195
+ logger(logging.INFO, "Running on RSE %s", rse)
196
+ dic = choose_element(probabilities, data)
197
+ metadata = dic["metadata"]
198
+ try:
199
+ nbfiles = dic["nbfiles"]
200
+ except KeyError:
201
+ nbfiles = 2
202
+ logger(
203
+ logging.WARNING, "No nbfiles defined in the configuration, will use 2"
204
+ )
205
+ try:
206
+ filesize = dic["filesize"]
207
+ except KeyError:
208
+ filesize = 1000000
209
+ logger(
210
+ logging.WARNING,
211
+ "No filesize defined in the configuration, will use 1M files",
212
+ )
213
+ dsn = generate_didname(metadata, None, "dataset")
214
+ fnames = []
215
+ lfns = []
216
+ physical_fnames = []
217
+ files = []
218
+ for _ in range(nbfiles):
219
+ fname = generate_didname(metadata=metadata, dsn=dsn, did_type="file")
220
+ lfns.append(fname)
221
+ logger(logging.INFO, "Generating file %s in dataset %s", fname, dsn)
222
+ physical_fname = "%s/%s" % (tmpdir, "".join(fname.split("/")))
223
+ physical_fnames.append(physical_fname)
224
+ generate_file(physical_fname, filesize, logger=logger)
225
+ fnames.append(fname)
226
+ file_ = {
227
+ "did_scope": scope,
228
+ "did_name": fname,
229
+ "dataset_scope": scope,
230
+ "dataset_name": dsn,
231
+ "rse": rse,
232
+ "path": physical_fname,
233
+ }
234
+ if set_metadata:
235
+ file_["dataset_meta"] = metadata
236
+ if dataset_lifetime:
237
+ file_["dataset_meta"]["lifetime"] = dataset_lifetime
238
+ files.append(file_)
239
+ logger(logging.INFO, "Upload %s:%s to %s", scope, dsn, rse)
240
+ try:
241
+ upload_client = UploadClient(client)
242
+ ret = upload_client.upload(files)
243
+ if ret == 0:
244
+ logger(logging.INFO, "%s successfully registered on %s", dsn, rse)
245
+ METRICS.counter(name="addnewdataset.done").inc()
246
+ METRICS.counter(name="addnewfile.done").inc(nbfiles)
247
+ METRICS.timer(name='datasetinjection').observe(stopwatch.elapsed)
248
+ successes.append(rse)
249
+ else:
250
+ logger(logging.INFO, "Error uploading files")
251
+ failures.append(rse)
252
+ except Exception as error:
253
+ logger(logging.ERROR, "Error uploading files on %s: %s", rse, str(error))
254
+ failures.append(rse)
255
+ finally:
256
+ for physical_fname in physical_fnames:
257
+ remove(physical_fname)
258
+ rmdir(tmpdir)
259
+ logger(
260
+ logging.INFO,
261
+ "It took %f seconds to upload datasets on %s RSEs: %s",
262
+ cycle_stopwatch.elapsed,
263
+ len(successes),
264
+ str(successes),
265
+ )
266
+ if failures:
267
+ logger(
268
+ logging.WARNING,
269
+ "Datasets could not be uploaded on %s RSEs: %s", len(failures), str(failures),
270
+ )
271
+ return True
272
+
273
+
274
+ def run(
275
+ total_workers: int = 1,
276
+ once: bool = False,
277
+ inputfile: str = "/opt/rucio/etc/automatix.json",
278
+ sleep_time: int = 60,
279
+ ) -> None:
280
+ """
281
+ Starts up the automatix threads.
282
+ """
283
+ setup_logging(process_name=DAEMON_NAME)
284
+
285
+ if rucio.db.sqla.util.is_old_db():
286
+ raise exception.DatabaseException(
287
+ "Database was not updated, daemon won't start"
288
+ )
289
+
290
+ threads = list()
291
+ for _ in range(total_workers):
292
+ kwargs = {"once": once, "sleep_time": sleep_time, "inputfile": inputfile}
293
+ threads.append(threading.Thread(target=automatix, kwargs=kwargs))
294
+ [thread.start() for thread in threads]
295
+ while threads[0].is_alive():
296
+ logging.log(logging.DEBUG, "Still %i active threads", len(threads))
297
+ [thread.join(timeout=3.14) for thread in threads]
298
+
299
+
300
+ def stop(signum: Optional[int] = None, frame: Optional["FrameType"] = None) -> None:
301
+ """
302
+ Graceful exit.
303
+ """
304
+ graceful_stop.set()
@@ -0,0 +1,13 @@
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.
@@ -0,0 +1,322 @@
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 functools
16
+ import logging
17
+ import math
18
+ import re
19
+ import threading
20
+ from datetime import datetime
21
+ from typing import TYPE_CHECKING, Any, Optional
22
+
23
+ from sqlalchemy.exc import DatabaseError
24
+
25
+ import rucio.db.sqla.util
26
+ from rucio.common.config import config_get_int
27
+ from rucio.common.exception import DatabaseException, DataIdentifierNotFound, ReplicaNotFound, UnsupportedOperation
28
+ from rucio.common.logging import setup_logging
29
+ from rucio.common.utils import chunks
30
+ from rucio.core.did import get_metadata
31
+ from rucio.core.replica import bulk_add_bad_replicas, bulk_delete_bad_pfns, declare_bad_file_replicas, get_bad_pfns, get_did_from_pfns, get_pfn_to_rse, get_replicas_state, update_replicas_states
32
+ from rucio.core.rse import get_rse_name
33
+ from rucio.daemons.common import run_daemon
34
+ from rucio.db.sqla.constants import MYSQL_LOCK_WAIT_TIMEOUT_EXCEEDED, ORACLE_DEADLOCK_DETECTED_REGEX, ORACLE_RESOURCE_BUSY_REGEX, BadFilesStatus, BadPFNStatus, ReplicaState
35
+ from rucio.db.sqla.session import get_session
36
+
37
+ if TYPE_CHECKING:
38
+ from collections.abc import Iterable, Mapping
39
+ from types import FrameType
40
+
41
+ from rucio.common.types import InternalAccount, LoggerFunction
42
+ from rucio.daemons.common import HeartbeatHandler
43
+
44
+ graceful_stop = threading.Event()
45
+ DAEMON_NAME = 'minos'
46
+
47
+
48
+ def __classify_bad_pfns(
49
+ pfns: "Iterable[Mapping[str, Any]]"
50
+ ) -> tuple[
51
+ dict[tuple["InternalAccount", str, str], list[str]],
52
+ dict[tuple["InternalAccount", str, datetime], list[str]]
53
+ ]:
54
+ """
55
+ Function that takes a list of PFNs and classify them in 2 dictionaries : bad_replicas and temporary_unvailables
56
+ :param pfns: List of PFNs
57
+
58
+ :returns: Tuple (bad_replicas, temporary_unvailables)
59
+ """
60
+ states_mapping = {BadPFNStatus.BAD: BadFilesStatus.BAD,
61
+ BadPFNStatus.SUSPICIOUS: BadFilesStatus.SUSPICIOUS,
62
+ BadPFNStatus.TEMPORARY_UNAVAILABLE: BadFilesStatus.TEMPORARY_UNAVAILABLE}
63
+ bad_replicas, temporary_unvailables = {}, {}
64
+ for pfn in pfns:
65
+ path = pfn['pfn']
66
+ account = pfn['account']
67
+ reason = pfn['reason']
68
+ expires_at = pfn['expires_at']
69
+ state = states_mapping[pfn['state']]
70
+ if state in [BadFilesStatus.BAD, BadFilesStatus.SUSPICIOUS]:
71
+ if (account, reason, state) not in bad_replicas:
72
+ bad_replicas[(account, reason, state)] = []
73
+ bad_replicas[(account, reason, state)].append(path)
74
+ elif state == BadFilesStatus.TEMPORARY_UNAVAILABLE:
75
+ if (account, reason, expires_at) not in temporary_unvailables:
76
+ temporary_unvailables[(account, reason, expires_at)] = []
77
+ temporary_unvailables[(account, reason, expires_at)].append(path)
78
+ return bad_replicas, temporary_unvailables
79
+
80
+
81
+ def __clean_unknown_replicas(
82
+ pfns: "Iterable[str]",
83
+ vo: str,
84
+ logger: "LoggerFunction"
85
+ ) -> dict[str, dict[str, list[str]]]:
86
+ """
87
+ Identify from the list of PFNs the one that are unknown and remove them from the bad_pfns table
88
+ :param pfns: List of PFNs
89
+ :param vo: The VO name
90
+ :param logger: The logger
91
+
92
+ :returns: Dictionary cleaned from unkwnon replicas
93
+ """
94
+ schemes = {}
95
+ dict_rse = {}
96
+ unknown_replicas = []
97
+ # Splitting the PFNs by schemes
98
+ for pfn in pfns:
99
+ scheme = pfn.split(':')[0]
100
+ if scheme not in schemes:
101
+ schemes[scheme] = []
102
+ schemes[scheme].append(pfn)
103
+ for scheme in schemes:
104
+ _, tmp_dict_rse, tmp_unknown_replicas = get_pfn_to_rse(schemes[scheme], vo=vo)
105
+ for rse_id in tmp_dict_rse:
106
+ if rse_id not in dict_rse:
107
+ dict_rse[rse_id] = {}
108
+ if scheme not in dict_rse[rse_id]:
109
+ dict_rse[rse_id][scheme] = []
110
+ dict_rse[rse_id][scheme].extend(tmp_dict_rse[rse_id])
111
+ unknown_replicas.extend(tmp_unknown_replicas.get('unknown', []))
112
+ # The replicas in unknown_replicas do not exist, so we flush them from bad_pfns
113
+ if unknown_replicas:
114
+ logger(logging.INFO, 'The following replicas are unknown and will be removed : %s', str(unknown_replicas))
115
+ bulk_delete_bad_pfns(pfns=unknown_replicas, session=None)
116
+ return dict_rse
117
+
118
+
119
+ def __update_temporary_unavailable(
120
+ chunk: "Iterable[dict[str, Any]]",
121
+ reason: str,
122
+ expires_at: datetime,
123
+ account: "InternalAccount",
124
+ logger: "LoggerFunction"
125
+ ) -> None:
126
+ """
127
+ Update temporary unavailable replicas one by one
128
+ :param chunk: List of unavailable replicas to update
129
+ :param reason: Reason of the temporary unavailable replica
130
+ :param expires_at: Expiration date of the temporary unavailability
131
+ :param account: Account who declared the replica
132
+ :param logger: The logger
133
+
134
+ """
135
+ for rep in chunk:
136
+ logger(logging.DEBUG, 'Working on %s', str(rep))
137
+ try:
138
+ get_metadata(rep['scope'], rep['name'])
139
+ unavailable_states = []
140
+ rep_state = get_replicas_state(rep['scope'], rep['name'])
141
+ unavailable_states.extend(rep_state.get(ReplicaState.TEMPORARY_UNAVAILABLE, []))
142
+ unavailable_states.extend(rep_state.get(ReplicaState.BEING_DELETED, []))
143
+ unavailable_states.extend(rep_state.get(ReplicaState.BAD, []))
144
+ # If the replica is already not available, it is removed from the bad PFNs table
145
+ if rep['rse_id'] in unavailable_states:
146
+ logger(logging.INFO, '%s is in unavailable state. Will be removed from the list of bad PFNs', str(rep['pfn']))
147
+ bulk_delete_bad_pfns(pfns=[rep['pfn']], session=None)
148
+ # If the expiration date of the TEMPORARY_UNAVAILABLE is in the past, it is removed from the bad PFNs table
149
+ elif expires_at < datetime.utcnow():
150
+ logger(logging.INFO, 'PFN %s expiration time (%s) is older than now and is not in unavailable state. Removing the PFNs from bad_pfns', str(rep['pfn']), expires_at)
151
+ bulk_delete_bad_pfns(pfns=[rep['pfn']], session=None)
152
+ # Else update everything in the same transaction
153
+ else:
154
+ try:
155
+ session = get_session()
156
+ update_replicas_states([rep], nowait=False, session=session)
157
+ bulk_add_bad_replicas([rep], account, state=BadFilesStatus.TEMPORARY_UNAVAILABLE, reason=reason, expires_at=expires_at, session=session)
158
+ bulk_delete_bad_pfns(pfns=[rep['pfn']], session=session)
159
+ session.commit() # pylint: disable=no-member
160
+ except Exception:
161
+ logger(logging.ERROR, 'Cannot update state of %s', str(rep['pfn']))
162
+ except (DataIdentifierNotFound, ReplicaNotFound):
163
+ logger(logging.ERROR, 'Will remove %s from the list of bad PFNs', str(rep['pfn']))
164
+ bulk_delete_bad_pfns(pfns=[rep['pfn']], session=None)
165
+
166
+
167
+ def minos(bulk: int = 1000, once: bool = False, sleep_time: int = 60) -> None:
168
+ """
169
+ Creates a Minos Worker that gets a list of bad PFNs,
170
+ extract the scope, name and rse_id and fill the bad_replicas table.
171
+
172
+ :param bulk: The number of requests to process.
173
+ :param once: Run only once.
174
+ :param sleep_time: Time between two cycles.
175
+ """
176
+ run_daemon(
177
+ once=once,
178
+ graceful_stop=graceful_stop,
179
+ executable=DAEMON_NAME,
180
+ partition_wait_time=10,
181
+ sleep_time=sleep_time,
182
+ run_once_fnc=functools.partial(
183
+ run_once,
184
+ bulk=bulk,
185
+ ),
186
+ )
187
+
188
+
189
+ def run_once(heartbeat_handler: "HeartbeatHandler", bulk: int, **_kwargs) -> bool:
190
+ worker_number, total_workers, logger = heartbeat_handler.live()
191
+ logger(logging.INFO, 'Minos started')
192
+
193
+ chunk_size = config_get_int('minos', 'commit_size', default=10, raise_exception=False) # The chunk size used for the commits
194
+
195
+ pfns = get_bad_pfns(thread=worker_number, total_threads=total_workers, limit=bulk)
196
+
197
+ # Class the PFNs into bad_replicas and temporary_unavailable
198
+ bad_replicas, temporary_unvailables = __classify_bad_pfns(pfns)
199
+
200
+ # Process the bad and suspicious files
201
+ # The scope, name, rse_id are extracted and filled into the bad_replicas table
202
+ for account, reason, state in bad_replicas:
203
+ vo = account.vo
204
+ pfns = bad_replicas[(account, reason, state)]
205
+ logger(logging.INFO, 'Declaring %s replicas with state %s and reason %s', len(pfns), str(state), reason)
206
+ session = get_session()
207
+ try:
208
+ dict_rse = __clean_unknown_replicas(pfns, vo, logger)
209
+ for rse_id, pfns_by_scheme in dict_rse.items():
210
+ rse = get_rse_name(rse_id=rse_id, session=None)
211
+ rse_vo_str = rse if vo == 'def' else '{} on VO {}'.format(rse, vo)
212
+ for scheme, pfns in pfns_by_scheme.items():
213
+ logger(logging.DEBUG, 'Running on RSE %s with %s replicas', rse_vo_str, len(pfns))
214
+ tot_chunk = int(math.ceil(len(pfns) / chunk_size))
215
+ for nchunk, chunk in enumerate(chunks(pfns, chunk_size)):
216
+ logger(logging.DEBUG, 'Running on %s chunk out of %s', nchunk + 1, tot_chunk)
217
+ unknown_replicas = declare_bad_file_replicas(chunk, reason=reason, issuer=account, status=state, session=session)
218
+ if unknown_replicas:
219
+ logger(logging.DEBUG, 'Unknown replicas : %s', str(unknown_replicas))
220
+ bulk_delete_bad_pfns(pfns=chunk, session=session)
221
+ session.commit() # pylint: disable=no-member
222
+ except (DatabaseException, DatabaseError) as error:
223
+ if re.match(ORACLE_RESOURCE_BUSY_REGEX, error.args[0]) or re.match(ORACLE_DEADLOCK_DETECTED_REGEX, error.args[0]) or MYSQL_LOCK_WAIT_TIMEOUT_EXCEEDED in error.args[0]:
224
+ logger(logging.WARNING, 'Lock detected when handling request - skipping: %s', str(error))
225
+ else:
226
+ logger(logging.ERROR, 'Exception', exc_info=True)
227
+ session.rollback() # pylint: disable=no-member
228
+ except Exception:
229
+ session.rollback() # pylint: disable=no-member
230
+ logger(logging.CRITICAL, 'Exception', exc_info=True)
231
+
232
+ worker_number, total_workers, logger = heartbeat_handler.live()
233
+
234
+ # Now get the temporary unavailable and update the replicas states
235
+ for account, reason, expires_at in temporary_unvailables:
236
+ vo = account.vo
237
+ pfns = temporary_unvailables[(account, reason, expires_at)]
238
+ logger(logging.INFO, 'Declaring %s replicas temporary available with timeout %s and reason %s', len(pfns), str(expires_at), reason)
239
+ logger(logging.DEBUG, 'Extracting RSEs')
240
+
241
+ dict_rse = __clean_unknown_replicas(pfns, vo, logger)
242
+ for rse_id in dict_rse:
243
+ replicas = []
244
+ rse = get_rse_name(rse_id=rse_id, session=None)
245
+ rse_vo_str = rse if vo == 'def' else '{} on VO {}'.format(rse, vo)
246
+ logger(logging.DEBUG, 'Running on RSE %s', rse_vo_str)
247
+ for rse_id, pfns_by_scheme in dict_rse.items():
248
+ for scheme, pfns in pfns_by_scheme.items():
249
+ for rep in get_did_from_pfns(pfns=pfns, rse_id=None, vo=vo, session=None):
250
+ for pfn in rep:
251
+ scope = rep[pfn]['scope']
252
+ name = rep[pfn]['name']
253
+ replicas.append({'scope': scope, 'name': name, 'rse_id': rse_id, 'state': ReplicaState.TEMPORARY_UNAVAILABLE, 'pfn': pfn})
254
+ # The following part needs to be atomic
255
+ # We update the replicas states to TEMPORARY_UNAVAILABLE
256
+ # then insert a row in the bad_replicas table. TODO Update the row if it already exists
257
+ # then delete the corresponding rows into the bad_pfns table
258
+ logger(logging.DEBUG, 'Running on %s replicas on RSE %s', len(replicas), rse_vo_str)
259
+ tot_chunk = int(math.ceil(len(replicas) / float(chunk_size)))
260
+ session = get_session()
261
+ for nchunk, chunk in enumerate(chunks(replicas, chunk_size)):
262
+ try:
263
+ logger(logging.DEBUG, 'Running on %s chunk out of %s', nchunk + 1, tot_chunk)
264
+ update_replicas_states(chunk, nowait=False, session=session)
265
+ bulk_add_bad_replicas(chunk, account, state=BadFilesStatus.TEMPORARY_UNAVAILABLE, reason=reason, expires_at=expires_at, session=session)
266
+ pfns = [entry['pfn'] for entry in chunk]
267
+ bulk_delete_bad_pfns(pfns=pfns, session=session)
268
+ session.commit() # pylint: disable=no-member
269
+ except (UnsupportedOperation, ReplicaNotFound) as error:
270
+ session.rollback() # pylint: disable=no-member
271
+ logger(logging.ERROR, 'Problem to bulk update PFNs. PFNs will be updated individually. Error : %s', str(error))
272
+ # Update all the replicas one by one
273
+ __update_temporary_unavailable(chunk=chunk, reason=reason, expires_at=expires_at, account=account, logger=logger)
274
+ session = get_session()
275
+ except (DatabaseException, DatabaseError) as error:
276
+ if re.match(ORACLE_RESOURCE_BUSY_REGEX, error.args[0]) or re.match(ORACLE_DEADLOCK_DETECTED_REGEX, error.args[0]) or MYSQL_LOCK_WAIT_TIMEOUT_EXCEEDED in error.args[0]:
277
+ logger(logging.WARNING, 'Lock detected when handling request - skipping: %s', str(error))
278
+ else:
279
+ logger(logging.ERROR, 'Exception', exc_info=True)
280
+ session.rollback() # pylint: disable=no-member
281
+ session = get_session()
282
+ except Exception:
283
+ session.rollback() # pylint: disable=no-member
284
+ logger(logging.CRITICAL, 'Exception', exc_info=True)
285
+ session = get_session()
286
+ must_sleep = True
287
+ if len(pfns) == bulk:
288
+ logger(logging.INFO, 'Processed maximum number of pfns according to the bulk size. Restart immediately next cycle')
289
+ must_sleep = False
290
+ return must_sleep
291
+ return must_sleep
292
+
293
+
294
+ def run(threads: int = 1, bulk: int = 100, once: bool = False, sleep_time: int = 60) -> None:
295
+ """
296
+ Starts up the minos threads.
297
+ """
298
+ setup_logging(process_name=DAEMON_NAME)
299
+
300
+ if rucio.db.sqla.util.is_old_db():
301
+ raise DatabaseException('Database was not updated, daemon won\'t start')
302
+
303
+ if once:
304
+ logging.info('Will run only one iteration in a single threaded mode')
305
+ minos(bulk=bulk, once=once)
306
+ else:
307
+ logging.info('starting transmogrifier threads')
308
+ thread_list = [threading.Thread(target=minos, kwargs={'once': once,
309
+ 'sleep_time': sleep_time,
310
+ 'bulk': bulk}) for _ in range(0, threads)]
311
+ [thread.start() for thread in thread_list]
312
+ logging.info('waiting for interrupts')
313
+ # Interruptible joins require a timeout.
314
+ while thread_list:
315
+ thread_list = [thread.join(timeout=3.14) for thread in thread_list if thread and thread.is_alive()]
316
+
317
+
318
+ def stop(signum: Optional[int] = None, frame: Optional["FrameType"] = None) -> None:
319
+ """
320
+ Graceful exit.
321
+ """
322
+ graceful_stop.set()