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
rucio/core/importer.py ADDED
@@ -0,0 +1,248 @@
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
+ from typing import TYPE_CHECKING
17
+
18
+ from rucio.common.config import config_get
19
+ from rucio.common.exception import RSEOperationNotSupported
20
+ from rucio.common.types import InternalAccount
21
+ from rucio.core import rse as rse_module, distance as distance_module, account as account_module, identity as identity_module
22
+ from rucio.db.sqla import models
23
+ from rucio.db.sqla.constants import RSEType, AccountType, IdentityType
24
+ from rucio.db.sqla.session import transactional_session
25
+
26
+ if TYPE_CHECKING:
27
+ from sqlalchemy.orm import Session
28
+
29
+
30
+ @transactional_session
31
+ def import_rses(rses, rse_sync_method='edit', attr_sync_method='edit', protocol_sync_method='edit', vo='def', *, session: "Session"):
32
+ new_rses = []
33
+ for rse_name in rses:
34
+ rse = rses[rse_name]
35
+ if isinstance(rse.get('rse_type'), str):
36
+ rse['rse_type'] = RSEType(rse['rse_type'])
37
+
38
+ if rse_module.rse_exists(rse_name, vo=vo, include_deleted=False, session=session):
39
+ # RSE exists and is active
40
+ rse_id = rse_module.get_rse_id(rse=rse_name, vo=vo, session=session)
41
+ selected_rse_properties = {key: rse[key] for key in rse if key in rse_module.MUTABLE_RSE_PROPERTIES}
42
+ rse_module.update_rse(rse_id=rse_id, parameters=selected_rse_properties, session=session)
43
+ elif rse_module.rse_exists(rse_name, vo=vo, include_deleted=True, session=session):
44
+ # RSE exists but in deleted state
45
+ # Should only modify the RSE if importer is configured for edit or hard sync
46
+ if rse_sync_method in ['edit', 'hard']:
47
+ rse_id = rse_module.get_rse_id(rse=rse_name, vo=vo, include_deleted=True, session=session)
48
+ rse_module.restore_rse(rse_id, session=session)
49
+ selected_rse_properties = {key: rse[key] for key in rse if key in rse_module.MUTABLE_RSE_PROPERTIES}
50
+ rse_module.update_rse(rse_id=rse_id, parameters=selected_rse_properties, session=session)
51
+ else:
52
+ # Config is in RSE append only mode, should not modify the disabled RSE
53
+ continue
54
+ else:
55
+ rse_id = rse_module.add_rse(rse=rse_name, vo=vo, deterministic=rse.get('deterministic'), volatile=rse.get('volatile'),
56
+ city=rse.get('city'), region_code=rse.get('region_code'), country_name=rse.get('country_name'),
57
+ staging_area=rse.get('staging_area'), continent=rse.get('continent'), time_zone=rse.get('time_zone'),
58
+ ISP=rse.get('ISP'), rse_type=rse.get('rse_type'), latitude=rse.get('latitude'),
59
+ longitude=rse.get('longitude'), ASN=rse.get('ASN'), availability_read=rse.get('availability_read'),
60
+ availability_write=rse.get('availability_write'), availability_delete=rse.get('availability_delete'),
61
+ session=session)
62
+
63
+ new_rses.append(rse_id)
64
+ # Protocols
65
+ new_protocols = rse.get('protocols')
66
+ if new_protocols:
67
+ # update existing, add missing and remove left over protocols
68
+ old_protocols = [{'scheme': protocol['scheme'], 'hostname': protocol['hostname'], 'port': protocol['port']} for protocol in rse_module.get_rse_protocols(rse_id=rse_id, session=session)['protocols']]
69
+ missing_protocols = [new_protocol for new_protocol in new_protocols if {'scheme': new_protocol['scheme'], 'hostname': new_protocol['hostname'], 'port': new_protocol['port']} not in old_protocols]
70
+ outdated_protocols = [new_protocol for new_protocol in new_protocols if {'scheme': new_protocol['scheme'], 'hostname': new_protocol['hostname'], 'port': new_protocol['port']} in old_protocols]
71
+ new_protocols = [{'scheme': protocol['scheme'], 'hostname': protocol['hostname'], 'port': protocol['port']} for protocol in new_protocols]
72
+ to_be_removed_protocols = [old_protocol for old_protocol in old_protocols if old_protocol not in new_protocols]
73
+
74
+ if protocol_sync_method == 'append':
75
+ outdated_protocols = []
76
+
77
+ for protocol in outdated_protocols:
78
+ scheme = protocol['scheme']
79
+ port = protocol['port']
80
+ hostname = protocol['hostname']
81
+ del protocol['scheme']
82
+ del protocol['hostname']
83
+ del protocol['port']
84
+ rse_module.update_protocols(rse_id=rse_id, scheme=scheme, data=protocol, hostname=hostname, port=port, session=session)
85
+
86
+ for protocol in missing_protocols:
87
+ rse_module.add_protocol(rse_id=rse_id, parameter=protocol, session=session)
88
+
89
+ if protocol_sync_method == 'hard':
90
+ for protocol in to_be_removed_protocols:
91
+ scheme = protocol['scheme']
92
+ port = protocol['port']
93
+ hostname = protocol['hostname']
94
+ rse_module.del_protocols(rse_id=rse_id, scheme=scheme, port=port, hostname=hostname, session=session)
95
+
96
+ # Limits
97
+ old_limits = rse_module.get_rse_limits(rse_id=rse_id, session=session)
98
+ for limit_name in ['MinFreeSpace']:
99
+ limit = rse.get(limit_name)
100
+ if limit:
101
+ if limit_name in old_limits:
102
+ rse_module.delete_rse_limits(rse_id=rse_id, name=limit_name, session=session)
103
+ rse_module.set_rse_limits(rse_id=rse_id, name=limit_name, value=limit, session=session)
104
+
105
+ # Attributes
106
+ attributes = rse.get('attributes', {})
107
+ attributes['lfn2pfn_algorithm'] = rse.get('lfn2pfn_algorithm')
108
+ attributes['verify_checksum'] = rse.get('verify_checksum')
109
+
110
+ old_attributes = rse_module.list_rse_attributes(rse_id=rse_id, session=session)
111
+ missing_attributes = [attribute for attribute in old_attributes if attribute not in attributes]
112
+
113
+ for attr in attributes:
114
+ value = attributes[attr]
115
+ if value is not None:
116
+ if attr in old_attributes:
117
+ if attr_sync_method not in ['append']:
118
+ rse_module.del_rse_attribute(rse_id=rse_id, key=attr, session=session)
119
+ rse_module.add_rse_attribute(rse_id=rse_id, key=attr, value=value, session=session)
120
+ else:
121
+ rse_module.add_rse_attribute(rse_id=rse_id, key=attr, value=value, session=session)
122
+ if attr_sync_method == 'hard':
123
+ for attr in missing_attributes:
124
+ if attr != rse_name:
125
+ rse_module.del_rse_attribute(rse_id=rse_id, key=attr, session=session)
126
+
127
+ # set deleted flag to RSEs that are missing in the import data
128
+ old_rses = [old_rse['id'] for old_rse in rse_module.list_rses(session=session)]
129
+ if rse_sync_method == 'hard':
130
+ for old_rse in old_rses:
131
+ if old_rse not in new_rses:
132
+ try:
133
+ rse_module.del_rse(rse_id=old_rse, session=session)
134
+ except RSEOperationNotSupported:
135
+ pass
136
+
137
+
138
+ @transactional_session
139
+ def import_distances(distances, vo='def', *, session: "Session"):
140
+ for src_rse_name in distances:
141
+ src = rse_module.get_rse_id(rse=src_rse_name, vo=vo, session=session)
142
+ for dest_rse_name in distances[src_rse_name]:
143
+ dest = rse_module.get_rse_id(rse=dest_rse_name, vo=vo, session=session)
144
+ distance_dict = distances[src_rse_name][dest_rse_name]
145
+ if 'src_rse_id' in distance_dict:
146
+ del distance_dict['src_rse_id']
147
+ if 'dest_rse_id' in distance_dict:
148
+ del distance_dict['dest_rse_id']
149
+
150
+ old_distance = distance_module.get_distances(src_rse_id=src, dest_rse_id=dest, session=session)
151
+ new_distance = distance_dict.get('distance', distance_dict.get('ranking'))
152
+ if old_distance:
153
+ distance_module.update_distances(src_rse_id=src, dest_rse_id=dest, distance=new_distance, session=session)
154
+ else:
155
+ distance_module.add_distance(src_rse_id=src, dest_rse_id=dest, distance=new_distance, session=session)
156
+
157
+
158
+ @transactional_session
159
+ def import_identities(identities, account_name, old_identities, old_identity_account, account_email, *, session: "Session"):
160
+ for identity in identities:
161
+ identity['type'] = IdentityType[identity['type'].upper()]
162
+
163
+ missing_identities = [identity for identity in identities if (identity['identity'], identity['type']) not in old_identities]
164
+ missing_identity_account = [identity for identity in identities if (identity['identity'], identity['type'], account_name) not in old_identity_account]
165
+ to_be_removed_identity_account = [old_identity for old_identity in old_identity_account if (old_identity[0], old_identity[1], old_identity[2]) not in
166
+ [(identity['identity'], identity['type'], account_name) for identity in identities] and old_identity[2] == account_name]
167
+
168
+ # add missing identities
169
+ for identity in missing_identities:
170
+ identity_type = identity['type']
171
+ password = identity.get('password')
172
+ identity = identity['identity']
173
+ if identity_type == IdentityType.USERPASS:
174
+ identity_module.add_identity(identity=identity, password=password, email=account_email, type_=identity_type, session=session)
175
+ elif identity_type == IdentityType.GSS or identity_type == IdentityType.SSH or identity_type == IdentityType.X509:
176
+ identity_module.add_identity(identity=identity, email=account_email, type_=identity_type, session=session)
177
+
178
+ # add missing identity-account association
179
+ for identity in missing_identity_account:
180
+ identity_module.add_account_identity(identity['identity'], identity['type'], account_name, email=account_email, session=session)
181
+
182
+ # remove identities from account-identity association
183
+ for identity in to_be_removed_identity_account:
184
+ identity_module.del_account_identity(identity=identity[0], type_=identity[1], account=identity[2], session=session)
185
+
186
+
187
+ @transactional_session
188
+ def import_accounts(accounts, vo='def', *, session: "Session"):
189
+ vo_filter = {'account': InternalAccount(account='*', vo=vo)}
190
+ old_accounts = {account['account']: account for account in account_module.list_accounts(filter_=vo_filter, session=session)}
191
+ missing_accounts = [account for account in accounts if account['account'] not in old_accounts]
192
+ outdated_accounts = [account for account in accounts if account['account'] in old_accounts]
193
+ to_be_removed_accounts = [old_account for old_account in old_accounts if old_account not in [account['account'] for account in accounts]]
194
+ old_identities = identity_module.list_identities(session=session)
195
+ old_identity_account = session.query(models.IdentityAccountAssociation.identity, models.IdentityAccountAssociation.identity_type, models.IdentityAccountAssociation.account).all()
196
+
197
+ # add missing accounts
198
+ for account_dict in missing_accounts:
199
+ account = account_dict['account']
200
+ email = account_dict['email']
201
+ account_module.add_account(account=account, type_=AccountType.USER, email=email, session=session)
202
+ identities = account_dict.get('identities', [])
203
+ if identities:
204
+ import_identities(identities, account, old_identities, old_identity_account, email, session=session)
205
+
206
+ # remove left over accounts
207
+ for account in to_be_removed_accounts:
208
+ if account.external != 'root':
209
+ account_module.del_account(account=account, session=session)
210
+
211
+ # update existing accounts
212
+ for account_dict in outdated_accounts:
213
+ account = account_dict['account']
214
+ email = account_dict['email']
215
+ old_account = old_accounts[account]
216
+ if email and old_account['email'] != email:
217
+ account_module.update_account(account, key='email', value=email, session=session)
218
+
219
+ identities = account_dict.get('identities', [])
220
+ if identities:
221
+ import_identities(identities, account, old_identities, old_identity_account, email, session=session)
222
+
223
+
224
+ @transactional_session
225
+ def import_data(data, vo='def', *, session: "Session"):
226
+ """
227
+ Import data to add and update records in Rucio.
228
+
229
+ :param data: data to be imported as dictionary.
230
+ :param session: database session in use.
231
+ """
232
+ rse_sync_method = config_get('importer', 'rse_sync_method', False, 'edit')
233
+ attr_sync_method = config_get('importer', 'attr_sync_method', False, 'edit')
234
+ protocol_sync_method = config_get('importer', 'rse_sync_method', False, 'edit')
235
+
236
+ rses = data.get('rses')
237
+ if rses:
238
+ import_rses(rses, rse_sync_method=rse_sync_method, attr_sync_method=attr_sync_method, protocol_sync_method=protocol_sync_method, vo=vo, session=session)
239
+
240
+ # Distances
241
+ distances = data.get('distances')
242
+ if distances:
243
+ import_distances(distances, vo=vo, session=session)
244
+
245
+ # Accounts
246
+ accounts = data.get('accounts')
247
+ if accounts:
248
+ import_accounts(accounts, vo=vo, session=session)
@@ -0,0 +1,377 @@
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
+ from configparser import NoSectionError
17
+ from datetime import datetime, timedelta
18
+ from re import match
19
+ from typing import TYPE_CHECKING
20
+
21
+ from sqlalchemy import or_, select, update
22
+ from sqlalchemy.exc import IntegrityError
23
+ from sqlalchemy.orm.exc import NoResultFound
24
+
25
+ import rucio.common.policy
26
+ from rucio.common.config import config_get, config_get_int, config_get_list
27
+ from rucio.common.exception import RucioException, LifetimeExceptionDuplicate, LifetimeExceptionNotFound, UnsupportedOperation, ConfigNotFound
28
+ from rucio.common.utils import generate_uuid, str_to_date
29
+ from rucio.core.message import add_message
30
+ from rucio.core.rse import list_rse_attributes
31
+ from rucio.db.sqla import models
32
+ from rucio.db.sqla.constants import DIDType, LifetimeExceptionsState
33
+ from rucio.db.sqla.session import transactional_session, stream_session, read_session
34
+
35
+ if TYPE_CHECKING:
36
+ from collections.abc import Iterator
37
+ from typing import Any, Optional, Union
38
+ from rucio.common.types import InternalAccount, InternalScope
39
+ from sqlalchemy.orm import Session
40
+
41
+
42
+ @stream_session
43
+ def list_exceptions(
44
+ exception_id: 'Optional[str]',
45
+ states: 'list[LifetimeExceptionsState]',
46
+ *,
47
+ session: 'Session',
48
+ ) -> 'Iterator[dict[str, Any]]':
49
+ """
50
+ List exceptions to Lifetime Model.
51
+
52
+ :param exception_id: The id of the exception
53
+ :param states: The states to filter
54
+ :param session: The database session in use.
55
+ """
56
+
57
+ state_clause = []
58
+ if states:
59
+ state_clause = [models.LifetimeExceptions.state == state for state in states]
60
+
61
+ query = select(models.LifetimeExceptions)
62
+ if state_clause != []:
63
+ query = query.where(or_(*state_clause))
64
+ if exception_id:
65
+ query = query.filter_by(id=exception_id)
66
+
67
+ for exception in session.execute(query).yield_per(5).scalars():
68
+ yield {'id': exception.id, 'scope': exception.scope, 'name': exception.name,
69
+ 'did_type': exception.did_type, 'account': exception.account,
70
+ 'pattern': exception.pattern, 'comments': exception.comments,
71
+ 'state': exception.state, 'created_at': exception.created_at,
72
+ 'expires_at': exception.expires_at}
73
+
74
+
75
+ @transactional_session
76
+ def add_exception(
77
+ dids: 'list[dict[str, Any]]',
78
+ account: 'InternalAccount',
79
+ pattern: 'Optional[str]',
80
+ comments: str,
81
+ expires_at: 'Optional[Union[str, datetime]]',
82
+ *,
83
+ session: 'Session'
84
+ ) -> 'dict[str, Any]':
85
+ """
86
+ Add exceptions to Lifetime Model.
87
+
88
+ :param dids: The list of dids
89
+ :param account: The account of the requester.
90
+ :param pattern: The account.
91
+ :param comments: The comments associated to the exception.
92
+ :param expires_at: The expiration date of the exception.
93
+ :param session: The database session in use.
94
+
95
+ returns: A dictionary with id of the exceptions split by scope, datatype.
96
+ """
97
+ from rucio.core.did import get_metadata_bulk
98
+ result = dict()
99
+ result['exceptions'] = dict()
100
+ try:
101
+ max_extension = config_get_int('lifetime_model', 'max_extension', session=session)
102
+ if max_extension:
103
+ if not expires_at:
104
+ expires_at = datetime.utcnow() + timedelta(days=max_extension)
105
+ else:
106
+ if isinstance(expires_at, str):
107
+ expires_at = str_to_date(expires_at)
108
+ if expires_at > datetime.utcnow() + timedelta(days=max_extension):
109
+ expires_at = datetime.utcnow() + timedelta(days=max_extension)
110
+ except (ConfigNotFound, ValueError, NoSectionError):
111
+ max_extension = None
112
+
113
+ try:
114
+ cutoff_date = config_get('lifetime_model', 'cutoff_date', session=session)
115
+ except (ConfigNotFound, NoSectionError):
116
+ raise UnsupportedOperation('Cannot submit exception at that date.')
117
+ try:
118
+ cutoff_date = datetime.strptime(cutoff_date, '%Y-%m-%d')
119
+ except ValueError:
120
+ raise UnsupportedOperation('Cannot submit exception at that date.')
121
+ if cutoff_date < datetime.utcnow():
122
+ raise UnsupportedOperation('Cannot submit exception at that date.')
123
+
124
+ did_group = dict()
125
+ not_affected = list()
126
+ list_dids = [(did['scope'], did['name']) for did in dids]
127
+ metadata = [meta for meta in get_metadata_bulk(dids=dids, session=session)]
128
+ for did in metadata:
129
+ scope, name, did_type = did['scope'], did['name'], did['did_type']
130
+ if (scope, name) in list_dids:
131
+ list_dids.remove((scope, name))
132
+ datatype = did.get('datatype', '')
133
+ eol_at = did.get('eol_at', None)
134
+ if eol_at and eol_at < cutoff_date:
135
+ if (scope, datatype) not in did_group:
136
+ did_group[(scope, datatype)] = [list(), 0]
137
+ did_group[(scope, datatype)][0].append({'scope': scope, 'name': name, 'did_type': did_type})
138
+ did_group[(scope, datatype)][1] += did['bytes'] or 0
139
+ else:
140
+ not_affected.append((scope, name, did_type))
141
+ for entry in did_group:
142
+ exception_id = __add_exception(did_group[entry][0], account=account, pattern=pattern, comments=comments, expires_at=expires_at, estimated_volume=did_group[entry][1], session=session)
143
+ result['exceptions'][exception_id] = did_group[entry][0]
144
+ result['unknown'] = [{'scope': did[0], 'name': did[1], 'did_type': DIDType.DATASET} for did in list_dids]
145
+ result['not_affected'] = [{'scope': did[0], 'name': did[1], 'did_type': did[2]} for did in not_affected]
146
+ return result
147
+
148
+
149
+ @transactional_session
150
+ def __add_exception(
151
+ dids: 'list[dict[str, Any]]',
152
+ account: 'InternalAccount',
153
+ pattern: 'Optional[str]',
154
+ comments: str,
155
+ expires_at: 'Optional[Union[str, datetime]]',
156
+ estimated_volume: 'Optional[int]' = None,
157
+ *,
158
+ session: 'Session',
159
+ ) -> str:
160
+ """
161
+ Add exceptions to Lifetime Model.
162
+
163
+ :param dids: The list of dids
164
+ :param account: The account of the requester.
165
+ :param pattern: The pattern of the exception (not used).
166
+ :param comments: The comments associated to the exception.
167
+ :param expires_at: The expiration date of the exception.
168
+ :params estimated_volume: The estimated logical volume of the exception.
169
+ :param session: The database session in use.
170
+
171
+ returns: The id of the exception.
172
+ """
173
+ exception_id = generate_uuid()
174
+ text = 'Account %s requested a lifetime extension for a list of DIDs that can be found below\n' % account
175
+ reason = comments
176
+ volume = None
177
+ lifetime = None
178
+ if comments.find('||||') > -1:
179
+ reason, volume = comments.split('||||')
180
+ text += 'The reason for the extension is "%s"\n' % reason
181
+ text += 'It represents %s datasets\n' % len(dids)
182
+ if estimated_volume:
183
+ text += 'The estimated logical volume is %s\n' % estimated_volume
184
+ if volume:
185
+ text += 'The estimated physical volume is %s\n' % volume
186
+ if expires_at and isinstance(expires_at, str):
187
+ lifetime = str_to_date(expires_at)
188
+ text += 'The lifetime exception should expires on %s\n' % str(expires_at)
189
+ elif isinstance(expires_at, datetime):
190
+ lifetime = expires_at
191
+ text += 'The lifetime exception should expires on %s\n' % str(expires_at)
192
+ text += 'Link to approve or reject this request can be found at the end of the mail\n'
193
+ text += '\n'
194
+ text += 'DIDTYPE SCOPE NAME\n'
195
+ text += '\n'
196
+ truncated_message = False
197
+ for did in dids:
198
+ did_type = None
199
+ if 'did_type' in did:
200
+ if isinstance(did['did_type'], str):
201
+ did_type = DIDType[did['did_type']]
202
+ else:
203
+ did_type = did['did_type']
204
+ new_exception = models.LifetimeExceptions(id=exception_id, scope=did['scope'], name=did['name'], did_type=did_type,
205
+ account=account, pattern=pattern, comments=reason, state=LifetimeExceptionsState.WAITING, expires_at=lifetime)
206
+ if len(text) < 3000:
207
+ text += '%s %s %s\n' % (str(did_type), did['scope'], did['name'])
208
+ else:
209
+ truncated_message = True
210
+ try:
211
+ new_exception.save(session=session, flush=False)
212
+ except IntegrityError as error:
213
+ if match('.*ORA-00001.*', str(error.args[0])) \
214
+ or match('.*IntegrityError.*UNIQUE constraint failed.*', str(error.args[0])) \
215
+ or match('.*1062.*Duplicate entry.*for key.*', str(error.args[0])) \
216
+ or match('.*IntegrityError.*columns? .*not unique.*', error.args[0]):
217
+ raise LifetimeExceptionDuplicate()
218
+ raise RucioException(error.args[0])
219
+ if truncated_message:
220
+ text += '...\n'
221
+ text += 'List too long. Truncated\n'
222
+ text += '\n'
223
+ text += 'Approve: https://rucio-ui.cern.ch/lifetime_exception?id=%s&action=approve\n' % str(exception_id)
224
+ text += 'Deny: https://rucio-ui.cern.ch/lifetime_exception?id=%s&action=deny\n' % str(exception_id)
225
+ approvers_email = config_get_list('lifetime_model', 'approvers_email', default=[], session=session)
226
+
227
+ add_message(event_type='email',
228
+ payload={'body': text, 'to': approvers_email,
229
+ 'subject': '[RUCIO] Request to approve lifetime exception %s' % str(exception_id)},
230
+ session=session)
231
+ return exception_id
232
+
233
+
234
+ @transactional_session
235
+ def update_exception(
236
+ exception_id: str,
237
+ state: LifetimeExceptionsState,
238
+ *,
239
+ session: 'Session',
240
+ ) -> None:
241
+ """
242
+ Update exceptions state to Lifetime Model.
243
+
244
+ :param exception_id: The id of the exception
245
+ :param state: The states to filter
246
+ :param session: The database session in use.
247
+ """
248
+ ALLOWED_STATES = (LifetimeExceptionsState.APPROVED, LifetimeExceptionsState.REJECTED)
249
+ if state not in ALLOWED_STATES:
250
+ raise UnsupportedOperation
251
+
252
+ query = update(
253
+ models.LifetimeExceptions
254
+ ).where(
255
+ models.LifetimeExceptions.id == exception_id
256
+ ).values(
257
+ state=state,
258
+ updated_at=datetime.utcnow()
259
+ )
260
+
261
+ if session.execute(query).rowcount == 0:
262
+ raise LifetimeExceptionNotFound
263
+
264
+
265
+ @read_session
266
+ def define_eol(
267
+ scope: 'InternalScope',
268
+ name: str,
269
+ rses: 'list[dict[str, Any]]',
270
+ *,
271
+ session: 'Session',
272
+ ) -> 'Optional[datetime]':
273
+ """
274
+ ATLAS policy for rules on SCRATCHDISK
275
+
276
+ :param scope: Scope of the DID.
277
+ :param name: Name of the DID.
278
+ :param rses: List of RSEs.
279
+ :param session: The database session in use.
280
+ """
281
+ policy = rucio.common.policy.get_policy()
282
+ if policy != 'atlas':
283
+ return None
284
+
285
+ # Check if on ATLAS managed space
286
+ if [rse for rse in rses if list_rse_attributes(rse_id=rse['id'], session=session).get('type') in ['LOCALGROUPDISK', 'LOCALGROUPTAPE', 'GROUPDISK', 'GROUPTAPE']]:
287
+ return None
288
+ # Now check the lifetime policy
289
+ try:
290
+ query = select(
291
+ models.DataIdentifier
292
+ ).where(
293
+ models.DataIdentifier.scope == scope,
294
+ models.DataIdentifier.name == name
295
+ )
296
+
297
+ did = session.execute(query).scalar_one()
298
+ except NoResultFound:
299
+ return None
300
+ policy_dict = rucio.common.policy.get_lifetime_policy()
301
+ did_type = 'other'
302
+ if scope.external.startswith('mc'):
303
+ did_type = 'mc'
304
+ elif scope.external.startswith('data'):
305
+ did_type = 'data'
306
+ elif scope.external.startswith('valid'):
307
+ did_type = 'valid'
308
+ else:
309
+ did_type = 'other'
310
+ for policy in policy_dict[did_type]:
311
+ if 'exclude' in policy:
312
+ to_exclude = False
313
+ for key in policy['exclude']:
314
+ meta_key = None
315
+ if key not in ['datatype', 'project', ]:
316
+ if key == 'stream':
317
+ meta_key = 'stream_name'
318
+ elif key == 'tags':
319
+ meta_key = 'version'
320
+ else:
321
+ meta_key = key
322
+ values = policy['exclude'][key]
323
+ for value in values:
324
+ value = value.replace('%', '.*')
325
+ if meta_key and did[meta_key] and value and match(value, did[meta_key]):
326
+ to_exclude = True
327
+ break
328
+ if to_exclude:
329
+ break
330
+ if to_exclude:
331
+ continue
332
+ if 'include' in policy:
333
+ match_policy = True
334
+ for key in policy['include']:
335
+ meta_key = None
336
+ if key not in ['datatype', 'project', ]:
337
+ if key == 'stream':
338
+ meta_key = 'stream_name'
339
+ elif key == 'tags':
340
+ meta_key = 'version'
341
+ else:
342
+ continue
343
+ else:
344
+ meta_key = key
345
+ values = policy['include'][key]
346
+ to_keep = False
347
+ for value in values:
348
+ value = value.replace('%', '.*')
349
+ if meta_key and did[meta_key] and value and match(value, did[meta_key]):
350
+ to_keep = True
351
+ break
352
+ match_policy = match_policy and to_keep
353
+ if not to_keep:
354
+ match_policy = False
355
+ break
356
+ if match_policy:
357
+ if int(policy['age']) >= 12:
358
+ years = int(int(policy['age']) / 12)
359
+ months = int(policy['age']) - years * 12
360
+ lifetime_value = 365 * years + 30 * months
361
+ else:
362
+ lifetime_value = int(policy['age']) * 30
363
+ if int(policy['extension']) >= 12:
364
+ years = int(int(policy['extension']) / 12)
365
+ months = int(policy['extension']) - years * 12
366
+ extension = 365 * years + 30 * months
367
+ else:
368
+ extension = int(policy['extension']) * 30
369
+
370
+ default_eol_at = did.created_at + timedelta(days=lifetime_value)
371
+ eol_at = default_eol_at
372
+ if did.accessed_at:
373
+ eol_at = did.accessed_at + timedelta(days=extension)
374
+ if eol_at < default_eol_at:
375
+ eol_at = default_eol_at
376
+ return eol_at
377
+ return None