rucio 35.7.0__py3-none-any.whl → 37.0.0__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 (266) 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/test_rucio_server.py +12 -6
  48. rucio/common/types.py +132 -52
  49. rucio/common/utils.py +93 -643
  50. rucio/core/account_limit.py +14 -12
  51. rucio/core/authentication.py +2 -2
  52. rucio/core/config.py +23 -42
  53. rucio/core/credential.py +14 -15
  54. rucio/core/did.py +5 -1
  55. rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
  56. rucio/core/did_meta_plugins/filter_engine.py +62 -3
  57. rucio/core/did_meta_plugins/json_meta.py +2 -2
  58. rucio/core/did_meta_plugins/mongo_meta.py +43 -30
  59. rucio/core/did_meta_plugins/postgres_meta.py +75 -39
  60. rucio/core/identity.py +6 -5
  61. rucio/core/importer.py +4 -3
  62. rucio/core/lifetime_exception.py +2 -2
  63. rucio/core/lock.py +8 -7
  64. rucio/core/message.py +6 -0
  65. rucio/core/monitor.py +30 -29
  66. rucio/core/naming_convention.py +2 -2
  67. rucio/core/nongrid_trace.py +2 -2
  68. rucio/core/oidc.py +11 -9
  69. rucio/core/permission/__init__.py +79 -37
  70. rucio/core/permission/generic.py +1 -7
  71. rucio/core/permission/generic_multi_vo.py +1 -7
  72. rucio/core/quarantined_replica.py +4 -3
  73. rucio/core/replica.py +464 -139
  74. rucio/core/replica_sorter.py +55 -59
  75. rucio/core/request.py +34 -32
  76. rucio/core/rse.py +301 -97
  77. rucio/core/rse_counter.py +1 -2
  78. rucio/core/rse_expression_parser.py +7 -7
  79. rucio/core/rse_selector.py +9 -7
  80. rucio/core/rule.py +41 -40
  81. rucio/core/rule_grouping.py +42 -40
  82. rucio/core/scope.py +5 -4
  83. rucio/core/subscription.py +26 -28
  84. rucio/core/topology.py +11 -11
  85. rucio/core/trace.py +2 -2
  86. rucio/core/transfer.py +29 -15
  87. rucio/core/volatile_replica.py +4 -3
  88. rucio/daemons/atropos/atropos.py +1 -1
  89. rucio/daemons/auditor/__init__.py +2 -2
  90. rucio/daemons/auditor/srmdumps.py +6 -6
  91. rucio/daemons/automatix/automatix.py +32 -21
  92. rucio/daemons/badreplicas/necromancer.py +2 -2
  93. rucio/daemons/bb8/nuclei_background_rebalance.py +1 -1
  94. rucio/daemons/bb8/t2_background_rebalance.py +1 -1
  95. rucio/daemons/common.py +15 -25
  96. rucio/daemons/conveyor/finisher.py +2 -2
  97. rucio/daemons/conveyor/poller.py +18 -28
  98. rucio/daemons/conveyor/receiver.py +2 -2
  99. rucio/daemons/conveyor/stager.py +1 -0
  100. rucio/daemons/conveyor/submitter.py +3 -3
  101. rucio/daemons/hermes/hermes.py +91 -30
  102. rucio/daemons/judge/evaluator.py +2 -2
  103. rucio/daemons/oauthmanager/oauthmanager.py +3 -3
  104. rucio/daemons/reaper/dark_reaper.py +7 -3
  105. rucio/daemons/reaper/reaper.py +12 -16
  106. rucio/daemons/rsedecommissioner/config.py +1 -1
  107. rucio/daemons/rsedecommissioner/profiles/generic.py +5 -4
  108. rucio/daemons/rsedecommissioner/profiles/types.py +7 -6
  109. rucio/daemons/rsedecommissioner/rse_decommissioner.py +1 -1
  110. rucio/daemons/storage/consistency/actions.py +8 -6
  111. rucio/daemons/tracer/kronos.py +4 -4
  112. rucio/db/sqla/constants.py +5 -0
  113. rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +4 -4
  114. rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
  115. rucio/db/sqla/models.py +157 -154
  116. rucio/db/sqla/session.py +58 -27
  117. rucio/db/sqla/types.py +2 -2
  118. rucio/db/sqla/util.py +2 -2
  119. rucio/gateway/account.py +18 -12
  120. rucio/gateway/account_limit.py +137 -60
  121. rucio/gateway/authentication.py +18 -12
  122. rucio/gateway/config.py +30 -20
  123. rucio/gateway/credential.py +9 -10
  124. rucio/gateway/did.py +70 -53
  125. rucio/gateway/dirac.py +6 -4
  126. rucio/gateway/exporter.py +3 -2
  127. rucio/gateway/heartbeat.py +6 -4
  128. rucio/gateway/identity.py +36 -51
  129. rucio/gateway/importer.py +3 -2
  130. rucio/gateway/lifetime_exception.py +3 -2
  131. rucio/gateway/meta_conventions.py +17 -6
  132. rucio/gateway/permission.py +4 -1
  133. rucio/gateway/quarantined_replica.py +3 -2
  134. rucio/gateway/replica.py +31 -22
  135. rucio/gateway/request.py +27 -18
  136. rucio/gateway/rse.py +69 -37
  137. rucio/gateway/rule.py +46 -26
  138. rucio/gateway/scope.py +3 -2
  139. rucio/gateway/subscription.py +14 -11
  140. rucio/gateway/vo.py +12 -8
  141. rucio/rse/__init__.py +3 -3
  142. rucio/rse/protocols/bittorrent.py +11 -1
  143. rucio/rse/protocols/cache.py +0 -11
  144. rucio/rse/protocols/dummy.py +0 -11
  145. rucio/rse/protocols/gfal.py +14 -9
  146. rucio/rse/protocols/globus.py +1 -1
  147. rucio/rse/protocols/http_cache.py +1 -1
  148. rucio/rse/protocols/posix.py +2 -2
  149. rucio/rse/protocols/protocol.py +84 -317
  150. rucio/rse/protocols/rclone.py +2 -1
  151. rucio/rse/protocols/rfio.py +10 -1
  152. rucio/rse/protocols/ssh.py +2 -1
  153. rucio/rse/protocols/storm.py +2 -13
  154. rucio/rse/protocols/webdav.py +74 -30
  155. rucio/rse/protocols/xrootd.py +2 -1
  156. rucio/rse/rsemanager.py +170 -53
  157. rucio/rse/translation.py +260 -0
  158. rucio/tests/common.py +23 -13
  159. rucio/tests/common_server.py +26 -9
  160. rucio/transfertool/bittorrent.py +15 -14
  161. rucio/transfertool/bittorrent_driver.py +5 -7
  162. rucio/transfertool/bittorrent_driver_qbittorrent.py +9 -8
  163. rucio/transfertool/fts3.py +20 -16
  164. rucio/transfertool/mock.py +2 -3
  165. rucio/vcsversion.py +4 -4
  166. rucio/version.py +7 -0
  167. rucio/web/rest/flaskapi/v1/accounts.py +17 -3
  168. rucio/web/rest/flaskapi/v1/auth.py +5 -5
  169. rucio/web/rest/flaskapi/v1/credentials.py +3 -2
  170. rucio/web/rest/flaskapi/v1/dids.py +21 -15
  171. rucio/web/rest/flaskapi/v1/identities.py +33 -9
  172. rucio/web/rest/flaskapi/v1/redirect.py +5 -4
  173. rucio/web/rest/flaskapi/v1/replicas.py +12 -8
  174. rucio/web/rest/flaskapi/v1/rses.py +15 -4
  175. rucio/web/rest/flaskapi/v1/traces.py +56 -19
  176. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic.ini.template +1 -1
  177. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
  178. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
  179. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.template +3 -19
  180. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
  181. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/requirements.server.txt +97 -68
  182. rucio-37.0.0.data/scripts/rucio +133 -0
  183. rucio-37.0.0.data/scripts/rucio-admin +97 -0
  184. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-atropos +2 -2
  185. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-auditor +2 -1
  186. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-automatix +2 -2
  187. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-client +17 -10
  188. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-receiver +1 -0
  189. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-kronos +1 -0
  190. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos +2 -2
  191. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos-temporary-expiration +2 -2
  192. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-necromancer +2 -2
  193. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-reaper +6 -6
  194. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-transmogrifier +2 -2
  195. rucio-37.0.0.dist-info/METADATA +92 -0
  196. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/RECORD +237 -243
  197. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/AUTHORS.rst +3 -0
  198. rucio/common/schema/atlas.py +0 -413
  199. rucio/common/schema/belleii.py +0 -408
  200. rucio/common/schema/domatpc.py +0 -401
  201. rucio/common/schema/escape.py +0 -426
  202. rucio/common/schema/icecube.py +0 -406
  203. rucio/core/permission/atlas.py +0 -1348
  204. rucio/core/permission/belleii.py +0 -1077
  205. rucio/core/permission/escape.py +0 -1078
  206. rucio/daemons/c3po/algorithms/__init__.py +0 -13
  207. rucio/daemons/c3po/algorithms/simple.py +0 -134
  208. rucio/daemons/c3po/algorithms/t2_free_space.py +0 -128
  209. rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +0 -130
  210. rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +0 -294
  211. rucio/daemons/c3po/c3po.py +0 -371
  212. rucio/daemons/c3po/collectors/agis.py +0 -108
  213. rucio/daemons/c3po/collectors/free_space.py +0 -81
  214. rucio/daemons/c3po/collectors/jedi_did.py +0 -57
  215. rucio/daemons/c3po/collectors/mock_did.py +0 -51
  216. rucio/daemons/c3po/collectors/network_metrics.py +0 -71
  217. rucio/daemons/c3po/collectors/workload.py +0 -112
  218. rucio/daemons/c3po/utils/__init__.py +0 -13
  219. rucio/daemons/c3po/utils/dataset_cache.py +0 -50
  220. rucio/daemons/c3po/utils/expiring_dataset_cache.py +0 -56
  221. rucio/daemons/c3po/utils/expiring_list.py +0 -62
  222. rucio/daemons/c3po/utils/popularity.py +0 -85
  223. rucio/daemons/c3po/utils/timeseries.py +0 -89
  224. rucio/rse/protocols/gsiftp.py +0 -92
  225. rucio-35.7.0.data/scripts/rucio-c3po +0 -85
  226. rucio-35.7.0.dist-info/METADATA +0 -72
  227. /rucio/{daemons/c3po → cli/bin_legacy}/__init__.py +0 -0
  228. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  229. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
  230. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  231. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  232. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  233. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  234. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  235. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  236. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  237. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/bootstrap.py +0 -0
  238. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  239. {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/reset_database.py +0 -0
  240. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-account +0 -0
  241. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-collection-replica +0 -0
  242. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-rse +0 -0
  243. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-bb8 +0 -0
  244. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-consumer +0 -0
  245. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-finisher +0 -0
  246. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-poller +0 -0
  247. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-preparer +0 -0
  248. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-stager +0 -0
  249. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-submitter +0 -0
  250. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-throttler +0 -0
  251. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dark-reaper +0 -0
  252. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dumper +0 -0
  253. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-follower +0 -0
  254. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-hermes +0 -0
  255. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-cleaner +0 -0
  256. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-evaluator +0 -0
  257. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-injector +0 -0
  258. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-repairer +0 -0
  259. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-oauth-manager +0 -0
  260. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-replica-recoverer +0 -0
  261. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-rse-decommissioner +0 -0
  262. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-storage-consistency-actions +0 -0
  263. {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-undertaker +0 -0
  264. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/WHEEL +0 -0
  265. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/LICENSE +0 -0
  266. {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/top_level.txt +0 -0
@@ -18,10 +18,17 @@ import os
18
18
  import re
19
19
  import subprocess
20
20
  import tempfile
21
+ from typing import TYPE_CHECKING, Any, Optional, Union, cast
21
22
 
22
23
  from rucio.common import dumper
23
24
  from rucio.common.dumper import DUMPS_CACHE_DIR, data_models, error, path_parsing
24
25
 
26
+ if TYPE_CHECKING:
27
+ from argparse import Namespace, _SubParsersAction
28
+ from collections.abc import Callable, Iterable, Iterator
29
+
30
+ from _typeshed import SupportsNext
31
+
25
32
  subcommands = ['consistency', 'consistency-manual']
26
33
 
27
34
 
@@ -32,11 +39,25 @@ class Consistency(data_models.DataModel):
32
39
  )
33
40
 
34
41
  @classmethod
35
- def dump(cls, subcommand, ddm_endpoint, storage_dump, prev_date_fname=None, next_date_fname=None,
36
- prev_date=None, next_date=None, sort_rucio_replica_dumps=True, date=None,
37
- cache_dir=DUMPS_CACHE_DIR):
42
+ def dump(
43
+ cls,
44
+ subcommand: str,
45
+ ddm_endpoint: str,
46
+ storage_dump: str,
47
+ prev_date_fname: Optional[str] = None,
48
+ next_date_fname: Optional[str] = None,
49
+ prev_date: Optional[Union[str, datetime.datetime]] = None,
50
+ next_date: Optional[Union[str, datetime.datetime]] = None,
51
+ sort_rucio_replica_dumps: bool = True,
52
+ date: Optional[datetime.datetime] = None,
53
+ cache_dir: str = DUMPS_CACHE_DIR
54
+ ):
38
55
  logger = logging.getLogger('auditor.consistency')
39
56
  if subcommand == 'consistency':
57
+ if prev_date is None:
58
+ prev_date = 'latest'
59
+ if next_date is None:
60
+ next_date = 'latest'
40
61
  prev_date_fname = data_models.Replica.download(
41
62
  ddm_endpoint, prev_date, cache_dir=cache_dir)
42
63
  next_date_fname = data_models.Replica.download(
@@ -50,7 +71,7 @@ class Consistency(data_models.DataModel):
50
71
 
51
72
  prefix_components = path_parsing.components(dumper.ddmendpoint_url(ddm_endpoint))
52
73
 
53
- def parser(line):
74
+ def parser(line: str) -> str:
54
75
  '''
55
76
  Simple parser for Rucio replica dumps.
56
77
 
@@ -63,7 +84,7 @@ class Consistency(data_models.DataModel):
63
84
 
64
85
  return ','.join((path, status))
65
86
 
66
- def strip_storage_dump(line):
87
+ def strip_storage_dump(line: str) -> str:
67
88
  '''
68
89
  Parser to have consistent paths in storage dumps.
69
90
 
@@ -80,26 +101,26 @@ class Consistency(data_models.DataModel):
80
101
 
81
102
  if sort_rucio_replica_dumps:
82
103
  prev_date_fname_sorted = gnu_sort(
83
- parse_and_filter_file(prev_date_fname, parser=parser, cache_dir=cache_dir),
104
+ parse_and_filter_file(prev_date_fname, parser=parser, cache_dir=cache_dir), # type: ignore
84
105
  delimiter=',',
85
106
  fieldspec='1',
86
107
  cache_dir=cache_dir,
87
108
  )
88
109
 
89
110
  next_date_fname_sorted = gnu_sort(
90
- parse_and_filter_file(next_date_fname, parser=parser, cache_dir=cache_dir),
111
+ parse_and_filter_file(next_date_fname, parser=parser, cache_dir=cache_dir), # type: ignore
91
112
  delimiter=',',
92
113
  fieldspec='1',
93
114
  cache_dir=cache_dir,
94
115
  )
95
116
  else:
96
117
  prev_date_fname_sorted = parse_and_filter_file(
97
- prev_date_fname,
118
+ prev_date_fname, # type: ignore
98
119
  parser=parser,
99
120
  cache_dir=cache_dir,
100
121
  )
101
122
  next_date_fname_sorted = parse_and_filter_file(
102
- next_date_fname,
123
+ next_date_fname, # type: ignore
103
124
  parser=parser,
104
125
  cache_dir=cache_dir,
105
126
  )
@@ -155,7 +176,10 @@ class Consistency(data_models.DataModel):
155
176
  yield cls('DARK', path)
156
177
 
157
178
 
158
- def _try_to_advance(it, default=None):
179
+ def _try_to_advance(
180
+ it: 'SupportsNext[str]',
181
+ default: Optional[str] = None
182
+ ) -> Optional[str]:
159
183
  try:
160
184
  el = next(it)
161
185
  except StopIteration:
@@ -163,21 +187,29 @@ def _try_to_advance(it, default=None):
163
187
  return el.strip()
164
188
 
165
189
 
166
- def min_value(*values):
190
+ def min_value(*values: Optional[str]) -> str:
167
191
  '''
168
192
  Minimum between the input values, ignoring None
169
193
  '''
170
- values = [value for value in values if value is not None]
171
- if len(values) == 0:
194
+ values_without_none = cast('list[str]', [value for value in values if value is not None])
195
+ if len(values_without_none) == 0:
172
196
  raise ValueError("Input contains 0 non-null values.")
173
- return min(values)
197
+ return min(values_without_none)
174
198
 
175
199
 
176
- def split_if_not_none(value, sep=',', fields=2):
200
+ def split_if_not_none(
201
+ value: Optional[str],
202
+ sep: str = ',',
203
+ fields: int = 2
204
+ ) -> Union[str, list]:
177
205
  return value.split(sep) if value is not None else ([None] * fields)
178
206
 
179
207
 
180
- def compare3(it0, it1, it2):
208
+ def compare3(
209
+ it0: 'Iterable[str]',
210
+ it1: 'Iterable[str]',
211
+ it2: 'Iterable[str]'
212
+ ) -> 'Iterator[tuple[str, tuple[bool, bool, bool], tuple[Optional[str], Optional[str]]]]':
181
213
  '''
182
214
  Generator to compare 3 sorted iterables, in each
183
215
  iteration it yields a tuple of the form (current, (bool, bool, bool))
@@ -240,7 +272,14 @@ def compare3(it0, it1, it2):
240
272
  path2, status2 = split_if_not_none(v2)
241
273
 
242
274
 
243
- def parse_and_filter_file(filepath, parser=lambda s: s, filter_=lambda s: s, prefix=None, postfix='parsed', cache_dir=DUMPS_CACHE_DIR):
275
+ def parse_and_filter_file(
276
+ filepath: str,
277
+ parser: 'Callable' = lambda s: s,
278
+ filter_: 'Callable' = lambda s: s,
279
+ prefix: Optional[str] = None,
280
+ postfix: str = 'parsed',
281
+ cache_dir: str = DUMPS_CACHE_DIR
282
+ ) -> str:
244
283
  '''
245
284
  Opens `filepath` as a read-only file, and for each line of the file
246
285
  for which the `filter_` function returns True, it writes a version
@@ -286,7 +325,13 @@ def parse_and_filter_file(filepath, parser=lambda s: s, filter_=lambda s: s, pre
286
325
  return output_path
287
326
 
288
327
 
289
- def gnu_sort(file_path, prefix=None, delimiter=None, fieldspec=None, cache_dir=DUMPS_CACHE_DIR):
328
+ def gnu_sort(
329
+ file_path: str,
330
+ prefix: Optional[str] = None,
331
+ delimiter: Optional[str] = None,
332
+ fieldspec: Optional[str] = None,
333
+ cache_dir: str = DUMPS_CACHE_DIR
334
+ ) -> str:
290
335
  '''
291
336
  Sort the file with path `file_path` using the GNU sort command, the
292
337
  original file is unchanged, the output file is saved with path
@@ -332,7 +377,7 @@ def gnu_sort(file_path, prefix=None, delimiter=None, fieldspec=None, cache_dir=D
332
377
  return sorted_path
333
378
 
334
379
 
335
- def populate_args(argparser):
380
+ def populate_args(argparser: '_SubParsersAction') -> None:
336
381
  # Option to download the rucio replica dumps automatically
337
382
  parser = argparser.add_parser(
338
383
  'consistency',
@@ -383,7 +428,7 @@ def populate_args(argparser):
383
428
  _date_re = re.compile(r'dump_(\d{8})')
384
429
 
385
430
 
386
- def _parse_args_consistency(args):
431
+ def _parse_args_consistency(args: 'Namespace') -> dict[str, datetime.datetime]:
387
432
  args_dict = {}
388
433
 
389
434
  # Filename should contain the date
@@ -392,14 +437,14 @@ def _parse_args_consistency(args):
392
437
  error('The storage dump filename must be of the form '
393
438
  '"dump_YYYYMMDD" where the date correspond to the date '
394
439
  'of the newest files included')
395
-
396
- date_str = date_str.group(1)
397
- if date_str is None:
398
- error('Invalid date {0}'.format(date_str))
399
- try:
400
- args_dict['date'] = date = datetime.datetime.strptime(date_str, '%Y%m%d')
401
- except ValueError:
402
- error('Invalid date {0}'.format(date_str))
440
+ else:
441
+ date_str = date_str.group(1)
442
+ if date_str is None:
443
+ error('Invalid date {0}'.format(date_str))
444
+ try:
445
+ args_dict['date'] = date = datetime.datetime.strptime(date_str, '%Y%m%d')
446
+ except ValueError:
447
+ error('Invalid date {0}'.format(date_str))
403
448
 
404
449
  if not os.path.exists(args.storage_dump):
405
450
  error('File "{0}" does not exist'.format(args.storage_dump))
@@ -426,7 +471,7 @@ def _parse_args_consistency(args):
426
471
  return args_dict
427
472
 
428
473
 
429
- def _parse_args_consistency_manual(args):
474
+ def _parse_args_consistency_manual(args: 'Namespace') -> dict[str, Any]:
430
475
  args_dict = {}
431
476
  args_dict['prev_date_fname'] = args.replicas_before
432
477
  args_dict['next_date_fname'] = args.replicas_after
@@ -438,7 +483,7 @@ def _parse_args_consistency_manual(args):
438
483
  return args_dict
439
484
 
440
485
 
441
- def parse_args(args):
486
+ def parse_args(args: 'Namespace') -> dict[str, Any]:
442
487
  args_dict = {}
443
488
  args_dict['subcommand'] = args.subcommand
444
489
  args_dict['ddm_endpoint'] = args.ddm_endpoint
@@ -23,11 +23,15 @@ import logging
23
23
  import operator
24
24
  import os
25
25
  import re
26
+ from typing import TYPE_CHECKING, Any, Optional, TextIO, Union
26
27
 
27
28
  from tabulate import tabulate
28
29
 
29
30
  from rucio.common.dumper import DUMPS_CACHE_DIR, HTTPDownloadFailed, get_requests_session, http_download_to_file, smart_open, temp_file, to_datetime
30
31
 
32
+ if TYPE_CHECKING:
33
+ from collections.abc import Callable, Iterable, Iterator
34
+
31
35
 
32
36
  class DataModel:
33
37
  """
@@ -35,12 +39,12 @@ class DataModel:
35
39
  """
36
40
 
37
41
  BASE_URL = 'https://rucio-hadoop.cern.ch/'
38
- _FIELD_NAMES = None
42
+ _FIELD_NAMES: Optional[list[str]] = None
39
43
  SCHEMA = []
40
44
  URI = None
41
45
  name = None
42
46
 
43
- def __init__(self, *args):
47
+ def __init__(self, *args) -> None:
44
48
  if len(args) != len(self.SCHEMA):
45
49
  raise TypeError(
46
50
  'Wrong number of parameters (fields) to initialize {0} '
@@ -62,6 +66,7 @@ class DataModel:
62
66
  raise err
63
67
 
64
68
  self.date = None
69
+ self.rse = None
65
70
 
66
71
  @classmethod
67
72
  def get_fieldnames(cls) -> list[str]:
@@ -72,7 +77,7 @@ class DataModel:
72
77
  cls._FIELD_NAMES = [name for name, _ in cls.SCHEMA]
73
78
  return cls._FIELD_NAMES
74
79
 
75
- def pprint(self):
80
+ def pprint(self) -> str:
76
81
  """
77
82
  Pretty print the dump
78
83
  """
@@ -80,14 +85,17 @@ class DataModel:
80
85
  ['{0}: {1}\n'.format(attr, getattr(self, attr)) for attr, _ in self.SCHEMA]
81
86
  )
82
87
 
83
- def __getitem__(self, index):
88
+ def __getitem__(self, index: int) -> Any:
84
89
  """
85
90
  Return the item
86
91
  """
87
92
  return getattr(self, self.SCHEMA[index][0])
88
93
 
89
94
  @classmethod
90
- def csv_header(cls, fields=None):
95
+ def csv_header(
96
+ cls,
97
+ fields: Optional["Iterable[str]"] = None
98
+ ) -> str:
91
99
  """
92
100
  Add the CSV header if necessary
93
101
  """
@@ -95,7 +103,10 @@ class DataModel:
95
103
  fields = (field for field, _ in cls.SCHEMA)
96
104
  return ','.join(fields)
97
105
 
98
- def formated_fields(self, print_fields=None):
106
+ def formated_fields(
107
+ self,
108
+ print_fields: Optional["Iterable[str]"] = None
109
+ ) -> list[str]:
99
110
  """
100
111
  Reformat the fields
101
112
  """
@@ -110,31 +121,54 @@ class DataModel:
110
121
  fields.append(str(field))
111
122
  return fields
112
123
 
113
- def csv(self, fields=None):
124
+ def csv(
125
+ self,
126
+ fields: Optional["Iterable[str]"] = None
127
+ ) -> str:
114
128
  """
115
129
  Generate a CSV line
116
130
  """
117
131
  return ','.join(self.formated_fields(fields))
118
132
 
119
133
  @classmethod
120
- def tabulate_from(cls, iter_, format_='simple', fields=None):
134
+ def tabulate_from(
135
+ cls,
136
+ iter_: "Iterable[DataModel]",
137
+ format_: str = 'simple',
138
+ fields: Optional["Iterable[str]"] = None
139
+ ) -> str:
121
140
  return tabulate(
122
141
  (row.formated_fields(fields) for row in iter_),
123
- (t[0] for t in cls.SCHEMA),
142
+ (t[0] for t in cls.SCHEMA), # type: ignore
124
143
  format_,
125
144
  )
126
145
 
127
146
  @classmethod
128
- def each(cls, file, rse=None, date=None, filter_=None):
147
+ def each(
148
+ cls,
149
+ file: "TextIO",
150
+ rse: Optional[str] = None,
151
+ date: Optional[Union[str, datetime.datetime]] = None,
152
+ filter_: Optional["Callable"] = None
153
+ ) -> "Iterator[DataModel]":
129
154
  if filter_ is None:
130
- filter_ = lambda x: True # NOQA
155
+
156
+ def placeholder_filter(record: "DataModel") -> bool:
157
+ return True
158
+
159
+ filter_ = placeholder_filter
131
160
  for line in file:
132
161
  record = cls.parse_line(line, rse, date)
133
162
  if filter_(record):
134
163
  yield record
135
164
 
136
165
  @classmethod
137
- def parse_line(cls, line, rse=None, date=None):
166
+ def parse_line(
167
+ cls,
168
+ line: str,
169
+ rse: Optional[str] = None,
170
+ date: Optional[Union[str, datetime.datetime]] = None
171
+ ) -> "DataModel":
138
172
  fields = (field.strip() for field in line.split('\t'))
139
173
  instance = cls(*fields)
140
174
  instance.rse = rse
@@ -142,7 +176,12 @@ class DataModel:
142
176
  return instance
143
177
 
144
178
  @classmethod
145
- def download(cls, rse, date='latest', cache_dir=DUMPS_CACHE_DIR):
179
+ def download(
180
+ cls,
181
+ rse: str,
182
+ date: Union[str, datetime.datetime] = 'latest',
183
+ cache_dir: str = DUMPS_CACHE_DIR
184
+ ) -> str:
146
185
  """
147
186
  Downloads the requested dump and returns an open read-only mode file
148
187
  like object.
@@ -191,13 +230,18 @@ class DataModel:
191
230
  return path
192
231
 
193
232
  @classmethod
194
- def dump(cls, rse, date='latest', filter_=None):
233
+ def dump(
234
+ cls,
235
+ rse: str,
236
+ date: Union[str, datetime.datetime] = 'latest',
237
+ filter_: Optional["Callable"] = None
238
+ ) -> "Iterator[DataModel]":
195
239
  filename = cls.download(rse, date)
196
240
 
197
241
  # Should check errors, content size at least
198
242
  file = smart_open(filename)
199
243
 
200
- return cls.each(file, rse, date, filter_)
244
+ return cls.each(file, rse, date, filter_) # type: ignore (file could be None)
201
245
 
202
246
 
203
247
  class Dataset(DataModel):
@@ -226,7 +270,7 @@ class CompleteDataset(DataModel):
226
270
  ('last_access', to_datetime),
227
271
  )
228
272
 
229
- def __init__(self, *args):
273
+ def __init__(self, *args) -> None:
230
274
  logger = logging.getLogger('auditor.data_models')
231
275
  super(CompleteDataset, self).__init__(*args[0:7])
232
276
  if len(args) == 8:
@@ -252,7 +296,7 @@ class Replica(DataModel):
252
296
  ('state', str),
253
297
  )
254
298
 
255
- def __init__(self, *args):
299
+ def __init__(self, *args) -> None:
256
300
  logger = logging.getLogger('auditor.data_models')
257
301
  if len(args) == 8:
258
302
  args = list(args)
@@ -269,7 +313,7 @@ class Replica(DataModel):
269
313
  class Filter:
270
314
  _Condition = collections.namedtuple('_Condition', ('comparator', 'attribute', 'expected'))
271
315
 
272
- def __init__(self, filter_str, record_class):
316
+ def __init__(self, filter_str: str, record_class: DataModel) -> None:
273
317
  '''
274
318
  Filter objects allow to match a DataModel subclass instance against
275
319
  one or more conditions.
@@ -300,7 +344,7 @@ class Filter:
300
344
  expected=parser(expected),
301
345
  ))
302
346
 
303
- def match(self, record):
347
+ def match(self, record: DataModel) -> bool:
304
348
  '''
305
349
  :param record: DataModel subclass instance.
306
350
  :returns: True if record matches all the conditions in this filter,
@@ -17,15 +17,18 @@ import logging
17
17
  logger = logging.getLogger('rucio_dumps')
18
18
 
19
19
 
20
- def prefix(agis_data, endpoint_name):
21
- ddmendpoint_data = list(filter(
22
- lambda d: d['name'] == endpoint_name,
23
- agis_data,
24
- ))[0]
25
- return ddmendpoint_data['endpoint']
20
+ def remove_prefix(prefix: list[str], path: list[str]) -> list[str]:
21
+ """
22
+ Remove the specified prefix from the given path.
26
23
 
24
+ :param prefix: The prefix to be removed from the path.
25
+ :param path: The path from which the prefix should be removed.
26
+
27
+ :return: The path with the prefix removed.
28
+ If the prefix is not found at the start of the path, the original path is returned.
29
+ If the path is a subset of the prefix, an empty list is returned.
30
+ """
27
31
 
28
- def remove_prefix(prefix, path):
29
32
  iprefix = iter(prefix)
30
33
  ipath = iter(path)
31
34
  try:
@@ -59,6 +62,14 @@ def remove_prefix(prefix, path):
59
62
  return rest
60
63
 
61
64
 
62
- def components(path):
65
+ def components(path: str) -> list[str]:
66
+ """
67
+ Extracts and returns the non-empty components of a given path.
68
+
69
+ :param path: input path string to be parsed.
70
+
71
+ :return: list of non-empty components of the path.
72
+ """
73
+
63
74
  components = path.strip().strip('/').split('/')
64
75
  return [component for component in components if component != '']
rucio/common/exception.py CHANGED
@@ -20,6 +20,8 @@
20
20
 
21
21
  """
22
22
 
23
+ from typing import Optional
24
+
23
25
  from rucio.common.constraints import AUTHORIZED_VALUE_TYPES
24
26
 
25
27
 
@@ -93,11 +95,12 @@ class ClientParameterMismatch(RucioException):
93
95
 
94
96
  class ClientProtocolNotSupported(RucioException):
95
97
  """
96
- RucioException
98
+ Client protocol not supported
97
99
  """
98
- def __init__(self, *args):
100
+
101
+ def __init__(self, host: str, protocol: str, protocols_allowed: Optional[list[str]] = None, *args):
99
102
  super(ClientProtocolNotSupported, self).__init__(*args)
100
- self._message = "Client protocol not supported."
103
+ self._message = f"Client protocol '{protocol}' not supported when connecting to host '{host}'.{' Allowed protocols: ' + ', '.join(protocols_allowed) if protocols_allowed else ''}"
101
104
  self.error_code = 6
102
105
 
103
106
 
@@ -1068,14 +1071,14 @@ class PolicyPackageVersionError(PolicyPackageBaseException):
1068
1071
  """
1069
1072
  Policy package is not compatible with this version of Rucio.
1070
1073
  """
1071
- def __init__(self, package: str, rucio_version: str, supported_versions: list[str], *args):
1074
+ def __init__(self, package: str, rucio_version: str, supported_versionset: str, *args):
1072
1075
  super(PolicyPackageVersionError, self).__init__(package, *args)
1073
1076
  self.rucio_version = rucio_version
1074
- self.supported_versions = supported_versions
1077
+ self.supported_versionset = supported_versionset
1075
1078
  self._message = 'Policy package %s is not compatible with this Rucio version.\nRucio version: %s\nVersions supported by the package: %s' % (
1076
1079
  self.package,
1077
1080
  self.rucio_version,
1078
- self.supported_versions
1081
+ self.supported_versionset
1079
1082
  )
1080
1083
  self.error_code = 103
1081
1084
 
@@ -1125,8 +1128,8 @@ class TraceValidationSchemaNotFound(RucioException):
1125
1128
  """
1126
1129
  Trace validation schema not found.
1127
1130
  """
1128
- def __init__(self, *args, **kwargs):
1129
- super(TraceValidationSchemaNotFound, self).__init__(*args, **kwargs)
1131
+ def __init__(self, *args):
1132
+ super(TraceValidationSchemaNotFound, self).__init__(*args)
1130
1133
  self._message = 'Trace validation schema not found.'
1131
1134
  self.error_code = 108
1132
1135
 
@@ -1149,3 +1152,57 @@ class UnsupportedMetadataPlugin(RucioException):
1149
1152
  super(UnsupportedMetadataPlugin, self).__init__(*args)
1150
1153
  self._message = "The requested metadata plugin is not enabled on the server."
1151
1154
  self.error_code = 110
1155
+
1156
+
1157
+ class ChecksumCalculationError(RucioException):
1158
+ """
1159
+ An error occurred while calculating the checksum.
1160
+ """
1161
+ def __init__(
1162
+ self,
1163
+ algorithm_name: str,
1164
+ filepath: str,
1165
+ *args,
1166
+ **kwargs
1167
+ ):
1168
+ super(ChecksumCalculationError, self).__init__(*args, **kwargs)
1169
+ self.algorithm_name = algorithm_name
1170
+ self.filepath = filepath
1171
+ self._message = 'An error occurred while calculating the %s checksum of file %s.' % (self.algorithm_name, self.filepath)
1172
+ self.error_code = 111
1173
+
1174
+
1175
+ class ConfigLoadingError(RucioException):
1176
+ """
1177
+ An error occurred while loading the configuration.
1178
+ """
1179
+ def __init__(
1180
+ self,
1181
+ config_file: str,
1182
+ *args,
1183
+ **kwargs
1184
+ ):
1185
+ super(ConfigLoadingError, self).__init__(*args, **kwargs)
1186
+ self._message = 'Could not load Rucio configuration file. Rucio tried loading the following configuration file:\n\t %s' % (config_file)
1187
+ self.error_code = 112
1188
+
1189
+
1190
+ class ClientProtocolNotFound(RucioException):
1191
+ """
1192
+ Missing protocol in client configuration (e.g. no http/https in url).
1193
+ """
1194
+
1195
+ def __init__(self, host: str, protocols_allowed: Optional[list[str]] = None, *args):
1196
+ super(ClientProtocolNotFound, self).__init__(*args)
1197
+ self._message = f"Client protocol missing when connecting to host '{host}'.{' Allowed protocols: ' + ', '.join(protocols_allowed) if protocols_allowed else ''}"
1198
+ self.error_code = 113
1199
+
1200
+
1201
+ class ConnectionParameterNotFound(RucioException):
1202
+ """
1203
+ Thrown when a required connection parameter is missing.
1204
+ """
1205
+ def __init__(self, param: str, *args):
1206
+ super(ConnectionParameterNotFound, self).__init__(*args)
1207
+ self._message = f"Required connection parameter '{param}' is not provided."
1208
+ self.error_code = 114
rucio/common/extra.py CHANGED
@@ -13,24 +13,19 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import importlib
16
- import warnings
17
- from typing import TYPE_CHECKING
16
+ from typing import TYPE_CHECKING, Optional
18
17
 
19
18
  if TYPE_CHECKING:
20
- from typing import Any
19
+ from collections.abc import Iterable
20
+ from types import ModuleType
21
21
 
22
22
 
23
- def import_extras(module_list: list[str]) -> "dict[str, Any]":
23
+ def import_extras(module_list: 'Iterable[str]') -> dict[str, "Optional[ModuleType]"]:
24
24
  out = dict()
25
25
  for mod in module_list:
26
26
  out[mod] = None
27
27
  try:
28
- with warnings.catch_warnings():
29
- # TODO: remove when https://github.com/paramiko/paramiko/issues/2038 is fixed
30
- warnings.filterwarnings('ignore', 'Blowfish has been deprecated', module='paramiko')
31
- # TODO: deprecated python 2 and 3.6 too ...
32
- warnings.filterwarnings('ignore', 'Python .* is no longer supported', module='paramiko')
33
- out[mod] = importlib.import_module(mod)
28
+ out[mod] = importlib.import_module(mod)
34
29
  except ImportError:
35
30
  pass
36
31
  return out