rucio 35.7.0__py3-none-any.whl → 37.0.0rc2__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 (268) hide show
  1. rucio/alembicrevision.py +1 -1
  2. rucio/{daemons/c3po/collectors → cli}/__init__.py +1 -0
  3. rucio/cli/account.py +216 -0
  4. rucio-35.7.0.data/scripts/rucio → rucio/cli/bin_legacy/rucio.py +769 -486
  5. rucio-35.7.0.data/scripts/rucio-admin → rucio/cli/bin_legacy/rucio_admin.py +476 -423
  6. rucio/cli/command.py +272 -0
  7. rucio/cli/config.py +72 -0
  8. rucio/cli/did.py +191 -0
  9. rucio/cli/download.py +128 -0
  10. rucio/cli/lifetime_exception.py +33 -0
  11. rucio/cli/replica.py +162 -0
  12. rucio/cli/rse.py +293 -0
  13. rucio/cli/rule.py +158 -0
  14. rucio/cli/scope.py +40 -0
  15. rucio/cli/subscription.py +73 -0
  16. rucio/cli/upload.py +60 -0
  17. rucio/cli/utils.py +226 -0
  18. rucio/client/accountclient.py +0 -1
  19. rucio/client/baseclient.py +33 -24
  20. rucio/client/client.py +45 -1
  21. rucio/client/didclient.py +5 -3
  22. rucio/client/downloadclient.py +6 -8
  23. rucio/client/replicaclient.py +0 -2
  24. rucio/client/richclient.py +317 -0
  25. rucio/client/rseclient.py +4 -4
  26. rucio/client/uploadclient.py +26 -12
  27. rucio/common/bittorrent.py +234 -0
  28. rucio/common/cache.py +66 -29
  29. rucio/common/checksum.py +168 -0
  30. rucio/common/client.py +122 -0
  31. rucio/common/config.py +22 -35
  32. rucio/common/constants.py +61 -3
  33. rucio/common/didtype.py +72 -24
  34. rucio/common/dumper/__init__.py +45 -38
  35. rucio/common/dumper/consistency.py +75 -30
  36. rucio/common/dumper/data_models.py +63 -19
  37. rucio/common/dumper/path_parsing.py +19 -8
  38. rucio/common/exception.py +65 -8
  39. rucio/common/extra.py +5 -10
  40. rucio/common/logging.py +13 -13
  41. rucio/common/pcache.py +8 -7
  42. rucio/common/plugins.py +59 -27
  43. rucio/common/policy.py +12 -3
  44. rucio/common/schema/__init__.py +84 -34
  45. rucio/common/schema/generic.py +0 -17
  46. rucio/common/schema/generic_multi_vo.py +0 -17
  47. rucio/common/stomp_utils.py +383 -119
  48. rucio/common/test_rucio_server.py +12 -6
  49. rucio/common/types.py +132 -52
  50. rucio/common/utils.py +93 -643
  51. rucio/core/account_limit.py +14 -12
  52. rucio/core/authentication.py +2 -2
  53. rucio/core/config.py +23 -42
  54. rucio/core/credential.py +14 -15
  55. rucio/core/did.py +5 -1
  56. rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
  57. rucio/core/did_meta_plugins/filter_engine.py +62 -3
  58. rucio/core/did_meta_plugins/json_meta.py +2 -2
  59. rucio/core/did_meta_plugins/mongo_meta.py +43 -30
  60. rucio/core/did_meta_plugins/postgres_meta.py +75 -39
  61. rucio/core/identity.py +6 -5
  62. rucio/core/importer.py +4 -3
  63. rucio/core/lifetime_exception.py +2 -2
  64. rucio/core/lock.py +8 -7
  65. rucio/core/message.py +6 -0
  66. rucio/core/monitor.py +30 -29
  67. rucio/core/naming_convention.py +2 -2
  68. rucio/core/nongrid_trace.py +2 -2
  69. rucio/core/oidc.py +11 -9
  70. rucio/core/permission/__init__.py +79 -37
  71. rucio/core/permission/generic.py +1 -7
  72. rucio/core/permission/generic_multi_vo.py +1 -7
  73. rucio/core/quarantined_replica.py +4 -3
  74. rucio/core/replica.py +464 -139
  75. rucio/core/replica_sorter.py +55 -59
  76. rucio/core/request.py +34 -32
  77. rucio/core/rse.py +301 -97
  78. rucio/core/rse_counter.py +1 -2
  79. rucio/core/rse_expression_parser.py +7 -7
  80. rucio/core/rse_selector.py +9 -7
  81. rucio/core/rule.py +41 -40
  82. rucio/core/rule_grouping.py +42 -40
  83. rucio/core/scope.py +5 -4
  84. rucio/core/subscription.py +26 -28
  85. rucio/core/topology.py +11 -11
  86. rucio/core/trace.py +2 -2
  87. rucio/core/transfer.py +29 -15
  88. rucio/core/volatile_replica.py +4 -3
  89. rucio/daemons/atropos/atropos.py +1 -1
  90. rucio/daemons/auditor/__init__.py +2 -2
  91. rucio/daemons/auditor/srmdumps.py +6 -6
  92. rucio/daemons/automatix/automatix.py +32 -21
  93. rucio/daemons/badreplicas/necromancer.py +2 -2
  94. rucio/daemons/bb8/nuclei_background_rebalance.py +1 -1
  95. rucio/daemons/bb8/t2_background_rebalance.py +1 -1
  96. rucio/daemons/cache/consumer.py +26 -90
  97. rucio/daemons/common.py +15 -25
  98. rucio/daemons/conveyor/finisher.py +2 -2
  99. rucio/daemons/conveyor/poller.py +18 -28
  100. rucio/daemons/conveyor/receiver.py +53 -123
  101. rucio/daemons/conveyor/stager.py +1 -0
  102. rucio/daemons/conveyor/submitter.py +3 -3
  103. rucio/daemons/hermes/hermes.py +129 -369
  104. rucio/daemons/judge/evaluator.py +2 -2
  105. rucio/daemons/oauthmanager/oauthmanager.py +3 -3
  106. rucio/daemons/reaper/dark_reaper.py +7 -3
  107. rucio/daemons/reaper/reaper.py +12 -16
  108. rucio/daemons/rsedecommissioner/config.py +1 -1
  109. rucio/daemons/rsedecommissioner/profiles/generic.py +5 -4
  110. rucio/daemons/rsedecommissioner/profiles/types.py +7 -6
  111. rucio/daemons/rsedecommissioner/rse_decommissioner.py +1 -1
  112. rucio/daemons/storage/consistency/actions.py +8 -6
  113. rucio/daemons/tracer/kronos.py +117 -142
  114. rucio/db/sqla/constants.py +5 -0
  115. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +4 -4
  116. rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
  117. rucio/db/sqla/models.py +157 -154
  118. rucio/db/sqla/session.py +58 -27
  119. rucio/db/sqla/types.py +2 -2
  120. rucio/db/sqla/util.py +2 -2
  121. rucio/gateway/account.py +18 -12
  122. rucio/gateway/account_limit.py +137 -60
  123. rucio/gateway/authentication.py +18 -12
  124. rucio/gateway/config.py +30 -20
  125. rucio/gateway/credential.py +9 -10
  126. rucio/gateway/did.py +70 -53
  127. rucio/gateway/dirac.py +6 -4
  128. rucio/gateway/exporter.py +3 -2
  129. rucio/gateway/heartbeat.py +6 -4
  130. rucio/gateway/identity.py +36 -51
  131. rucio/gateway/importer.py +3 -2
  132. rucio/gateway/lifetime_exception.py +3 -2
  133. rucio/gateway/meta_conventions.py +17 -6
  134. rucio/gateway/permission.py +4 -1
  135. rucio/gateway/quarantined_replica.py +3 -2
  136. rucio/gateway/replica.py +31 -22
  137. rucio/gateway/request.py +27 -18
  138. rucio/gateway/rse.py +69 -37
  139. rucio/gateway/rule.py +46 -26
  140. rucio/gateway/scope.py +3 -2
  141. rucio/gateway/subscription.py +14 -11
  142. rucio/gateway/vo.py +12 -8
  143. rucio/rse/__init__.py +3 -3
  144. rucio/rse/protocols/bittorrent.py +11 -1
  145. rucio/rse/protocols/cache.py +0 -11
  146. rucio/rse/protocols/dummy.py +0 -11
  147. rucio/rse/protocols/gfal.py +14 -9
  148. rucio/rse/protocols/globus.py +1 -1
  149. rucio/rse/protocols/http_cache.py +1 -1
  150. rucio/rse/protocols/posix.py +2 -2
  151. rucio/rse/protocols/protocol.py +84 -317
  152. rucio/rse/protocols/rclone.py +2 -1
  153. rucio/rse/protocols/rfio.py +10 -1
  154. rucio/rse/protocols/ssh.py +2 -1
  155. rucio/rse/protocols/storm.py +2 -13
  156. rucio/rse/protocols/webdav.py +74 -30
  157. rucio/rse/protocols/xrootd.py +2 -1
  158. rucio/rse/rsemanager.py +170 -53
  159. rucio/rse/translation.py +260 -0
  160. rucio/tests/common.py +23 -13
  161. rucio/tests/common_server.py +26 -9
  162. rucio/transfertool/bittorrent.py +15 -14
  163. rucio/transfertool/bittorrent_driver.py +5 -7
  164. rucio/transfertool/bittorrent_driver_qbittorrent.py +9 -8
  165. rucio/transfertool/fts3.py +20 -16
  166. rucio/transfertool/mock.py +2 -3
  167. rucio/vcsversion.py +4 -4
  168. rucio/version.py +7 -0
  169. rucio/web/rest/flaskapi/v1/accounts.py +17 -3
  170. rucio/web/rest/flaskapi/v1/auth.py +5 -5
  171. rucio/web/rest/flaskapi/v1/credentials.py +3 -2
  172. rucio/web/rest/flaskapi/v1/dids.py +21 -15
  173. rucio/web/rest/flaskapi/v1/identities.py +33 -9
  174. rucio/web/rest/flaskapi/v1/redirect.py +5 -4
  175. rucio/web/rest/flaskapi/v1/replicas.py +12 -8
  176. rucio/web/rest/flaskapi/v1/rses.py +15 -4
  177. rucio/web/rest/flaskapi/v1/traces.py +56 -19
  178. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/alembic.ini.template +1 -1
  179. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
  180. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
  181. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio.cfg.template +3 -19
  182. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
  183. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/requirements.server.txt +97 -68
  184. rucio-37.0.0rc2.data/scripts/rucio +133 -0
  185. rucio-37.0.0rc2.data/scripts/rucio-admin +97 -0
  186. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-atropos +2 -2
  187. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-auditor +2 -1
  188. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-automatix +2 -2
  189. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-cache-client +17 -10
  190. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-receiver +1 -0
  191. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-kronos +1 -0
  192. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-minos +2 -2
  193. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-minos-temporary-expiration +2 -2
  194. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-necromancer +2 -2
  195. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-reaper +6 -6
  196. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-transmogrifier +2 -2
  197. rucio-37.0.0rc2.dist-info/METADATA +92 -0
  198. {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/RECORD +239 -245
  199. {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/licenses/AUTHORS.rst +3 -0
  200. rucio/common/schema/atlas.py +0 -413
  201. rucio/common/schema/belleii.py +0 -408
  202. rucio/common/schema/domatpc.py +0 -401
  203. rucio/common/schema/escape.py +0 -426
  204. rucio/common/schema/icecube.py +0 -406
  205. rucio/core/permission/atlas.py +0 -1348
  206. rucio/core/permission/belleii.py +0 -1077
  207. rucio/core/permission/escape.py +0 -1078
  208. rucio/daemons/c3po/algorithms/__init__.py +0 -13
  209. rucio/daemons/c3po/algorithms/simple.py +0 -134
  210. rucio/daemons/c3po/algorithms/t2_free_space.py +0 -128
  211. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +0 -130
  212. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +0 -294
  213. rucio/daemons/c3po/c3po.py +0 -371
  214. rucio/daemons/c3po/collectors/agis.py +0 -108
  215. rucio/daemons/c3po/collectors/free_space.py +0 -81
  216. rucio/daemons/c3po/collectors/jedi_did.py +0 -57
  217. rucio/daemons/c3po/collectors/mock_did.py +0 -51
  218. rucio/daemons/c3po/collectors/network_metrics.py +0 -71
  219. rucio/daemons/c3po/collectors/workload.py +0 -112
  220. rucio/daemons/c3po/utils/__init__.py +0 -13
  221. rucio/daemons/c3po/utils/dataset_cache.py +0 -50
  222. rucio/daemons/c3po/utils/expiring_dataset_cache.py +0 -56
  223. rucio/daemons/c3po/utils/expiring_list.py +0 -62
  224. rucio/daemons/c3po/utils/popularity.py +0 -85
  225. rucio/daemons/c3po/utils/timeseries.py +0 -89
  226. rucio/rse/protocols/gsiftp.py +0 -92
  227. rucio-35.7.0.data/scripts/rucio-c3po +0 -85
  228. rucio-35.7.0.dist-info/METADATA +0 -72
  229. /rucio/{daemons/c3po → cli/bin_legacy}/__init__.py +0 -0
  230. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/globus-config.yml.template +0 -0
  231. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/ldap.cfg.template +0 -0
  232. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  233. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  234. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  235. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  236. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  237. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  238. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  239. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/bootstrap.py +0 -0
  240. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  241. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/reset_database.py +0 -0
  242. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-account +0 -0
  243. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-collection-replica +0 -0
  244. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-rse +0 -0
  245. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-bb8 +0 -0
  246. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-cache-consumer +0 -0
  247. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-finisher +0 -0
  248. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-poller +0 -0
  249. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-preparer +0 -0
  250. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-stager +0 -0
  251. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-submitter +0 -0
  252. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-throttler +0 -0
  253. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-dark-reaper +0 -0
  254. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-dumper +0 -0
  255. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-follower +0 -0
  256. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-hermes +0 -0
  257. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-cleaner +0 -0
  258. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-evaluator +0 -0
  259. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-injector +0 -0
  260. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-repairer +0 -0
  261. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-oauth-manager +0 -0
  262. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-replica-recoverer +0 -0
  263. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-rse-decommissioner +0 -0
  264. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-storage-consistency-actions +0 -0
  265. {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-undertaker +0 -0
  266. {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/WHEEL +0 -0
  267. {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/licenses/LICENSE +0 -0
  268. {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -28,12 +28,14 @@ from dogpile.cache.api import NoValue
28
28
  from requests.adapters import ReadTimeout
29
29
  from requests.packages.urllib3 import disable_warnings # pylint: disable=import-error
30
30
 
31
- from rucio.common.cache import make_region_memcached
31
+ from rucio.common.cache import MemcacheRegion
32
+ from rucio.common.checksum import PREFERRED_CHECKSUM
32
33
  from rucio.common.config import config_get, config_get_bool, config_get_int, config_get_list
33
34
  from rucio.common.constants import FTS_COMPLETE_STATE, FTS_JOB_TYPE, FTS_STATE, RseAttr
34
35
  from rucio.common.exception import DuplicateFileTransferSubmission, TransferToolTimeout, TransferToolWrongAnswer
36
+ from rucio.common.policy import get_policy
35
37
  from rucio.common.stopwatch import Stopwatch
36
- from rucio.common.utils import PREFERRED_CHECKSUM, APIEncoder, chunks, deep_merge_dict
38
+ from rucio.common.utils import APIEncoder, chunks, deep_merge_dict
37
39
  from rucio.core.monitor import MetricManager
38
40
  from rucio.core.oidc import request_token
39
41
  from rucio.core.request import get_source_rse, get_transfer_error
@@ -54,7 +56,7 @@ if TYPE_CHECKING:
54
56
  logging.getLogger("requests").setLevel(logging.CRITICAL)
55
57
  disable_warnings()
56
58
 
57
- REGION_SHORT = make_region_memcached(expiration_time=900)
59
+ REGION_SHORT = MemcacheRegion(expiration_time=900)
58
60
  METRICS = MetricManager(module=__name__)
59
61
 
60
62
  SUBMISSION_COUNTER = METRICS.counter(name='{host}.submission.{state}',
@@ -109,6 +111,9 @@ _SCITAGS_NEXT_REFRESH = datetime.datetime.utcnow()
109
111
  _SCITAGS_EXP_ID = None
110
112
  _SCITAGS_ACTIVITY_IDS = {}
111
113
 
114
+ FTS_FILE_EXISTS_ERROR_MSG = 'Destination file exists and is on tape' # used in FTS >= 3.12.12
115
+ FTS_FILE_EXISTS_ERROR_MSG_LEGACY = 'Destination file exists and overwrite is not enabled' # Error message used in FTS < 3.12.12, checked in Rucio for backwards compatibility
116
+
112
117
 
113
118
  def _scitags_ids(logger: "LoggerFunction" = logging.log) -> "tuple[int | None, dict[str, int]]":
114
119
  """
@@ -613,9 +618,9 @@ class Fts3TransferStatusReport(TransferStatusReport):
613
618
  def _transfer_link(self) -> str:
614
619
  return '%s/fts3/ftsmon/#/job/%s' % (self._fts_address.replace('8446', '8449'), self._transfer_id)
615
620
 
616
- def _find_attribute_updates(self, request: dict, new_state: RequestState, reason: str, overwrite_corrupted_files: Optional[bool] = None) -> Optional[dict[str, Any]]:
621
+ def _find_attribute_updates(self, request: dict, new_state: RequestState, reason: Optional[str], overwrite_corrupted_files: Optional[bool] = None) -> Optional[dict[str, Any]]:
617
622
  attributes = None
618
- if new_state == RequestState.FAILED and 'Destination file exists and overwrite is not enabled' in (reason or ''):
623
+ if new_state == RequestState.FAILED and (reason and any(s in reason for s in (FTS_FILE_EXISTS_ERROR_MSG, FTS_FILE_EXISTS_ERROR_MSG_LEGACY))):
619
624
  dst_file = self._file_metadata.get('dst_file', {})
620
625
  if self._dst_file_set_and_file_corrupted(request, dst_file):
621
626
  if overwrite_corrupted_files:
@@ -668,7 +673,7 @@ class Fts3TransferStatusReport(TransferStatusReport):
668
673
  return False
669
674
 
670
675
  @classmethod
671
- def _is_recoverable_fts_overwrite_error(cls, request: dict[str, Any], reason: str,
676
+ def _is_recoverable_fts_overwrite_error(cls, request: dict[str, Any], reason: Optional[str],
672
677
  file_metadata: dict[str, Any]) -> bool:
673
678
  """
674
679
  Verify the special case when FTS cannot copy a file because destination exists and overwrite is disabled,
@@ -685,7 +690,7 @@ class Fts3TransferStatusReport(TransferStatusReport):
685
690
  dst_type = file_metadata.get('dst_type', None)
686
691
  METRICS.counter('overwrite.check.{rsetype}.{rse}').labels(rse=file_metadata["dst_rse"], rsetype=dst_type).inc()
687
692
 
688
- if 'Destination file exists and overwrite is not enabled' in (reason or ''):
693
+ if reason and any(s in reason for s in (FTS_FILE_EXISTS_ERROR_MSG, FTS_FILE_EXISTS_ERROR_MSG_LEGACY)):
689
694
  if cls._dst_file_set_and_file_correct(request, dst_file):
690
695
  if dst_type == 'DISK' or dst_file.get('file_on_tape'):
691
696
  METRICS.counter('overwrite.ok.{rsetype}.{rse}').labels(rse=file_metadata["dst_rse"], rsetype=dst_type).inc()
@@ -869,7 +874,6 @@ class FTS3Transfertool(Transfertool):
869
874
 
870
875
  def __init__(self,
871
876
  external_host: str,
872
- oidc_account: Optional[str] = None,
873
877
  oidc_support: bool = False,
874
878
  vo: Optional[str] = None,
875
879
  group_bulk: int = 1,
@@ -1045,6 +1049,9 @@ class FTS3Transfertool(Transfertool):
1045
1049
  for plugin in self.tape_metadata_plugins:
1046
1050
  t_file = deep_merge_dict(source=plugin.hints(t_file['metadata']), destination=t_file)
1047
1051
 
1052
+ if t_file['metadata']['src_type'] == 'TAPE':
1053
+ t_file['staging_metadata'] = {"default": {"activity": rws.activity}}
1054
+
1048
1055
  return t_file
1049
1056
 
1050
1057
  def submit(self, transfers: "Sequence[DirectTransfer]", job_params: dict[str, str], timeout: Optional[int] = None) -> str:
@@ -1338,8 +1345,8 @@ class FTS3Transfertool(Transfertool):
1338
1345
  except Exception:
1339
1346
  self.logger(logging.WARNING, 'Could not get config of %s on %s - %s', storage_element, self.external_host, str(traceback.format_exc()))
1340
1347
  if result and result.status_code == 200:
1341
- C = result.json()
1342
- config_se = C[storage_element]
1348
+ result_json = result.json()
1349
+ config_se = result_json[storage_element]
1343
1350
  return config_se
1344
1351
  raise Exception('Could not get the configuration of %s , status code returned : %s', (storage_element, result.status_code if result else None))
1345
1352
 
@@ -1366,10 +1373,7 @@ class FTS3Transfertool(Transfertool):
1366
1373
 
1367
1374
  params_dict = {storage_element: {'operations': {}, 'se_info': {}}}
1368
1375
  if staging is not None:
1369
- try:
1370
- policy = config_get('policy', 'permission')
1371
- except Exception:
1372
- self.logger(logging.WARNING, 'Could not get policy from config')
1376
+ policy = get_policy()
1373
1377
  params_dict[storage_element]['operations'] = {policy: {'staging': staging}}
1374
1378
  # A lot of try-excepts to avoid dictionary overwrite's,
1375
1379
  # see https://stackoverflow.com/questions/27118687/updating-nested-dictionaries-when-data-has-existing-key/27118776
@@ -1407,8 +1411,8 @@ class FTS3Transfertool(Transfertool):
1407
1411
  except Exception:
1408
1412
  self.logger(logging.WARNING, 'Could not set the config of %s on %s - %s', storage_element, self.external_host, str(traceback.format_exc()))
1409
1413
  if result and result.status_code == 200:
1410
- configSe = result.json()
1411
- return configSe
1414
+ config_se = result.json()
1415
+ return config_se
1412
1416
  raise Exception('Could not set the configuration of %s , status code returned : %s', (storage_element, result.status_code if result else None))
1413
1417
 
1414
1418
  def set_se_status(self, storage_element: str, message: str, ban: bool = True, timeout: Optional[int] = None) -> int:
@@ -15,14 +15,13 @@
15
15
  import itertools
16
16
  import logging
17
17
  import uuid
18
- from collections.abc import Sequence
19
18
  from typing import TYPE_CHECKING, Any, Optional
20
19
 
21
20
  from rucio.db.sqla.constants import RequestState
22
21
  from rucio.transfertool.transfertool import TransferStatusReport, Transfertool, TransferToolBuilder
23
22
 
24
23
  if TYPE_CHECKING:
25
- from collections.abc import Iterable, Mapping
24
+ from collections.abc import Iterable, Mapping, Sequence
26
25
 
27
26
  from rucio.common.types import LoggerFunction
28
27
  from rucio.core.request import DirectTransfer
@@ -83,7 +82,7 @@ class MockTransfertool(Transfertool):
83
82
  response.setdefault(transfer_id, {})[request_id] = MockTransferStatusReport(request_id, transfer_id)
84
83
  return response
85
84
 
86
- def cancel(self, transfer_ids: Sequence[str], timeout: Optional[int] = None) -> bool:
85
+ def cancel(self, transfer_ids: 'Sequence[str]', timeout: Optional[int] = None) -> bool:
87
86
  return True
88
87
 
89
88
  def update_priority(self, transfer_id: str, priority: int, timeout: Optional[int] = None) -> bool:
rucio/vcsversion.py CHANGED
@@ -4,8 +4,8 @@ This file is automatically generated; Do not edit it. :)
4
4
  '''
5
5
  VERSION_INFO = {
6
6
  'final': True,
7
- 'version': '35.7.0',
8
- 'branch_nick': 'release-35-LTS',
9
- 'revision_id': 'c96dca5cf90ca93cb024f30f987ed8c48815a64d',
10
- 'revno': 13202
7
+ 'version': '37.0.0rc2',
8
+ 'branch_nick': 'master',
9
+ 'revision_id': '6fb393d63e8f50733e00ce0b0234380ac12cd607',
10
+ 'revno': 13608
11
11
  }
rucio/version.py CHANGED
@@ -35,4 +35,11 @@ def vcs_version_string() -> str:
35
35
 
36
36
  def version_string_with_vcs() -> str:
37
37
  """ Get the version string with VCS """
38
+
38
39
  return "%s-%s" % (canonical_version_string(), vcs_version_string())
40
+
41
+
42
+ def current_version() -> str:
43
+ """ Get the current version """
44
+ components = 2 if version_string().startswith("1.") else 1
45
+ return ".".join(version_string().split(".")[:components])
@@ -682,6 +682,13 @@ class Identities(ErrorHandlingMethodView):
682
682
  identity = param_get(parameters, 'identity')
683
683
  authtype = param_get(parameters, 'authtype')
684
684
  email = param_get(parameters, 'email')
685
+
686
+ issuer = request.environ.get('issuer')
687
+ vo = request.environ.get('vo')
688
+
689
+ if not issuer or not vo:
690
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
691
+
685
692
  try:
686
693
  add_account_identity(
687
694
  identity_key=identity,
@@ -689,9 +696,9 @@ class Identities(ErrorHandlingMethodView):
689
696
  account=account,
690
697
  email=email,
691
698
  password=param_get(parameters, 'password', default=None),
692
- issuer=request.environ.get('issuer'),
699
+ issuer=issuer,
693
700
  default=param_get(parameters, 'default', default=False),
694
- vo=request.environ.get('vo'),
701
+ vo=vo,
695
702
  )
696
703
  except AccessDenied as error:
697
704
  return generate_http_error_flask(401, error)
@@ -788,8 +795,15 @@ class Identities(ErrorHandlingMethodView):
788
795
  parameters = json_parameters()
789
796
  identity = param_get(parameters, 'identity')
790
797
  authtype = param_get(parameters, 'authtype')
798
+
799
+ issuer = request.environ.get('issuer')
800
+ vo = request.environ.get('vo')
801
+
802
+ if not issuer or not vo:
803
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
804
+
791
805
  try:
792
- del_account_identity(identity, authtype, account, request.environ.get('issuer'), vo=request.environ.get('vo'))
806
+ del_account_identity(identity, authtype, account, issuer, vo=vo)
793
807
  except AccessDenied as error:
794
808
  return generate_http_error_flask(401, error)
795
809
  except (AccountNotFound, IdentityError) as error:
@@ -939,7 +939,7 @@ class GSS(ErrorHandlingMethodView):
939
939
  return '', 200, headers
940
940
 
941
941
 
942
- class x509(ErrorHandlingMethodView):
942
+ class x509(ErrorHandlingMethodView): # noqa: N801
943
943
  """
944
944
  Authenticate a Rucio account temporarily via an x509 certificate.
945
945
  """
@@ -1476,10 +1476,10 @@ class SAML(ErrorHandlingMethodView):
1476
1476
  return '', 200, headers
1477
1477
 
1478
1478
  # Path to the SAML config folder
1479
- SAML_PATH = config_get('saml', 'config_path')
1479
+ saml_path = config_get('saml', 'config_path')
1480
1480
 
1481
1481
  req = prepare_saml_request(request.environ, dict(request.args.items(multi=False)))
1482
- auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH)
1482
+ auth = OneLogin_Saml2_Auth(req, custom_base_path=saml_path)
1483
1483
 
1484
1484
  headers.set('X-Rucio-SAML-Auth-URL', auth.login())
1485
1485
  return '', 200, headers
@@ -1500,9 +1500,9 @@ class SAML(ErrorHandlingMethodView):
1500
1500
  if not EXTRA_MODULES['onelogin']:
1501
1501
  return "SAML not configured on the server side.", 200, [('X-Rucio-Auth-Token', '')]
1502
1502
 
1503
- SAML_PATH = config_get('saml', 'config_path')
1503
+ saml_path = config_get('saml', 'config_path')
1504
1504
  req = prepare_saml_request(request.environ, dict(request.args.items(multi=False)))
1505
- auth = OneLogin_Saml2_Auth(req, custom_base_path=SAML_PATH)
1505
+ auth = OneLogin_Saml2_Auth(req, custom_base_path=saml_path)
1506
1506
 
1507
1507
  auth.process_response()
1508
1508
  errors = auth.get_errors()
@@ -17,6 +17,7 @@ from typing import TYPE_CHECKING
17
17
  from flask import Flask, request
18
18
  from werkzeug.datastructures import Headers
19
19
 
20
+ from rucio.common.constants import RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS, SUPPORTED_SIGN_URL_SERVICES
20
21
  from rucio.common.exception import CannotAuthenticate
21
22
  from rucio.gateway.credential import get_signed_url
22
23
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
@@ -177,10 +178,10 @@ class SignURL(ErrorHandlingMethodView):
177
178
  return generate_http_error_flask(400, ValueError.__name__, 'Parameter "url" not found', headers=headers)
178
179
  url = request.args.get('url')
179
180
 
180
- if service not in ['gcs', 's3', 'swift']:
181
+ if service not in SUPPORTED_SIGN_URL_SERVICES:
181
182
  return generate_http_error_flask(400, ValueError.__name__, 'Parameter "svc" must be either empty(=gcs), gcs, s3 or swift', headers=headers)
182
183
 
183
- if operation not in ['read', 'write', 'delete']:
184
+ if operation not in RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS:
184
185
  return generate_http_error_flask(400, ValueError.__name__, 'Parameter "op" must be either empty(=read), read, write, or delete.', headers=headers)
185
186
 
186
187
  result = get_signed_url(account, appid, ip, rse=rse, service=service, operation=operation, url=url, lifetime=lifetime, vo=vo)
@@ -88,28 +88,29 @@ class Scope(ErrorHandlingMethodView):
88
88
  - name: scope
89
89
  in: path
90
90
  description: The scope.
91
+ required: true
91
92
  schema:
92
93
  type: string
93
94
  style: simple
94
- requestBody:
95
- content:
96
- application/json:
97
- schema:
98
- type: object
99
- properties:
100
- name:
101
- description: The name of the did.
102
- type: string
103
- recursive:
104
- description: If specified, also returns the child ids recursively.
105
- type: boolean
95
+ - name: name
96
+ in: query
97
+ description: The name of the data identifier (did).
98
+ required: false
99
+ schema:
100
+ type: string
101
+ - name: recursive
102
+ in: query
103
+ description: If true, retrieves child identifiers recursively for non-file types.
104
+ required: false
105
+ schema:
106
+ type: boolean
106
107
  responses:
107
108
  200:
108
109
  description: OK
109
110
  content:
110
111
  application/x-json-stream:
111
112
  schema:
112
- description: Line separated dictionary of dids.
113
+ description: Line-separated dictionary of dids.
113
114
  type: array
114
115
  items:
115
116
  type: object
@@ -143,7 +144,7 @@ class Scope(ErrorHandlingMethodView):
143
144
  for did in scope_list(scope=scope, name=name, recursive=recursive, vo=vo):
144
145
  yield render_json(**did) + '\n'
145
146
 
146
- recursive = 'recursive' in request.args
147
+ recursive = request.args.get('recursive', 'false').lower() in ['true', '1']
147
148
 
148
149
  return try_stream(
149
150
  generate(
@@ -1699,6 +1700,10 @@ class BulkMeta(ErrorHandlingMethodView):
1699
1700
  description: Concatenated the metadata of the parent if set to true.
1700
1701
  type: boolean
1701
1702
  default: false
1703
+ plugin:
1704
+ description: The did meta plugin to query or "ALL" for all available plugins
1705
+ type: string
1706
+ default: "JSON"
1702
1707
  responses:
1703
1708
  200:
1704
1709
  description: OK
@@ -1722,10 +1727,11 @@ class BulkMeta(ErrorHandlingMethodView):
1722
1727
  parameters = json_parameters()
1723
1728
  dids = param_get(parameters, 'dids')
1724
1729
  inherit = param_get(parameters, 'inherit', default=False)
1730
+ plugin = param_get(parameters, 'plugin', default='JSON')
1725
1731
 
1726
1732
  try:
1727
1733
  def generate(vo):
1728
- for meta in get_metadata_bulk(dids, inherit=inherit, vo=vo):
1734
+ for meta in get_metadata_bulk(dids, inherit=inherit, plugin=plugin, vo=vo):
1729
1735
  yield render_json(**meta) + '\n'
1730
1736
 
1731
1737
  return try_stream(generate(vo=request.environ.get('vo')))
@@ -16,7 +16,7 @@ from flask import Flask, jsonify, request
16
16
 
17
17
  from rucio.gateway.identity import add_account_identity, add_identity, list_accounts_for_identity
18
18
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
19
- from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, response_headers
19
+ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, response_headers
20
20
 
21
21
 
22
22
  class UserPass(ErrorHandlingMethodView):
@@ -74,8 +74,14 @@ class UserPass(ErrorHandlingMethodView):
74
74
  password = request.headers.get('X-Rucio-Password', default=None)
75
75
  email = request.headers.get('X-Rucio-Email', default=None)
76
76
 
77
- if not username or not password:
78
- return 'Username and Password must be set.', 400
77
+ if not username or not password or not email:
78
+ return generate_http_error_flask(400, ValueError.__name__, 'Username, Email and Password must be set.')
79
+
80
+ issuer = request.environ.get('issuer')
81
+ vo = request.environ.get('vo')
82
+
83
+ if not issuer or not vo:
84
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
79
85
 
80
86
  add_identity(username, 'userpass', email, password)
81
87
 
@@ -85,8 +91,8 @@ class UserPass(ErrorHandlingMethodView):
85
91
  account=account,
86
92
  email=email,
87
93
  password=password,
88
- issuer=request.environ.get('issuer'),
89
- vo=request.environ.get('vo'),
94
+ issuer=issuer,
95
+ vo=vo,
90
96
  )
91
97
 
92
98
  return 'Created', 201
@@ -130,14 +136,23 @@ class X509(ErrorHandlingMethodView):
130
136
  dn = request.environ.get('SSL_CLIENT_S_DN')
131
137
  email = request.headers.get('X-Rucio-Email', default=None)
132
138
 
139
+ if not dn or not email:
140
+ return generate_http_error_flask(400, ValueError.__name__, 'SSL_CLIENT_S_DN and email must be set.')
141
+
142
+ issuer = request.environ.get('issuer')
143
+ vo = request.environ.get('vo')
144
+
145
+ if not issuer or not vo:
146
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
147
+
133
148
  add_identity(dn, 'x509', email=email)
134
149
  add_account_identity(
135
150
  identity_key=dn,
136
151
  id_type='x509',
137
152
  account=account,
138
153
  email=email,
139
- issuer=request.environ.get('issuer'),
140
- vo=request.environ.get('vo'),
154
+ issuer=issuer,
155
+ vo=vo,
141
156
  )
142
157
 
143
158
  return 'Created', 201
@@ -181,14 +196,23 @@ class GSS(ErrorHandlingMethodView):
181
196
  gsscred = request.environ.get('REMOTE_USER')
182
197
  email = request.headers.get('X-Rucio-Email', default=None)
183
198
 
199
+ if not gsscred or not email:
200
+ return generate_http_error_flask(400, ValueError.__name__, 'REMOTE_USER and email must be set.')
201
+
202
+ issuer = request.environ.get('issuer')
203
+ vo = request.environ.get('vo')
204
+
205
+ if not issuer or not vo:
206
+ return generate_http_error_flask(400, ValueError.__name__, 'Issuer and VO must be set.')
207
+
184
208
  add_identity(gsscred, 'gss', email=email)
185
209
  add_account_identity(
186
210
  identity_key=gsscred,
187
211
  id_type='gss',
188
212
  account=account,
189
213
  email=email,
190
- issuer=request.environ.get('issuer'),
191
- vo=request.environ.get('vo'),
214
+ issuer=issuer,
215
+ vo=vo,
192
216
  )
193
217
 
194
218
  return 'Created', 201
@@ -26,6 +26,7 @@ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_acc
26
26
  if TYPE_CHECKING:
27
27
  from typing import Optional
28
28
 
29
+ from rucio.common.types import IPDict, ReplicaDict
29
30
  from rucio.web.rest.flaskapi.v1.types import HeadersType
30
31
 
31
32
 
@@ -116,7 +117,7 @@ class MetaLinkRedirector(ErrorHandlingMethodView):
116
117
  # set the correct client IP
117
118
  client_ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
118
119
 
119
- client_location = {
120
+ client_location: 'IPDict' = {
120
121
  'ip': request.args.get('ip', default=client_ip),
121
122
  'fqdn': request.args.get('fqdn', default=None),
122
123
  'site': request.args.get('site', default=None),
@@ -274,7 +275,7 @@ class HeaderRedirector(ErrorHandlingMethodView):
274
275
  try:
275
276
  client_ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
276
277
 
277
- client_location = {
278
+ client_location: 'IPDict' = {
278
279
  'ip': request.args.get('ip', default=client_ip),
279
280
  'fqdn': request.args.get('fqdn', default=None),
280
281
  'site': request.args.get('site', default=None),
@@ -295,7 +296,7 @@ class HeaderRedirector(ErrorHandlingMethodView):
295
296
  vo = extract_vo(request.headers)
296
297
 
297
298
  replicas = list(
298
- list_replicas(
299
+ list_replicas( # type: ignore (session parameter missing)
299
300
  dids=[{'scope': scope, 'name': name, 'type': 'FILE'}],
300
301
  schemes=schemes,
301
302
  client_location=client_location,
@@ -306,7 +307,7 @@ class HeaderRedirector(ErrorHandlingMethodView):
306
307
  selected_url = None
307
308
  for r in replicas:
308
309
  if r['rses']:
309
- dictreplica = {}
310
+ dictreplica: dict[str, 'ReplicaDict'] = {}
310
311
 
311
312
  if rse:
312
313
  if rse in r['rses'] and r['rses'][rse]:
@@ -15,6 +15,7 @@
15
15
  from datetime import datetime
16
16
  from itertools import chain
17
17
  from json import dumps, loads
18
+ from typing import TYPE_CHECKING
18
19
  from urllib.parse import parse_qs, unquote
19
20
  from xml.sax.saxutils import escape
20
21
 
@@ -63,6 +64,9 @@ from rucio.gateway.replica import (
63
64
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
64
65
  from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, param_get, parse_scope_name, response_headers, try_stream
65
66
 
67
+ if TYPE_CHECKING:
68
+ from rucio.common.types import IPDict
69
+
66
70
 
67
71
  def _sorted_with_priorities(replicas, sorted_pfns, limit=None):
68
72
  """
@@ -175,7 +179,7 @@ class Replicas(ErrorHandlingMethodView):
175
179
  description: The sorting algorithm.
176
180
  schema:
177
181
  type: string
178
- enum: ["geoip", "closeness", "dynamic", "ranking", "random"]
182
+ enum: ["geoip", "random"]
179
183
  - name: limit
180
184
  in: query
181
185
  description: The maximum number of replicas returned.
@@ -232,7 +236,7 @@ class Replicas(ErrorHandlingMethodView):
232
236
  select, limit = None, None
233
237
 
234
238
  client_ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
235
- client_location = {'ip': client_ip, 'fqdn': None, 'site': None}
239
+ client_location: 'IPDict' = {'ip': client_ip, 'fqdn': None, 'site': None}
236
240
 
237
241
  schemes = request.args.get('schemes', default=None)
238
242
  select = request.args.get('select', default=None)
@@ -519,12 +523,12 @@ class ListReplicas(ErrorHandlingMethodView):
519
523
  type: integer
520
524
  - name: select
521
525
  in: query
522
- description: Requested sorting of the result, e.g., 'geoip', 'closeness', 'dynamic', 'ranking', 'random'.
526
+ description: Requested sorting of the result, e.g., 'geoip', 'random'.
523
527
  schema:
524
528
  type: string
525
529
  - name: sort
526
530
  in: query
527
- description: Requested sorting of the result, e.g., 'geoip', 'closeness', 'dynamic', 'ranking', 'random'.
531
+ description: Requested sorting of the result, e.g., 'geoip', 'random'.
528
532
  schema:
529
533
  type: string
530
534
  requestBody:
@@ -554,7 +558,7 @@ class ListReplicas(ErrorHandlingMethodView):
554
558
  items:
555
559
  type: string
556
560
  sort:
557
- description: Requested sorting of the result, e.g., 'geoip', 'closeness', 'dynamic', 'ranking', 'random'.
561
+ description: Requested sorting of the result, e.g., 'geoip', 'random'.
558
562
  type: string
559
563
  unavailable:
560
564
  description: If unavailable rse should be considered.
@@ -659,9 +663,9 @@ class ListReplicas(ErrorHandlingMethodView):
659
663
 
660
664
  parameters = json_parameters(parse_response)
661
665
 
662
- client_location = {'ip': client_ip,
663
- 'fqdn': None,
664
- 'site': None}
666
+ client_location: 'IPDict' = {'ip': client_ip,
667
+ 'fqdn': None,
668
+ 'site': None}
665
669
  client_location.update(param_get(parameters, 'client_location', default={}))
666
670
 
667
671
  # making sure IP address is not overwritten
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  from json import dumps
16
+ from typing import TYPE_CHECKING
16
17
 
17
18
  from flask import Flask, Response, jsonify, request
18
19
 
@@ -66,6 +67,9 @@ from rucio.rse import rsemanager
66
67
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
67
68
  from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, param_get, response_headers, try_stream
68
69
 
70
+ if TYPE_CHECKING:
71
+ from rucio.common.types import LFNDict
72
+
69
73
 
70
74
  class RSEs(ErrorHandlingMethodView):
71
75
  """ List all RSEs in the database. """
@@ -899,7 +903,7 @@ class LFNS2PFNS(ErrorHandlingMethodView):
899
903
  if any(filter(lambda info: len(info) != 2, lfns)):
900
904
  invalid_lfns = ', '.join(filter(lambda info: len(info) != 2, lfns))
901
905
  return generate_http_error_flask(400, InvalidPath.__name__, 'LFN(s) in invalid format: ' + invalid_lfns)
902
- lfns = list(map(lambda info: {'scope': info[0], 'name': info[1]}, lfns))
906
+ lfns_list: list["LFNDict"] = list(map(lambda info: {'scope': info[0], 'name': info[1]}, lfns))
903
907
  scheme = request.args.get('scheme', default=None)
904
908
  domain = request.args.get('domain', default='wan')
905
909
  operation = request.args.get('operation', default='write')
@@ -909,7 +913,7 @@ class LFNS2PFNS(ErrorHandlingMethodView):
909
913
  except (RSENotFound, RSEProtocolNotSupported, RSEProtocolDomainNotSupported) as error:
910
914
  return generate_http_error_flask(404, error)
911
915
 
912
- pfns = rsemanager.lfns2pfns(rse_settings, lfns, operation=operation, scheme=scheme, domain=domain)
916
+ pfns = rsemanager.lfns2pfns(rse_settings, lfns_list, operation=operation, scheme=scheme, domain=domain)
913
917
  if not pfns:
914
918
  return generate_http_error_flask(404, ReplicaNotFound.__name__, 'No replicas found')
915
919
 
@@ -1334,7 +1338,7 @@ class Protocol(ErrorHandlingMethodView):
1334
1338
  vo=request.environ.get('vo'),
1335
1339
  scheme=scheme,
1336
1340
  hostname=hostname,
1337
- port=port,
1341
+ port=int(port) if port else None,
1338
1342
  data=parameters,
1339
1343
  )
1340
1344
  except InvalidObject as error:
@@ -1389,7 +1393,14 @@ class Protocol(ErrorHandlingMethodView):
1389
1393
  description: Rse not found or protocol not supported
1390
1394
  """
1391
1395
  try:
1392
- del_protocols(rse, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'), scheme=scheme, hostname=hostname, port=port)
1396
+ del_protocols(
1397
+ rse,
1398
+ issuer=request.environ.get('issuer'),
1399
+ vo=request.environ.get('vo'),
1400
+ scheme=scheme,
1401
+ hostname=hostname,
1402
+ port=int(port) if port else None
1403
+ )
1393
1404
  except (RSEProtocolNotSupported, RSENotFound) as error:
1394
1405
  return generate_http_error_flask(404, error)
1395
1406