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,117 @@
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
+ Abacus-RSE is a daemon to update RSE counters.
17
+ """
18
+
19
+ import logging
20
+ import threading
21
+ import time
22
+ from typing import TYPE_CHECKING
23
+
24
+ import rucio.db.sqla.util
25
+ from rucio.common import exception
26
+ from rucio.common.logging import setup_logging
27
+ from rucio.common.utils import get_thread_with_periodic_running_function
28
+ from rucio.core.rse_counter import fill_rse_counter_history_table, get_updated_rse_counters, update_rse_counter
29
+ from rucio.daemons.common import HeartbeatHandler, run_daemon
30
+
31
+ if TYPE_CHECKING:
32
+ from types import FrameType
33
+ from typing import Optional
34
+
35
+ graceful_stop = threading.Event()
36
+ DAEMON_NAME = 'abacus-rse'
37
+
38
+
39
+ def rse_update(
40
+ once: bool = False,
41
+ sleep_time: int = 10
42
+ ) -> None:
43
+ """
44
+ Main loop to check and update the RSE Counters.
45
+ """
46
+ run_daemon(
47
+ once=once,
48
+ graceful_stop=graceful_stop,
49
+ executable=DAEMON_NAME,
50
+ partition_wait_time=1,
51
+ sleep_time=sleep_time,
52
+ run_once_fnc=run_once,
53
+ )
54
+
55
+
56
+ def run_once(
57
+ heartbeat_handler: HeartbeatHandler,
58
+ **_kwargs
59
+ ) -> None:
60
+ worker_number, total_workers, logger = heartbeat_handler.live()
61
+
62
+ # Select a bunch of rses for to update for this worker
63
+ start = time.time() # NOQA
64
+ rse_ids = get_updated_rse_counters(total_workers=total_workers,
65
+ worker_number=worker_number)
66
+ logger(logging.DEBUG, 'Index query time %f size=%d' % (time.time() - start, len(rse_ids)))
67
+
68
+ # If the list is empty, sent the worker to sleep
69
+ if not rse_ids:
70
+ logger(logging.INFO, 'did not get any work')
71
+ return
72
+
73
+ for rse_id in rse_ids:
74
+ worker_number, total_workers, logger = heartbeat_handler.live()
75
+ if graceful_stop.is_set():
76
+ break
77
+ start_time = time.time()
78
+ update_rse_counter(rse_id=rse_id)
79
+ logger(logging.DEBUG, 'update of rse "%s" took %f' % (rse_id, time.time() - start_time))
80
+
81
+
82
+ def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
83
+ """
84
+ Graceful exit.
85
+ """
86
+
87
+ graceful_stop.set()
88
+
89
+
90
+ def run(
91
+ once: bool = False,
92
+ threads: int = 1,
93
+ fill_history_table: bool = False,
94
+ sleep_time: int = 10
95
+ ) -> None:
96
+ """
97
+ Starts up the Abacus-RSE threads.
98
+ """
99
+ setup_logging(process_name=DAEMON_NAME)
100
+
101
+ if rucio.db.sqla.util.is_old_db():
102
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
103
+
104
+ if once:
105
+ logging.info('main: executing one iteration only')
106
+ rse_update(once)
107
+ else:
108
+ logging.info('main: starting threads')
109
+ thread_list = [threading.Thread(target=rse_update, kwargs={'once': once, 'sleep_time': sleep_time}) for i in
110
+ range(0, threads)]
111
+ if fill_history_table:
112
+ thread_list.append(get_thread_with_periodic_running_function(3600, fill_rse_counter_history_table, graceful_stop))
113
+ [t.start() for t in thread_list]
114
+ logging.info('main: waiting for interrupts')
115
+ # Interruptible joins require a timeout.
116
+ while thread_list[0].is_alive():
117
+ [t.join(timeout=3.14) for t in thread_list]
@@ -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,242 @@
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 datetime
16
+ import functools
17
+ import logging
18
+ import random
19
+ import threading
20
+ from sys import exc_info
21
+ from traceback import format_exception
22
+ from typing import TYPE_CHECKING
23
+
24
+ import rucio.core.lifetime_exception
25
+ import rucio.db.sqla.util
26
+ from rucio.common import exception
27
+ from rucio.common.exception import InvalidRSEExpression, RuleNotFound
28
+ from rucio.common.logging import setup_logging
29
+ from rucio.core.did import set_metadata
30
+ from rucio.core.lock import get_dataset_locks
31
+ from rucio.core.rse import get_rse_name, get_rse_vo
32
+ from rucio.core.rse_expression_parser import parse_expression
33
+ from rucio.core.rule import get_rules_beyond_eol, update_rule
34
+ from rucio.daemons.common import run_daemon
35
+ from rucio.db.sqla.constants import LifetimeExceptionsState
36
+
37
+ if TYPE_CHECKING:
38
+ from types import FrameType
39
+ from typing import Optional
40
+
41
+ from rucio.daemons.common import HeartbeatHandler
42
+
43
+ GRACEFUL_STOP = threading.Event()
44
+ DAEMON_NAME = 'atropos'
45
+
46
+
47
+ def atropos(
48
+ date_check: datetime.datetime,
49
+ dry_run: bool,
50
+ grace_period: int,
51
+ purge_replicas: bool,
52
+ spread_period: bool,
53
+ unlock: bool,
54
+ once: bool,
55
+ sleep_time: int
56
+ ) -> None:
57
+ """
58
+ Creates an Atropos Worker that gets a list of rules which have an eol_at expired and delete them.
59
+
60
+ :param grace_period: The grace_period for the rules.
61
+ :param once: Run only once.
62
+ :param sleep_time: Thread sleep time after each chunk of work.
63
+ """
64
+ run_daemon(
65
+ once=once,
66
+ graceful_stop=GRACEFUL_STOP,
67
+ executable=DAEMON_NAME,
68
+ partition_wait_time=10,
69
+ sleep_time=sleep_time,
70
+ run_once_fnc=functools.partial(
71
+ run_once,
72
+ date_check=date_check,
73
+ dry_run=dry_run,
74
+ grace_period=grace_period,
75
+ purge_replicas=purge_replicas,
76
+ spread_period=spread_period,
77
+ unlock=unlock
78
+ )
79
+ )
80
+
81
+
82
+ def run_once(
83
+ heartbeat_handler: 'HeartbeatHandler',
84
+ activity: None, # NOQA: ARG001
85
+ date_check: datetime.datetime,
86
+ dry_run: bool,
87
+ grace_period: int,
88
+ purge_replicas: bool,
89
+ spread_period: int,
90
+ unlock: bool,
91
+ ) -> None:
92
+ """An iteration of an Atropos worker."""
93
+ worker_number, total_workers, logger = heartbeat_handler.live()
94
+ logger(logging.DEBUG, 'Starting worker')
95
+ if not dry_run and date_check > datetime.datetime.utcnow():
96
+ logger(logging.ERROR, 'Atropos cannot run in non-dry-run mode for date in the future')
97
+ return
98
+
99
+ # Process the list of approved exceptions. In case a DID has
100
+ # multiple exceptions, the one with the expiration date further in
101
+ # the future is what matters.
102
+ summary = {}
103
+ lifetime_exceptions = {}
104
+ for excep in rucio.core.lifetime_exception.list_exceptions(exception_id=None, states=[LifetimeExceptionsState.APPROVED, ], session=None):
105
+ key = '{}:{}'.format(excep['scope'].internal, excep['name'])
106
+ if key not in lifetime_exceptions:
107
+ lifetime_exceptions[key] = excep['expires_at']
108
+ elif lifetime_exceptions[key] < excep['expires_at']:
109
+ lifetime_exceptions[key] = excep['expires_at']
110
+ logger(logging.DEBUG, '%d active exceptions', len(lifetime_exceptions))
111
+
112
+ rand = random.Random(worker_number) # noqa: S311
113
+
114
+ try:
115
+ rules = get_rules_beyond_eol(date_check, worker_number, total_workers, session=None)
116
+ logger(logging.INFO, '%d rules to process', len(rules))
117
+ for rule_idx, rule in enumerate(rules, start=1):
118
+ did = '%s:%s' % (rule.scope, rule.name)
119
+ did_key = '{}:{}'.format(rule.scope.internal, rule.name)
120
+ logger(logging.DEBUG, 'Working on rule %s on DID %s on %s', rule.id, did, rule.rse_expression)
121
+
122
+ if (rule_idx % 1000) == 0:
123
+ logger(logging.INFO, '%s/%s rules processed', rule_idx, len(rules))
124
+
125
+ # We compute the expected eol_at
126
+ try:
127
+ rses = parse_expression(rule.rse_expression, filter_={'vo': rule.account.vo})
128
+ except InvalidRSEExpression:
129
+ logger(logging.WARNING, 'Rule %s has an RSE expression that results in an empty set: %s', rule.id, rule.rse_expression)
130
+ continue
131
+ eol_at = rucio.core.lifetime_exception.define_eol(rule.scope, rule.name, rses)
132
+ if eol_at != rule.eol_at:
133
+ logger(logging.WARNING, 'The computed eol %s differs from the one recorded %s for rule %s on %s at %s',
134
+ eol_at, rule.eol_at, rule.id, did, rule.rse_expression)
135
+ try:
136
+ update_rule(rule.id, options={'eol_at': eol_at})
137
+ except RuleNotFound:
138
+ logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
139
+ continue
140
+
141
+ # Check the exceptions
142
+ if did_key in lifetime_exceptions:
143
+ if eol_at > lifetime_exceptions[did_key]:
144
+ logger(logging.INFO, 'Rule %s on DID %s on %s has longer expiration date than the one requested : %s',
145
+ rule.id, did, rule.rse_expression, lifetime_exceptions[did_key])
146
+ else:
147
+ # If eol_at < requested extension, update eol_at
148
+ logger(logging.INFO, 'Updating rule %s on DID %s on %s according to the exception till %s',
149
+ rule.id, did, rule.rse_expression, lifetime_exceptions[did_key])
150
+ eol_at = lifetime_exceptions[did_key]
151
+ try:
152
+ update_rule(rule.id, options={'eol_at': lifetime_exceptions[did_key]})
153
+ except RuleNotFound:
154
+ logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
155
+ continue
156
+
157
+ # Now check that the new eol_at is expired
158
+ if eol_at and eol_at <= date_check:
159
+ set_metadata(scope=rule.scope, name=rule.name, key='eol_at', value=eol_at)
160
+ no_locks = True
161
+ for lock in get_dataset_locks(rule.scope, rule.name):
162
+ if lock['rule_id'] == rule[4]:
163
+ no_locks = False
164
+ if lock['rse_id'] not in summary:
165
+ summary[lock['rse_id']] = {}
166
+ if did_key not in summary[lock['rse_id']]:
167
+ summary[lock['rse_id']][did_key] = {'length': lock['length'] or 0, 'bytes': lock['bytes'] or 0}
168
+ if no_locks:
169
+ logger(logging.WARNING, 'Cannot find a lock for rule %s on DID %s', rule.id, did)
170
+ if not dry_run:
171
+ lifetime = grace_period + rand.randrange(spread_period + 1)
172
+ logger(logging.INFO, 'Setting %s seconds lifetime for rule %s', lifetime, rule.id)
173
+ options = {'lifetime': lifetime}
174
+ if purge_replicas:
175
+ options['purge_replicas'] = True
176
+ if rule.locked and unlock:
177
+ logger(logging.INFO, 'Unlocking rule %s', rule.id)
178
+ options['locked'] = False
179
+ try:
180
+ update_rule(rule.id, options=options)
181
+ except RuleNotFound:
182
+ logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
183
+ continue
184
+ except Exception:
185
+ exc_type, exc_value, exc_traceback = exc_info()
186
+ logger(logging.CRITICAL, ''.join(format_exception(exc_type, exc_value, exc_traceback)).strip())
187
+
188
+ for rse_id in summary:
189
+ tot_size, tot_files, tot_datasets = 0, 0, 0
190
+ for did in summary[rse_id]:
191
+ tot_datasets += 1
192
+ tot_files += summary[rse_id][did].get('length', 0)
193
+ tot_size += summary[rse_id][did].get('bytes', 0)
194
+ vo = get_rse_vo(rse_id=rse_id)
195
+ logger(logging.INFO, 'For RSE %s%s %d datasets will be deleted representing %d files and %d bytes',
196
+ get_rse_name(rse_id=rse_id), '' if vo == 'def' else ' on VO ' + vo, tot_datasets, tot_files, tot_size)
197
+
198
+
199
+ def run(
200
+ date_check: datetime.datetime = datetime.datetime.utcnow(),
201
+ dry_run: bool = True,
202
+ grace_period: int = 86400,
203
+ purge_replicas: bool = False,
204
+ spread_period: int = 0,
205
+ unlock: bool = False,
206
+ once: bool = True,
207
+ sleep_time: int = 60,
208
+ threads: int = 1,
209
+ ) -> None:
210
+ """
211
+ Starts up the atropos threads.
212
+ """
213
+ setup_logging(process_name=DAEMON_NAME)
214
+
215
+ if rucio.db.sqla.util.is_old_db():
216
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
217
+
218
+ if once:
219
+ logging.info('Will run only one iteration')
220
+ logging.info('starting atropos threads')
221
+ thread_list = [threading.Thread(target=atropos, kwargs={'date_check': date_check,
222
+ 'dry_run': dry_run,
223
+ 'grace_period': grace_period,
224
+ 'purge_replicas': purge_replicas,
225
+ 'spread_period': spread_period,
226
+ 'unlock': unlock,
227
+ 'once': once,
228
+ 'sleep_time': sleep_time}) for i in range(0, threads)]
229
+ [t.start() for t in thread_list]
230
+
231
+ logging.info('waiting for interrupts')
232
+
233
+ # Interruptible joins require a timeout.
234
+ while thread_list:
235
+ thread_list = [t.join(timeout=3.14) for t in thread_list if t and t.is_alive()]
236
+
237
+
238
+ def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
239
+ """
240
+ Graceful exit.
241
+ """
242
+ GRACEFUL_STOP.set()
@@ -0,0 +1,289 @@
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 bz2
16
+ import glob
17
+ import logging
18
+ import os
19
+ import select
20
+ from datetime import datetime, timedelta
21
+ from queue import Empty as EmptyQueue
22
+ from typing import TYPE_CHECKING, Optional
23
+
24
+ from rucio.common import config
25
+ from rucio.common.dumper import LogPipeHandler, mkdir, temp_file
26
+ from rucio.common.dumper.consistency import Consistency
27
+ from rucio.common.types import InternalAccount, InternalScope
28
+ from rucio.common.utils import chunks
29
+ from rucio.core.quarantined_replica import add_quarantined_replicas
30
+ from rucio.core.replica import declare_bad_file_replicas, list_replicas
31
+ from rucio.core.rse import get_rse_id, get_rse_usage
32
+ from rucio.daemons.auditor import srmdumps
33
+ from rucio.daemons.auditor.hdfs import ReplicaFromHDFS
34
+ from rucio.db.sqla.constants import BadFilesStatus
35
+
36
+ if TYPE_CHECKING:
37
+ from collections.abc import Iterable
38
+ from configparser import RawConfigParser
39
+ from multiprocessing import Queue as QueueType
40
+ from multiprocessing.connection import Connection
41
+ from multiprocessing.synchronize import Event
42
+
43
+
44
+ def consistency(
45
+ rse: str,
46
+ delta: timedelta,
47
+ configuration: "RawConfigParser",
48
+ cache_dir: str,
49
+ results_dir: str
50
+ ) -> Optional[str]:
51
+ logger = logging.getLogger('auditor-worker')
52
+ rsedump, rsedate = srmdumps.download_rse_dump(rse, configuration, destdir=cache_dir)
53
+ results_path = os.path.join(results_dir, '{0}_{1}'.format(rse, rsedate.strftime('%Y%m%d'))) # pylint: disable=no-member
54
+
55
+ if os.path.exists(results_path + '.bz2') or os.path.exists(results_path):
56
+ logger.warning('Consistency check for "%s" (dump dated %s) already done, skipping check', rse, rsedate.strftime('%Y%m%d')) # pylint: disable=no-member
57
+ return None
58
+
59
+ rrdump_prev = ReplicaFromHDFS.download(rse, rsedate - delta, cache_dir=cache_dir)
60
+ rrdump_next = ReplicaFromHDFS.download(rse, rsedate + delta, cache_dir=cache_dir)
61
+ results = Consistency.dump(
62
+ 'consistency-manual',
63
+ rse,
64
+ rsedump,
65
+ rrdump_prev,
66
+ rrdump_next,
67
+ date=rsedate,
68
+ cache_dir=cache_dir,
69
+ )
70
+ mkdir(results_dir)
71
+ with temp_file(results_dir, results_path) as (output, _):
72
+ for result in results:
73
+ output.write('{0}\n'.format(result.csv()))
74
+
75
+ return results_path
76
+
77
+
78
+ def guess_replica_info(
79
+ path: str
80
+ ) -> tuple[Optional[str], str]:
81
+ """Try to extract the scope and name from a path.
82
+
83
+ ``path``: relative path to the file on the RSE.
84
+
85
+ Returns a ``tuple`` of which the first element is the scope of the
86
+ replica and the second element is its name.
87
+ """
88
+ items = path.split('/')
89
+ if len(items) == 1:
90
+ return None, path
91
+ elif len(items) > 2 and items[0] in ['group', 'user']:
92
+ return '.'.join(items[0:2]), items[-1]
93
+ else:
94
+ return items[0], items[-1]
95
+
96
+
97
+ def bz2_compress_file(
98
+ source: str,
99
+ chunk_size: int = 65000
100
+ ) -> str:
101
+ """Compress a file with bzip2.
102
+
103
+ The destination is the path passed through ``source`` extended with
104
+ '.bz2'. The original file is deleted.
105
+
106
+ Errors are deliberately not handled gracefully. Any exceptions
107
+ should be propagated to the caller.
108
+
109
+ ``source``: absolute path to the file to compress.
110
+
111
+ ``chunk_size``: size (in bytes) of the chunks by which to read the file.
112
+
113
+ Returns the destination path.
114
+ """
115
+ destination = '{}.bz2'.format(source)
116
+ with open(source) as plain, bz2.BZ2File(destination, 'w') as compressed:
117
+ while True:
118
+ chunk = plain.read(chunk_size)
119
+ if not chunk:
120
+ break
121
+ compressed.write(chunk.encode())
122
+ os.remove(source)
123
+ return destination
124
+
125
+
126
+ def process_output(
127
+ output: str,
128
+ sanity_check: bool = True,
129
+ compress: bool = True
130
+ ) -> None:
131
+ """Perform post-consistency-check actions.
132
+
133
+ DARK files are put in the quarantined-replica table so that they
134
+ may be deleted by the Dark Reaper. LOST files are reported as
135
+ suspicious so that they may be further checked by the cloud squads.
136
+
137
+ ``output``: absolute path to the file
138
+ produced by ``consistency()``. It must maintain its naming
139
+ convention.
140
+
141
+ If ``sanity_check`` is ``True`` (default) and the number of entries
142
+ in the output file is deemed excessive, the actions are aborted.
143
+
144
+ If ``compress`` is ``True`` (default), the file is compressed with
145
+ bzip2 after the actions are successfully performed.
146
+ """
147
+ logger = logging.getLogger('auditor-worker')
148
+ dark_replicas = []
149
+ lost_replicas = []
150
+ try:
151
+ with open(output) as f:
152
+ for line in f:
153
+ label, path = line.rstrip().split(',', 1)
154
+ scope, name = guess_replica_info(path)
155
+ if label == 'DARK':
156
+ dark_replicas.append({'path': path,
157
+ 'scope': InternalScope(scope),
158
+ 'name': name})
159
+ elif label == 'LOST':
160
+ lost_replicas.append({'scope': InternalScope(scope),
161
+ 'name': name})
162
+ else:
163
+ raise ValueError('unexpected label')
164
+ # Since the file is read immediately after its creation, any error
165
+ # exposes a bug in the Auditor.
166
+ except Exception as error:
167
+ logger.critical('Error processing "%s"', output, exc_info=True)
168
+ raise error
169
+
170
+ rse = os.path.basename(output[:output.rfind('_')])
171
+ rse_id = get_rse_id(rse=rse)
172
+ usage = get_rse_usage(rse_id=rse_id, source='rucio')[0]
173
+ threshold = config.config_get_float('auditor', 'threshold', False, 0.1)
174
+
175
+ # Perform a basic sanity check by comparing the number of entries
176
+ # with the total number of files on the RSE. If the percentage is
177
+ # significant, there is most likely an issue with the site dump.
178
+ found_error = False
179
+ if len(dark_replicas) > threshold * usage['files']:
180
+ logger.warning('Number of DARK files is exceeding threshold: "%s"',
181
+ output)
182
+ found_error = True
183
+ if len(lost_replicas) > threshold * usage['files']:
184
+ logger.warning('Number of LOST files is exceeding threshold: "%s"',
185
+ output)
186
+ found_error = True
187
+ if found_error and sanity_check:
188
+ raise AssertionError('sanity check failed')
189
+
190
+ # While converting LOST replicas to PFNs, entries that do not
191
+ # correspond to a replica registered in Rucio are silently dropped.
192
+ lost_pfns = [r['rses'][rse_id][0] for chunk in chunks(lost_replicas, 1000) for r in list_replicas(chunk) if rse_id in r['rses']]
193
+
194
+ for chunk in chunks(dark_replicas, 1000):
195
+ add_quarantined_replicas(rse_id=rse_id, replicas=chunk)
196
+ logger.debug('Processed %d DARK files from "%s"', len(dark_replicas),
197
+ output)
198
+ declare_bad_file_replicas(lost_pfns, reason='Reported by Auditor',
199
+ issuer=InternalAccount('root'), status=BadFilesStatus.SUSPICIOUS)
200
+ logger.debug('Processed %d LOST files from "%s"', len(lost_replicas),
201
+ output)
202
+
203
+ if compress:
204
+ destination = bz2_compress_file(output)
205
+ logger.debug('Compressed "%s"', destination)
206
+
207
+
208
+ def check(
209
+ queue: "QueueType",
210
+ retry: "QueueType",
211
+ terminate: "Event",
212
+ logpipe: "Connection",
213
+ cache_dir: str,
214
+ results_dir: str,
215
+ keep_dumps: bool,
216
+ delta_in_days: int
217
+ ) -> None:
218
+ logger = logging.getLogger('auditor-worker')
219
+ lib_logger = logging.getLogger('dumper')
220
+
221
+ loglevel = logging.getLevelName(config.config_get('common', 'loglevel', False, 'DEBUG'))
222
+ logger.setLevel(loglevel)
223
+ lib_logger.setLevel(loglevel)
224
+
225
+ handler = LogPipeHandler(logpipe)
226
+ logger.addHandler(handler)
227
+ lib_logger.addHandler(handler)
228
+
229
+ formatter = logging.Formatter(
230
+ "%(asctime)s %(name)-22s %(levelname)-8s [PID %(process)8d] %(message)s"
231
+ )
232
+ handler.setFormatter(formatter)
233
+
234
+ delta = timedelta(days=delta_in_days)
235
+
236
+ configuration = srmdumps.parse_configuration()
237
+
238
+ while not terminate.is_set():
239
+ try:
240
+ rse, attempts = queue.get(timeout=30)
241
+ except EmptyQueue:
242
+ continue
243
+ start = datetime.now()
244
+ try:
245
+ logger.debug('Checking "%s"', rse)
246
+ output = consistency(rse, delta, configuration, cache_dir,
247
+ results_dir)
248
+ if output:
249
+ process_output(output)
250
+ except:
251
+ elapsed = (datetime.now() - start).total_seconds() / 60
252
+ logger.error('Check of "%s" failed in %d minutes, %d remaining attempts', rse, elapsed, attempts, exc_info=True)
253
+ success = False
254
+ else:
255
+ elapsed = (datetime.now() - start).total_seconds() / 60
256
+ logger.info('SUCCESS checking "%s" in %d minutes', rse, elapsed)
257
+ success = True
258
+
259
+ if not keep_dumps:
260
+ remove = glob.glob(os.path.join(cache_dir, 'replicafromhdfs_{0}_*'.format(rse)))
261
+ remove.extend(glob.glob(os.path.join(cache_dir, 'ddmendpoint_{0}_*'.format(rse))))
262
+ logger.debug('Removing: %s', remove)
263
+ for fil in remove:
264
+ os.remove(fil)
265
+
266
+ if not success and attempts > 0:
267
+ retry.put((rse, attempts - 1))
268
+
269
+
270
+ def activity_logger(
271
+ logpipes: "Iterable[Connection]",
272
+ logfilename: str,
273
+ terminate: "Event"
274
+ ) -> None:
275
+ handler = logging.handlers.RotatingFileHandler(
276
+ logfilename,
277
+ maxBytes=20971520,
278
+ backupCount=10,
279
+ )
280
+ handler.setFormatter(logging.Formatter(fmt=None))
281
+ logger = logging.getLogger('auditor-logger-raw')
282
+ logger.addHandler(handler)
283
+ logger.setLevel(logging.CRITICAL) # The level of this logger is irrelevant
284
+
285
+ while not terminate.is_set():
286
+ ready, _, _ = select.select(logpipes, tuple(), tuple(), 30)
287
+ if ready:
288
+ for logpipe in ready:
289
+ logger.critical(logpipe.recv())