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,1473 @@
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 logging
17
+ from datetime import datetime
18
+ from typing import TYPE_CHECKING
19
+
20
+ from sqlalchemy import func
21
+ from sqlalchemy.orm.exc import NoResultFound
22
+
23
+ import rucio.core.did
24
+ import rucio.core.lock
25
+ import rucio.core.replica
26
+ from rucio.common.config import config_get_int
27
+ from rucio.common.exception import InsufficientTargetRSEs
28
+ from rucio.core import account_counter, rse_counter, request as request_core
29
+ from rucio.core.rse import get_rse, get_rse_attribute, get_rse_name
30
+ from rucio.db.sqla import models
31
+ from rucio.db.sqla.constants import LockState, RuleGrouping, ReplicaState, RequestType, DIDType, OBSOLETE
32
+ from rucio.db.sqla.session import transactional_session
33
+
34
+ if TYPE_CHECKING:
35
+ from sqlalchemy.orm import Session
36
+
37
+
38
+ @transactional_session
39
+ def apply_rule_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, preferred_rse_ids=[], source_rses=[], *, session: "Session"):
40
+ """
41
+ Apply rule grouping to files.
42
+
43
+ :param datasetfiles: Dict holding all datasets and files.
44
+ :param locks: Dict holding all locks.
45
+ :param replicas: Dict holding all replicas.
46
+ :param source_replicas: Dict holding all source_replicas.
47
+ :param rseselector: The RSESelector to be used.
48
+ :param rule: The rule object.
49
+ :param preferred_rse_ids: Preferred RSE's to select.
50
+ :param source_rses: RSE ids of eglible source replicas.
51
+ :param session: Session of the db.
52
+ :returns: List of replicas to create, List of locks to create, List of transfers to create
53
+ :raises: InsufficientQuota, InsufficientTargetRSEs, RSEOverQuota
54
+ :attention: This method modifies the contents of the locks and replicas input parameters.
55
+ """
56
+
57
+ # locks_to_create = {'rse_id': [locks]}
58
+ # replicas_to_create = {'rse_id': [replicas]}
59
+ # transfers_to_create = [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
60
+
61
+ if rule.grouping == RuleGrouping.NONE:
62
+ replicas_to_create, locks_to_create,\
63
+ transfers_to_create = __apply_rule_to_files_none_grouping(datasetfiles=datasetfiles,
64
+ locks=locks,
65
+ replicas=replicas,
66
+ source_replicas=source_replicas,
67
+ rseselector=rseselector,
68
+ rule=rule,
69
+ preferred_rse_ids=preferred_rse_ids,
70
+ source_rses=source_rses,
71
+ session=session)
72
+ elif rule.grouping == RuleGrouping.ALL:
73
+ replicas_to_create, locks_to_create,\
74
+ transfers_to_create = __apply_rule_to_files_all_grouping(datasetfiles=datasetfiles,
75
+ locks=locks,
76
+ replicas=replicas,
77
+ source_replicas=source_replicas,
78
+ rseselector=rseselector,
79
+ rule=rule,
80
+ preferred_rse_ids=preferred_rse_ids,
81
+ source_rses=source_rses,
82
+ session=session)
83
+ else: # rule.grouping == RuleGrouping.DATASET:
84
+ replicas_to_create, locks_to_create,\
85
+ transfers_to_create = __apply_rule_to_files_dataset_grouping(datasetfiles=datasetfiles,
86
+ locks=locks,
87
+ replicas=replicas,
88
+ source_replicas=source_replicas,
89
+ rseselector=rseselector,
90
+ rule=rule,
91
+ preferred_rse_ids=preferred_rse_ids,
92
+ source_rses=source_rses,
93
+ session=session)
94
+
95
+ return replicas_to_create, locks_to_create, transfers_to_create
96
+
97
+
98
+ @transactional_session
99
+ def repair_stuck_locks_and_apply_rule_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, source_rses, *, session: "Session"):
100
+ """
101
+ Apply rule grouping to files.
102
+
103
+ :param datasetfiles: Dict holding all datasets and files.
104
+ :param locks: Dict holding all locks.
105
+ :param replicas: Dict holding all replicas.
106
+ :param source_replicas: Dict holding all source_replicas.
107
+ :param rseselector: The RSESelector to be used.
108
+ :param rule: The rule object.
109
+ :param source_rses: RSE ids of eglible source_rses.
110
+ :param session: Session of the db.
111
+ :returns: List of replicas to create, List of locks to create, List of transfers to create, List of locks to Delete
112
+ :raises: InsufficientQuota, InsufficientTargetRSEs
113
+ :attention: This method modifies the contents of the locks and replicas input parameters.
114
+ """
115
+
116
+ # locks_to_create = {'rse_id': [locks]}
117
+ # replicas_to_create = {'rse_id': [replicas]}
118
+ # transfers_to_create = [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
119
+ # locks_to_delete = {'rse_id': [locks]}
120
+
121
+ if rule.grouping == RuleGrouping.NONE:
122
+ replicas_to_create, locks_to_create, transfers_to_create,\
123
+ locks_to_delete = __repair_stuck_locks_with_none_grouping(datasetfiles=datasetfiles,
124
+ locks=locks,
125
+ replicas=replicas,
126
+ source_replicas=source_replicas,
127
+ rseselector=rseselector,
128
+ rule=rule,
129
+ source_rses=source_rses,
130
+ session=session)
131
+ elif rule.grouping == RuleGrouping.ALL:
132
+ replicas_to_create, locks_to_create, transfers_to_create,\
133
+ locks_to_delete = __repair_stuck_locks_with_all_grouping(datasetfiles=datasetfiles,
134
+ locks=locks,
135
+ replicas=replicas,
136
+ source_replicas=source_replicas,
137
+ rseselector=rseselector,
138
+ rule=rule,
139
+ source_rses=source_rses,
140
+ session=session)
141
+ else:
142
+ replicas_to_create, locks_to_create, transfers_to_create,\
143
+ locks_to_delete = __repair_stuck_locks_with_dataset_grouping(datasetfiles=datasetfiles,
144
+ locks=locks,
145
+ replicas=replicas,
146
+ source_replicas=source_replicas,
147
+ rseselector=rseselector,
148
+ rule=rule,
149
+ source_rses=source_rses,
150
+ session=session)
151
+ return replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
152
+
153
+
154
+ @transactional_session
155
+ def create_transfer_dict(dest_rse_id, request_type, scope, name, rule, lock=None, bytes_=None, md5=None, adler32=None, ds_scope=None, ds_name=None, copy_pin_lifetime=None, activity=None, retry_count=None, *, session: "Session"):
156
+ """
157
+ This method creates a transfer dictionary and returns it
158
+
159
+ :param dest_rse_id: The destination RSE id.
160
+ :param request_Type: The request type.
161
+ :param scope: The scope of the file.
162
+ :param name: The name of the file.
163
+ :param rule: The rule responsible for the transfer.
164
+ :param lock: The lock responsible for the transfer.
165
+ :param bytes_: The filesize of the file in bytes.
166
+ :param md5: The md5 checksum of the file.
167
+ :param adler32: The adler32 checksum of the file.
168
+ :param ds_scope: Dataset the file belongs to.
169
+ :param ds_name: Dataset the file belongs to.
170
+ :param copy_pin_lifetime: Lifetime in the case of STAGIN requests.
171
+ :param activity: Activity to be used.
172
+ :param session: Session of the db.
173
+ :returns: Request dictionary.
174
+ """
175
+ attributes = {'activity': activity or rule.activity or 'default',
176
+ 'source_replica_expression': rule.source_replica_expression,
177
+ 'lifetime': copy_pin_lifetime,
178
+ 'ds_scope': ds_scope,
179
+ 'ds_name': ds_name,
180
+ 'bytes': bytes_,
181
+ 'md5': md5,
182
+ 'adler32': adler32,
183
+ 'priority': rule.priority,
184
+ # 'allow_tape_source': has_account_attribute(account=rule.account, key='admin', session=session)}
185
+ 'allow_tape_source': True}
186
+
187
+ return {'dest_rse_id': dest_rse_id,
188
+ 'scope': scope,
189
+ 'name': name,
190
+ 'rule_id': rule.id,
191
+ 'attributes': attributes,
192
+ 'request_type': request_type,
193
+ 'retry_count': retry_count,
194
+ 'account': rule.account,
195
+ 'requested_at': lock.created_at if lock else rule.created_at}
196
+
197
+
198
+ @transactional_session
199
+ def __apply_rule_to_files_none_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, preferred_rse_ids=[], source_rses=[], *, session: "Session"):
200
+ """
201
+ Apply a rule to files with NONE grouping.
202
+
203
+ :param datasetfiles: Dict holding all datasets and files.
204
+ :param locks: Dict holding all locks.
205
+ :param replicas: Dict holding all replicas.
206
+ :param source_replicas: Dict holding all source_replicas.
207
+ :param rseselector: The RSESelector to be used.
208
+ :param rule: The rule object.
209
+ :param preferred_rse_ids: Preferred RSE's to select.
210
+ :param source_rses: RSE ids of eglible source replicas.
211
+ :param session: Session of the db.
212
+ :returns: replicas_to_create, locks_to_create, transfers_to_create
213
+ :raises: InsufficientAccountLimit, InsufficientTargetRSEs
214
+ :attention: This method modifies the contents of the locks and replicas input parameters.
215
+ """
216
+ locks_to_create = {} # {'rse_id': [locks]}
217
+ replicas_to_create = {} # {'rse_id': [replicas]}
218
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
219
+
220
+ for dataset in datasetfiles:
221
+ selected_rse_ids = []
222
+ for file in dataset['files']:
223
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id]) == rule.copies:
224
+ # Nothing to do as the file already has the requested amount of locks
225
+ continue
226
+ rse_coverage = {replica.rse_id: file['bytes'] for replica in replicas[(file['scope'], file['name'])] if replica.state in (ReplicaState.AVAILABLE, ReplicaState.COPYING, ReplicaState.TEMPORARY_UNAVAILABLE)}
227
+ if len(preferred_rse_ids) == 0:
228
+ rse_tuples = rseselector.select_rse(size=file['bytes'],
229
+ preferred_rse_ids=rse_coverage.keys(),
230
+ blocklist=[replica.rse_id for replica in replicas[(file['scope'], file['name'])] if replica.state == ReplicaState.BEING_DELETED],
231
+ existing_rse_size=rse_coverage)
232
+ else:
233
+ rse_tuples = rseselector.select_rse(size=file['bytes'],
234
+ preferred_rse_ids=preferred_rse_ids,
235
+ blocklist=[replica.rse_id for replica in replicas[(file['scope'], file['name'])] if replica.state == ReplicaState.BEING_DELETED],
236
+ existing_rse_size=rse_coverage)
237
+ for rse_tuple in rse_tuples:
238
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id and lock.rse_id == rse_tuple[0]]) == 1:
239
+ # Due to a bug a lock could have been already submitted for this, in that case, skip it
240
+ continue
241
+ __create_lock_and_replica(file=file,
242
+ dataset=dataset,
243
+ rule=rule,
244
+ rse_id=rse_tuple[0],
245
+ staging_area=rse_tuple[1],
246
+ availability_write=rse_tuple[2],
247
+ locks_to_create=locks_to_create,
248
+ locks=locks,
249
+ source_rses=source_rses,
250
+ replicas_to_create=replicas_to_create,
251
+ replicas=replicas,
252
+ source_replicas=source_replicas,
253
+ transfers_to_create=transfers_to_create,
254
+ session=session)
255
+ selected_rse_ids.append(rse_tuple[0])
256
+ if dataset['scope'] is not None:
257
+ for rse_id in list(set(selected_rse_ids)):
258
+ try:
259
+ session.query(models.CollectionReplica).filter(models.CollectionReplica.scope == dataset['scope'],
260
+ models.CollectionReplica.name == dataset['name'],
261
+ models.CollectionReplica.rse_id == rse_id).one()
262
+ except NoResultFound:
263
+ models.CollectionReplica(scope=dataset['scope'],
264
+ name=dataset['name'],
265
+ did_type=DIDType.DATASET,
266
+ rse_id=rse_id,
267
+ bytes=0,
268
+ length=0,
269
+ available_bytes=0,
270
+ available_replicas_cnt=0,
271
+ state=ReplicaState.UNAVAILABLE).save(session=session)
272
+ models.UpdatedCollectionReplica(scope=dataset['scope'],
273
+ name=dataset['name'],
274
+ did_type=DIDType.DATASET).save(flush=False, session=session)
275
+
276
+ return replicas_to_create, locks_to_create, transfers_to_create
277
+
278
+
279
+ @transactional_session
280
+ def __apply_rule_to_files_all_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, preferred_rse_ids=[], source_rses=[], *, session: "Session"):
281
+ """
282
+ Apply a rule to files with ALL grouping.
283
+
284
+ :param datasetfiles: Dict holding all datasets and files.
285
+ :param locks: Dict holding all locks.
286
+ :param replicas: Dict holding all replicas.
287
+ :param source_replicas: Dict holding all source_replicas.
288
+ :param rseselector: The RSESelector to be used.
289
+ :param rule: The rule object.
290
+ :param preferred_rse_ids: Preferred RSE's to select.
291
+ :param source_rses: RSE ids of eglible source replicas.
292
+ :param session: Session of the db.
293
+ :returns: replicas_to_create, locks_to_create, transfers_to_create
294
+ :raises: InsufficientQuota, InsufficientTargetRSEs
295
+ :attention: This method modifies the contents of the locks and replicas input parameters.
296
+ """
297
+
298
+ locks_to_create = {} # {'rse_id': [locks]}
299
+ replicas_to_create = {} # {'rse_id': [replicas]}
300
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
301
+
302
+ bytes_ = 0
303
+ rse_coverage = {} # {'rse_id': coverage }
304
+ blocklist = set()
305
+ for dataset in datasetfiles:
306
+ for file in dataset['files']:
307
+ bytes_ += file['bytes']
308
+ for replica in replicas[(file['scope'], file['name'])]:
309
+ if replica.state == ReplicaState.BEING_DELETED:
310
+ blocklist.add(replica.rse_id)
311
+ continue
312
+ if replica.state in [ReplicaState.AVAILABLE, ReplicaState.COPYING, ReplicaState.TEMPORARY_UNAVAILABLE]:
313
+ if replica.rse_id in rse_coverage:
314
+ rse_coverage[replica.rse_id] += file['bytes']
315
+ else:
316
+ rse_coverage[replica.rse_id] = file['bytes']
317
+
318
+ if not preferred_rse_ids:
319
+ rse_tuples = rseselector.select_rse(size=bytes_,
320
+ preferred_rse_ids=[x[0] for x in sorted(rse_coverage.items(), key=lambda tup: tup[1], reverse=True)],
321
+ blocklist=list(blocklist),
322
+ prioritize_order_over_weight=True,
323
+ existing_rse_size=rse_coverage)
324
+ else:
325
+ rse_tuples = rseselector.select_rse(size=bytes_,
326
+ preferred_rse_ids=preferred_rse_ids,
327
+ blocklist=list(blocklist),
328
+ existing_rse_size=rse_coverage)
329
+ for rse_tuple in rse_tuples:
330
+ for dataset in datasetfiles:
331
+ for file in dataset['files']:
332
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id]) == rule.copies:
333
+ continue
334
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id and lock.rse_id == rse_tuple[0]]) == 1:
335
+ # Due to a bug a lock could have been already submitted for this, in that case, skip it
336
+ continue
337
+ __create_lock_and_replica(file=file,
338
+ dataset=dataset,
339
+ rule=rule,
340
+ rse_id=rse_tuple[0],
341
+ staging_area=rse_tuple[1],
342
+ availability_write=rse_tuple[2],
343
+ locks_to_create=locks_to_create,
344
+ locks=locks,
345
+ source_rses=source_rses,
346
+ replicas_to_create=replicas_to_create,
347
+ replicas=replicas,
348
+ source_replicas=source_replicas,
349
+ transfers_to_create=transfers_to_create,
350
+ session=session)
351
+ # Add a DatasetLock to the DB
352
+ if dataset['scope'] is not None:
353
+ try:
354
+ session.query(models.DatasetLock).filter(models.DatasetLock.scope == dataset['scope'],
355
+ models.DatasetLock.name == dataset['name'],
356
+ models.DatasetLock.rule_id == rule.id,
357
+ models.DatasetLock.rse_id == rse_tuple[0]).one()
358
+ except NoResultFound:
359
+ # Get dataset Information
360
+ is_open, bytes_, length = True, 0, 0
361
+ try:
362
+ is_open, bytes_, length = session.query(models.DataIdentifier.is_open,
363
+ models.DataIdentifier.bytes,
364
+ models.DataIdentifier.length).filter_by(scope=dataset['scope'], name=dataset['name']).one()
365
+ except NoResultFound:
366
+ pass
367
+
368
+ models.DatasetLock(scope=dataset['scope'],
369
+ name=dataset['name'],
370
+ rule_id=rule.id,
371
+ rse_id=rse_tuple[0],
372
+ state=LockState.REPLICATING,
373
+ account=rule.account,
374
+ length=length if not is_open else None,
375
+ bytes=bytes_ if not is_open else None).save(flush=False, session=session)
376
+ # Add a Dataset Replica to the DB
377
+ if dataset['scope'] is not None:
378
+ try:
379
+ session.query(models.CollectionReplica).filter(models.CollectionReplica.scope == dataset['scope'],
380
+ models.CollectionReplica.name == dataset['name'],
381
+ models.CollectionReplica.rse_id == rse_tuple[0]).one()
382
+ except NoResultFound:
383
+ models.CollectionReplica(scope=dataset['scope'],
384
+ name=dataset['name'],
385
+ did_type=DIDType.DATASET,
386
+ rse_id=rse_tuple[0],
387
+ bytes=0,
388
+ length=0,
389
+ available_bytes=0,
390
+ available_replicas_cnt=0,
391
+ state=ReplicaState.UNAVAILABLE).save(session=session)
392
+ models.UpdatedCollectionReplica(scope=dataset['scope'],
393
+ name=dataset['name'],
394
+ did_type=DIDType.DATASET).save(flush=False, session=session)
395
+
396
+ return replicas_to_create, locks_to_create, transfers_to_create
397
+
398
+
399
+ @transactional_session
400
+ def __apply_rule_to_files_dataset_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, preferred_rse_ids=[], source_rses=[], *, session: "Session"):
401
+ """
402
+ Apply a rule to files with ALL grouping.
403
+
404
+ :param datasetfiles: Dict holding all datasets and files.
405
+ :param locks: Dict holding all locks.
406
+ :param replicas: Dict holding all replicas.
407
+ :param source_replicas: Dict holding all source replicas.
408
+ :param rseselector: The RSESelector to be used.
409
+ :param rule: The rule object.
410
+ :param preferred_rse_ids: Preferred RSE's to select.
411
+ :param source_rses: RSE ids of eglible source replicas.
412
+ :param session: Session of the db.
413
+ :returns: replicas_to_create, locks_to_create, transfers_to_create
414
+ :raises: InsufficientQuota, InsufficientTargetRSEs
415
+ :attention: This method modifies the contents of the locks and replicas input parameters.
416
+ """
417
+ locks_to_create = {} # {'rse_id': [locks]}
418
+ replicas_to_create = {} # {'rse_id': [replicas]}
419
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
420
+
421
+ for dataset in datasetfiles:
422
+ bytes_ = sum([file['bytes'] for file in dataset['files']])
423
+ rse_coverage = {} # {'rse_id': coverage }
424
+ blocklist = set()
425
+ for file in dataset['files']:
426
+ for replica in replicas[(file['scope'], file['name'])]:
427
+ if replica.state == ReplicaState.BEING_DELETED:
428
+ blocklist.add(replica.rse_id)
429
+ continue
430
+ if replica.state in [ReplicaState.AVAILABLE, ReplicaState.COPYING, ReplicaState.TEMPORARY_UNAVAILABLE]:
431
+ if replica.rse_id in rse_coverage:
432
+ rse_coverage[replica.rse_id] += file['bytes']
433
+ else:
434
+ rse_coverage[replica.rse_id] = file['bytes']
435
+
436
+ if not preferred_rse_ids:
437
+ rse_tuples = rseselector.select_rse(size=bytes_,
438
+ preferred_rse_ids=[x[0] for x in sorted(rse_coverage.items(), key=lambda tup: tup[1], reverse=True)],
439
+ blocklist=list(blocklist),
440
+ prioritize_order_over_weight=True,
441
+ existing_rse_size=rse_coverage)
442
+ else:
443
+ rse_tuples = rseselector.select_rse(size=bytes_,
444
+ preferred_rse_ids=preferred_rse_ids,
445
+ blocklist=list(blocklist),
446
+ existing_rse_size=rse_coverage)
447
+ for rse_tuple in rse_tuples:
448
+ for file in dataset['files']:
449
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id]) == rule.copies:
450
+ continue
451
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id and lock.rse_id == rse_tuple[0]]) == 1:
452
+ # Due to a bug a lock could have been already submitted for this, in that case, skip it
453
+ continue
454
+ __create_lock_and_replica(file=file,
455
+ dataset=dataset,
456
+ rule=rule,
457
+ rse_id=rse_tuple[0],
458
+ staging_area=rse_tuple[1],
459
+ availability_write=rse_tuple[2],
460
+ locks_to_create=locks_to_create,
461
+ locks=locks,
462
+ source_rses=source_rses,
463
+ replicas_to_create=replicas_to_create,
464
+ replicas=replicas,
465
+ source_replicas=source_replicas,
466
+ transfers_to_create=transfers_to_create,
467
+ session=session)
468
+ # Add a DatasetLock to the DB
469
+ if dataset['scope'] is not None:
470
+ try:
471
+ session.query(models.DatasetLock).filter(models.DatasetLock.scope == dataset['scope'],
472
+ models.DatasetLock.name == dataset['name'],
473
+ models.DatasetLock.rule_id == rule.id,
474
+ models.DatasetLock.rse_id == rse_tuple[0]).one()
475
+ except NoResultFound:
476
+ # Get dataset Information
477
+ is_open, bytes_, length = True, None, None
478
+ try:
479
+ is_open, bytes_, length = session.query(models.DataIdentifier.is_open,
480
+ models.DataIdentifier.bytes,
481
+ models.DataIdentifier.length).filter_by(scope=dataset['scope'], name=dataset['name']).one()
482
+ except NoResultFound:
483
+ pass
484
+
485
+ models.DatasetLock(scope=dataset['scope'],
486
+ name=dataset['name'],
487
+ rule_id=rule.id,
488
+ rse_id=rse_tuple[0],
489
+ state=LockState.REPLICATING,
490
+ account=rule.account,
491
+ length=length if not is_open else None,
492
+ bytes=bytes_ if not is_open else None).save(flush=False, session=session)
493
+
494
+ # Add a Dataset Replica to the DB
495
+ if dataset['scope'] is not None:
496
+ try:
497
+ session.query(models.CollectionReplica).filter(models.CollectionReplica.scope == dataset['scope'],
498
+ models.CollectionReplica.name == dataset['name'],
499
+ models.CollectionReplica.rse_id == rse_tuple[0]).one()
500
+ except NoResultFound:
501
+ models.CollectionReplica(scope=dataset['scope'],
502
+ name=dataset['name'],
503
+ did_type=DIDType.DATASET,
504
+ rse_id=rse_tuple[0],
505
+ bytes=0,
506
+ length=0,
507
+ available_bytes=0,
508
+ available_replicas_cnt=0,
509
+ state=ReplicaState.UNAVAILABLE).save(session=session)
510
+ models.UpdatedCollectionReplica(scope=dataset['scope'],
511
+ name=dataset['name'],
512
+ did_type=DIDType.DATASET).save(flush=False, session=session)
513
+
514
+ return replicas_to_create, locks_to_create, transfers_to_create
515
+
516
+
517
+ @transactional_session
518
+ def __repair_stuck_locks_with_none_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, source_rses, *, session: "Session", logger=logging.log):
519
+ """
520
+ Apply a rule to files with NONE grouping.
521
+
522
+ :param datasetfiles: Dict holding all datasets and files.
523
+ :param locks: Dict holding all locks.
524
+ :param replicas: Dict holding all replicas.
525
+ :param source_replicas: Dict holding all source_replicas.
526
+ :param rseselector: The RSESelector to be used.
527
+ :param rule: The rule object.
528
+ :param source_rses: RSE ids of eglible source replicas.
529
+ :param session: Session of the db.
530
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
531
+ :returns: replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
532
+ :raises: InsufficientAccountLimit, InsufficientTargetRSEs
533
+ :attention: This method modifies the contents of the locks and replicas input parameters.
534
+ """
535
+
536
+ locks_to_create = {} # {'rse_id': [locks]}
537
+ replicas_to_create = {} # {'rse_id': [replicas]}
538
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
539
+ locks_to_delete = {} # {'rse_id': [locks]}
540
+
541
+ selector_rse_dict = rseselector.get_rse_dictionary()
542
+
543
+ # Iterate the datasetfiles structure and search for stuck locks
544
+ for dataset in datasetfiles:
545
+ for file in dataset['files']:
546
+ # Iterate and try to repair STUCK locks
547
+ for lock in [stucked_lock for stucked_lock in locks[(file['scope'], file['name'])] if stucked_lock.rule_id == rule.id and stucked_lock.state == LockState.STUCK]:
548
+ # Check if there are actually already enough locks
549
+ if len([good_lock for good_lock in locks[(file['scope'], file['name'])] if good_lock.rule_id == rule.id and good_lock.state != LockState.STUCK]) >= rule.copies:
550
+ # Remove the lock
551
+ logger(logging.DEBUG, 'There are too many locks for %s:%s for rule %s. Deleting lock', file['scope'], file['name'], str(rule.id))
552
+ if lock.rse_id in locks_to_delete:
553
+ locks_to_delete[lock.rse_id].append(lock)
554
+ else:
555
+ locks_to_delete[lock.rse_id] = [lock]
556
+ rule.locks_stuck_cnt -= 1
557
+ continue
558
+ # Check if the replica is AVAILABLE now
559
+ if [replica for replica in replicas[(file['scope'], file['name'])] if replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE] and replica.rse_id == lock.rse_id]:
560
+ lock.state = LockState.OK
561
+ rule.locks_stuck_cnt -= 1
562
+ rule.locks_ok_cnt += 1
563
+ # Recalculate the replica_lock_cnt
564
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE] and replica.rse_id == lock.rse_id][0]
565
+ associated_replica.tombstone = None
566
+ associated_replica.lock_cnt = session.query(func.count(models.ReplicaLock.rule_id)).filter_by(scope=associated_replica.scope, name=associated_replica.name, rse_id=lock.rse_id).one()[0]
567
+ continue
568
+ # Check if this is a STUCK lock due to source_replica filtering
569
+ if source_rses:
570
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0]
571
+ # Check if there is an eglible source replica for this lock
572
+ if set(source_replicas.get((file['scope'], file['name']), [])).intersection(source_rses) and (selector_rse_dict.get(lock.rse_id, {}).get('availability_write', True) or rule.ignore_availability):
573
+ __update_lock_replica_and_create_transfer(lock=lock,
574
+ replica=associated_replica,
575
+ rule=rule,
576
+ dataset=dataset,
577
+ transfers_to_create=transfers_to_create,
578
+ session=session)
579
+ else:
580
+ blocklist_rses = [bl_lock.rse_id for bl_lock in locks[(file['scope'], file['name'])] if bl_lock.rule_id == rule.id]
581
+ try:
582
+ rse_coverage = {replica.rse_id: file['bytes'] for replica in replicas[(file['scope'], file['name'])] if replica.state in (ReplicaState.AVAILABLE, ReplicaState.COPYING, ReplicaState.TEMPORARY_UNAVAILABLE)}
583
+ rse_tuples = rseselector.select_rse(size=file['bytes'],
584
+ preferred_rse_ids=rse_coverage.keys(),
585
+ copies=1,
586
+ blocklist=[replica.rse_id for replica in replicas[(file['scope'], file['name'])] if replica.state == ReplicaState.BEING_DELETED] + blocklist_rses + [lock.rse_id],
587
+ existing_rse_size=rse_coverage)
588
+ for rse_tuple in rse_tuples:
589
+ __create_lock_and_replica(file=file,
590
+ dataset=dataset,
591
+ rule=rule,
592
+ rse_id=rse_tuple[0],
593
+ staging_area=rse_tuple[1],
594
+ availability_write=rse_tuple[2],
595
+ locks_to_create=locks_to_create,
596
+ locks=locks,
597
+ source_rses=source_rses,
598
+ replicas_to_create=replicas_to_create,
599
+ replicas=replicas,
600
+ source_replicas=source_replicas,
601
+ transfers_to_create=transfers_to_create,
602
+ session=session)
603
+ rule.locks_stuck_cnt -= 1
604
+ __set_replica_unavailable(replica=[replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0],
605
+ session=session)
606
+ if lock.rse_id in locks_to_delete:
607
+ locks_to_delete[lock.rse_id].append(lock)
608
+ else:
609
+ locks_to_delete[lock.rse_id] = [lock]
610
+ except InsufficientTargetRSEs:
611
+ # Just retry the already existing lock
612
+ if __is_retry_required(lock=lock, activity=rule.activity) and (selector_rse_dict.get(lock.rse_id, {}).get('availability_write', True) or rule.ignore_availability):
613
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0]
614
+ __update_lock_replica_and_create_transfer(lock=lock,
615
+ replica=associated_replica,
616
+ rule=rule,
617
+ dataset=dataset,
618
+ transfers_to_create=transfers_to_create,
619
+ session=session)
620
+
621
+ return replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
622
+
623
+
624
+ @transactional_session
625
+ def __repair_stuck_locks_with_all_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, source_rses, *, session: "Session", logger=logging.log):
626
+ """
627
+ Apply a rule to files with ALL grouping.
628
+
629
+ :param datasetfiles: Dict holding all datasets and files.
630
+ :param locks: Dict holding all locks.
631
+ :param replicas: Dict holding all replicas.
632
+ :param source_replicas: Dict holding all source_replicas.
633
+ :param rseselector: The RSESelector to be used.
634
+ :param rule: The rule object.
635
+ :param source_rses: RSE ids of eglible source replicas.
636
+ :param session: Session of the db.
637
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
638
+ :returns: replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
639
+ :raises: InsufficientAccountLimit, InsufficientTargetRSEs
640
+ :attention: This method modifies the contents of the locks and replicas input parameters.
641
+ """
642
+
643
+ locks_to_create = {} # {'rse_id': [locks]}
644
+ replicas_to_create = {} # {'rse_id': [replicas]}
645
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
646
+ locks_to_delete = {} # {'rse_id': [locks]}
647
+
648
+ selector_rse_dict = rseselector.get_rse_dictionary()
649
+
650
+ # Iterate the datasetfiles structure and search for stuck locks
651
+ for dataset in datasetfiles:
652
+ for file in dataset['files']:
653
+ # Iterate and try to repair STUCK locks
654
+ for lock in [stucked_lock for stucked_lock in locks[(file['scope'], file['name'])] if stucked_lock.rule_id == rule.id and stucked_lock.state == LockState.STUCK]:
655
+ # Check if there are actually already enough locks
656
+ if len([good_lock for good_lock in locks[(file['scope'], file['name'])] if good_lock.rule_id == rule.id and good_lock.state != LockState.STUCK]) >= rule.copies:
657
+ # Remove the lock
658
+ logger(logging.DEBUG, 'There are too many locks for %s:%s for rule %s. Deleting lock', file['scope'], file['name'], str(rule.id))
659
+ if lock.rse_id in locks_to_delete:
660
+ locks_to_delete[lock.rse_id].append(lock)
661
+ else:
662
+ locks_to_delete[lock.rse_id] = [lock]
663
+ rule.locks_stuck_cnt -= 1
664
+ continue
665
+ # Check if the replica is AVAILABLE now
666
+ if [replica for replica in replicas[(file['scope'], file['name'])] if replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE] and replica.rse_id == lock.rse_id]:
667
+ lock.state = LockState.OK
668
+ rule.locks_stuck_cnt -= 1
669
+ rule.locks_ok_cnt += 1
670
+ # Recalculate the replica_lock_cnt
671
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE] and replica.rse_id == lock.rse_id][0]
672
+ associated_replica.tombstone = None
673
+ associated_replica.lock_cnt = session.query(func.count(models.ReplicaLock.rule_id)).filter_by(scope=associated_replica.scope, name=associated_replica.name, rse_id=lock.rse_id).one()[0]
674
+ continue
675
+ # Check if this is a STUCK lock due to source_replica filtering
676
+ if source_rses:
677
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0]
678
+ # Check if there is an eglible source replica for this lock
679
+ if set(source_replicas.get((file['scope'], file['name']), [])).intersection(source_rses) and (selector_rse_dict.get(lock.rse_id, {}).get('availability_write', True) or rule.ignore_availability):
680
+ __update_lock_replica_and_create_transfer(lock=lock,
681
+ replica=associated_replica,
682
+ rule=rule,
683
+ dataset=dataset,
684
+ transfers_to_create=transfers_to_create,
685
+ session=session)
686
+ else:
687
+ # Just retry the already existing lock
688
+ if __is_retry_required(lock=lock, activity=rule.activity) and (selector_rse_dict.get(lock.rse_id, {}).get('availability_write', True) or rule.ignore_availability):
689
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0]
690
+ __update_lock_replica_and_create_transfer(lock=lock,
691
+ replica=associated_replica,
692
+ rule=rule,
693
+ dataset=dataset,
694
+ transfers_to_create=transfers_to_create,
695
+ session=session)
696
+
697
+ return replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
698
+
699
+
700
+ @transactional_session
701
+ def __repair_stuck_locks_with_dataset_grouping(datasetfiles, locks, replicas, source_replicas, rseselector, rule, source_rses, *, session: "Session", logger=logging.log):
702
+ """
703
+ Apply a rule to files with DATASET grouping.
704
+
705
+ :param datasetfiles: Dict holding all datasets and files.
706
+ :param locks: Dict holding all locks.
707
+ :param replicas: Dict holding all replicas.
708
+ :param source_replicas: Dict holding all source_replicas.
709
+ :param rseselector: The RSESelector to be used.
710
+ :param rule: The rule object.
711
+ :param source_rses: RSE ids of eglible source replicas.
712
+ :param session: Session of the db.
713
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
714
+ :returns: replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
715
+ :raises: InsufficientAccountLimit, InsufficientTargetRSEs
716
+ :attention: This method modifies the contents of the locks and replicas input parameters.
717
+ """
718
+
719
+ locks_to_create = {} # {'rse_id': [locks]}
720
+ replicas_to_create = {} # {'rse_id': [replicas]}
721
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
722
+ locks_to_delete = {} # {'rse_id': [locks]}
723
+
724
+ selector_rse_dict = rseselector.get_rse_dictionary()
725
+
726
+ # Iterate the datasetfiles structure and search for stuck locks
727
+ for dataset in datasetfiles:
728
+ for file in dataset['files']:
729
+ # Iterate and try to repair STUCK locks
730
+ for lock in [stucked_lock for stucked_lock in locks[(file['scope'], file['name'])] if stucked_lock.rule_id == rule.id and stucked_lock.state == LockState.STUCK]:
731
+ # Check if there are actually already enough locks
732
+ if len([good_lock for good_lock in locks[(file['scope'], file['name'])] if good_lock.rule_id == rule.id and good_lock.state != LockState.STUCK]) >= rule.copies:
733
+ # Remove the lock
734
+ logger(logging.DEBUG, 'There are too many locks for %s:%s for rule %s. Deleting lock', file['scope'], file['name'], str(rule.id))
735
+ if lock.rse_id in locks_to_delete:
736
+ locks_to_delete[lock.rse_id].append(lock)
737
+ else:
738
+ locks_to_delete[lock.rse_id] = [lock]
739
+ rule.locks_stuck_cnt -= 1
740
+ continue
741
+ # Check if the replica is AVAILABLE now
742
+ if [replica for replica in replicas[(file['scope'], file['name'])] if replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE] and replica.rse_id == lock.rse_id]:
743
+ lock.state = LockState.OK
744
+ rule.locks_stuck_cnt -= 1
745
+ rule.locks_ok_cnt += 1
746
+ # Recalculate the replica_lock_cnt
747
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE] and replica.rse_id == lock.rse_id][0]
748
+ associated_replica.tombstone = None
749
+ associated_replica.lock_cnt = session.query(func.count(models.ReplicaLock.rule_id)).filter_by(scope=associated_replica.scope, name=associated_replica.name, rse_id=lock.rse_id).one()[0]
750
+ continue
751
+ # Check if this is a STUCK lock due to source_replica filtering
752
+ if source_rses:
753
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0]
754
+ # Check if there is an eglible source replica for this lock
755
+ if set(source_replicas.get((file['scope'], file['name']), [])).intersection(source_rses) and (selector_rse_dict.get(lock.rse_id, {}).get('availability_write', True) or rule.ignore_availability):
756
+ __update_lock_replica_and_create_transfer(lock=lock,
757
+ replica=associated_replica,
758
+ rule=rule,
759
+ dataset=dataset,
760
+ transfers_to_create=transfers_to_create,
761
+ session=session)
762
+ else:
763
+ # Just retry the already existing lock
764
+ if __is_retry_required(lock=lock, activity=rule.activity) and (selector_rse_dict.get(lock.rse_id, {}).get('availability_write', True) or rule.ignore_availability):
765
+ associated_replica = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == lock.rse_id][0]
766
+ __update_lock_replica_and_create_transfer(lock=lock,
767
+ replica=associated_replica,
768
+ rule=rule,
769
+ dataset=dataset,
770
+ transfers_to_create=transfers_to_create,
771
+ session=session)
772
+
773
+ return replicas_to_create, locks_to_create, transfers_to_create, locks_to_delete
774
+
775
+
776
+ def __is_retry_required(lock, activity):
777
+ """
778
+ :param lock: The lock to check.
779
+ :param activity: The activity of the rule.
780
+ """
781
+
782
+ created_at_diff = (datetime.utcnow() - lock.created_at).days * 24 * 3600 + (datetime.utcnow() - lock.created_at).seconds
783
+ updated_at_diff = (datetime.utcnow() - lock.updated_at).days * 24 * 3600 + (datetime.utcnow() - lock.updated_at).seconds
784
+
785
+ if activity == 'Express':
786
+ if updated_at_diff > 3600 * 2:
787
+ return True
788
+ elif activity == 'DebugJudge':
789
+ return True
790
+ elif created_at_diff < 24 * 3600: # First Day
791
+ # Retry every 2 hours
792
+ if updated_at_diff > 3600 * 2:
793
+ return True
794
+ elif created_at_diff < 2 * 24 * 3600: # Second Day
795
+ # Retry every 4 hours
796
+ if updated_at_diff > 3600 * 4:
797
+ return True
798
+ elif created_at_diff < 3 * 24 * 3600: # Third Day
799
+ # Retry every 6 hours
800
+ if updated_at_diff > 3600 * 6:
801
+ return True
802
+ else: # Four and more days
803
+ if updated_at_diff > 3600 * 8:
804
+ return True
805
+ return False
806
+
807
+
808
+ @transactional_session
809
+ def __create_lock_and_replica(file, dataset, rule, rse_id, staging_area, availability_write, locks_to_create, locks, source_rses, replicas_to_create, replicas, source_replicas, transfers_to_create, *, session: "Session", logger=logging.log):
810
+ """
811
+ This method creates a lock and if necessary a new replica and fills the corresponding dictionaries.
812
+
813
+ :param file: File dictionary holding the file information.
814
+ :param dataset: Dataset dictionary holding the dataset information.
815
+ :param rule: Rule object.
816
+ :param rse_id: RSE id the lock and replica should be created at.
817
+ :param staging_area: Boolean variable if the RSE is a staging area.
818
+ :param availability_write: Boolean variable if the RSE is write enabled.
819
+ :param locks_to_create: Dictionary of the locks to create.
820
+ :param locks: Dictionary of all locks.
821
+ :param source_rses: RSE ids of eglible source replicas.
822
+ :param replicas_to_create: Dictionary of the replicas to create.
823
+ :param replicas: Dictionary of the replicas.
824
+ :param source_replicas: Dictionary of the source replicas.
825
+ :param transfers_to_create: List of transfers to create.
826
+ :param session: The db session in use.
827
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
828
+ :returns: True, if the created lock is replicating, False otherwise.
829
+ :attention: This method modifies the contents of the locks, locks_to_create, replicas_to_create and replicas input parameters.
830
+ """
831
+
832
+ if rule.expires_at:
833
+ copy_pin_lifetime = rule.expires_at - datetime.utcnow()
834
+ copy_pin_lifetime = copy_pin_lifetime.seconds + copy_pin_lifetime.days * 24 * 3600
835
+ else:
836
+ copy_pin_lifetime = None
837
+
838
+ # If it is a Staging Area, the pin has to be extended
839
+ if staging_area:
840
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=rse_id,
841
+ request_type=RequestType.STAGEIN,
842
+ scope=file['scope'],
843
+ name=file['name'],
844
+ rule=rule,
845
+ bytes_=file['bytes'],
846
+ md5=file['md5'],
847
+ adler32=file['adler32'],
848
+ ds_scope=dataset['scope'],
849
+ ds_name=dataset['name'],
850
+ copy_pin_lifetime=copy_pin_lifetime,
851
+ session=session))
852
+
853
+ # If staging_required type RSE then set pin to RSE attribute maximum_pin_lifetime
854
+ staging_required = get_rse_attribute(rse_id, 'staging_required', session=session)
855
+ maximum_pin_lifetime = get_rse_attribute(rse_id, 'maximum_pin_lifetime', session=session)
856
+
857
+ if staging_required:
858
+ if (not copy_pin_lifetime and maximum_pin_lifetime) or (copy_pin_lifetime and maximum_pin_lifetime and copy_pin_lifetime < int(maximum_pin_lifetime)):
859
+ copy_pin_lifetime = maximum_pin_lifetime
860
+ rse_name = get_rse_name(rse_id=rse_id, session=session)
861
+ logger(logging.DEBUG, f'Destination RSE {rse_name} is type staging_required with pin value: {copy_pin_lifetime}')
862
+
863
+ existing_replicas = [replica for replica in replicas[(file['scope'], file['name'])] if replica.rse_id == rse_id]
864
+
865
+ if existing_replicas: # A replica already exists (But could be UNAVAILABLE)
866
+ existing_replica = existing_replicas[0]
867
+
868
+ # Replica is fully available -- AVAILABLE
869
+ if existing_replica.state in [ReplicaState.AVAILABLE, ReplicaState.TEMPORARY_UNAVAILABLE]:
870
+ new_lock = __create_lock(rule=rule,
871
+ rse_id=rse_id,
872
+ scope=file['scope'],
873
+ name=file['name'],
874
+ bytes_=file['bytes'],
875
+ existing_replica=existing_replica,
876
+ state=LockState.OK if not staging_required else LockState.REPLICATING)
877
+ if rse_id not in locks_to_create:
878
+ locks_to_create[rse_id] = []
879
+ locks_to_create[rse_id].append(new_lock)
880
+ locks[(file['scope'], file['name'])].append(new_lock)
881
+ if not staging_required:
882
+ return False
883
+
884
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=rse_id,
885
+ request_type=RequestType.STAGEIN,
886
+ scope=file['scope'],
887
+ name=file['name'],
888
+ rule=rule,
889
+ lock=new_lock,
890
+ bytes_=file['bytes'],
891
+ md5=file['md5'],
892
+ adler32=file['adler32'],
893
+ ds_scope=dataset['scope'],
894
+ ds_name=dataset['name'],
895
+ copy_pin_lifetime=copy_pin_lifetime,
896
+ session=session))
897
+
898
+ # Replica is not available -- UNAVAILABLE
899
+ elif existing_replica.state == ReplicaState.UNAVAILABLE:
900
+ available_source_replica = True
901
+ if source_rses:
902
+ available_source_replica = False
903
+ # Check if there is an eglible source replica for this lock
904
+ if set(source_replicas.get((file['scope'], file['name']), [])).intersection(source_rses):
905
+ available_source_replica = True
906
+ new_lock = __create_lock(rule=rule,
907
+ rse_id=rse_id,
908
+ scope=file['scope'],
909
+ name=file['name'],
910
+ bytes_=file['bytes'],
911
+ existing_replica=existing_replica,
912
+ state=LockState.REPLICATING if (available_source_replica and availability_write) else LockState.STUCK)
913
+ if rse_id not in locks_to_create:
914
+ locks_to_create[rse_id] = []
915
+ locks_to_create[rse_id].append(new_lock)
916
+ locks[(file['scope'], file['name'])].append(new_lock)
917
+ if not staging_area and not staging_required and available_source_replica and availability_write:
918
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=rse_id,
919
+ request_type=RequestType.TRANSFER,
920
+ scope=file['scope'],
921
+ name=file['name'],
922
+ rule=rule,
923
+ lock=new_lock,
924
+ bytes_=file['bytes'],
925
+ md5=file['md5'],
926
+ adler32=file['adler32'],
927
+ ds_scope=dataset['scope'],
928
+ ds_name=dataset['name'],
929
+ session=session))
930
+ return True
931
+ return False
932
+ # Replica is not available at the rse yet -- COPYING
933
+ else:
934
+ new_lock = __create_lock(rule=rule,
935
+ rse_id=rse_id,
936
+ scope=file['scope'],
937
+ name=file['name'],
938
+ bytes_=file['bytes'],
939
+ existing_replica=existing_replica,
940
+ state=LockState.REPLICATING)
941
+ if rse_id not in locks_to_create:
942
+ locks_to_create[rse_id] = []
943
+ locks_to_create[rse_id].append(new_lock)
944
+ locks[(file['scope'], file['name'])].append(new_lock)
945
+ return True
946
+ else: # Replica has to be created
947
+ available_source_replica = True
948
+ if source_rses:
949
+ available_source_replica = False
950
+ # Check if there is an eglible source replica for this lock
951
+ if set(source_replicas.get((file['scope'], file['name']), [])).intersection(source_rses):
952
+ available_source_replica = True
953
+
954
+ new_replica = __create_replica(rse_id=rse_id,
955
+ scope=file['scope'],
956
+ name=file['name'],
957
+ bytes_=file['bytes'],
958
+ md5=file['md5'],
959
+ adler32=file['adler32'],
960
+ state=ReplicaState.COPYING if (available_source_replica and availability_write) else ReplicaState.UNAVAILABLE)
961
+ if rse_id not in replicas_to_create:
962
+ replicas_to_create[rse_id] = []
963
+ replicas_to_create[rse_id].append(new_replica)
964
+ replicas[(file['scope'], file['name'])].append(new_replica)
965
+
966
+ new_lock = __create_lock(rule=rule,
967
+ rse_id=rse_id,
968
+ scope=file['scope'],
969
+ name=file['name'],
970
+ bytes_=file['bytes'],
971
+ existing_replica=new_replica,
972
+ state=LockState.REPLICATING if (available_source_replica and availability_write) else LockState.STUCK)
973
+ if rse_id not in locks_to_create:
974
+ locks_to_create[rse_id] = []
975
+ locks_to_create[rse_id].append(new_lock)
976
+ locks[(file['scope'], file['name'])].append(new_lock)
977
+
978
+ if not staging_area and not staging_required and available_source_replica and availability_write:
979
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=rse_id,
980
+ request_type=RequestType.TRANSFER,
981
+ scope=file['scope'],
982
+ name=file['name'],
983
+ rule=rule,
984
+ lock=new_lock,
985
+ bytes_=file['bytes'],
986
+ md5=file['md5'],
987
+ adler32=file['adler32'],
988
+ ds_scope=dataset['scope'],
989
+ ds_name=dataset['name'],
990
+ session=session))
991
+ return True
992
+ elif staging_required:
993
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=rse_id,
994
+ request_type=RequestType.TRANSFER,
995
+ scope=file['scope'],
996
+ name=file['name'],
997
+ rule=rule,
998
+ lock=new_lock,
999
+ bytes_=file['bytes'],
1000
+ md5=file['md5'],
1001
+ adler32=file['adler32'],
1002
+ ds_scope=dataset['scope'],
1003
+ ds_name=dataset['name'],
1004
+ session=session))
1005
+ return True
1006
+ return False
1007
+
1008
+
1009
+ def __create_lock(rule, rse_id, scope, name, bytes_, state, existing_replica, logger=logging.log):
1010
+ """
1011
+ Create and return a new SQLAlchemy Lock object.
1012
+
1013
+ :param rule: The SQLAlchemy rule object.
1014
+ :param rse_id: The rse_id of the lock.
1015
+ :param scope: The scope of the lock.
1016
+ :param name: The name of the lock.
1017
+ :param bytes_: Bytes of the lock.
1018
+ :param state: State of the lock.
1019
+ :param existing_replica: Replica object.
1020
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
1021
+ """
1022
+
1023
+ new_lock = models.ReplicaLock(rule_id=rule.id,
1024
+ rse_id=rse_id,
1025
+ scope=scope,
1026
+ name=name,
1027
+ account=rule.account,
1028
+ bytes=bytes_,
1029
+ state=state)
1030
+ if state == LockState.OK:
1031
+ existing_replica.lock_cnt += 1
1032
+ existing_replica.tombstone = None
1033
+ rule.locks_ok_cnt += 1
1034
+ logger(logging.DEBUG, 'Creating OK Lock %s:%s on %s for rule %s', scope, name, rse_id, str(rule.id))
1035
+ elif state == LockState.REPLICATING:
1036
+ existing_replica.state = ReplicaState.COPYING
1037
+ existing_replica.lock_cnt += 1
1038
+ existing_replica.tombstone = None
1039
+ rule.locks_replicating_cnt += 1
1040
+ logger(logging.DEBUG, 'Creating REPLICATING Lock %s:%s on %s for rule %s', scope, rse_id, name, str(rule.id))
1041
+ elif state == LockState.STUCK:
1042
+ existing_replica.lock_cnt += 1
1043
+ existing_replica.tombstone = None
1044
+ rule.locks_stuck_cnt += 1
1045
+ logger(logging.DEBUG, 'Creating STUCK Lock %s:%s on %s for rule %s', scope, name, rse_id, str(rule.id))
1046
+ return new_lock
1047
+
1048
+
1049
+ def __create_replica(rse_id, scope, name, bytes_, state, md5, adler32, logger=logging.log):
1050
+ """
1051
+ Create and return a new SQLAlchemy replica object.
1052
+
1053
+ :param rse_id: RSE id of the replica.
1054
+ :param scope: Scope of the replica.
1055
+ :param name: Name of the replica.
1056
+ :param bytes_: Bytes of the replica.
1057
+ :param state: State of the replica.
1058
+ :param md5: MD5 checksum of the replica.
1059
+ :param adler32: ADLER32 checksum of the replica.
1060
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
1061
+ """
1062
+
1063
+ new_replica = models.RSEFileAssociation(rse_id=rse_id,
1064
+ scope=scope,
1065
+ name=name,
1066
+ bytes=bytes_,
1067
+ md5=md5,
1068
+ adler32=adler32,
1069
+ tombstone=None,
1070
+ state=state,
1071
+ lock_cnt=0)
1072
+ logger(logging.DEBUG, 'Creating %s replica for %s:%s on %s', state, scope, name, rse_id)
1073
+ return new_replica
1074
+
1075
+
1076
+ @transactional_session
1077
+ def __update_lock_replica_and_create_transfer(lock, replica, rule, dataset, transfers_to_create, *, session: "Session", logger=logging.log):
1078
+ """
1079
+ This method updates a lock and replica and fills the corresponding dictionaries.
1080
+
1081
+ :param lock: The lock to update.
1082
+ :param replica: The replica to update.
1083
+ :param rule: Rule to update.
1084
+ :param dataset: Dataset dictionary holding the dataset information.
1085
+ :param transfers_to_create: List of transfers to create.
1086
+ :param session: The db session in use.
1087
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
1088
+ :attention: This method modifies the contents of the transfers_to_create input parameters.
1089
+ """
1090
+
1091
+ logger(logging.DEBUG, 'Updating Lock %s:%s for rule %s', lock.scope, lock.name, str(rule.id))
1092
+ lock.state = LockState.REPLICATING
1093
+ rule.locks_stuck_cnt -= 1
1094
+ rule.locks_replicating_cnt += 1
1095
+ replica.state = ReplicaState.COPYING
1096
+
1097
+ if not lock.repair_cnt:
1098
+ lock.repair_cnt = 1
1099
+ else:
1100
+ lock.repair_cnt += 1
1101
+
1102
+ if get_rse(rse_id=lock.rse_id, session=session)['staging_area']:
1103
+ copy_pin_lifetime = rule.expires_at - datetime.utcnow()
1104
+ copy_pin_lifetime = copy_pin_lifetime.seconds + copy_pin_lifetime.days * 24 * 3600
1105
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=lock.rse_id,
1106
+ scope=lock.scope,
1107
+ name=lock.name,
1108
+ rule=rule,
1109
+ lock=lock,
1110
+ bytes_=replica.bytes,
1111
+ md5=replica.md5,
1112
+ adler32=replica.adler32,
1113
+ ds_scope=dataset['scope'],
1114
+ ds_name=dataset['name'],
1115
+ copy_pin_lifetime=copy_pin_lifetime,
1116
+ request_type=RequestType.STAGEIN,
1117
+ session=session))
1118
+ else:
1119
+ transfers_to_create.append(create_transfer_dict(dest_rse_id=lock.rse_id,
1120
+ scope=lock.scope,
1121
+ name=lock.name,
1122
+ rule=rule,
1123
+ lock=lock,
1124
+ bytes_=replica.bytes,
1125
+ md5=replica.md5,
1126
+ adler32=replica.adler32,
1127
+ ds_scope=dataset['scope'],
1128
+ ds_name=dataset['name'],
1129
+ request_type=RequestType.TRANSFER,
1130
+ retry_count=1,
1131
+ session=session))
1132
+
1133
+
1134
+ @transactional_session
1135
+ def __set_replica_unavailable(replica, *, session: "Session"):
1136
+ """
1137
+ This method updates a replica and sets it to UNAVAILABLE.
1138
+
1139
+ :param replica: The replica to update.
1140
+ :param session: The db session in use.
1141
+ """
1142
+
1143
+ replica.lock_cnt -= 1
1144
+ if replica.lock_cnt == 0:
1145
+ replica.tombstone = OBSOLETE
1146
+ replica.state = ReplicaState.UNAVAILABLE
1147
+
1148
+
1149
+ # # debug helper functions used in apply_rule
1150
+ #
1151
+ # def prnt(x, header=None):
1152
+ # print()
1153
+ # if header:
1154
+ # print(header)
1155
+ # if isinstance(x, list) and len(x):
1156
+ # for elem in x:
1157
+ # print(' ', elem)
1158
+ # elif isinstance(x, dict) and len(x) and isinstance(x.values()[0], list):
1159
+ # for k, v in x.items():
1160
+ # if isinstance(v,list) and len(v):
1161
+ # print(' ', k, ':')
1162
+ # for elem in v:
1163
+ # print(' ', elem)
1164
+ # else:
1165
+ # print(' ', k, ':', v)
1166
+ # else:
1167
+ # print(x)
1168
+ #
1169
+ # import os
1170
+ # def mem():
1171
+ # # start your debug python session with harmless -R option to easily grep it out
1172
+ # os.system("ps -U root -o pid,user,rss:10,vsz:10,args:100 | grep 'python -R' | grep -v bin | grep -v grep")
1173
+
1174
+
1175
+ @transactional_session
1176
+ def apply_rule(did, rule, rses, source_rses, rseselector, *, session: "Session", logger=logging.log):
1177
+ """
1178
+ Apply a replication rule to one did.
1179
+
1180
+ :param did: the did object
1181
+ :param rule: the rule object
1182
+ :param rses: target rses_ids
1183
+ :param source_rses: source rses_ids
1184
+ :param rseselector: the rseselector object
1185
+ :param logger: Optional decorated logger that can be passed from the calling daemons or servers.
1186
+ :param session: the database session in use
1187
+ """
1188
+
1189
+ max_partition_size = config_get_int('rules', 'apply_rule_max_partition_size', default=2000, session=session) # process dataset files in bunches of max this size
1190
+
1191
+ # accounting counters
1192
+ rse_counters_files = {}
1193
+ rse_counters_bytes = {}
1194
+ account_counters_files = {}
1195
+ account_counters_bytes = {}
1196
+
1197
+ if did.did_type == DIDType.FILE:
1198
+ # NOTE: silently ignore rule.grouping
1199
+ if True: # instead of -> if rule.grouping == RuleGrouping.NONE:
1200
+ locks = {} # {(scope,name): [SQLAlchemy]}
1201
+ replicas = {} # {(scope, name): [SQLAlchemy]}
1202
+ source_replicas = {} # {(scope, name): [rse_id]
1203
+ # get files and replicas, lock the replicas
1204
+ replicas[(did.scope, did.name)] = rucio.core.replica.get_and_lock_file_replicas(scope=did.scope, name=did.name, nowait=True, restrict_rses=rses,
1205
+ session=session)
1206
+ # prnt(did, 'file')
1207
+ # prnt(replicas, 'replicas')
1208
+
1209
+ # get and lock the locks
1210
+ locks[(did.scope, did.name)] = rucio.core.lock.get_replica_locks(scope=did.scope, name=did.name, nowait=True, restrict_rses=rses,
1211
+ session=session)
1212
+ # prnt(locks, 'locks')
1213
+
1214
+ # if needed get source replicas
1215
+ if source_rses:
1216
+ source_replicas[(did.scope, did.name)] = rucio.core.replica.get_source_replicas(scope=did.scope, name=did.name, source_rses=source_rses,
1217
+ session=session)
1218
+ else:
1219
+ source_replicas = {}
1220
+ # prnt(source_replicas, 'source_replicas')
1221
+
1222
+ # to align code with cases below, create file dict
1223
+ file = {'name': did.name, 'scope': did.scope,
1224
+ 'bytes': did.bytes, 'md5': did.md5, 'adler32': did.adler32}
1225
+
1226
+ # calculate target RSEs
1227
+ rse_coverage = {replica.rse_id: file['bytes'] for replica in replicas[(file['scope'], file['name'])]}
1228
+ # prnt(rse_coverage)
1229
+ preferred_rse_ids = rse_coverage.keys()
1230
+ # prnt(preferred_rse_ids)
1231
+ rse_tuples = rseselector.select_rse(size=file['bytes'], preferred_rse_ids=preferred_rse_ids,
1232
+ prioritize_order_over_weight=True, existing_rse_size=rse_coverage)
1233
+ # prnt(rse_tuples)
1234
+
1235
+ # initialize accumulators for __create_lock_and_replica calls
1236
+ locks_to_create = {} # {'rse_id': [locks]}
1237
+ replicas_to_create = {} # {'rse_id': [replicas]}
1238
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
1239
+
1240
+ for rse_id, staging_area, availability_write in rse_tuples:
1241
+ # check for bug ????
1242
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id and lock.rse_id == rse_id]) == 1:
1243
+ logger(logging.DEBUG, '>>> WARNING unexpected duplicate lock for file %s at RSE %s' % (file, rse_id))
1244
+ continue
1245
+ # proceed
1246
+ __create_lock_and_replica(file=file, dataset={'scope': None, 'name': None}, rule=rule,
1247
+ rse_id=rse_id, staging_area=staging_area, availability_write=availability_write, source_rses=source_rses,
1248
+ replicas=replicas, locks=locks, source_replicas=source_replicas,
1249
+ locks_to_create=locks_to_create, replicas_to_create=replicas_to_create, transfers_to_create=transfers_to_create,
1250
+ session=session)
1251
+
1252
+ # prnt(locks_to_create, 'locks_to_create')
1253
+ # prnt(replicas_to_create, 'replicas_to_create')
1254
+ # prnt(transfers_to_create, 'transfers_to_create')
1255
+
1256
+ # flush to DB
1257
+ session.add_all([item for sublist in replicas_to_create.values() for item in sublist])
1258
+ session.add_all([item for sublist in locks_to_create.values() for item in sublist])
1259
+ request_core.queue_requests(requests=transfers_to_create, session=session)
1260
+ session.flush()
1261
+
1262
+ # increment counters
1263
+ # align code with the one used inside the file loop below
1264
+ for rse_id in replicas_to_create.keys():
1265
+ rse_counters_files[rse_id] = len(replicas_to_create[rse_id]) + rse_counters_files.get(rse_id, 0)
1266
+ rse_counters_bytes[rse_id] = sum([replica.bytes for replica in replicas_to_create[rse_id]]) + rse_counters_bytes.get(rse_id, 0)
1267
+ # prnt(rse_counters_files, 'rse_counters_files')
1268
+ # prnt(rse_counters_bytes, 'rse_counters_bytes')
1269
+
1270
+ for rse_id in locks_to_create.keys():
1271
+ account_counters_files[rse_id] = len(locks_to_create[rse_id]) + account_counters_files.get(rse_id, 0)
1272
+ account_counters_bytes[rse_id] = sum([lock.bytes for lock in locks_to_create[rse_id]]) + account_counters_bytes.get(rse_id, 0)
1273
+ # prnt(account_counters_files, 'account_counters_files')
1274
+ # prnt(account_counters_bytes, 'account_counters_bytes')
1275
+
1276
+ else:
1277
+ # handle dataset case by converting it to singleton container case
1278
+ # NOTE: this will handle DATASET/ALL as if it was DATASET/DATASET
1279
+ datasets = [] # [(scope,name)]
1280
+ if did.did_type == DIDType.DATASET:
1281
+ datasets.append((did.scope, did.name, ))
1282
+ elif did.did_type == DIDType.CONTAINER:
1283
+ for child_dataset in rucio.core.did.list_child_datasets(scope=did.scope, name=did.name, session=session):
1284
+ # ensure theer are no duplicates
1285
+ newds = (child_dataset['scope'], child_dataset['name'], )
1286
+ if newds not in datasets:
1287
+ datasets.append(newds)
1288
+ # sort alphabetically for deterministic order
1289
+ try:
1290
+ datasets = sorted(datasets)
1291
+ except Exception:
1292
+ pass
1293
+
1294
+ # prnt(datasets)
1295
+
1296
+ rse_coverage = {} # rse_coverage = { rse_id : bytes }
1297
+ rse_tuples = [] # rse_tuples = [(rse_id, staging_area, availability_write)]
1298
+ used_rse_ids = [] # for NONE grouping keep track of actual used RSEs
1299
+
1300
+ if rule.grouping == RuleGrouping.ALL:
1301
+ # calculate target RSEs
1302
+ nbytes = 0
1303
+ rse_coverage = {}
1304
+ # simply loop over child datasets
1305
+ # this is an approximation because ignoring the possibility of file overlap
1306
+ for ds_scope, ds_name in datasets:
1307
+ ds = rucio.core.did.get_did(scope=ds_scope, name=ds_name, dynamic_depth=DIDType.FILE, session=session) # this will be retrieved again later on -> could be optimized
1308
+ nbytes += ds['bytes']
1309
+ one_rse_coverage = rucio.core.replica.get_RSEcoverage_of_dataset(scope=ds_scope, name=ds_name, session=session)
1310
+ for rse_id, bytes_ in one_rse_coverage.items():
1311
+ rse_coverage[rse_id] = bytes_ + rse_coverage.get(rse_id, 0)
1312
+
1313
+ # prnt(rse_coverage)
1314
+ preferred_rse_ids = [x[0] for x in sorted(rse_coverage.items(), key=lambda tup: tup[1], reverse=True)]
1315
+ # prnt(preferred_rse_ids)
1316
+ rse_tuples = rseselector.select_rse(size=nbytes, preferred_rse_ids=preferred_rse_ids,
1317
+ prioritize_order_over_weight=True, existing_rse_size=rse_coverage)
1318
+ # prnt(rse_tuples)
1319
+
1320
+ for ds_scope, ds_name in datasets:
1321
+ # prnt(('processing dataset ',ds_scope, ds_name))
1322
+ #
1323
+ ds = rucio.core.did.get_did(scope=ds_scope, name=ds_name, dynamic_depth=DIDType.FILE, session=session)
1324
+ ds_length = ds['length']
1325
+ ds_bytes = ds['bytes']
1326
+ ds_open = ds['open']
1327
+ # prnt(ds)
1328
+
1329
+ # calculate number of partitions based on nr of files
1330
+ npartitions = int(ds_length / max_partition_size) + 1
1331
+ # prnt(npartitions)
1332
+
1333
+ if rule.grouping == RuleGrouping.DATASET:
1334
+ # calculate target RSEs
1335
+ rse_coverage = rucio.core.replica.get_RSEcoverage_of_dataset(scope=ds_scope, name=ds_name, session=session)
1336
+ # prnt(rse_coverage)
1337
+ preferred_rse_ids = [x[0] for x in sorted(rse_coverage.items(), key=lambda tup: tup[1], reverse=True)]
1338
+ # prnt(preferred_rse_ids)
1339
+ rse_tuples = rseselector.select_rse(size=ds_bytes, preferred_rse_ids=preferred_rse_ids,
1340
+ prioritize_order_over_weight=True, existing_rse_size=rse_coverage)
1341
+ # prnt(rse_tuples)
1342
+
1343
+ # loop over the partitions even if it is just one
1344
+ for p in range(npartitions):
1345
+ # prnt(('processing partition ', p, npartitions))
1346
+
1347
+ # files is [{'scope':, 'name':, 'bytes':, 'md5':, 'adler32':}]
1348
+ # locks is {(scope,name): [SQLAlchemy]}
1349
+ # replicas = {(scope, name): [SQLAlchemy]}
1350
+ # source replicas is {(scope, name): [SQLAlchemy]}
1351
+
1352
+ # get files and replicas, lock the replicas
1353
+ files, replicas = rucio.core.replica.get_and_lock_file_replicas_for_dataset(scope=ds_scope, name=ds_name, nowait=True, restrict_rses=rses,
1354
+ total_threads=npartitions, thread_id=p, session=session)
1355
+ # prnt(files, 'files')
1356
+ # prnt(replicas, 'replicas')
1357
+
1358
+ # get and lock the replica locks
1359
+ locks = rucio.core.lock.get_files_and_replica_locks_of_dataset(scope=ds_scope, name=ds_name, nowait=True, restrict_rses=rses,
1360
+ total_threads=npartitions, thread_id=p, session=session)
1361
+ # prnt(locks, 'locks')
1362
+
1363
+ # if needed get source replicas
1364
+ if source_rses:
1365
+ source_replicas = rucio.core.replica.get_source_replicas_for_dataset(scope=ds_scope, name=ds_name, source_rses=source_rses,
1366
+ total_threads=npartitions, thread_id=p, session=session)
1367
+ else:
1368
+ source_replicas = {}
1369
+ # prnt(source_replicas, 'source_replicas')
1370
+
1371
+ # initialize accumulators for __create_lock_and_replica calls
1372
+ locks_to_create = {} # {'rse_id': [locks]}
1373
+ replicas_to_create = {} # {'rse_id': [replicas]}
1374
+ transfers_to_create = [] # [{'dest_rse_id':, 'scope':, 'name':, 'request_type':, 'metadata':}]
1375
+
1376
+ # loop over the rse tuples
1377
+ for file in files:
1378
+ # check for duplicate due to dataset overlap within container
1379
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id]) == rule.copies:
1380
+ logger(logging.DEBUG, '>>> WARNING skipping (shared?) file %s' % file)
1381
+ continue
1382
+
1383
+ if rule.grouping == RuleGrouping.NONE:
1384
+ # calculate target RSEs
1385
+ rse_coverage = {replica.rse_id: file['bytes'] for replica in replicas[(file['scope'], file['name'])]}
1386
+ # prnt(rse_coverage)
1387
+ preferred_rse_ids = rse_coverage.keys()
1388
+ # prnt(preferred_rse_ids)
1389
+ rse_tuples = rseselector.select_rse(size=file['bytes'], preferred_rse_ids=preferred_rse_ids,
1390
+ prioritize_order_over_weight=True, existing_rse_size=rse_coverage)
1391
+ # prnt(rse_tuples)
1392
+ # keep track of used RSEs
1393
+ for rt in rse_tuples:
1394
+ if not rt[0] in used_rse_ids:
1395
+ used_rse_ids.append(rt[0])
1396
+
1397
+ for rse_id, staging_area, availability_write in rse_tuples:
1398
+ # check for bug ????
1399
+ if len([lock for lock in locks[(file['scope'], file['name'])] if lock.rule_id == rule.id and lock.rse_id == rse_id]) == 1:
1400
+ logger(logging.DEBUG, '>>> WARNING unexpected duplicate lock for file %s at RSE %s' % (file, rse_id))
1401
+ continue
1402
+ # proceed
1403
+ __create_lock_and_replica(file=file, dataset={'scope': ds_scope, 'name': ds_name}, rule=rule,
1404
+ rse_id=rse_id, staging_area=staging_area, availability_write=availability_write, source_rses=source_rses,
1405
+ replicas=replicas, locks=locks, source_replicas=source_replicas,
1406
+ locks_to_create=locks_to_create, replicas_to_create=replicas_to_create, transfers_to_create=transfers_to_create,
1407
+ session=session)
1408
+
1409
+ # prnt(locks_to_create, 'locks_to_create')
1410
+ # prnt(replicas_to_create, 'replicas_to_create')
1411
+ # prnt(transfers_to_create, 'transfers_to_create')
1412
+
1413
+ # flush to DB
1414
+ session.add_all([item for sublist in replicas_to_create.values() for item in sublist])
1415
+ session.add_all([item for sublist in locks_to_create.values() for item in sublist])
1416
+ request_core.queue_requests(requests=transfers_to_create, session=session)
1417
+ session.flush()
1418
+
1419
+ # increment counters
1420
+ # do not update (and lock !) counters inside loop here, update at very end and only once
1421
+ for rse_id in replicas_to_create.keys():
1422
+ rse_counters_files[rse_id] = len(replicas_to_create[rse_id]) + rse_counters_files.get(rse_id, 0)
1423
+ rse_counters_bytes[rse_id] = sum([replica.bytes for replica in replicas_to_create[rse_id]]) + rse_counters_bytes.get(rse_id, 0)
1424
+ # prnt(rse_counters_files, 'rse_counters_files')
1425
+ # prnt(rse_counters_bytes, 'rse_counters_bytes')
1426
+
1427
+ for rse_id in locks_to_create.keys():
1428
+ account_counters_files[rse_id] = len(locks_to_create[rse_id]) + account_counters_files.get(rse_id, 0)
1429
+ account_counters_bytes[rse_id] = sum([lock.bytes for lock in locks_to_create[rse_id]]) + account_counters_bytes.get(rse_id, 0)
1430
+ # prnt(account_counters_files, 'account_counters_files')
1431
+ # prnt(account_counters_bytes, 'account_counters_bytes')
1432
+
1433
+ # mem()
1434
+
1435
+ # dataset lock/replica
1436
+ u_rses = (used_rse_ids if rule.grouping == RuleGrouping.NONE else [x[0] for x in rse_tuples])
1437
+ # prnt(u_rses, 'used RSE ids')
1438
+ for u_rse in u_rses:
1439
+ # prnt('creating dataset lock/replica for %s on %s' % (ds_name,u_rse))
1440
+ if rule.grouping == RuleGrouping.DATASET or rule.grouping == RuleGrouping.ALL:
1441
+ # add dataset lock
1442
+ models.DatasetLock(scope=ds_scope, name=ds_name,
1443
+ rule_id=rule.id,
1444
+ rse_id=u_rse,
1445
+ state=LockState.REPLICATING,
1446
+ account=rule.account,
1447
+ length=ds_length if not ds_open else None,
1448
+ bytes=ds_bytes if not ds_open else None
1449
+ ).save(session=session)
1450
+
1451
+ # add dataset replica if not already existing (rule_id is not in PK)
1452
+ try:
1453
+ session.query(models.CollectionReplica).filter(models.CollectionReplica.scope == ds_scope,
1454
+ models.CollectionReplica.name == ds_name,
1455
+ models.CollectionReplica.rse_id == u_rse).one()
1456
+ except NoResultFound:
1457
+ models.CollectionReplica(scope=ds_scope, name=ds_name, did_type=DIDType.DATASET,
1458
+ rse_id=u_rse,
1459
+ bytes=0, length=0, available_bytes=0, available_replicas_cnt=0,
1460
+ state=ReplicaState.UNAVAILABLE
1461
+ ).save(session=session)
1462
+
1463
+ models.UpdatedCollectionReplica(scope=ds_scope, name=ds_name, did_type=DIDType.DATASET
1464
+ ).save(session=session)
1465
+
1466
+ # update account and rse counters
1467
+ for rse_id in rse_counters_files:
1468
+ rse_counter.increase(rse_id=rse_id, files=rse_counters_files[rse_id], bytes_=rse_counters_bytes[rse_id], session=session)
1469
+ for rse_id in account_counters_files:
1470
+ account_counter.increase(rse_id=rse_id, account=rule.account, files=account_counters_files[rse_id], bytes_=account_counters_bytes[rse_id], session=session)
1471
+ session.flush()
1472
+
1473
+ return