rucio 32.8.6__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 (481) hide show
  1. rucio/__init__.py +18 -0
  2. rucio/alembicrevision.py +16 -0
  3. rucio/api/__init__.py +14 -0
  4. rucio/api/account.py +266 -0
  5. rucio/api/account_limit.py +287 -0
  6. rucio/api/authentication.py +302 -0
  7. rucio/api/config.py +218 -0
  8. rucio/api/credential.py +60 -0
  9. rucio/api/did.py +726 -0
  10. rucio/api/dirac.py +71 -0
  11. rucio/api/exporter.py +60 -0
  12. rucio/api/heartbeat.py +62 -0
  13. rucio/api/identity.py +160 -0
  14. rucio/api/importer.py +46 -0
  15. rucio/api/lifetime_exception.py +95 -0
  16. rucio/api/lock.py +131 -0
  17. rucio/api/meta.py +85 -0
  18. rucio/api/permission.py +72 -0
  19. rucio/api/quarantined_replica.py +69 -0
  20. rucio/api/replica.py +528 -0
  21. rucio/api/request.py +220 -0
  22. rucio/api/rse.py +601 -0
  23. rucio/api/rule.py +335 -0
  24. rucio/api/scope.py +89 -0
  25. rucio/api/subscription.py +255 -0
  26. rucio/api/temporary_did.py +49 -0
  27. rucio/api/vo.py +112 -0
  28. rucio/client/__init__.py +16 -0
  29. rucio/client/accountclient.py +413 -0
  30. rucio/client/accountlimitclient.py +155 -0
  31. rucio/client/baseclient.py +929 -0
  32. rucio/client/client.py +77 -0
  33. rucio/client/configclient.py +113 -0
  34. rucio/client/credentialclient.py +54 -0
  35. rucio/client/didclient.py +691 -0
  36. rucio/client/diracclient.py +48 -0
  37. rucio/client/downloadclient.py +1674 -0
  38. rucio/client/exportclient.py +44 -0
  39. rucio/client/fileclient.py +51 -0
  40. rucio/client/importclient.py +42 -0
  41. rucio/client/lifetimeclient.py +74 -0
  42. rucio/client/lockclient.py +99 -0
  43. rucio/client/metaclient.py +137 -0
  44. rucio/client/pingclient.py +45 -0
  45. rucio/client/replicaclient.py +444 -0
  46. rucio/client/requestclient.py +109 -0
  47. rucio/client/rseclient.py +664 -0
  48. rucio/client/ruleclient.py +287 -0
  49. rucio/client/scopeclient.py +88 -0
  50. rucio/client/subscriptionclient.py +161 -0
  51. rucio/client/touchclient.py +78 -0
  52. rucio/client/uploadclient.py +871 -0
  53. rucio/common/__init__.py +14 -0
  54. rucio/common/cache.py +74 -0
  55. rucio/common/config.py +796 -0
  56. rucio/common/constants.py +92 -0
  57. rucio/common/constraints.py +18 -0
  58. rucio/common/didtype.py +187 -0
  59. rucio/common/dumper/__init__.py +306 -0
  60. rucio/common/dumper/consistency.py +449 -0
  61. rucio/common/dumper/data_models.py +325 -0
  62. rucio/common/dumper/path_parsing.py +65 -0
  63. rucio/common/exception.py +1092 -0
  64. rucio/common/extra.py +37 -0
  65. rucio/common/logging.py +404 -0
  66. rucio/common/pcache.py +1387 -0
  67. rucio/common/policy.py +84 -0
  68. rucio/common/schema/__init__.py +143 -0
  69. rucio/common/schema/atlas.py +411 -0
  70. rucio/common/schema/belleii.py +406 -0
  71. rucio/common/schema/cms.py +478 -0
  72. rucio/common/schema/domatpc.py +399 -0
  73. rucio/common/schema/escape.py +424 -0
  74. rucio/common/schema/generic.py +431 -0
  75. rucio/common/schema/generic_multi_vo.py +410 -0
  76. rucio/common/schema/icecube.py +404 -0
  77. rucio/common/schema/lsst.py +423 -0
  78. rucio/common/stomp_utils.py +160 -0
  79. rucio/common/stopwatch.py +56 -0
  80. rucio/common/test_rucio_server.py +148 -0
  81. rucio/common/types.py +158 -0
  82. rucio/common/utils.py +1946 -0
  83. rucio/core/__init__.py +14 -0
  84. rucio/core/account.py +426 -0
  85. rucio/core/account_counter.py +171 -0
  86. rucio/core/account_limit.py +357 -0
  87. rucio/core/authentication.py +563 -0
  88. rucio/core/config.py +386 -0
  89. rucio/core/credential.py +218 -0
  90. rucio/core/did.py +3102 -0
  91. rucio/core/did_meta_plugins/__init__.py +250 -0
  92. rucio/core/did_meta_plugins/did_column_meta.py +326 -0
  93. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +116 -0
  94. rucio/core/did_meta_plugins/filter_engine.py +573 -0
  95. rucio/core/did_meta_plugins/json_meta.py +215 -0
  96. rucio/core/did_meta_plugins/mongo_meta.py +199 -0
  97. rucio/core/did_meta_plugins/postgres_meta.py +317 -0
  98. rucio/core/dirac.py +208 -0
  99. rucio/core/distance.py +164 -0
  100. rucio/core/exporter.py +59 -0
  101. rucio/core/heartbeat.py +263 -0
  102. rucio/core/identity.py +290 -0
  103. rucio/core/importer.py +248 -0
  104. rucio/core/lifetime_exception.py +377 -0
  105. rucio/core/lock.py +474 -0
  106. rucio/core/message.py +241 -0
  107. rucio/core/meta.py +190 -0
  108. rucio/core/monitor.py +441 -0
  109. rucio/core/naming_convention.py +154 -0
  110. rucio/core/nongrid_trace.py +124 -0
  111. rucio/core/oidc.py +1339 -0
  112. rucio/core/permission/__init__.py +107 -0
  113. rucio/core/permission/atlas.py +1333 -0
  114. rucio/core/permission/belleii.py +1076 -0
  115. rucio/core/permission/cms.py +1166 -0
  116. rucio/core/permission/escape.py +1076 -0
  117. rucio/core/permission/generic.py +1128 -0
  118. rucio/core/permission/generic_multi_vo.py +1148 -0
  119. rucio/core/quarantined_replica.py +190 -0
  120. rucio/core/replica.py +3627 -0
  121. rucio/core/replica_sorter.py +368 -0
  122. rucio/core/request.py +2241 -0
  123. rucio/core/rse.py +1835 -0
  124. rucio/core/rse_counter.py +155 -0
  125. rucio/core/rse_expression_parser.py +460 -0
  126. rucio/core/rse_selector.py +277 -0
  127. rucio/core/rule.py +3419 -0
  128. rucio/core/rule_grouping.py +1473 -0
  129. rucio/core/scope.py +152 -0
  130. rucio/core/subscription.py +316 -0
  131. rucio/core/temporary_did.py +188 -0
  132. rucio/core/topology.py +448 -0
  133. rucio/core/trace.py +361 -0
  134. rucio/core/transfer.py +1233 -0
  135. rucio/core/vo.py +151 -0
  136. rucio/core/volatile_replica.py +123 -0
  137. rucio/daemons/__init__.py +14 -0
  138. rucio/daemons/abacus/__init__.py +14 -0
  139. rucio/daemons/abacus/account.py +106 -0
  140. rucio/daemons/abacus/collection_replica.py +113 -0
  141. rucio/daemons/abacus/rse.py +107 -0
  142. rucio/daemons/atropos/__init__.py +14 -0
  143. rucio/daemons/atropos/atropos.py +243 -0
  144. rucio/daemons/auditor/__init__.py +261 -0
  145. rucio/daemons/auditor/hdfs.py +86 -0
  146. rucio/daemons/auditor/srmdumps.py +284 -0
  147. rucio/daemons/automatix/__init__.py +14 -0
  148. rucio/daemons/automatix/automatix.py +281 -0
  149. rucio/daemons/badreplicas/__init__.py +14 -0
  150. rucio/daemons/badreplicas/minos.py +311 -0
  151. rucio/daemons/badreplicas/minos_temporary_expiration.py +173 -0
  152. rucio/daemons/badreplicas/necromancer.py +200 -0
  153. rucio/daemons/bb8/__init__.py +14 -0
  154. rucio/daemons/bb8/bb8.py +356 -0
  155. rucio/daemons/bb8/common.py +762 -0
  156. rucio/daemons/bb8/nuclei_background_rebalance.py +147 -0
  157. rucio/daemons/bb8/t2_background_rebalance.py +146 -0
  158. rucio/daemons/c3po/__init__.py +14 -0
  159. rucio/daemons/c3po/algorithms/__init__.py +14 -0
  160. rucio/daemons/c3po/algorithms/simple.py +131 -0
  161. rucio/daemons/c3po/algorithms/t2_free_space.py +125 -0
  162. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +127 -0
  163. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +279 -0
  164. rucio/daemons/c3po/c3po.py +342 -0
  165. rucio/daemons/c3po/collectors/__init__.py +14 -0
  166. rucio/daemons/c3po/collectors/agis.py +108 -0
  167. rucio/daemons/c3po/collectors/free_space.py +62 -0
  168. rucio/daemons/c3po/collectors/jedi_did.py +48 -0
  169. rucio/daemons/c3po/collectors/mock_did.py +46 -0
  170. rucio/daemons/c3po/collectors/network_metrics.py +63 -0
  171. rucio/daemons/c3po/collectors/workload.py +110 -0
  172. rucio/daemons/c3po/utils/__init__.py +14 -0
  173. rucio/daemons/c3po/utils/dataset_cache.py +40 -0
  174. rucio/daemons/c3po/utils/expiring_dataset_cache.py +45 -0
  175. rucio/daemons/c3po/utils/expiring_list.py +63 -0
  176. rucio/daemons/c3po/utils/popularity.py +82 -0
  177. rucio/daemons/c3po/utils/timeseries.py +76 -0
  178. rucio/daemons/cache/__init__.py +14 -0
  179. rucio/daemons/cache/consumer.py +191 -0
  180. rucio/daemons/common.py +391 -0
  181. rucio/daemons/conveyor/__init__.py +14 -0
  182. rucio/daemons/conveyor/common.py +530 -0
  183. rucio/daemons/conveyor/finisher.py +492 -0
  184. rucio/daemons/conveyor/poller.py +372 -0
  185. rucio/daemons/conveyor/preparer.py +198 -0
  186. rucio/daemons/conveyor/receiver.py +206 -0
  187. rucio/daemons/conveyor/stager.py +127 -0
  188. rucio/daemons/conveyor/submitter.py +379 -0
  189. rucio/daemons/conveyor/throttler.py +468 -0
  190. rucio/daemons/follower/__init__.py +14 -0
  191. rucio/daemons/follower/follower.py +97 -0
  192. rucio/daemons/hermes/__init__.py +14 -0
  193. rucio/daemons/hermes/hermes.py +738 -0
  194. rucio/daemons/judge/__init__.py +14 -0
  195. rucio/daemons/judge/cleaner.py +149 -0
  196. rucio/daemons/judge/evaluator.py +172 -0
  197. rucio/daemons/judge/injector.py +154 -0
  198. rucio/daemons/judge/repairer.py +144 -0
  199. rucio/daemons/oauthmanager/__init__.py +14 -0
  200. rucio/daemons/oauthmanager/oauthmanager.py +199 -0
  201. rucio/daemons/reaper/__init__.py +14 -0
  202. rucio/daemons/reaper/dark_reaper.py +272 -0
  203. rucio/daemons/reaper/light_reaper.py +255 -0
  204. rucio/daemons/reaper/reaper.py +701 -0
  205. rucio/daemons/replicarecoverer/__init__.py +14 -0
  206. rucio/daemons/replicarecoverer/suspicious_replica_recoverer.py +487 -0
  207. rucio/daemons/storage/__init__.py +14 -0
  208. rucio/daemons/storage/consistency/__init__.py +14 -0
  209. rucio/daemons/storage/consistency/actions.py +753 -0
  210. rucio/daemons/tracer/__init__.py +14 -0
  211. rucio/daemons/tracer/kronos.py +513 -0
  212. rucio/daemons/transmogrifier/__init__.py +14 -0
  213. rucio/daemons/transmogrifier/transmogrifier.py +753 -0
  214. rucio/daemons/undertaker/__init__.py +14 -0
  215. rucio/daemons/undertaker/undertaker.py +137 -0
  216. rucio/db/__init__.py +14 -0
  217. rucio/db/sqla/__init__.py +38 -0
  218. rucio/db/sqla/constants.py +192 -0
  219. rucio/db/sqla/migrate_repo/__init__.py +14 -0
  220. rucio/db/sqla/migrate_repo/env.py +111 -0
  221. rucio/db/sqla/migrate_repo/versions/01eaf73ab656_add_new_rule_notification_state_progress.py +71 -0
  222. rucio/db/sqla/migrate_repo/versions/0437a40dbfd1_add_eol_at_in_rules.py +50 -0
  223. rucio/db/sqla/migrate_repo/versions/0f1adb7a599a_create_transfer_hops_table.py +61 -0
  224. rucio/db/sqla/migrate_repo/versions/102efcf145f4_added_stuck_at_column_to_rules.py +46 -0
  225. rucio/db/sqla/migrate_repo/versions/13d4f70c66a9_introduce_transfer_limits.py +93 -0
  226. rucio/db/sqla/migrate_repo/versions/140fef722e91_cleanup_distances_table.py +78 -0
  227. rucio/db/sqla/migrate_repo/versions/14ec5aeb64cf_add_request_external_host.py +46 -0
  228. rucio/db/sqla/migrate_repo/versions/156fb5b5a14_add_request_type_to_requests_idx.py +53 -0
  229. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +69 -0
  230. rucio/db/sqla/migrate_repo/versions/16a0aca82e12_create_index_on_table_replicas_path.py +42 -0
  231. rucio/db/sqla/migrate_repo/versions/1803333ac20f_adding_provenance_and_phys_group.py +46 -0
  232. rucio/db/sqla/migrate_repo/versions/1a29d6a9504c_add_didtype_chck_to_requests.py +61 -0
  233. rucio/db/sqla/migrate_repo/versions/1a80adff031a_create_index_on_rules_hist_recent.py +42 -0
  234. rucio/db/sqla/migrate_repo/versions/1c45d9730ca6_increase_identity_length.py +141 -0
  235. rucio/db/sqla/migrate_repo/versions/1d1215494e95_add_quarantined_replicas_table.py +75 -0
  236. rucio/db/sqla/migrate_repo/versions/1d96f484df21_asynchronous_rules_and_rule_approval.py +75 -0
  237. rucio/db/sqla/migrate_repo/versions/1f46c5f240ac_add_bytes_column_to_bad_replicas.py +46 -0
  238. rucio/db/sqla/migrate_repo/versions/1fc15ab60d43_add_message_history_table.py +51 -0
  239. rucio/db/sqla/migrate_repo/versions/2190e703eb6e_move_rse_settings_to_rse_attributes.py +135 -0
  240. rucio/db/sqla/migrate_repo/versions/21d6b9dc9961_add_mismatch_scheme_state_to_requests.py +65 -0
  241. rucio/db/sqla/migrate_repo/versions/22cf51430c78_add_availability_column_to_table_rses.py +42 -0
  242. rucio/db/sqla/migrate_repo/versions/22d887e4ec0a_create_sources_table.py +66 -0
  243. rucio/db/sqla/migrate_repo/versions/25821a8a45a3_remove_unique_constraint_on_requests.py +54 -0
  244. rucio/db/sqla/migrate_repo/versions/25fc855625cf_added_unique_constraint_to_rules.py +43 -0
  245. rucio/db/sqla/migrate_repo/versions/269fee20dee9_add_repair_cnt_to_locks.py +46 -0
  246. rucio/db/sqla/migrate_repo/versions/271a46ea6244_add_ignore_availability_column_to_rules.py +47 -0
  247. rucio/db/sqla/migrate_repo/versions/277b5fbb41d3_switch_heartbeats_executable.py +54 -0
  248. rucio/db/sqla/migrate_repo/versions/27e3a68927fb_remove_replicas_tombstone_and_replicas_.py +39 -0
  249. rucio/db/sqla/migrate_repo/versions/2854cd9e168_added_rule_id_column.py +48 -0
  250. rucio/db/sqla/migrate_repo/versions/295289b5a800_processed_by_and__at_in_requests.py +47 -0
  251. rucio/db/sqla/migrate_repo/versions/2962ece31cf4_add_nbaccesses_column_in_the_did_table.py +48 -0
  252. rucio/db/sqla/migrate_repo/versions/2af3291ec4c_added_replicas_history_table.py +59 -0
  253. rucio/db/sqla/migrate_repo/versions/2b69addda658_add_columns_for_third_party_copy_read_.py +47 -0
  254. rucio/db/sqla/migrate_repo/versions/2b8e7bcb4783_add_config_table.py +72 -0
  255. rucio/db/sqla/migrate_repo/versions/2ba5229cb54c_add_submitted_at_to_requests_table.py +46 -0
  256. rucio/db/sqla/migrate_repo/versions/2cbee484dcf9_added_column_volume_to_rse_transfer_.py +45 -0
  257. rucio/db/sqla/migrate_repo/versions/2edee4a83846_add_source_to_requests_and_requests_.py +48 -0
  258. rucio/db/sqla/migrate_repo/versions/2eef46be23d4_change_tokens_pk.py +48 -0
  259. rucio/db/sqla/migrate_repo/versions/2f648fc909f3_index_in_rule_history_on_scope_name.py +42 -0
  260. rucio/db/sqla/migrate_repo/versions/3082b8cef557_add_naming_convention_table_and_closed_.py +69 -0
  261. rucio/db/sqla/migrate_repo/versions/30fa38b6434e_add_index_on_service_column_in_the_message_table.py +46 -0
  262. rucio/db/sqla/migrate_repo/versions/3152492b110b_added_staging_area_column.py +78 -0
  263. rucio/db/sqla/migrate_repo/versions/32c7d2783f7e_create_bad_replicas_table.py +62 -0
  264. rucio/db/sqla/migrate_repo/versions/3345511706b8_replicas_table_pk_definition_is_in_.py +74 -0
  265. rucio/db/sqla/migrate_repo/versions/35ef10d1e11b_change_index_on_table_requests.py +44 -0
  266. rucio/db/sqla/migrate_repo/versions/379a19b5332d_create_rse_limits_table.py +67 -0
  267. rucio/db/sqla/migrate_repo/versions/384b96aa0f60_created_rule_history_tables.py +134 -0
  268. rucio/db/sqla/migrate_repo/versions/3ac1660a1a72_extend_distance_table.py +58 -0
  269. rucio/db/sqla/migrate_repo/versions/3ad36e2268b0_create_collection_replicas_updates_table.py +79 -0
  270. rucio/db/sqla/migrate_repo/versions/3c9df354071b_extend_waiting_request_state.py +61 -0
  271. rucio/db/sqla/migrate_repo/versions/3d9813fab443_add_a_new_state_lost_in_badfilesstatus.py +45 -0
  272. rucio/db/sqla/migrate_repo/versions/40ad39ce3160_add_transferred_at_to_requests_table.py +46 -0
  273. rucio/db/sqla/migrate_repo/versions/4207be2fd914_add_notification_column_to_rules.py +65 -0
  274. rucio/db/sqla/migrate_repo/versions/42db2617c364_create_index_on_requests_external_id.py +42 -0
  275. rucio/db/sqla/migrate_repo/versions/436827b13f82_added_column_activity_to_table_requests.py +46 -0
  276. rucio/db/sqla/migrate_repo/versions/44278720f774_update_requests_typ_sta_upd_idx_index.py +46 -0
  277. rucio/db/sqla/migrate_repo/versions/45378a1e76a8_create_collection_replica_table.py +80 -0
  278. rucio/db/sqla/migrate_repo/versions/469d262be19_removing_created_at_index.py +43 -0
  279. rucio/db/sqla/migrate_repo/versions/4783c1f49cb4_create_distance_table.py +61 -0
  280. rucio/db/sqla/migrate_repo/versions/49a21b4d4357_create_index_on_table_tokens.py +47 -0
  281. rucio/db/sqla/migrate_repo/versions/4a2cbedda8b9_add_source_replica_expression_column_to_.py +46 -0
  282. rucio/db/sqla/migrate_repo/versions/4a7182d9578b_added_bytes_length_accessed_at_columns.py +52 -0
  283. rucio/db/sqla/migrate_repo/versions/4bab9edd01fc_create_index_on_requests_rule_id.py +42 -0
  284. rucio/db/sqla/migrate_repo/versions/4c3a4acfe006_new_attr_account_table.py +65 -0
  285. rucio/db/sqla/migrate_repo/versions/4cf0a2e127d4_adding_transient_metadata.py +46 -0
  286. rucio/db/sqla/migrate_repo/versions/50280c53117c_add_qos_class_to_rse.py +47 -0
  287. rucio/db/sqla/migrate_repo/versions/52153819589c_add_rse_id_to_replicas_table.py +45 -0
  288. rucio/db/sqla/migrate_repo/versions/52fd9f4916fa_added_activity_to_rules.py +46 -0
  289. rucio/db/sqla/migrate_repo/versions/53b479c3cb0f_fix_did_meta_table_missing_updated_at_.py +48 -0
  290. rucio/db/sqla/migrate_repo/versions/5673b4b6e843_add_wfms_metadata_to_rule_tables.py +50 -0
  291. rucio/db/sqla/migrate_repo/versions/575767d9f89_added_source_history_table.py +59 -0
  292. rucio/db/sqla/migrate_repo/versions/58bff7008037_add_started_at_to_requests.py +48 -0
  293. rucio/db/sqla/migrate_repo/versions/58c8b78301ab_rename_callback_to_message.py +108 -0
  294. rucio/db/sqla/migrate_repo/versions/5f139f77382a_added_child_rule_id_column.py +57 -0
  295. rucio/db/sqla/migrate_repo/versions/688ef1840840_adding_did_meta_table.py +51 -0
  296. rucio/db/sqla/migrate_repo/versions/6e572a9bfbf3_add_new_split_container_column_to_rules.py +50 -0
  297. rucio/db/sqla/migrate_repo/versions/70587619328_add_comment_column_for_subscriptions.py +46 -0
  298. rucio/db/sqla/migrate_repo/versions/739064d31565_remove_history_table_pks.py +42 -0
  299. rucio/db/sqla/migrate_repo/versions/7541902bf173_add_didsfollowed_and_followevents_table.py +93 -0
  300. rucio/db/sqla/migrate_repo/versions/7ec22226cdbf_new_replica_state_for_temporary_.py +73 -0
  301. rucio/db/sqla/migrate_repo/versions/810a41685bc1_added_columns_rse_transfer_limits.py +52 -0
  302. rucio/db/sqla/migrate_repo/versions/83f991c63a93_correct_rse_expression_length.py +45 -0
  303. rucio/db/sqla/migrate_repo/versions/8523998e2e76_increase_size_of_extended_attributes_.py +46 -0
  304. rucio/db/sqla/migrate_repo/versions/8ea9122275b1_adding_missing_function_based_indices.py +54 -0
  305. rucio/db/sqla/migrate_repo/versions/90f47792bb76_add_clob_payload_to_messages.py +48 -0
  306. rucio/db/sqla/migrate_repo/versions/914b8f02df38_new_table_for_lifetime_model_exceptions.py +70 -0
  307. rucio/db/sqla/migrate_repo/versions/94a5961ddbf2_add_estimator_columns.py +48 -0
  308. rucio/db/sqla/migrate_repo/versions/9a1b149a2044_add_saml_identity_type.py +95 -0
  309. rucio/db/sqla/migrate_repo/versions/9a45bc4ea66d_add_vp_table.py +55 -0
  310. rucio/db/sqla/migrate_repo/versions/9eb936a81eb1_true_is_true.py +74 -0
  311. rucio/db/sqla/migrate_repo/versions/a118956323f8_added_vo_table_and_vo_col_to_rse.py +78 -0
  312. rucio/db/sqla/migrate_repo/versions/a193a275255c_add_status_column_in_messages.py +49 -0
  313. rucio/db/sqla/migrate_repo/versions/a5f6f6e928a7_1_7_0.py +124 -0
  314. rucio/db/sqla/migrate_repo/versions/a616581ee47_added_columns_to_table_requests.py +60 -0
  315. rucio/db/sqla/migrate_repo/versions/a6eb23955c28_state_idx_non_functional.py +53 -0
  316. rucio/db/sqla/migrate_repo/versions/a74275a1ad30_added_global_quota_table.py +56 -0
  317. rucio/db/sqla/migrate_repo/versions/a93e4e47bda_heartbeats.py +67 -0
  318. rucio/db/sqla/migrate_repo/versions/ae2a56fcc89_added_comment_column_to_rules.py +50 -0
  319. rucio/db/sqla/migrate_repo/versions/b4293a99f344_added_column_identity_to_table_tokens.py +46 -0
  320. rucio/db/sqla/migrate_repo/versions/b7d287de34fd_removal_of_replicastate_source.py +92 -0
  321. rucio/db/sqla/migrate_repo/versions/b818052fa670_add_index_to_quarantined_replicas.py +42 -0
  322. rucio/db/sqla/migrate_repo/versions/b8caac94d7f0_add_comments_column_for_subscriptions_.py +46 -0
  323. rucio/db/sqla/migrate_repo/versions/b96a1c7e1cc4_new_bad_pfns_table_and_bad_replicas_.py +147 -0
  324. rucio/db/sqla/migrate_repo/versions/bb695f45c04_extend_request_state.py +78 -0
  325. rucio/db/sqla/migrate_repo/versions/bc68e9946deb_add_staging_timestamps_to_request.py +53 -0
  326. rucio/db/sqla/migrate_repo/versions/bf3baa1c1474_correct_pk_and_idx_for_history_tables.py +74 -0
  327. rucio/db/sqla/migrate_repo/versions/c0937668555f_add_qos_policy_map_table.py +56 -0
  328. rucio/db/sqla/migrate_repo/versions/c129ccdb2d5_add_lumiblocknr_to_dids.py +46 -0
  329. rucio/db/sqla/migrate_repo/versions/ccdbcd48206e_add_did_type_column_index_on_did_meta_.py +68 -0
  330. rucio/db/sqla/migrate_repo/versions/cebad904c4dd_new_payload_column_for_heartbeats.py +48 -0
  331. rucio/db/sqla/migrate_repo/versions/d1189a09c6e0_oauth2_0_and_jwt_feature_support_adding_.py +149 -0
  332. rucio/db/sqla/migrate_repo/versions/d23453595260_extend_request_state_for_preparer.py +106 -0
  333. rucio/db/sqla/migrate_repo/versions/d6dceb1de2d_added_purge_column_to_rules.py +47 -0
  334. rucio/db/sqla/migrate_repo/versions/d6e2c3b2cf26_remove_third_party_copy_column_from_rse.py +45 -0
  335. rucio/db/sqla/migrate_repo/versions/d91002c5841_new_account_limits_table.py +105 -0
  336. rucio/db/sqla/migrate_repo/versions/e138c364ebd0_extending_columns_for_filter_and_.py +52 -0
  337. rucio/db/sqla/migrate_repo/versions/e59300c8b179_support_for_archive.py +106 -0
  338. rucio/db/sqla/migrate_repo/versions/f1b14a8c2ac1_postgres_use_check_constraints.py +30 -0
  339. rucio/db/sqla/migrate_repo/versions/f41ffe206f37_oracle_global_temporary_tables.py +75 -0
  340. rucio/db/sqla/migrate_repo/versions/f85a2962b021_adding_transfertool_column_to_requests_.py +49 -0
  341. rucio/db/sqla/migrate_repo/versions/fa7a7d78b602_increase_refresh_token_size.py +45 -0
  342. rucio/db/sqla/migrate_repo/versions/fb28a95fe288_add_replicas_rse_id_tombstone_idx.py +38 -0
  343. rucio/db/sqla/migrate_repo/versions/fe1a65b176c9_set_third_party_copy_read_and_write_.py +44 -0
  344. rucio/db/sqla/migrate_repo/versions/fe8ea2fa9788_added_third_party_copy_column_to_rse_.py +46 -0
  345. rucio/db/sqla/models.py +1834 -0
  346. rucio/db/sqla/sautils.py +48 -0
  347. rucio/db/sqla/session.py +470 -0
  348. rucio/db/sqla/types.py +207 -0
  349. rucio/db/sqla/util.py +521 -0
  350. rucio/rse/__init__.py +97 -0
  351. rucio/rse/protocols/__init__.py +14 -0
  352. rucio/rse/protocols/cache.py +123 -0
  353. rucio/rse/protocols/dummy.py +112 -0
  354. rucio/rse/protocols/gfal.py +701 -0
  355. rucio/rse/protocols/globus.py +243 -0
  356. rucio/rse/protocols/gsiftp.py +93 -0
  357. rucio/rse/protocols/http_cache.py +83 -0
  358. rucio/rse/protocols/mock.py +124 -0
  359. rucio/rse/protocols/ngarc.py +210 -0
  360. rucio/rse/protocols/posix.py +251 -0
  361. rucio/rse/protocols/protocol.py +530 -0
  362. rucio/rse/protocols/rclone.py +365 -0
  363. rucio/rse/protocols/rfio.py +137 -0
  364. rucio/rse/protocols/srm.py +339 -0
  365. rucio/rse/protocols/ssh.py +414 -0
  366. rucio/rse/protocols/storm.py +207 -0
  367. rucio/rse/protocols/webdav.py +547 -0
  368. rucio/rse/protocols/xrootd.py +295 -0
  369. rucio/rse/rsemanager.py +752 -0
  370. rucio/tests/__init__.py +14 -0
  371. rucio/tests/common.py +244 -0
  372. rucio/tests/common_server.py +132 -0
  373. rucio/transfertool/__init__.py +14 -0
  374. rucio/transfertool/fts3.py +1484 -0
  375. rucio/transfertool/globus.py +200 -0
  376. rucio/transfertool/globus_library.py +182 -0
  377. rucio/transfertool/mock.py +81 -0
  378. rucio/transfertool/transfertool.py +212 -0
  379. rucio/vcsversion.py +11 -0
  380. rucio/version.py +46 -0
  381. rucio/web/__init__.py +14 -0
  382. rucio/web/rest/__init__.py +14 -0
  383. rucio/web/rest/flaskapi/__init__.py +14 -0
  384. rucio/web/rest/flaskapi/authenticated_bp.py +28 -0
  385. rucio/web/rest/flaskapi/v1/__init__.py +14 -0
  386. rucio/web/rest/flaskapi/v1/accountlimits.py +234 -0
  387. rucio/web/rest/flaskapi/v1/accounts.py +1088 -0
  388. rucio/web/rest/flaskapi/v1/archives.py +100 -0
  389. rucio/web/rest/flaskapi/v1/auth.py +1642 -0
  390. rucio/web/rest/flaskapi/v1/common.py +385 -0
  391. rucio/web/rest/flaskapi/v1/config.py +305 -0
  392. rucio/web/rest/flaskapi/v1/credentials.py +213 -0
  393. rucio/web/rest/flaskapi/v1/dids.py +2204 -0
  394. rucio/web/rest/flaskapi/v1/dirac.py +116 -0
  395. rucio/web/rest/flaskapi/v1/export.py +77 -0
  396. rucio/web/rest/flaskapi/v1/heartbeats.py +129 -0
  397. rucio/web/rest/flaskapi/v1/identities.py +263 -0
  398. rucio/web/rest/flaskapi/v1/import.py +133 -0
  399. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +315 -0
  400. rucio/web/rest/flaskapi/v1/locks.py +360 -0
  401. rucio/web/rest/flaskapi/v1/main.py +83 -0
  402. rucio/web/rest/flaskapi/v1/meta.py +226 -0
  403. rucio/web/rest/flaskapi/v1/metrics.py +37 -0
  404. rucio/web/rest/flaskapi/v1/nongrid_traces.py +97 -0
  405. rucio/web/rest/flaskapi/v1/ping.py +89 -0
  406. rucio/web/rest/flaskapi/v1/redirect.py +366 -0
  407. rucio/web/rest/flaskapi/v1/replicas.py +1866 -0
  408. rucio/web/rest/flaskapi/v1/requests.py +841 -0
  409. rucio/web/rest/flaskapi/v1/rses.py +2204 -0
  410. rucio/web/rest/flaskapi/v1/rules.py +824 -0
  411. rucio/web/rest/flaskapi/v1/scopes.py +161 -0
  412. rucio/web/rest/flaskapi/v1/subscriptions.py +646 -0
  413. rucio/web/rest/flaskapi/v1/templates/auth_crash.html +80 -0
  414. rucio/web/rest/flaskapi/v1/templates/auth_granted.html +82 -0
  415. rucio/web/rest/flaskapi/v1/tmp_dids.py +115 -0
  416. rucio/web/rest/flaskapi/v1/traces.py +100 -0
  417. rucio/web/rest/flaskapi/v1/vos.py +280 -0
  418. rucio/web/rest/main.py +19 -0
  419. rucio/web/rest/metrics.py +28 -0
  420. rucio-32.8.6.data/data/rucio/etc/alembic.ini.template +71 -0
  421. rucio-32.8.6.data/data/rucio/etc/alembic_offline.ini.template +74 -0
  422. rucio-32.8.6.data/data/rucio/etc/globus-config.yml.template +5 -0
  423. rucio-32.8.6.data/data/rucio/etc/ldap.cfg.template +30 -0
  424. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approval_request.tmpl +38 -0
  425. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +4 -0
  426. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_approved_user.tmpl +17 -0
  427. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +6 -0
  428. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_denied_user.tmpl +17 -0
  429. rucio-32.8.6.data/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +19 -0
  430. rucio-32.8.6.data/data/rucio/etc/rse-accounts.cfg.template +25 -0
  431. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.atlas.client.template +42 -0
  432. rucio-32.8.6.data/data/rucio/etc/rucio.cfg.template +257 -0
  433. rucio-32.8.6.data/data/rucio/etc/rucio_multi_vo.cfg.template +234 -0
  434. rucio-32.8.6.data/data/rucio/requirements.txt +55 -0
  435. rucio-32.8.6.data/data/rucio/tools/bootstrap.py +34 -0
  436. rucio-32.8.6.data/data/rucio/tools/merge_rucio_configs.py +147 -0
  437. rucio-32.8.6.data/data/rucio/tools/reset_database.py +40 -0
  438. rucio-32.8.6.data/scripts/rucio +2540 -0
  439. rucio-32.8.6.data/scripts/rucio-abacus-account +75 -0
  440. rucio-32.8.6.data/scripts/rucio-abacus-collection-replica +47 -0
  441. rucio-32.8.6.data/scripts/rucio-abacus-rse +79 -0
  442. rucio-32.8.6.data/scripts/rucio-admin +2434 -0
  443. rucio-32.8.6.data/scripts/rucio-atropos +61 -0
  444. rucio-32.8.6.data/scripts/rucio-auditor +199 -0
  445. rucio-32.8.6.data/scripts/rucio-automatix +51 -0
  446. rucio-32.8.6.data/scripts/rucio-bb8 +58 -0
  447. rucio-32.8.6.data/scripts/rucio-c3po +86 -0
  448. rucio-32.8.6.data/scripts/rucio-cache-client +135 -0
  449. rucio-32.8.6.data/scripts/rucio-cache-consumer +43 -0
  450. rucio-32.8.6.data/scripts/rucio-conveyor-finisher +59 -0
  451. rucio-32.8.6.data/scripts/rucio-conveyor-poller +67 -0
  452. rucio-32.8.6.data/scripts/rucio-conveyor-preparer +38 -0
  453. rucio-32.8.6.data/scripts/rucio-conveyor-receiver +44 -0
  454. rucio-32.8.6.data/scripts/rucio-conveyor-stager +77 -0
  455. rucio-32.8.6.data/scripts/rucio-conveyor-submitter +140 -0
  456. rucio-32.8.6.data/scripts/rucio-conveyor-throttler +105 -0
  457. rucio-32.8.6.data/scripts/rucio-dark-reaper +54 -0
  458. rucio-32.8.6.data/scripts/rucio-dumper +159 -0
  459. rucio-32.8.6.data/scripts/rucio-follower +45 -0
  460. rucio-32.8.6.data/scripts/rucio-hermes +55 -0
  461. rucio-32.8.6.data/scripts/rucio-judge-cleaner +90 -0
  462. rucio-32.8.6.data/scripts/rucio-judge-evaluator +138 -0
  463. rucio-32.8.6.data/scripts/rucio-judge-injector +45 -0
  464. rucio-32.8.6.data/scripts/rucio-judge-repairer +45 -0
  465. rucio-32.8.6.data/scripts/rucio-kronos +45 -0
  466. rucio-32.8.6.data/scripts/rucio-light-reaper +53 -0
  467. rucio-32.8.6.data/scripts/rucio-minos +54 -0
  468. rucio-32.8.6.data/scripts/rucio-minos-temporary-expiration +51 -0
  469. rucio-32.8.6.data/scripts/rucio-necromancer +121 -0
  470. rucio-32.8.6.data/scripts/rucio-oauth-manager +64 -0
  471. rucio-32.8.6.data/scripts/rucio-reaper +84 -0
  472. rucio-32.8.6.data/scripts/rucio-replica-recoverer +249 -0
  473. rucio-32.8.6.data/scripts/rucio-storage-consistency-actions +75 -0
  474. rucio-32.8.6.data/scripts/rucio-transmogrifier +78 -0
  475. rucio-32.8.6.data/scripts/rucio-undertaker +77 -0
  476. rucio-32.8.6.dist-info/METADATA +83 -0
  477. rucio-32.8.6.dist-info/RECORD +481 -0
  478. rucio-32.8.6.dist-info/WHEEL +5 -0
  479. rucio-32.8.6.dist-info/licenses/AUTHORS.rst +94 -0
  480. rucio-32.8.6.dist-info/licenses/LICENSE +201 -0
  481. rucio-32.8.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,243 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import datetime
17
+ import functools
18
+ import logging
19
+ import random
20
+ import threading
21
+ from sys import exc_info
22
+ from traceback import format_exception
23
+ from typing import TYPE_CHECKING
24
+
25
+ import rucio.core.lifetime_exception
26
+ import rucio.db.sqla.util
27
+ from rucio.common import exception
28
+ from rucio.common.exception import InvalidRSEExpression, RuleNotFound
29
+ from rucio.common.logging import setup_logging
30
+ from rucio.core.did import set_metadata
31
+ from rucio.core.lock import get_dataset_locks
32
+ from rucio.core.rse import get_rse_name, get_rse_vo
33
+ from rucio.core.rse_expression_parser import parse_expression
34
+ from rucio.core.rule import get_rules_beyond_eol, update_rule
35
+ from rucio.daemons.common import run_daemon
36
+ from rucio.db.sqla.constants import LifetimeExceptionsState
37
+
38
+ if TYPE_CHECKING:
39
+ from types import FrameType
40
+ from typing import Optional
41
+
42
+ from rucio.daemons.common import HeartbeatHandler
43
+
44
+ GRACEFUL_STOP = threading.Event()
45
+ DAEMON_NAME = 'atropos'
46
+
47
+
48
+ def atropos(
49
+ date_check: datetime.datetime,
50
+ dry_run: bool,
51
+ grace_period: int,
52
+ purge_replicas: bool,
53
+ spread_period: bool,
54
+ unlock: bool,
55
+ once: bool,
56
+ sleep_time: int
57
+ ) -> None:
58
+ """
59
+ Creates an Atropos Worker that gets a list of rules which have an eol_at expired and delete them.
60
+
61
+ :param grace_period: The grace_period for the rules.
62
+ :param once: Run only once.
63
+ :param sleep_time: Thread sleep time after each chunk of work.
64
+ """
65
+ run_daemon(
66
+ once=once,
67
+ graceful_stop=GRACEFUL_STOP,
68
+ executable=DAEMON_NAME,
69
+ partition_wait_time=10,
70
+ sleep_time=sleep_time,
71
+ run_once_fnc=functools.partial(
72
+ run_once,
73
+ date_check=date_check,
74
+ dry_run=dry_run,
75
+ grace_period=grace_period,
76
+ purge_replicas=purge_replicas,
77
+ spread_period=spread_period,
78
+ unlock=unlock
79
+ )
80
+ )
81
+
82
+
83
+ def run_once(
84
+ heartbeat_handler: 'HeartbeatHandler',
85
+ activity: None, # NOQA, pylint: disable=W0613
86
+ date_check: datetime.datetime,
87
+ dry_run: bool,
88
+ grace_period: int,
89
+ purge_replicas: bool,
90
+ spread_period: int,
91
+ unlock: bool,
92
+ ) -> None:
93
+ """An iteration of an Atropos worker."""
94
+ worker_number, total_workers, logger = heartbeat_handler.live()
95
+ logger(logging.DEBUG, 'Starting worker')
96
+ if not dry_run and date_check > datetime.datetime.utcnow():
97
+ logger(logging.ERROR, 'Atropos cannot run in non-dry-run mode for date in the future')
98
+ return
99
+
100
+ # Process the list of approved exceptions. In case a DID has
101
+ # multiple exceptions, the one with the expiration date further in
102
+ # the future is what matters.
103
+ summary = {}
104
+ lifetime_exceptions = {}
105
+ for excep in rucio.core.lifetime_exception.list_exceptions(exception_id=None, states=[LifetimeExceptionsState.APPROVED, ], session=None):
106
+ key = '{}:{}'.format(excep['scope'].internal, excep['name'])
107
+ if key not in lifetime_exceptions:
108
+ lifetime_exceptions[key] = excep['expires_at']
109
+ elif lifetime_exceptions[key] < excep['expires_at']:
110
+ lifetime_exceptions[key] = excep['expires_at']
111
+ logger(logging.DEBUG, '%d active exceptions', len(lifetime_exceptions))
112
+
113
+ rand = random.Random(worker_number)
114
+
115
+ try:
116
+ rules = get_rules_beyond_eol(date_check, worker_number, total_workers, session=None)
117
+ logger(logging.INFO, '%d rules to process', len(rules))
118
+ for rule_idx, rule in enumerate(rules, start=1):
119
+ did = '%s:%s' % (rule.scope, rule.name)
120
+ did_key = '{}:{}'.format(rule.scope.internal, rule.name)
121
+ logger(logging.DEBUG, 'Working on rule %s on DID %s on %s', rule.id, did, rule.rse_expression)
122
+
123
+ if (rule_idx % 1000) == 0:
124
+ logger(logging.INFO, '%s/%s rules processed', rule_idx, len(rules))
125
+
126
+ # We compute the expected eol_at
127
+ try:
128
+ rses = parse_expression(rule.rse_expression, filter_={'vo': rule.account.vo})
129
+ except InvalidRSEExpression:
130
+ logger(logging.WARNING, 'Rule %s has an RSE expression that results in an empty set: %s', rule.id, rule.rse_expression)
131
+ continue
132
+ eol_at = rucio.core.lifetime_exception.define_eol(rule.scope, rule.name, rses)
133
+ if eol_at != rule.eol_at:
134
+ logger(logging.WARNING, 'The computed eol %s differs from the one recorded %s for rule %s on %s at %s',
135
+ eol_at, rule.eol_at, rule.id, did, rule.rse_expression)
136
+ try:
137
+ update_rule(rule.id, options={'eol_at': eol_at})
138
+ except RuleNotFound:
139
+ logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
140
+ continue
141
+
142
+ # Check the exceptions
143
+ if did_key in lifetime_exceptions:
144
+ if eol_at > lifetime_exceptions[did_key]:
145
+ logger(logging.INFO, 'Rule %s on DID %s on %s has longer expiration date than the one requested : %s',
146
+ rule.id, did, rule.rse_expression, lifetime_exceptions[did_key])
147
+ else:
148
+ # If eol_at < requested extension, update eol_at
149
+ logger(logging.INFO, 'Updating rule %s on DID %s on %s according to the exception till %s',
150
+ rule.id, did, rule.rse_expression, lifetime_exceptions[did_key])
151
+ eol_at = lifetime_exceptions[did_key]
152
+ try:
153
+ update_rule(rule.id, options={'eol_at': lifetime_exceptions[did_key]})
154
+ except RuleNotFound:
155
+ logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
156
+ continue
157
+
158
+ # Now check that the new eol_at is expired
159
+ if eol_at and eol_at <= date_check:
160
+ set_metadata(scope=rule.scope, name=rule.name, key='eol_at', value=eol_at)
161
+ no_locks = True
162
+ for lock in get_dataset_locks(rule.scope, rule.name):
163
+ if lock['rule_id'] == rule[4]:
164
+ no_locks = False
165
+ if lock['rse_id'] not in summary:
166
+ summary[lock['rse_id']] = {}
167
+ if did_key not in summary[lock['rse_id']]:
168
+ summary[lock['rse_id']][did_key] = {'length': lock['length'] or 0, 'bytes': lock['bytes'] or 0}
169
+ if no_locks:
170
+ logger(logging.WARNING, 'Cannot find a lock for rule %s on DID %s', rule.id, did)
171
+ if not dry_run:
172
+ lifetime = grace_period + rand.randrange(spread_period + 1)
173
+ logger(logging.INFO, 'Setting %s seconds lifetime for rule %s', lifetime, rule.id)
174
+ options = {'lifetime': lifetime}
175
+ if purge_replicas:
176
+ options['purge_replicas'] = True
177
+ if rule.locked and unlock:
178
+ logger(logging.INFO, 'Unlocking rule %s', rule.id)
179
+ options['locked'] = False
180
+ try:
181
+ update_rule(rule.id, options=options)
182
+ except RuleNotFound:
183
+ logger(logging.WARNING, 'Cannot find rule %s on DID %s', rule.id, did)
184
+ continue
185
+ except Exception:
186
+ exc_type, exc_value, exc_traceback = exc_info()
187
+ logger(logging.CRITICAL, ''.join(format_exception(exc_type, exc_value, exc_traceback)).strip())
188
+
189
+ for rse_id in summary:
190
+ tot_size, tot_files, tot_datasets = 0, 0, 0
191
+ for did in summary[rse_id]:
192
+ tot_datasets += 1
193
+ tot_files += summary[rse_id][did].get('length', 0)
194
+ tot_size += summary[rse_id][did].get('bytes', 0)
195
+ vo = get_rse_vo(rse_id=rse_id)
196
+ logger(logging.INFO, 'For RSE %s%s %d datasets will be deleted representing %d files and %d bytes',
197
+ get_rse_name(rse_id=rse_id), '' if vo == 'def' else ' on VO ' + vo, tot_datasets, tot_files, tot_size)
198
+
199
+
200
+ def run(
201
+ date_check: datetime.datetime = datetime.datetime.utcnow(),
202
+ dry_run: bool = True,
203
+ grace_period: int = 86400,
204
+ purge_replicas: bool = False,
205
+ spread_period: int = 0,
206
+ unlock: bool = False,
207
+ once: bool = True,
208
+ sleep_time: int = 60,
209
+ threads: int = 1,
210
+ ) -> None:
211
+ """
212
+ Starts up the atropos threads.
213
+ """
214
+ setup_logging(process_name=DAEMON_NAME)
215
+
216
+ if rucio.db.sqla.util.is_old_db():
217
+ raise exception.DatabaseException('Database was not updated, daemon won\'t start')
218
+
219
+ if once:
220
+ logging.info('Will run only one iteration')
221
+ logging.info('starting atropos threads')
222
+ thread_list = [threading.Thread(target=atropos, kwargs={'date_check': date_check,
223
+ 'dry_run': dry_run,
224
+ 'grace_period': grace_period,
225
+ 'purge_replicas': purge_replicas,
226
+ 'spread_period': spread_period,
227
+ 'unlock': unlock,
228
+ 'once': once,
229
+ 'sleep_time': sleep_time}) for i in range(0, threads)]
230
+ [t.start() for t in thread_list]
231
+
232
+ logging.info('waiting for interrupts')
233
+
234
+ # Interruptible joins require a timeout.
235
+ while thread_list:
236
+ thread_list = [t.join(timeout=3.14) for t in thread_list if t and t.is_alive()]
237
+
238
+
239
+ def stop(signum: "Optional[int]" = None, frame: "Optional[FrameType]" = None) -> None:
240
+ """
241
+ Graceful exit.
242
+ """
243
+ GRACEFUL_STOP.set()
@@ -0,0 +1,261 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import bz2
17
+ import glob
18
+ import logging
19
+ import os
20
+ import queue as Queue
21
+ from datetime import datetime
22
+ from datetime import timedelta
23
+
24
+ import select
25
+
26
+ from rucio.common import config
27
+ from rucio.common.dumper import LogPipeHandler
28
+ from rucio.common.dumper import mkdir
29
+ from rucio.common.dumper import temp_file
30
+ from rucio.common.dumper.consistency import Consistency
31
+ from rucio.common.types import InternalAccount, InternalScope
32
+ from rucio.common.utils import chunks
33
+ from rucio.core.quarantined_replica import add_quarantined_replicas
34
+ from rucio.core.replica import declare_bad_file_replicas, list_replicas
35
+ from rucio.core.rse import get_rse_usage, get_rse_id
36
+ from rucio.daemons.auditor import srmdumps
37
+ from rucio.daemons.auditor.hdfs import ReplicaFromHDFS
38
+ from rucio.db.sqla.constants import BadFilesStatus
39
+
40
+
41
+ def consistency(rse, delta, configuration, cache_dir, results_dir):
42
+ logger = logging.getLogger('auditor-worker')
43
+ rsedump, rsedate = srmdumps.download_rse_dump(rse, configuration, destdir=cache_dir)
44
+ results_path = os.path.join(results_dir, '{0}_{1}'.format(rse, rsedate.strftime('%Y%m%d'))) # pylint: disable=no-member
45
+
46
+ if os.path.exists(results_path + '.bz2') or os.path.exists(results_path):
47
+ logger.warning('Consistency check for "%s" (dump dated %s) already done, skipping check', rse, rsedate.strftime('%Y%m%d')) # pylint: disable=no-member
48
+ return None
49
+
50
+ rrdump_prev = ReplicaFromHDFS.download(rse, rsedate - delta, cache_dir=cache_dir)
51
+ rrdump_next = ReplicaFromHDFS.download(rse, rsedate + delta, cache_dir=cache_dir)
52
+ results = Consistency.dump(
53
+ 'consistency-manual',
54
+ rse,
55
+ rsedump,
56
+ rrdump_prev,
57
+ rrdump_next,
58
+ date=rsedate,
59
+ cache_dir=cache_dir,
60
+ )
61
+ mkdir(results_dir)
62
+ with temp_file(results_dir, results_path) as (output, _):
63
+ for result in results:
64
+ output.write('{0}\n'.format(result.csv()))
65
+
66
+ return results_path
67
+
68
+
69
+ def guess_replica_info(path):
70
+ """Try to extract the scope and name from a path.
71
+
72
+ ``path`` should be an ``str`` with the relative path to the file on
73
+ the RSE.
74
+
75
+ Returns a ``tuple`` of which the first element is the scope of the
76
+ replica and the second element is its name.
77
+ """
78
+ items = path.split('/')
79
+ if len(items) == 1:
80
+ return None, path
81
+ elif len(items) > 2 and items[0] in ['group', 'user']:
82
+ return '.'.join(items[0:2]), items[-1]
83
+ else:
84
+ return items[0], items[-1]
85
+
86
+
87
+ def bz2_compress_file(source, chunk_size=65000):
88
+ """Compress a file with bzip2.
89
+
90
+ The destination is the path passed through ``source`` extended with
91
+ '.bz2'. The original file is deleted.
92
+
93
+ Errors are deliberately not handled gracefully. Any exceptions
94
+ should be propagated to the caller.
95
+
96
+ ``source`` should be an ``str`` with the absolute path to the file
97
+ to compress.
98
+
99
+ ``chunk_size`` should be an ``int`` with the size (in bytes) of the
100
+ chunks by which to read the file.
101
+
102
+ Returns an ``str`` with the destination path.
103
+ """
104
+ destination = '{}.bz2'.format(source)
105
+ with open(source) as plain, bz2.BZ2File(destination, 'w') as compressed:
106
+ while True:
107
+ chunk = plain.read(chunk_size)
108
+ if not chunk:
109
+ break
110
+ compressed.write(chunk.encode())
111
+ os.remove(source)
112
+ return destination
113
+
114
+
115
+ def process_output(output, sanity_check=True, compress=True):
116
+ """Perform post-consistency-check actions.
117
+
118
+ DARK files are put in the quarantined-replica table so that they
119
+ may be deleted by the Dark Reaper. LOST files are reported as
120
+ suspicious so that they may be further checked by the cloud squads.
121
+
122
+ ``output`` should be an ``str`` with the absolute path to the file
123
+ produced by ``consistency()``. It must maintain its naming
124
+ convention.
125
+
126
+ If ``sanity_check`` is ``True`` (default) and the number of entries
127
+ in the output file is deemed excessive, the actions are aborted.
128
+
129
+ If ``compress`` is ``True`` (default), the file is compressed with
130
+ bzip2 after the actions are successfully performed.
131
+ """
132
+ logger = logging.getLogger('auditor-worker')
133
+ dark_replicas = []
134
+ lost_replicas = []
135
+ try:
136
+ with open(output) as f:
137
+ for line in f:
138
+ label, path = line.rstrip().split(',', 1)
139
+ scope, name = guess_replica_info(path)
140
+ if label == 'DARK':
141
+ dark_replicas.append({'path': path,
142
+ 'scope': InternalScope(scope),
143
+ 'name': name})
144
+ elif label == 'LOST':
145
+ lost_replicas.append({'scope': InternalScope(scope),
146
+ 'name': name})
147
+ else:
148
+ raise ValueError('unexpected label')
149
+ # Since the file is read immediately after its creation, any error
150
+ # exposes a bug in the Auditor.
151
+ except Exception as error:
152
+ logger.critical('Error processing "%s"', output, exc_info=True)
153
+ raise error
154
+
155
+ rse = os.path.basename(output[:output.rfind('_')])
156
+ rse_id = get_rse_id(rse=rse)
157
+ usage = get_rse_usage(rse_id=rse_id, source='rucio')[0]
158
+ threshold = config.config_get_float('auditor', 'threshold', False, 0.2)
159
+
160
+ # Perform a basic sanity check by comparing the number of entries
161
+ # with the total number of files on the RSE. If the percentage is
162
+ # significant, there is most likely an issue with the site dump.
163
+ found_error = False
164
+ if len(dark_replicas) > threshold * usage['files']:
165
+ logger.warning('Number of DARK files is exceeding threshold: "%s"',
166
+ output)
167
+ found_error = True
168
+ if len(lost_replicas) > threshold * usage['files']:
169
+ logger.warning('Number of LOST files is exceeding threshold: "%s"',
170
+ output)
171
+ found_error = True
172
+ if found_error and sanity_check:
173
+ raise AssertionError('sanity check failed')
174
+
175
+ # While converting LOST replicas to PFNs, entries that do not
176
+ # correspond to a replica registered in Rucio are silently dropped.
177
+ 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']]
178
+
179
+ for chunk in chunks(dark_replicas, 1000):
180
+ add_quarantined_replicas(rse_id=rse_id, replicas=chunk)
181
+ logger.debug('Processed %d DARK files from "%s"', len(dark_replicas),
182
+ output)
183
+ declare_bad_file_replicas(lost_pfns, reason='Reported by Auditor',
184
+ issuer=InternalAccount('root'), status=BadFilesStatus.SUSPICIOUS)
185
+ logger.debug('Processed %d LOST files from "%s"', len(lost_replicas),
186
+ output)
187
+
188
+ if compress:
189
+ destination = bz2_compress_file(output)
190
+ logger.debug('Compressed "%s"', destination)
191
+
192
+
193
+ def check(queue, retry, terminate, logpipe, cache_dir, results_dir, keep_dumps, delta_in_days):
194
+ logger = logging.getLogger('auditor-worker')
195
+ lib_logger = logging.getLogger('dumper')
196
+
197
+ loglevel = logging.getLevelName(config.config_get('common', 'loglevel', False, 'DEBUG'))
198
+ logger.setLevel(loglevel)
199
+ lib_logger.setLevel(loglevel)
200
+
201
+ handler = LogPipeHandler(logpipe)
202
+ logger.addHandler(handler)
203
+ lib_logger.addHandler(handler)
204
+
205
+ formatter = logging.Formatter(
206
+ "%(asctime)s %(name)-22s %(levelname)-8s [PID %(process)8d] %(message)s"
207
+ )
208
+ handler.setFormatter(formatter)
209
+
210
+ delta = timedelta(days=delta_in_days)
211
+
212
+ configuration = srmdumps.parse_configuration()
213
+
214
+ while not terminate.is_set():
215
+ try:
216
+ rse, attemps = queue.get(timeout=30)
217
+ except Queue.Empty:
218
+ continue
219
+ start = datetime.now()
220
+ try:
221
+ logger.debug('Checking "%s"', rse)
222
+ output = consistency(rse, delta, configuration, cache_dir,
223
+ results_dir)
224
+ if output:
225
+ process_output(output)
226
+ except:
227
+ elapsed = (datetime.now() - start).total_seconds() / 60
228
+ logger.error('Check of "%s" failed in %d minutes, %d remaining attemps', rse, elapsed, attemps, exc_info=True)
229
+ success = False
230
+ else:
231
+ elapsed = (datetime.now() - start).total_seconds() / 60
232
+ logger.info('SUCCESS checking "%s" in %d minutes', rse, elapsed)
233
+ success = True
234
+
235
+ if not keep_dumps:
236
+ remove = glob.glob(os.path.join(cache_dir, 'replicafromhdfs_{0}_*'.format(rse)))
237
+ remove.extend(glob.glob(os.path.join(cache_dir, 'ddmendpoint_{0}_*'.format(rse))))
238
+ logger.debug('Removing: %s', remove)
239
+ for fil in remove:
240
+ os.remove(fil)
241
+
242
+ if not success and attemps > 0:
243
+ retry.put((rse, attemps - 1))
244
+
245
+
246
+ def activity_logger(logpipes, logfilename, terminate):
247
+ handler = logging.handlers.RotatingFileHandler(
248
+ logfilename,
249
+ maxBytes=20971520,
250
+ backupCount=10,
251
+ )
252
+ handler.setFormatter(logging.Formatter(fmt=None))
253
+ logger = logging.getLogger('auditor-logger-raw')
254
+ logger.addHandler(handler)
255
+ logger.setLevel(logging.CRITICAL) # The level of this logger is irrelevant
256
+
257
+ while not terminate.is_set():
258
+ ready, _, _ = select.select(logpipes, tuple(), tuple(), 30)
259
+ if ready:
260
+ for logpipe in ready:
261
+ logger.critical(logpipe.recv())
@@ -0,0 +1,86 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import hashlib
17
+ import logging
18
+ import os
19
+ import re
20
+ import shutil
21
+ import subprocess
22
+ import tempfile
23
+
24
+ from rucio.common.dumper import DUMPS_CACHE_DIR
25
+ from rucio.common.dumper import temp_file
26
+ from rucio.common.dumper.data_models import Replica
27
+
28
+
29
+ def _hdfs_get(src_url, dst_path):
30
+ cmd = ['hadoop', 'fs', '-get', src_url, dst_path]
31
+ get = subprocess.Popen(
32
+ cmd,
33
+ stderr=subprocess.PIPE,
34
+ )
35
+ _, stderr = get.communicate()
36
+ if get.returncode != 0:
37
+ raise IOError('_hdfs_get(): "{0}": {1}. Return code {2}'.format(
38
+ ' '.join(cmd),
39
+ stderr,
40
+ get.returncode,
41
+ ))
42
+
43
+
44
+ class ReplicaFromHDFS(Replica):
45
+ BASE_URL = '/user/rucio01/reports/{0}/replicas_per_rse/{1}/*'
46
+
47
+ @classmethod
48
+ def download(cls, rse, date, cache_dir=DUMPS_CACHE_DIR, buffer_size=65536):
49
+ logger = logging.getLogger('auditor.hdfs')
50
+
51
+ if not os.path.isdir(cache_dir):
52
+ os.mkdir(cache_dir)
53
+ tmp_dir = tempfile.mkdtemp(dir=cache_dir)
54
+
55
+ url = cls.BASE_URL.format(date.strftime('%Y-%m-%d'), rse)
56
+ filename = '{0}_{1}_{2}_{3}'.format(
57
+ cls.__name__.lower(),
58
+ rse,
59
+ date.strftime('%d-%m-%Y'),
60
+ hashlib.sha1(url.encode()).hexdigest()
61
+ )
62
+ filename = re.sub(r'\W', '-', filename)
63
+ path = os.path.join(cache_dir, filename)
64
+
65
+ if os.path.exists(path):
66
+ logger.debug('Taking Rucio Replica Dump %s for %s from cache', path, rse)
67
+ return path
68
+
69
+ try:
70
+ logging.debug('Trying to download: %s for %s', url, rse)
71
+
72
+ _hdfs_get(cls.BASE_URL.format(date.strftime('%Y-%m-%d'), rse), tmp_dir)
73
+ files = (os.path.join(tmp_dir, file_) for file_ in sorted(os.listdir(tmp_dir)))
74
+
75
+ with temp_file(cache_dir, filename, binary=True) as (full_dump, _):
76
+ for chunk_file in files:
77
+ with open(chunk_file, 'rb') as partial_dump:
78
+ while True:
79
+ data_chunk = partial_dump.read(buffer_size)
80
+ if not data_chunk:
81
+ break
82
+ full_dump.write(data_chunk)
83
+ finally:
84
+ shutil.rmtree(tmp_dir)
85
+
86
+ return path