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,573 @@
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 ast
17
+ import fnmatch
18
+ import operator
19
+ from datetime import datetime, timedelta, date
20
+ from importlib import import_module
21
+ from typing import TYPE_CHECKING
22
+
23
+ import sqlalchemy
24
+ from sqlalchemy import cast, or_, and_
25
+ from sqlalchemy.sql.expression import text
26
+
27
+ from rucio.common import exception
28
+ from rucio.common.utils import parse_did_filter_from_string_fe
29
+ from rucio.db.sqla.constants import DIDType
30
+ from rucio.db.sqla.session import read_session
31
+
32
+ if TYPE_CHECKING:
33
+ from sqlalchemy.orm import Session
34
+
35
+
36
+ # lookup table converting keyword suffixes to pythonic operators.
37
+ OPERATORS_CONVERSION_LUT = {
38
+ "gte": operator.ge,
39
+ "lte": operator.le,
40
+ "lt": operator.lt,
41
+ "gt": operator.gt,
42
+ "ne": operator.ne,
43
+ "": operator.eq
44
+ }
45
+
46
+ # lookup table converting pythonic operators to oracle operators
47
+ ORACLE_OP_MAP = {
48
+ operator.eq: "==",
49
+ operator.ne: "<>",
50
+ operator.gt: ">",
51
+ operator.lt: "<",
52
+ operator.ge: ">=",
53
+ operator.le: "<="
54
+ }
55
+
56
+ # lookup table converting pythonic operators to postgres operators
57
+ POSTGRES_OP_MAP = {
58
+ operator.eq: "=",
59
+ operator.ne: "!=",
60
+ operator.gt: ">",
61
+ operator.lt: "<",
62
+ operator.ge: ">=",
63
+ operator.le: "<="
64
+ }
65
+
66
+ # understood date formats.
67
+ VALID_DATE_FORMATS = (
68
+ '%Y-%m-%d %H:%M:%S',
69
+ '%Y-%m-%dT%H:%M:%S',
70
+ '%Y-%m-%d %H:%M:%S.%fZ',
71
+ '%Y-%m-%dT%H:%M:%S.%fZ',
72
+ '%a, %d %b %Y %H:%M:%S UTC'
73
+ )
74
+
75
+
76
+ class FilterEngine:
77
+ """
78
+ An engine to provide advanced filtering functionality to DID listing requests.
79
+ """
80
+ def __init__(self, filters, model_class=None, strict_coerce=True):
81
+ if isinstance(filters, str):
82
+ self._filters, _ = parse_did_filter_from_string_fe(filters, omit_name=True)
83
+ elif isinstance(filters, dict):
84
+ self._filters = [filters]
85
+ elif isinstance(filters, list):
86
+ self._filters = filters
87
+ else:
88
+ raise exception.DIDFilterSyntaxError("Input filters are of an unrecognised type.")
89
+
90
+ self._make_input_backwards_compatible()
91
+ self.mandatory_model_attributes = self._translate_filters(model_class=model_class, strict_coerce=strict_coerce)
92
+ self._sanity_check_translated_filters()
93
+
94
+ @property
95
+ def filters(self):
96
+ return self._filters
97
+
98
+ def _coerce_filter_word_to_model_attribute(self, word, model_class, strict=True):
99
+ """
100
+ Attempts to coerce a filter word to an attribute of a <model_class>.
101
+
102
+ :param model_class: The word.
103
+ :param model_class: The SQL model class.
104
+ :params: strict: Enforce that keywords must be coercable to a model attribute.
105
+ :returns: The coerced attribute if successful or (if strict is False) the word if not.
106
+ :raises: KeyNotFound
107
+ """
108
+ if isinstance(word, str):
109
+ if hasattr(model_class, word):
110
+ return getattr(model_class, word)
111
+ else:
112
+ if strict:
113
+ raise exception.KeyNotFound("'{}' keyword could not be coerced to model class attribute. Attribute not found.".format(word))
114
+ return word
115
+
116
+ def _make_input_backwards_compatible(self):
117
+ """
118
+ Backwards compatibility for previous versions of filtering.
119
+
120
+ Does the following:
121
+ - converts "created_after" key to "created_at.gte"
122
+ - converts "created_before" key to "created_at.lte"
123
+ """
124
+ for or_group in self._filters:
125
+ if 'created_after' in or_group:
126
+ or_group['created_at.gte'] = or_group.pop('created_after')
127
+ elif 'created_before' in or_group:
128
+ or_group['created_at.lte'] = or_group.pop('created_before')
129
+
130
+ def _sanity_check_translated_filters(self):
131
+ """
132
+ Perform a few sanity checks on translated filters.
133
+
134
+ Checks the following are all true:
135
+ 1. 'did_type' filters use an equals operator,
136
+ 2. 'name' filters use an equality operator,
137
+ 3. 'length' filters are parsable as an int type,
138
+ 4. wildcard expressions use an equality operator,
139
+ 5. 'created_at' value adheres to one of the date formats <VALID_DATE_FORMATS>,
140
+ 6. there are no duplicate key+operator criteria.
141
+
142
+ :raises: ValueError, DIDFilterSyntaxError, DuplicateCriteriaInDIDFilter
143
+ """
144
+ for or_group in self._filters:
145
+ or_group_test_duplicates = []
146
+ for and_group in or_group:
147
+ key, oper, value = and_group
148
+ if key == 'did_type': # (1)
149
+ if oper != operator.eq:
150
+ raise ValueError("Type operator must be equals.")
151
+ if key == 'name': # (2)
152
+ if oper not in (operator.eq, operator.ne):
153
+ raise ValueError("Name operator must be an equality operator.")
154
+ if key == 'length': # (3)
155
+ try:
156
+ int(value)
157
+ except ValueError:
158
+ raise ValueError('Length has to be an integer value.')
159
+
160
+ if isinstance(value, str): # (4)
161
+ if any([char in value for char in ['*', '%']]):
162
+ if oper not in [operator.eq, operator.ne]:
163
+ raise exception.DIDFilterSyntaxError("Wildcards can only be used with equality operators")
164
+
165
+ if key == 'created_at': # (5)
166
+ if not isinstance(value, datetime):
167
+ raise exception.DIDFilterSyntaxError("Couldn't parse date '{}'. Valid formats are: {}".format(value, VALID_DATE_FORMATS))
168
+
169
+ or_group_test_duplicates.append((key, oper))
170
+ if len(set(or_group_test_duplicates)) != len(or_group_test_duplicates): # (6)
171
+ raise exception.DuplicateCriteriaInDIDFilter()
172
+
173
+ def _translate_filters(self, model_class, strict_coerce=True):
174
+ """
175
+ Reformats filters from:
176
+
177
+ [{or_group_1->key_1.or_group_1->operator_1: or_group_1->value_1,
178
+ {or_group_1->key_m.or_group_1->operator_m: or_group_1->value_m}
179
+ ...
180
+ {or_group_n->key_1.or_group_n->operator_1: or_group_n->value_1,
181
+ {or_group_n->key_m.or_group_n->operator_m: or_group_n->value_m}
182
+ ]
183
+
184
+ to the format used by the engine:
185
+
186
+ [[[or_group_1->key_1, or_group_1->operator_1, or_group_1->value_1],
187
+ ...
188
+ [or_group_1->key_m, or_group_1->operator_m, or_group_1->value_m]
189
+ ],
190
+ ...
191
+ [[or_group_n->key_1, or_group_n->operator_1, or_group_n->value_1],
192
+ ...
193
+ [or_group_n->key_m, or_group_n->operator_m, or_group_n->value_m]
194
+ ]
195
+ ]
196
+
197
+ replacing all filter operator suffixes with python equivalents using the LUT, <OPERATORS_CONVERSION_LUT>, and
198
+ coercing all filter words to their corresponding <model_class> attribute.
199
+
200
+ Typecasting of values is also attempted.
201
+
202
+ :param model_class: The SQL model class.
203
+ :param strict_coerce: Enforce that keywords must be coercable to a model attribute.
204
+ :returns: The set of mandatory model attributes to be used in the filter query.
205
+ :raises: MissingModuleException, DIDFilterSyntaxError
206
+ """
207
+ if model_class:
208
+ try:
209
+ import_module(model_class.__module__)
210
+ except ModuleNotFoundError:
211
+ raise exception.MissingModuleException("Model class module not found.")
212
+
213
+ mandatory_model_attributes = set()
214
+ filters_translated = []
215
+ for or_group in self._filters:
216
+ and_group_parsed = []
217
+ for key, value in or_group.items():
218
+ # KEY
219
+ # Separate key for key name and possible operator.
220
+ key_tokenised = key.split('.')
221
+ if len(key_tokenised) == 1: # no operator suffix found, assume eq
222
+ try:
223
+ key_no_suffix = ast.literal_eval(key)
224
+ except ValueError:
225
+ key_no_suffix = key
226
+ oper = ''
227
+ elif len(key_tokenised) == 2: # operator suffix found
228
+ try:
229
+ key_no_suffix = ast.literal_eval(key_tokenised[0])
230
+ except ValueError:
231
+ key_no_suffix = key_tokenised[0]
232
+ oper = key_tokenised[1]
233
+ else:
234
+ raise exception.DIDFilterSyntaxError
235
+ key_no_suffix = self._coerce_filter_word_to_model_attribute(key_no_suffix, model_class, strict=strict_coerce)
236
+ if not isinstance(key_no_suffix, str):
237
+ mandatory_model_attributes.add(key_no_suffix)
238
+
239
+ # VALUE
240
+ # Typecasting is required when the entry point is the CLI as values will always be string.
241
+ if isinstance(value, str):
242
+ value = self._try_typecast_string(value)
243
+
244
+ # Convert string operator to pythonic operator.
245
+ and_group_parsed.append(
246
+ (key_no_suffix, OPERATORS_CONVERSION_LUT.get(oper), value))
247
+ filters_translated.append(and_group_parsed)
248
+ self._filters = filters_translated
249
+ return list(mandatory_model_attributes)
250
+
251
+ def _try_typecast_string(self, value):
252
+ """
253
+ Check if string can be typecasted to bool, datetime or float.
254
+
255
+ :param value: The value to be typecasted.
256
+ :returns: The typecasted value.
257
+ """
258
+ value = value.replace('true', 'True').replace('TRUE', 'True')
259
+ value = value.replace('false', 'False').replace('FALSE', 'False')
260
+ for format in VALID_DATE_FORMATS: # try parsing multiple date formats.
261
+ try:
262
+ value = datetime.strptime(value, format)
263
+ except ValueError:
264
+ continue
265
+ else:
266
+ return value
267
+ try:
268
+ operators = ('+', '-', '*', '/')
269
+ if not any(operator in value for operator in operators): # fix for lax ast literal_eval in earlier python versions
270
+ value = ast.literal_eval(value) # will catch float, int and bool
271
+ except (ValueError, SyntaxError):
272
+ pass
273
+ return value
274
+
275
+ def create_mongo_query(self, additional_filters={}):
276
+ """
277
+ Returns a single mongo query describing the filters expression.
278
+
279
+ :param additional_filters: additional filters to be applied to all clauses.
280
+ :returns: a mongo query string describing the filters expression.
281
+ """
282
+ # Add additional filters, applied as AND clauses to each OR group.
283
+ for or_group in self._filters:
284
+ for filter in additional_filters:
285
+ or_group.append(list(filter))
286
+
287
+ or_expressions = []
288
+ for or_group in self._filters:
289
+ and_expressions = []
290
+ for and_group in or_group:
291
+ key, oper, value = and_group
292
+ if isinstance(value, str) and any([char in value for char in ['*', '%']]): # wildcards
293
+ if value in ('*', '%', '*', '%'): # match wildcard exactly == no filtering on key
294
+ continue
295
+ else: # partial match with wildcard == like || notlike
296
+ if oper == operator.eq:
297
+ expression = {
298
+ key: {
299
+ '$regex': fnmatch.translate(value) # translate partial wildcard expression to regex
300
+ }
301
+ }
302
+ elif oper == operator.ne:
303
+ expression = {
304
+ key: {
305
+ '$not': {
306
+ '$regex': fnmatch.translate(value) # translate partial wildcard expression to regex
307
+ }
308
+ }
309
+ }
310
+ else:
311
+ # mongodb operator keywords follow the same function names as operator package but prefixed with $
312
+ expression = {
313
+ key: {
314
+ '${}'.format(oper.__name__): value
315
+ }
316
+ }
317
+
318
+ and_expressions.append(expression)
319
+ if len(and_expressions) > 1: # $and key must have array as value...
320
+ or_expressions.append({'$and': and_expressions})
321
+ else:
322
+ or_expressions.append(and_expressions[0]) # ...otherwise just use the first, and only, entry.
323
+ if len(or_expressions) > 1:
324
+ query_str = {'$or': or_expressions} # $or key must have array as value...
325
+ else:
326
+ query_str = or_expressions[0] # ...otherwise just use the first, and only, entry.
327
+
328
+ return query_str
329
+
330
+ def create_postgres_query(self, additional_filters={}, fixed_table_columns=('scope', 'name', 'vo'),
331
+ jsonb_column='data'):
332
+ """
333
+ Returns a single postgres query describing the filters expression.
334
+
335
+ :param additional_filters: additional filters to be applied to all clauses.
336
+ :param fixed_table_columns: the table columns
337
+ :returns: a postgres query string describing the filters expression.
338
+ """
339
+ # Add additional filters, applied as AND clauses to each OR group.
340
+ for or_group in self._filters:
341
+ for _filter in additional_filters:
342
+ or_group.append(list(_filter))
343
+
344
+ or_expressions = []
345
+ for or_group in self._filters:
346
+ and_expressions = []
347
+ for and_group in or_group:
348
+ key, oper, value = and_group
349
+ if key in fixed_table_columns: # is this key filtering on a column or in the jsonb?
350
+ is_in_json_column = False
351
+ else:
352
+ is_in_json_column = True
353
+ if isinstance(value, str) and any([char in value for char in ['*', '%']]): # wildcards
354
+ if value in ('*', '%', '*', '%'): # match wildcard exactly == no filtering on key
355
+ continue
356
+ else: # partial match with wildcard == like || notlike
357
+ if oper == operator.eq:
358
+ if is_in_json_column:
359
+ expression = "{}->>'{}' LIKE '{}' ".format(jsonb_column, key, value.replace('*', '%').replace('_', '\_')) # NOQA: W605
360
+ else:
361
+ expression = "{} LIKE '{}' ".format(key, value.replace('*', '%').replace('_', '\_')) # NOQA: W605
362
+ elif oper == operator.ne:
363
+ if is_in_json_column:
364
+ expression = "{}->>'{}' NOT LIKE '{}' ".format(jsonb_column, key, value.replace('*', '%').replace('_', '\_')) # NOQA: W605
365
+ else:
366
+ expression = "{} NOT LIKE '{}' ".format(key, value.replace('*', '%').replace('_', '\_')) # NOQA: W605
367
+ else:
368
+ # Infer what type key should be cast to from typecasting the value in the expression.
369
+ try:
370
+ if isinstance(value, int): # this could be bool or int (as bool subclass of int)
371
+ if type(value) == bool:
372
+ if is_in_json_column:
373
+ expression = "({}->>'{}')::boolean {} {}".format(jsonb_column, key, POSTGRES_OP_MAP[oper], str(value).lower())
374
+ else:
375
+ expression = "{}::boolean {} {}".format(key, POSTGRES_OP_MAP[oper], str(value).lower())
376
+ else:
377
+ # cast as float, not integer, to avoid potentially losing precision in key
378
+ if is_in_json_column:
379
+ expression = "({}->>'{}')::float {} {}".format(jsonb_column, key, POSTGRES_OP_MAP[oper], value)
380
+ else:
381
+ expression = "{}::float {} {}".format(key, POSTGRES_OP_MAP[oper], value)
382
+ elif isinstance(value, float):
383
+ if is_in_json_column:
384
+ expression = "({}->>'{}')::float {} {}".format(jsonb_column, key, POSTGRES_OP_MAP[oper], value)
385
+ else:
386
+ expression = "{}::float {} {}".format(key, POSTGRES_OP_MAP[oper], value)
387
+ elif isinstance(value, datetime):
388
+ if is_in_json_column:
389
+ expression = "({}->>'{}')::timestamp {} '{}'".format(jsonb_column, key, POSTGRES_OP_MAP[oper], value)
390
+ else:
391
+ expression = "{}::timestamp {} '{}'".format(key, POSTGRES_OP_MAP[oper], value)
392
+ else:
393
+ if is_in_json_column:
394
+ expression = "{}->>'{}' {} '{}'".format(jsonb_column, key, POSTGRES_OP_MAP[oper], value)
395
+ else:
396
+ expression = "{} {} '{}'".format(key, POSTGRES_OP_MAP[oper], value)
397
+ except Exception as e:
398
+ raise exception.FilterEngineGenericError(e)
399
+ and_expressions.append(expression)
400
+ or_expressions.append(' AND '.join(and_expressions))
401
+ return ' OR '.join(or_expressions)
402
+
403
+ @read_session
404
+ def create_sqla_query(self, *, session: "Session", additional_model_attributes=[], additional_filters={}, json_column=None):
405
+ """
406
+ Returns a database query that fully describes the filters.
407
+
408
+ The logic for construction of syntax describing a filter for key is dependent on whether the key has been previously coerced to a model attribute (i.e. key
409
+ is a table column).
410
+
411
+ :param session: The database session.
412
+ :param additional_model_attributes: Additional model attributes to retrieve.
413
+ :param additional_filters: Additional filters to be applied to all clauses.
414
+ :param json_column: Column to be checked if filter key has not been coerced to a model attribute. Only valid if engine instantiated with strict_coerce=False.
415
+ :returns: A database query.
416
+ :raises: FilterEngineGenericError
417
+ """
418
+ all_model_attributes = set(self.mandatory_model_attributes + additional_model_attributes)
419
+
420
+ # Add additional filters, applied as AND clauses to each OR group.
421
+ for or_group in self._filters:
422
+ for _filter in additional_filters:
423
+ or_group.append(list(_filter))
424
+
425
+ or_expressions = []
426
+ for or_group in self._filters:
427
+ and_expressions = []
428
+ for and_group in or_group:
429
+ key, oper, value = and_group
430
+ if isinstance(key, sqlalchemy.orm.attributes.InstrumentedAttribute): # -> this key filters on a table column.
431
+ if isinstance(value, str) and any([char in value for char in ['*', '%']]): # wildcards
432
+ if value in ('*', '%', '*', '%'): # match wildcard exactly == no filtering on key
433
+ continue
434
+ else: # partial match with wildcard == like || notlike
435
+ if oper == operator.eq:
436
+ expression = key.like(value.replace('*', '%').replace('_', '\_'), escape='\\') # NOQA: W605
437
+ elif oper == operator.ne:
438
+ expression = key.notlike(value.replace('*', '%').replace('_', '\_'), escape='\\') # NOQA: W605
439
+ else:
440
+ expression = oper(key, value)
441
+ if oper == operator.ne: # set .ne operator to include NULLs.
442
+ expression = or_(expression, key.is_(None))
443
+ elif json_column: # -> this key filters on the content of a json column
444
+ if session.bind.dialect.name == 'oracle':
445
+ if isinstance(value, str) and any([char in value for char in ['*', '%']]): # wildcards
446
+ if value in ('*', '%', '*', '%'): # match wildcard exactly == no filtering on key
447
+ continue
448
+ else: # partial match with wildcard == like || notlike
449
+ if oper == operator.eq:
450
+ expression = text("json_exists({},'$?(@.{} like \"{}\")')".format(json_column.key, key, value.replace('*', '%')))
451
+ elif oper == operator.ne:
452
+ raise exception.FilterEngineGenericError("Oracle implementation does not support this operator.")
453
+ else:
454
+ try:
455
+ if isinstance(value, (bool)): # bool must be checked first (as bool subclass of int)
456
+ expression = text("json_exists({},'$?(@.{}.boolean() {} \"{}\")')".format(json_column.key, key, ORACLE_OP_MAP[oper], value))
457
+ elif isinstance(value, (int, float)):
458
+ expression = text("json_exists({},'$?(@.{} {} {})')".format(json_column.key, key, ORACLE_OP_MAP[oper], value))
459
+ else:
460
+ expression = text("json_exists({},'$?(@.{} {} \"{}\")')".format(json_column.key, key, ORACLE_OP_MAP[oper], value))
461
+ except Exception as e:
462
+ raise exception.FilterEngineGenericError(e)
463
+ else:
464
+ if isinstance(value, str) and any([char in value for char in ['*', '%']]): # wildcards
465
+ if value in ('*', '%', '*', '%'): # match wildcard exactly == no filtering on key
466
+ continue
467
+ else: # partial match with wildcard == like || notlike
468
+ if oper == operator.eq:
469
+ expression = json_column[key].as_string().like(value.replace('*', '%').replace('_', '\_'), escape='\\') # NOQA: W605
470
+ elif oper == operator.ne:
471
+ expression = json_column[key].as_string().notlike(value.replace('*', '%').replace('_', '\_'), escape='\\') # NOQA: W605
472
+ else:
473
+ # Infer what type key should be cast to from typecasting the value in the expression.
474
+ try:
475
+ if isinstance(value, int): # this could be bool or int (as bool subclass of int)
476
+ if type(value) == bool:
477
+ expression = oper(json_column[key].as_boolean(), value)
478
+ else:
479
+ expression = oper(json_column[key].as_float(), value) # cast as float, not integer, to avoid potentially losing precision in key
480
+ elif isinstance(value, float):
481
+ expression = oper(json_column[key].as_float(), value)
482
+ elif isinstance(value, datetime):
483
+ expression = oper(cast(cast(json_column[key], sqlalchemy.types.Text), sqlalchemy.types.DateTime), value)
484
+ else:
485
+ expression = oper(json_column[key].as_string(), value)
486
+ except Exception as e:
487
+ raise exception.FilterEngineGenericError(e)
488
+ else:
489
+ raise exception.FilterEngineGenericError("Requested filter on key without model attribute, but [json_column] not set.")
490
+
491
+ and_expressions.append(expression)
492
+ or_expressions.append(and_(*and_expressions))
493
+ return session.query(*all_model_attributes).filter(or_(*or_expressions))
494
+
495
+ def evaluate(self):
496
+ """
497
+ Evaluates an expression and returns a boolean result.
498
+
499
+ :returns: boolean output
500
+ """
501
+ or_group_evaluations = []
502
+ for or_group in self._filters:
503
+ and_group_evaluations = []
504
+ for and_group in or_group:
505
+ key, oper, value = and_group
506
+ and_group_evaluations.append(oper(key, value))
507
+ or_group_evaluations.append(all(and_group_evaluations))
508
+ return any(or_group_evaluations)
509
+
510
+ def print_filters(self):
511
+ """
512
+ A (more) human readable format of <filters>.
513
+ """
514
+ operators_conversion_LUT_inv = {op2: op1 for op1, op2 in OPERATORS_CONVERSION_LUT.items()}
515
+
516
+ filters = '\n'
517
+ for or_group in self._filters:
518
+ for and_group in or_group:
519
+ key, oper, value = and_group
520
+ if isinstance(key, sqlalchemy.orm.attributes.InstrumentedAttribute):
521
+ key = and_group[0].key
522
+ if operators_conversion_LUT_inv[oper] == "":
523
+ oper = "eq"
524
+ else:
525
+ oper = operators_conversion_LUT_inv[oper]
526
+ if isinstance(value, sqlalchemy.orm.attributes.InstrumentedAttribute):
527
+ value = and_group[2].key
528
+ elif isinstance(value, DIDType):
529
+ value = and_group[2].name
530
+ filters = "{}{} {} {}".format(filters, key, oper, value)
531
+ if and_group != or_group[-1]:
532
+ filters += ' AND '
533
+ if or_group != self._filters[-1]:
534
+ filters += ' OR\n'
535
+ return filters
536
+
537
+ @staticmethod
538
+ def print_query(statement, dialect=sqlalchemy.dialects.postgresql.dialect()):
539
+ """
540
+ Generates SQL expression from SQLA expression with parameters rendered inline.
541
+
542
+ For debugging ONLY.
543
+
544
+ :param dialect: the sql dialect.
545
+ :returns: The query statement in the chosen dialect.
546
+ """
547
+ if isinstance(statement, sqlalchemy.orm.Query):
548
+ if dialect is None:
549
+ dialect = statement.session.bind.dialect
550
+ statement = statement.statement
551
+ elif dialect is None:
552
+ dialect = statement.bind.dialect
553
+
554
+ class LiteralCompiler(dialect.statement_compiler):
555
+ def visit_bindparam(self, bindparam, within_columns_clause=False,
556
+ literal_binds=False, **kwargs):
557
+ return self.render_literal_value(bindparam.value, bindparam.type)
558
+
559
+ def render_array_value(self, val, item_type):
560
+ if isinstance(val, list):
561
+ return "{%s}" % ",".join([self.render_array_value(x, item_type) for x in val])
562
+ return self.render_literal_value(val, item_type)
563
+
564
+ def render_literal_value(self, value, type_):
565
+ if isinstance(value, int):
566
+ return str(value)
567
+ elif isinstance(value, (str, date, datetime, timedelta)):
568
+ return "'%s'" % str(value).replace("'", "''")
569
+ elif isinstance(value, list):
570
+ return "'{%s}'" % (",".join([self.render_array_value(x, type_.item_type) for x in value]))
571
+ return super(LiteralCompiler, self).render_literal_value(value, type_)
572
+
573
+ return LiteralCompiler(dialect, statement).process(statement)