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.
- rucio/alembicrevision.py +1 -1
- rucio/{daemons/c3po/collectors → cli}/__init__.py +1 -0
- rucio/cli/account.py +216 -0
- rucio-35.7.0.data/scripts/rucio → rucio/cli/bin_legacy/rucio.py +769 -486
- rucio-35.7.0.data/scripts/rucio-admin → rucio/cli/bin_legacy/rucio_admin.py +476 -423
- rucio/cli/command.py +272 -0
- rucio/cli/config.py +72 -0
- rucio/cli/did.py +191 -0
- rucio/cli/download.py +128 -0
- rucio/cli/lifetime_exception.py +33 -0
- rucio/cli/replica.py +162 -0
- rucio/cli/rse.py +293 -0
- rucio/cli/rule.py +158 -0
- rucio/cli/scope.py +40 -0
- rucio/cli/subscription.py +73 -0
- rucio/cli/upload.py +60 -0
- rucio/cli/utils.py +226 -0
- rucio/client/accountclient.py +0 -1
- rucio/client/baseclient.py +33 -24
- rucio/client/client.py +45 -1
- rucio/client/didclient.py +5 -3
- rucio/client/downloadclient.py +6 -8
- rucio/client/replicaclient.py +0 -2
- rucio/client/richclient.py +317 -0
- rucio/client/rseclient.py +4 -4
- rucio/client/uploadclient.py +26 -12
- rucio/common/bittorrent.py +234 -0
- rucio/common/cache.py +66 -29
- rucio/common/checksum.py +168 -0
- rucio/common/client.py +122 -0
- rucio/common/config.py +22 -35
- rucio/common/constants.py +61 -3
- rucio/common/didtype.py +72 -24
- rucio/common/dumper/__init__.py +45 -38
- rucio/common/dumper/consistency.py +75 -30
- rucio/common/dumper/data_models.py +63 -19
- rucio/common/dumper/path_parsing.py +19 -8
- rucio/common/exception.py +65 -8
- rucio/common/extra.py +5 -10
- rucio/common/logging.py +13 -13
- rucio/common/pcache.py +8 -7
- rucio/common/plugins.py +59 -27
- rucio/common/policy.py +12 -3
- rucio/common/schema/__init__.py +84 -34
- rucio/common/schema/generic.py +0 -17
- rucio/common/schema/generic_multi_vo.py +0 -17
- rucio/common/test_rucio_server.py +12 -6
- rucio/common/types.py +132 -52
- rucio/common/utils.py +93 -643
- rucio/core/account_limit.py +14 -12
- rucio/core/authentication.py +2 -2
- rucio/core/config.py +23 -42
- rucio/core/credential.py +14 -15
- rucio/core/did.py +5 -1
- rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
- rucio/core/did_meta_plugins/filter_engine.py +62 -3
- rucio/core/did_meta_plugins/json_meta.py +2 -2
- rucio/core/did_meta_plugins/mongo_meta.py +43 -30
- rucio/core/did_meta_plugins/postgres_meta.py +75 -39
- rucio/core/identity.py +6 -5
- rucio/core/importer.py +4 -3
- rucio/core/lifetime_exception.py +2 -2
- rucio/core/lock.py +8 -7
- rucio/core/message.py +6 -0
- rucio/core/monitor.py +30 -29
- rucio/core/naming_convention.py +2 -2
- rucio/core/nongrid_trace.py +2 -2
- rucio/core/oidc.py +11 -9
- rucio/core/permission/__init__.py +79 -37
- rucio/core/permission/generic.py +1 -7
- rucio/core/permission/generic_multi_vo.py +1 -7
- rucio/core/quarantined_replica.py +4 -3
- rucio/core/replica.py +464 -139
- rucio/core/replica_sorter.py +55 -59
- rucio/core/request.py +34 -32
- rucio/core/rse.py +301 -97
- rucio/core/rse_counter.py +1 -2
- rucio/core/rse_expression_parser.py +7 -7
- rucio/core/rse_selector.py +9 -7
- rucio/core/rule.py +41 -40
- rucio/core/rule_grouping.py +42 -40
- rucio/core/scope.py +5 -4
- rucio/core/subscription.py +26 -28
- rucio/core/topology.py +11 -11
- rucio/core/trace.py +2 -2
- rucio/core/transfer.py +29 -15
- rucio/core/volatile_replica.py +4 -3
- rucio/daemons/atropos/atropos.py +1 -1
- rucio/daemons/auditor/__init__.py +2 -2
- rucio/daemons/auditor/srmdumps.py +6 -6
- rucio/daemons/automatix/automatix.py +32 -21
- rucio/daemons/badreplicas/necromancer.py +2 -2
- rucio/daemons/bb8/nuclei_background_rebalance.py +1 -1
- rucio/daemons/bb8/t2_background_rebalance.py +1 -1
- rucio/daemons/common.py +15 -25
- rucio/daemons/conveyor/finisher.py +2 -2
- rucio/daemons/conveyor/poller.py +18 -28
- rucio/daemons/conveyor/receiver.py +2 -2
- rucio/daemons/conveyor/stager.py +1 -0
- rucio/daemons/conveyor/submitter.py +3 -3
- rucio/daemons/hermes/hermes.py +91 -30
- rucio/daemons/judge/evaluator.py +2 -2
- rucio/daemons/oauthmanager/oauthmanager.py +3 -3
- rucio/daemons/reaper/dark_reaper.py +7 -3
- rucio/daemons/reaper/reaper.py +12 -16
- rucio/daemons/rsedecommissioner/config.py +1 -1
- rucio/daemons/rsedecommissioner/profiles/generic.py +5 -4
- rucio/daemons/rsedecommissioner/profiles/types.py +7 -6
- rucio/daemons/rsedecommissioner/rse_decommissioner.py +1 -1
- rucio/daemons/storage/consistency/actions.py +8 -6
- rucio/daemons/tracer/kronos.py +4 -4
- rucio/db/sqla/constants.py +5 -0
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +4 -4
- rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
- rucio/db/sqla/models.py +157 -154
- rucio/db/sqla/session.py +58 -27
- rucio/db/sqla/types.py +2 -2
- rucio/db/sqla/util.py +2 -2
- rucio/gateway/account.py +18 -12
- rucio/gateway/account_limit.py +137 -60
- rucio/gateway/authentication.py +18 -12
- rucio/gateway/config.py +30 -20
- rucio/gateway/credential.py +9 -10
- rucio/gateway/did.py +70 -53
- rucio/gateway/dirac.py +6 -4
- rucio/gateway/exporter.py +3 -2
- rucio/gateway/heartbeat.py +6 -4
- rucio/gateway/identity.py +36 -51
- rucio/gateway/importer.py +3 -2
- rucio/gateway/lifetime_exception.py +3 -2
- rucio/gateway/meta_conventions.py +17 -6
- rucio/gateway/permission.py +4 -1
- rucio/gateway/quarantined_replica.py +3 -2
- rucio/gateway/replica.py +31 -22
- rucio/gateway/request.py +27 -18
- rucio/gateway/rse.py +69 -37
- rucio/gateway/rule.py +46 -26
- rucio/gateway/scope.py +3 -2
- rucio/gateway/subscription.py +14 -11
- rucio/gateway/vo.py +12 -8
- rucio/rse/__init__.py +3 -3
- rucio/rse/protocols/bittorrent.py +11 -1
- rucio/rse/protocols/cache.py +0 -11
- rucio/rse/protocols/dummy.py +0 -11
- rucio/rse/protocols/gfal.py +14 -9
- rucio/rse/protocols/globus.py +1 -1
- rucio/rse/protocols/http_cache.py +1 -1
- rucio/rse/protocols/posix.py +2 -2
- rucio/rse/protocols/protocol.py +84 -317
- rucio/rse/protocols/rclone.py +2 -1
- rucio/rse/protocols/rfio.py +10 -1
- rucio/rse/protocols/ssh.py +2 -1
- rucio/rse/protocols/storm.py +2 -13
- rucio/rse/protocols/webdav.py +74 -30
- rucio/rse/protocols/xrootd.py +2 -1
- rucio/rse/rsemanager.py +170 -53
- rucio/rse/translation.py +260 -0
- rucio/tests/common.py +23 -13
- rucio/tests/common_server.py +26 -9
- rucio/transfertool/bittorrent.py +15 -14
- rucio/transfertool/bittorrent_driver.py +5 -7
- rucio/transfertool/bittorrent_driver_qbittorrent.py +9 -8
- rucio/transfertool/fts3.py +20 -16
- rucio/transfertool/mock.py +2 -3
- rucio/vcsversion.py +4 -4
- rucio/version.py +7 -0
- rucio/web/rest/flaskapi/v1/accounts.py +17 -3
- rucio/web/rest/flaskapi/v1/auth.py +5 -5
- rucio/web/rest/flaskapi/v1/credentials.py +3 -2
- rucio/web/rest/flaskapi/v1/dids.py +21 -15
- rucio/web/rest/flaskapi/v1/identities.py +33 -9
- rucio/web/rest/flaskapi/v1/redirect.py +5 -4
- rucio/web/rest/flaskapi/v1/replicas.py +12 -8
- rucio/web/rest/flaskapi/v1/rses.py +15 -4
- rucio/web/rest/flaskapi/v1/traces.py +56 -19
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.template +3 -19
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/requirements.server.txt +97 -68
- rucio-37.0.0.data/scripts/rucio +133 -0
- rucio-37.0.0.data/scripts/rucio-admin +97 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-atropos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-auditor +2 -1
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-automatix +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-client +17 -10
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-receiver +1 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-kronos +1 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos-temporary-expiration +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-necromancer +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-reaper +6 -6
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-transmogrifier +2 -2
- rucio-37.0.0.dist-info/METADATA +92 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/RECORD +237 -243
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/AUTHORS.rst +3 -0
- rucio/common/schema/atlas.py +0 -413
- rucio/common/schema/belleii.py +0 -408
- rucio/common/schema/domatpc.py +0 -401
- rucio/common/schema/escape.py +0 -426
- rucio/common/schema/icecube.py +0 -406
- rucio/core/permission/atlas.py +0 -1348
- rucio/core/permission/belleii.py +0 -1077
- rucio/core/permission/escape.py +0 -1078
- rucio/daemons/c3po/algorithms/__init__.py +0 -13
- rucio/daemons/c3po/algorithms/simple.py +0 -134
- rucio/daemons/c3po/algorithms/t2_free_space.py +0 -128
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +0 -130
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +0 -294
- rucio/daemons/c3po/c3po.py +0 -371
- rucio/daemons/c3po/collectors/agis.py +0 -108
- rucio/daemons/c3po/collectors/free_space.py +0 -81
- rucio/daemons/c3po/collectors/jedi_did.py +0 -57
- rucio/daemons/c3po/collectors/mock_did.py +0 -51
- rucio/daemons/c3po/collectors/network_metrics.py +0 -71
- rucio/daemons/c3po/collectors/workload.py +0 -112
- rucio/daemons/c3po/utils/__init__.py +0 -13
- rucio/daemons/c3po/utils/dataset_cache.py +0 -50
- rucio/daemons/c3po/utils/expiring_dataset_cache.py +0 -56
- rucio/daemons/c3po/utils/expiring_list.py +0 -62
- rucio/daemons/c3po/utils/popularity.py +0 -85
- rucio/daemons/c3po/utils/timeseries.py +0 -89
- rucio/rse/protocols/gsiftp.py +0 -92
- rucio-35.7.0.data/scripts/rucio-c3po +0 -85
- rucio-35.7.0.dist-info/METADATA +0 -72
- /rucio/{daemons/c3po → cli/bin_legacy}/__init__.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-account +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-bb8 +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dumper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-follower +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-hermes +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-injector +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-undertaker +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/WHEEL +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/LICENSE +0 -0
- {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(
|
|
36
|
-
|
|
37
|
-
|
|
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(
|
|
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
|
-
|
|
171
|
-
if len(
|
|
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(
|
|
197
|
+
return min(values_without_none)
|
|
174
198
|
|
|
175
199
|
|
|
176
|
-
def split_if_not_none(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
98
|
+
Client protocol not supported
|
|
97
99
|
"""
|
|
98
|
-
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
|
1129
|
-
super(TraceValidationSchemaNotFound, self).__init__(*args
|
|
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
|
|
17
|
-
from typing import TYPE_CHECKING
|
|
16
|
+
from typing import TYPE_CHECKING, Optional
|
|
18
17
|
|
|
19
18
|
if TYPE_CHECKING:
|
|
20
|
-
from
|
|
19
|
+
from collections.abc import Iterable
|
|
20
|
+
from types import ModuleType
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def import_extras(module_list:
|
|
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
|
-
|
|
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
|