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.
- 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/stomp_utils.py +383 -119
- 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/cache/consumer.py +26 -90
- 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 +53 -123
- rucio/daemons/conveyor/stager.py +1 -0
- rucio/daemons/conveyor/submitter.py +3 -3
- rucio/daemons/hermes/hermes.py +129 -369
- 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 +117 -142
- 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.0rc2.data}/data/rucio/etc/alembic.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio.cfg.template +3 -19
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/requirements.server.txt +97 -68
- rucio-37.0.0rc2.data/scripts/rucio +133 -0
- rucio-37.0.0rc2.data/scripts/rucio-admin +97 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-atropos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-auditor +2 -1
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-automatix +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-cache-client +17 -10
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-receiver +1 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-kronos +1 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-minos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-minos-temporary-expiration +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-necromancer +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-reaper +6 -6
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-transmogrifier +2 -2
- rucio-37.0.0rc2.dist-info/METADATA +92 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/RECORD +239 -245
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.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.0rc2.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-account +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-bb8 +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-dumper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-follower +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-hermes +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-injector +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-undertaker +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/WHEEL +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/licenses/LICENSE +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/top_level.txt +0 -0
rucio/transfertool/fts3.py
CHANGED
|
@@ -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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
1342
|
-
config_se =
|
|
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
|
-
|
|
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
|
-
|
|
1411
|
-
return
|
|
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:
|
rucio/transfertool/mock.py
CHANGED
|
@@ -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': '
|
|
8
|
-
'branch_nick': '
|
|
9
|
-
'revision_id': '
|
|
10
|
-
'revno':
|
|
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=
|
|
699
|
+
issuer=issuer,
|
|
693
700
|
default=param_get(parameters, 'default', default=False),
|
|
694
|
-
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,
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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=
|
|
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
|
|
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
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
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
|
|
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.'
|
|
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=
|
|
89
|
-
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=
|
|
140
|
-
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=
|
|
191
|
-
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", "
|
|
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', '
|
|
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', '
|
|
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', '
|
|
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
|
-
|
|
664
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
|