rucio 38.2.0__py3-none-any.whl → 38.4.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/cli/bin_legacy/rucio.py +26 -23
- rucio/cli/command.py +36 -26
- rucio/cli/config.py +22 -7
- rucio/cli/did.py +2 -2
- rucio/cli/download.py +1 -1
- rucio/cli/opendata.py +78 -10
- rucio/cli/utils.py +13 -1
- rucio/client/accountclient.py +20 -19
- rucio/client/accountlimitclient.py +5 -4
- rucio/client/baseclient.py +25 -25
- rucio/client/configclient.py +29 -5
- rucio/client/credentialclient.py +2 -1
- rucio/client/didclient.py +33 -32
- rucio/client/diracclient.py +2 -1
- rucio/client/exportclient.py +2 -1
- rucio/client/importclient.py +2 -1
- rucio/client/lifetimeclient.py +3 -2
- rucio/client/lockclient.py +4 -3
- rucio/client/metaconventionsclient.py +5 -4
- rucio/client/opendataclient.py +8 -7
- rucio/client/pingclient.py +2 -1
- rucio/client/replicaclient.py +27 -26
- rucio/client/requestclient.py +8 -8
- rucio/client/richclient.py +6 -0
- rucio/client/rseclient.py +31 -28
- rucio/client/ruleclient.py +13 -12
- rucio/client/scopeclient.py +4 -3
- rucio/client/subscriptionclient.py +6 -5
- rucio/common/constants.py +23 -0
- rucio/common/exception.py +30 -0
- rucio/common/plugins.py +33 -15
- rucio/common/utils.py +3 -3
- rucio/core/config.py +8 -6
- rucio/core/credential.py +19 -26
- rucio/core/did.py +1 -1
- rucio/core/did_meta_plugins/did_column_meta.py +226 -69
- rucio/core/opendata.py +150 -8
- rucio/core/replica.py +3 -4
- rucio/core/request.py +1 -1
- rucio/core/rule.py +6 -3
- rucio/core/rule_grouping.py +5 -5
- rucio/gateway/account.py +8 -7
- rucio/gateway/config.py +2 -37
- rucio/gateway/opendata.py +2 -2
- rucio/gateway/request.py +2 -117
- rucio/gateway/rule.py +2 -2
- rucio/rse/protocols/webdav.py +5 -2
- rucio/rse/translation.py +3 -3
- rucio/transfertool/fts3.py +0 -19
- rucio/transfertool/fts3_plugins.py +3 -3
- rucio/vcsversion.py +3 -3
- rucio/web/rest/flaskapi/v1/accountlimits.py +4 -3
- rucio/web/rest/flaskapi/v1/accounts.py +26 -25
- rucio/web/rest/flaskapi/v1/archives.py +2 -2
- rucio/web/rest/flaskapi/v1/auth.py +15 -14
- rucio/web/rest/flaskapi/v1/common.py +4 -4
- rucio/web/rest/flaskapi/v1/config.py +57 -17
- rucio/web/rest/flaskapi/v1/credentials.py +3 -3
- rucio/web/rest/flaskapi/v1/dids.py +25 -24
- rucio/web/rest/flaskapi/v1/dirac.py +3 -2
- rucio/web/rest/flaskapi/v1/export.py +4 -2
- rucio/web/rest/flaskapi/v1/heartbeats.py +2 -1
- rucio/web/rest/flaskapi/v1/identities.py +5 -4
- rucio/web/rest/flaskapi/v1/import.py +3 -2
- rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +3 -2
- rucio/web/rest/flaskapi/v1/locks.py +4 -3
- rucio/web/rest/flaskapi/v1/meta_conventions.py +4 -3
- rucio/web/rest/flaskapi/v1/metrics.py +2 -1
- rucio/web/rest/flaskapi/v1/nongrid_traces.py +2 -1
- rucio/web/rest/flaskapi/v1/opendata.py +7 -6
- rucio/web/rest/flaskapi/v1/opendata_public.py +6 -5
- rucio/web/rest/flaskapi/v1/ping.py +3 -2
- rucio/web/rest/flaskapi/v1/redirect.py +4 -3
- rucio/web/rest/flaskapi/v1/replicas.py +31 -31
- rucio/web/rest/flaskapi/v1/requests.py +7 -7
- rucio/web/rest/flaskapi/v1/rses.py +23 -16
- rucio/web/rest/flaskapi/v1/rules.py +9 -8
- rucio/web/rest/flaskapi/v1/scopes.py +4 -3
- rucio/web/rest/flaskapi/v1/subscriptions.py +9 -8
- rucio/web/rest/flaskapi/v1/traces.py +2 -1
- rucio/web/rest/flaskapi/v1/vos.py +4 -3
- {rucio-38.2.0.dist-info → rucio-38.4.0.dist-info}/METADATA +1 -1
- {rucio-38.2.0.dist-info → rucio-38.4.0.dist-info}/RECORD +142 -142
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/alembic.ini.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/requirements.server.txt +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-abacus-account +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-admin +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-atropos +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-auditor +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-automatix +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-bb8 +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-cache-client +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-receiver +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-dumper +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-follower +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-hermes +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-judge-injector +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-kronos +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-minos +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-necromancer +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-reaper +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-transmogrifier +0 -0
- {rucio-38.2.0.data → rucio-38.4.0.data}/scripts/rucio-undertaker +0 -0
- {rucio-38.2.0.dist-info → rucio-38.4.0.dist-info}/WHEEL +0 -0
- {rucio-38.2.0.dist-info → rucio-38.4.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {rucio-38.2.0.dist-info → rucio-38.4.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-38.2.0.dist-info → rucio-38.4.0.dist-info}/top_level.txt +0 -0
rucio/client/ruleclient.py
CHANGED
|
@@ -19,6 +19,7 @@ from urllib.parse import quote_plus
|
|
|
19
19
|
from requests.status_codes import codes
|
|
20
20
|
|
|
21
21
|
from rucio.client.baseclient import BaseClient, choice
|
|
22
|
+
from rucio.common.constants import HTTPMethod
|
|
22
23
|
from rucio.common.utils import build_url
|
|
23
24
|
|
|
24
25
|
if TYPE_CHECKING:
|
|
@@ -110,7 +111,7 @@ class RuleClient(BaseClient):
|
|
|
110
111
|
'activity': activity, 'notify': notify, 'purge_replicas': purge_replicas,
|
|
111
112
|
'ignore_availability': ignore_availability, 'comment': comment, 'ask_approval': ask_approval,
|
|
112
113
|
'asynchronous': asynchronous, 'delay_injection': delay_injection, 'priority': priority, 'meta': meta})
|
|
113
|
-
r = self._send_request(url,
|
|
114
|
+
r = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
114
115
|
if r.status_code == codes.created:
|
|
115
116
|
return loads(r.text)
|
|
116
117
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
@@ -140,7 +141,7 @@ class RuleClient(BaseClient):
|
|
|
140
141
|
|
|
141
142
|
data = dumps({'purge_replicas': purge_replicas})
|
|
142
143
|
|
|
143
|
-
r = self._send_request(url,
|
|
144
|
+
r = self._send_request(url, method=HTTPMethod.DELETE, data=data)
|
|
144
145
|
|
|
145
146
|
if r.status_code == codes.ok:
|
|
146
147
|
return True
|
|
@@ -162,7 +163,7 @@ class RuleClient(BaseClient):
|
|
|
162
163
|
"""
|
|
163
164
|
path = self.RULE_BASEURL + '/' + rule_id
|
|
164
165
|
url = build_url(choice(self.list_hosts), path=path)
|
|
165
|
-
r = self._send_request(url,
|
|
166
|
+
r = self._send_request(url, method=HTTPMethod.GET)
|
|
166
167
|
if r.status_code == codes.ok:
|
|
167
168
|
return next(self._load_json_data(r))
|
|
168
169
|
else:
|
|
@@ -185,7 +186,7 @@ class RuleClient(BaseClient):
|
|
|
185
186
|
path = self.RULE_BASEURL + '/' + rule_id
|
|
186
187
|
url = build_url(choice(self.list_hosts), path=path)
|
|
187
188
|
data = dumps({'options': options})
|
|
188
|
-
r = self._send_request(url,
|
|
189
|
+
r = self._send_request(url, method=HTTPMethod.PUT, data=data)
|
|
189
190
|
if r.status_code == codes.ok:
|
|
190
191
|
return True
|
|
191
192
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
@@ -216,7 +217,7 @@ class RuleClient(BaseClient):
|
|
|
216
217
|
path = self.RULE_BASEURL + '/' + rule_id + '/reduce'
|
|
217
218
|
url = build_url(choice(self.list_hosts), path=path)
|
|
218
219
|
data = dumps({'copies': copies, 'exclude_expression': exclude_expression})
|
|
219
|
-
r = self._send_request(url,
|
|
220
|
+
r = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
220
221
|
if r.status_code == codes.ok:
|
|
221
222
|
return loads(r.text)
|
|
222
223
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
@@ -252,7 +253,7 @@ class RuleClient(BaseClient):
|
|
|
252
253
|
'rse_expression': rse_expression,
|
|
253
254
|
'override': override,
|
|
254
255
|
})
|
|
255
|
-
r = self._send_request(url,
|
|
256
|
+
r = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
256
257
|
if r.status_code == codes.created:
|
|
257
258
|
return loads(r.text)
|
|
258
259
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
@@ -273,7 +274,7 @@ class RuleClient(BaseClient):
|
|
|
273
274
|
path = self.RULE_BASEURL + '/' + rule_id
|
|
274
275
|
url = build_url(choice(self.list_hosts), path=path)
|
|
275
276
|
data = dumps({'options': {'approve': True}})
|
|
276
|
-
r = self._send_request(url,
|
|
277
|
+
r = self._send_request(url, method=HTTPMethod.PUT, data=data)
|
|
277
278
|
if r.status_code == codes.ok:
|
|
278
279
|
return True
|
|
279
280
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
@@ -300,7 +301,7 @@ class RuleClient(BaseClient):
|
|
|
300
301
|
if reason:
|
|
301
302
|
options['comment'] = reason
|
|
302
303
|
data = dumps({'options': options})
|
|
303
|
-
r = self._send_request(url,
|
|
304
|
+
r = self._send_request(url, method=HTTPMethod.PUT, data=data)
|
|
304
305
|
if r.status_code == codes.ok:
|
|
305
306
|
return True
|
|
306
307
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
@@ -321,7 +322,7 @@ class RuleClient(BaseClient):
|
|
|
321
322
|
"""
|
|
322
323
|
path = '/'.join([self.RULE_BASEURL, quote_plus(scope), quote_plus(name), 'history'])
|
|
323
324
|
url = build_url(choice(self.list_hosts), path=path)
|
|
324
|
-
r = self._send_request(url,
|
|
325
|
+
r = self._send_request(url, method=HTTPMethod.GET)
|
|
325
326
|
if r.status_code == codes.ok:
|
|
326
327
|
return self._load_json_data(r)
|
|
327
328
|
exc_cls, exc_msg = self._get_exception(r.headers, r.status_code)
|
|
@@ -341,7 +342,7 @@ class RuleClient(BaseClient):
|
|
|
341
342
|
"""
|
|
342
343
|
path = self.RULE_BASEURL + '/' + rule_id + '/analysis'
|
|
343
344
|
url = build_url(choice(self.list_hosts), path=path)
|
|
344
|
-
r = self._send_request(url,
|
|
345
|
+
r = self._send_request(url, method=HTTPMethod.GET)
|
|
345
346
|
if r.status_code == codes.ok:
|
|
346
347
|
return next(self._load_json_data(r))
|
|
347
348
|
exc_cls, exc_msg = self._get_exception(r.headers, r.status_code)
|
|
@@ -361,7 +362,7 @@ class RuleClient(BaseClient):
|
|
|
361
362
|
"""
|
|
362
363
|
path = self.RULE_BASEURL + '/' + rule_id + '/locks'
|
|
363
364
|
url = build_url(choice(self.list_hosts), path=path)
|
|
364
|
-
r = self._send_request(url,
|
|
365
|
+
r = self._send_request(url, method=HTTPMethod.GET)
|
|
365
366
|
if r.status_code == codes.ok:
|
|
366
367
|
return self._load_json_data(r)
|
|
367
368
|
exc_cls, exc_msg = self._get_exception(r.headers, r.status_code)
|
|
@@ -382,7 +383,7 @@ class RuleClient(BaseClient):
|
|
|
382
383
|
filters = filters or {}
|
|
383
384
|
path = self.RULE_BASEURL + '/'
|
|
384
385
|
url = build_url(choice(self.list_hosts), path=path)
|
|
385
|
-
r = self._send_request(url,
|
|
386
|
+
r = self._send_request(url, method=HTTPMethod.GET, params=filters)
|
|
386
387
|
if r.status_code == codes.ok:
|
|
387
388
|
return self._load_json_data(r)
|
|
388
389
|
else:
|
rucio/client/scopeclient.py
CHANGED
|
@@ -18,6 +18,7 @@ from urllib.parse import quote_plus
|
|
|
18
18
|
from requests.status_codes import codes
|
|
19
19
|
|
|
20
20
|
from rucio.client.baseclient import BaseClient, choice
|
|
21
|
+
from rucio.common.constants import HTTPMethod
|
|
21
22
|
from rucio.common.utils import build_url
|
|
22
23
|
|
|
23
24
|
|
|
@@ -56,7 +57,7 @@ class ScopeClient(BaseClient):
|
|
|
56
57
|
|
|
57
58
|
path = '/'.join([self.SCOPE_BASEURL, account, 'scopes', quote_plus(scope)])
|
|
58
59
|
url = build_url(choice(self.list_hosts), path=path)
|
|
59
|
-
r = self._send_request(url,
|
|
60
|
+
r = self._send_request(url, method=HTTPMethod.POST)
|
|
60
61
|
if r.status_code == codes.created:
|
|
61
62
|
return True
|
|
62
63
|
else:
|
|
@@ -74,7 +75,7 @@ class ScopeClient(BaseClient):
|
|
|
74
75
|
|
|
75
76
|
path = '/'.join(['scopes/'])
|
|
76
77
|
url = build_url(choice(self.list_hosts), path=path)
|
|
77
|
-
r = self._send_request(url)
|
|
78
|
+
r = self._send_request(url, method=HTTPMethod.GET)
|
|
78
79
|
if r.status_code == codes.ok:
|
|
79
80
|
scopes = loads(r.text)
|
|
80
81
|
return scopes
|
|
@@ -106,7 +107,7 @@ class ScopeClient(BaseClient):
|
|
|
106
107
|
path = '/'.join([self.SCOPE_BASEURL, account, 'scopes/'])
|
|
107
108
|
url = build_url(choice(self.list_hosts), path=path)
|
|
108
109
|
|
|
109
|
-
r = self._send_request(url)
|
|
110
|
+
r = self._send_request(url, method=HTTPMethod.GET)
|
|
110
111
|
if r.status_code == codes.ok:
|
|
111
112
|
scopes = loads(r.text)
|
|
112
113
|
return scopes
|
|
@@ -18,6 +18,7 @@ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
|
18
18
|
from requests.status_codes import codes
|
|
19
19
|
|
|
20
20
|
from rucio.client.baseclient import BaseClient, choice
|
|
21
|
+
from rucio.common.constants import HTTPMethod
|
|
21
22
|
from rucio.common.utils import build_url
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
@@ -78,7 +79,7 @@ class SubscriptionClient(BaseClient):
|
|
|
78
79
|
raise TypeError('replication_rules should be a list')
|
|
79
80
|
data = dumps({'options': {'filter': filter_, 'replication_rules': replication_rules, 'comments': comments,
|
|
80
81
|
'lifetime': lifetime, 'retroactive': retroactive, 'dry_run': dry_run, 'priority': priority}})
|
|
81
|
-
result = self._send_request(url,
|
|
82
|
+
result = self._send_request(url, method=HTTPMethod.POST, data=data)
|
|
82
83
|
if result.status_code == codes.created: # pylint: disable=no-member
|
|
83
84
|
return result.text
|
|
84
85
|
else:
|
|
@@ -120,7 +121,7 @@ class SubscriptionClient(BaseClient):
|
|
|
120
121
|
else:
|
|
121
122
|
path += '/'
|
|
122
123
|
url = build_url(choice(self.list_hosts), path=path)
|
|
123
|
-
result = self._send_request(url,
|
|
124
|
+
result = self._send_request(url, method=HTTPMethod.GET)
|
|
124
125
|
if result.status_code == codes.ok: # pylint: disable=no-member
|
|
125
126
|
return self._load_json_data(result)
|
|
126
127
|
if result.status_code == codes.not_found:
|
|
@@ -173,7 +174,7 @@ class SubscriptionClient(BaseClient):
|
|
|
173
174
|
raise TypeError('replication_rules should be a list')
|
|
174
175
|
data = dumps({'options': {'filter': filter_, 'replication_rules': replication_rules, 'comments': comments,
|
|
175
176
|
'lifetime': lifetime, 'retroactive': retroactive, 'dry_run': dry_run, 'priority': priority}})
|
|
176
|
-
result = self._send_request(url,
|
|
177
|
+
result = self._send_request(url, method=HTTPMethod.PUT, data=data)
|
|
177
178
|
if result.status_code == codes.created: # pylint: disable=no-member
|
|
178
179
|
return True
|
|
179
180
|
else:
|
|
@@ -203,7 +204,7 @@ class SubscriptionClient(BaseClient):
|
|
|
203
204
|
path = self.SUB_BASEURL + '/' + account + '/' + name # type: ignore
|
|
204
205
|
url = build_url(choice(self.list_hosts), path=path)
|
|
205
206
|
data = dumps({'options': {'state': 'I'}})
|
|
206
|
-
result = self._send_request(url,
|
|
207
|
+
result = self._send_request(url, method=HTTPMethod.PUT, data=data)
|
|
207
208
|
if result.status_code == codes.created: # pylint: disable=no-member
|
|
208
209
|
return True
|
|
209
210
|
else:
|
|
@@ -228,7 +229,7 @@ class SubscriptionClient(BaseClient):
|
|
|
228
229
|
|
|
229
230
|
path = '/'.join([self.SUB_BASEURL, account, name, 'rules'])
|
|
230
231
|
url = build_url(choice(self.list_hosts), path=path)
|
|
231
|
-
result = self._send_request(url,
|
|
232
|
+
result = self._send_request(url, method=HTTPMethod.GET)
|
|
232
233
|
if result.status_code == codes.ok: # pylint: disable=no-member
|
|
233
234
|
return self._load_json_data(result)
|
|
234
235
|
else:
|
rucio/common/constants.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import enum
|
|
16
|
+
import sys
|
|
16
17
|
from collections import namedtuple
|
|
17
18
|
from typing import Literal, get_args
|
|
18
19
|
|
|
@@ -29,6 +30,8 @@ RESERVED_KEYS = ['scope', 'name', 'account', 'did_type', 'is_open', 'monotonic',
|
|
|
29
30
|
|
|
30
31
|
DEFAULT_VO = 'def'
|
|
31
32
|
|
|
33
|
+
DEFAULT_ACTIVITY = 'User Subscriptions'
|
|
34
|
+
|
|
32
35
|
KEY_TYPES = ['ALL', 'COLLECTION', 'FILE', 'DERIVED']
|
|
33
36
|
# all(container, dataset, file), collection(dataset or container), file, derived(compute from file for collection)
|
|
34
37
|
|
|
@@ -219,3 +222,23 @@ SUPPORTED_SIGN_URL_SERVICES = list(get_args(SUPPORTED_SIGN_URL_SERVICES_LITERAL)
|
|
|
219
222
|
|
|
220
223
|
OPENDATA_DID_STATE_LITERAL = Literal['draft', 'public', 'suspended']
|
|
221
224
|
OPENDATA_DID_STATE_LITERAL_LIST = list(get_args(OPENDATA_DID_STATE_LITERAL))
|
|
225
|
+
|
|
226
|
+
POLICY_ALGORITHM_TYPES_LITERAL = Literal['non_deterministic_pfn', 'scope', 'lfn2pfn', 'pfn2lfn', 'fts3_tape_metadata_plugins', 'fts3_plugins_init', 'auto_approve']
|
|
227
|
+
POLICY_ALGORITHM_TYPES = list(get_args(POLICY_ALGORITHM_TYPES_LITERAL))
|
|
228
|
+
|
|
229
|
+
# https://github.com/rucio/rucio/issues/7958
|
|
230
|
+
# When Python 3.11 is the minimum supported version, we can use the standard library enum and remove this logic
|
|
231
|
+
if sys.version_info >= (3, 11):
|
|
232
|
+
from http import HTTPMethod
|
|
233
|
+
else:
|
|
234
|
+
@enum.unique
|
|
235
|
+
class HTTPMethod(str, enum.Enum):
|
|
236
|
+
"""HTTP verbs used in Rucio requests."""
|
|
237
|
+
|
|
238
|
+
HEAD = "HEAD"
|
|
239
|
+
OPTIONS = "OPTIONS"
|
|
240
|
+
PATCH = "PATCH"
|
|
241
|
+
GET = "GET"
|
|
242
|
+
POST = "POST"
|
|
243
|
+
PUT = "PUT"
|
|
244
|
+
DELETE = "DELETE"
|
rucio/common/exception.py
CHANGED
|
@@ -1261,3 +1261,33 @@ class OpenDataInvalidStateUpdate(OpenDataError):
|
|
|
1261
1261
|
super(OpenDataInvalidStateUpdate, self).__init__(*args)
|
|
1262
1262
|
self._message = "Invalid state update attempted on open data entry."
|
|
1263
1263
|
self.error_code = 119
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
class InvalidPolicyPackageAlgorithmType(RucioException):
|
|
1267
|
+
"""
|
|
1268
|
+
Thrown when an unknown algorithm type name is encountered.
|
|
1269
|
+
"""
|
|
1270
|
+
def __init__(self, param: str, *args):
|
|
1271
|
+
super(InvalidPolicyPackageAlgorithmType, self).__init__(*args)
|
|
1272
|
+
self._message = f"Invalid policy package algorithm type '{param}'."
|
|
1273
|
+
self.error_code = 120
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
class InvalidAccountType(RucioException):
|
|
1277
|
+
"""
|
|
1278
|
+
Thrown when an account is created with an invalid type
|
|
1279
|
+
"""
|
|
1280
|
+
def __init__(self, *args):
|
|
1281
|
+
super(InvalidAccountType, self).__init__(*args)
|
|
1282
|
+
self._message = "Cannot create an account with an invalid type."
|
|
1283
|
+
self.error_code = 121
|
|
1284
|
+
|
|
1285
|
+
class OpenDataDuplicateDOI(OpenDataError):
|
|
1286
|
+
"""
|
|
1287
|
+
Throws when a data identifier with the same DOI already exists in the open data catalog.
|
|
1288
|
+
"""
|
|
1289
|
+
|
|
1290
|
+
def __init__(self, doi: str, *args):
|
|
1291
|
+
super(OpenDataDuplicateDOI, self).__init__(*args)
|
|
1292
|
+
self._message = f"Data identifier with the same DOI ({doi}) already exists in the open data catalog."
|
|
1293
|
+
self.error_code = 122
|
rucio/common/plugins.py
CHANGED
|
@@ -22,8 +22,8 @@ from packaging.specifiers import SpecifierSet
|
|
|
22
22
|
|
|
23
23
|
from rucio.common import config
|
|
24
24
|
from rucio.common.client import get_client_vo
|
|
25
|
-
from rucio.common.constants import DEFAULT_VO
|
|
26
|
-
from rucio.common.exception import InvalidAlgorithmName, PolicyPackageIsNotVersioned, PolicyPackageVersionError
|
|
25
|
+
from rucio.common.constants import DEFAULT_VO, POLICY_ALGORITHM_TYPES, POLICY_ALGORITHM_TYPES_LITERAL
|
|
26
|
+
from rucio.common.exception import InvalidAlgorithmName, InvalidPolicyPackageAlgorithmType, PolicyPackageIsNotVersioned, PolicyPackageVersionError
|
|
27
27
|
from rucio.version import current_version
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
@@ -75,9 +75,9 @@ class PolicyPackageAlgorithms:
|
|
|
75
75
|
- the key is the algorithm type
|
|
76
76
|
- the value is a dictionary of algorithm names and their callables
|
|
77
77
|
"""
|
|
78
|
-
_ALGORITHMS: dict[
|
|
78
|
+
_ALGORITHMS: dict[POLICY_ALGORITHM_TYPES_LITERAL, dict[str, 'Callable[..., Any]']] = {}
|
|
79
79
|
_loaded_policy_modules = False
|
|
80
|
-
_default_algorithms: dict[str, 'Callable[..., Any]'] = {}
|
|
80
|
+
_default_algorithms: dict[str, Optional['Callable[..., Any]']] = {}
|
|
81
81
|
|
|
82
82
|
def __init__(self) -> None:
|
|
83
83
|
if not self._loaded_policy_modules:
|
|
@@ -85,12 +85,15 @@ class PolicyPackageAlgorithms:
|
|
|
85
85
|
self._loaded_policy_modules = True
|
|
86
86
|
|
|
87
87
|
@classmethod
|
|
88
|
-
def _get_default_algorithm(cls: type[PolicyPackageAlgorithmsT], algorithm_type:
|
|
88
|
+
def _get_default_algorithm(cls: type[PolicyPackageAlgorithmsT], algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL, vo: str = "") -> Optional['Callable[..., Any]']:
|
|
89
89
|
"""
|
|
90
90
|
Gets the default algorithm of this type, if present in the policy package.
|
|
91
91
|
The default algorithm is the function named algorithm_type within the module named algorithm_type.
|
|
92
92
|
Returns None if no default algorithm present.
|
|
93
93
|
"""
|
|
94
|
+
if algorithm_type not in POLICY_ALGORITHM_TYPES:
|
|
95
|
+
raise InvalidPolicyPackageAlgorithmType(algorithm_type)
|
|
96
|
+
|
|
94
97
|
# check if default algorithm for this VO is already cached
|
|
95
98
|
type_for_vo = vo + "_" + algorithm_type
|
|
96
99
|
if type_for_vo in cls._default_algorithms:
|
|
@@ -102,52 +105,71 @@ class PolicyPackageAlgorithms:
|
|
|
102
105
|
vo = ''
|
|
103
106
|
package = cls._get_policy_package_name(vo)
|
|
104
107
|
except (NoOptionError, NoSectionError):
|
|
108
|
+
cls._default_algorithms[type_for_vo] = default_algorithm
|
|
105
109
|
return default_algorithm
|
|
106
110
|
|
|
107
111
|
module_name = package + "." + algorithm_type
|
|
112
|
+
LOGGER.info('Attempting to find algorithm %s in default location %s...' % (algorithm_type, module_name))
|
|
108
113
|
try:
|
|
109
114
|
module = importlib.import_module(module_name)
|
|
110
115
|
|
|
111
116
|
if hasattr(module, algorithm_type):
|
|
112
117
|
default_algorithm = getattr(module, algorithm_type)
|
|
113
|
-
|
|
118
|
+
except ModuleNotFoundError:
|
|
119
|
+
LOGGER.info('Algorithm %s not found in default location %s' % (algorithm_type, module_name))
|
|
114
120
|
except ImportError:
|
|
115
|
-
LOGGER.info('
|
|
121
|
+
LOGGER.info('Algorithm %s found in default location %s, but could not be loaded' % (algorithm_type, module_name))
|
|
122
|
+
# if the default algorithm is not present, this will store None and we will
|
|
123
|
+
# not attempt to load the same algorithm again
|
|
124
|
+
cls._default_algorithms[type_for_vo] = default_algorithm
|
|
116
125
|
return default_algorithm
|
|
117
126
|
|
|
118
127
|
@classmethod
|
|
119
|
-
def _get_one_algorithm(cls: type[PolicyPackageAlgorithmsT], algorithm_type:
|
|
128
|
+
def _get_one_algorithm(cls: type[PolicyPackageAlgorithmsT], algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL, name: str) -> 'Callable[..., Any]':
|
|
120
129
|
"""
|
|
121
130
|
Get the algorithm from the dictionary of algorithms
|
|
122
131
|
"""
|
|
132
|
+
if algorithm_type not in POLICY_ALGORITHM_TYPES:
|
|
133
|
+
raise InvalidPolicyPackageAlgorithmType(algorithm_type)
|
|
123
134
|
return cls._ALGORITHMS[algorithm_type][name]
|
|
124
135
|
|
|
125
136
|
@classmethod
|
|
126
|
-
def _get_algorithms(cls: type[PolicyPackageAlgorithmsT], algorithm_type:
|
|
137
|
+
def _get_algorithms(cls: type[PolicyPackageAlgorithmsT], algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL) -> dict[str, 'Callable[..., Any]']:
|
|
127
138
|
"""
|
|
128
139
|
Get the dictionary of algorithms for a given type
|
|
129
140
|
"""
|
|
141
|
+
if algorithm_type not in POLICY_ALGORITHM_TYPES:
|
|
142
|
+
raise InvalidPolicyPackageAlgorithmType(algorithm_type)
|
|
130
143
|
return cls._ALGORITHMS[algorithm_type]
|
|
131
144
|
|
|
132
145
|
@classmethod
|
|
133
146
|
def _register(
|
|
134
147
|
cls: type[PolicyPackageAlgorithmsT],
|
|
135
|
-
algorithm_type:
|
|
148
|
+
algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL,
|
|
149
|
+
algorithm_dict: dict[str, 'Callable[..., Any]']) -> None:
|
|
136
150
|
"""
|
|
137
151
|
Provided a dictionary of callable function,
|
|
138
152
|
and the associated algorithm type,
|
|
139
153
|
register it as one of the valid algorithms.
|
|
140
154
|
"""
|
|
155
|
+
if algorithm_type not in POLICY_ALGORITHM_TYPES:
|
|
156
|
+
raise InvalidPolicyPackageAlgorithmType(algorithm_type)
|
|
157
|
+
|
|
141
158
|
if algorithm_type in cls._ALGORITHMS:
|
|
142
159
|
cls._ALGORITHMS[algorithm_type].update(algorithm_dict)
|
|
143
160
|
else:
|
|
144
161
|
cls._ALGORITHMS[algorithm_type] = algorithm_dict
|
|
145
162
|
|
|
146
163
|
@classmethod
|
|
147
|
-
def _supports(
|
|
164
|
+
def _supports(
|
|
165
|
+
cls: type[PolicyPackageAlgorithmsT],
|
|
166
|
+
algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL,
|
|
167
|
+
name: str) -> bool:
|
|
148
168
|
"""
|
|
149
169
|
Check if a algorithm is supported by the plugin
|
|
150
170
|
"""
|
|
171
|
+
if algorithm_type not in POLICY_ALGORITHM_TYPES:
|
|
172
|
+
raise InvalidPolicyPackageAlgorithmType(algorithm_type)
|
|
151
173
|
return name in cls._ALGORITHMS.get(algorithm_type, {})
|
|
152
174
|
|
|
153
175
|
@classmethod
|
|
@@ -196,10 +218,6 @@ class PolicyPackageAlgorithms:
|
|
|
196
218
|
if hasattr(module, 'get_algorithms'):
|
|
197
219
|
all_algorithms = module.get_algorithms()
|
|
198
220
|
|
|
199
|
-
# for backward compatibility, rename 'surl' to 'non_deterministic_pfn' here
|
|
200
|
-
if 'surl' in all_algorithms:
|
|
201
|
-
all_algorithms['non_deterministic_pfn'] = all_algorithms['surl']
|
|
202
|
-
|
|
203
221
|
# check that the names are correctly prefixed for multi-VO
|
|
204
222
|
if vo:
|
|
205
223
|
for _, algorithms in all_algorithms.items():
|
rucio/common/utils.py
CHANGED
|
@@ -46,7 +46,7 @@ import requests
|
|
|
46
46
|
from typing_extensions import ParamSpec
|
|
47
47
|
|
|
48
48
|
from rucio.common.config import config_get, config_get_bool
|
|
49
|
-
from rucio.common.constants import BASE_SCHEME_MAP, DEFAULT_VO
|
|
49
|
+
from rucio.common.constants import BASE_SCHEME_MAP, DEFAULT_VO, POLICY_ALGORITHM_TYPES_LITERAL
|
|
50
50
|
from rucio.common.exception import DIDFilterSyntaxError, DuplicateCriteriaInDIDFilter, InputValidationError, InvalidType, MetalinkJsonParsingError, MissingModuleException, RucioException
|
|
51
51
|
from rucio.common.extra import import_extras
|
|
52
52
|
from rucio.common.plugins import PolicyPackageAlgorithms
|
|
@@ -392,7 +392,7 @@ class NonDeterministicPFNAlgorithms(PolicyPackageAlgorithms):
|
|
|
392
392
|
from policy packages
|
|
393
393
|
"""
|
|
394
394
|
|
|
395
|
-
_algorithm_type = 'non_deterministic_pfn'
|
|
395
|
+
_algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL = 'non_deterministic_pfn'
|
|
396
396
|
|
|
397
397
|
def __init__(self, vo: str = DEFAULT_VO) -> None:
|
|
398
398
|
"""
|
|
@@ -560,7 +560,7 @@ class ScopeExtractionAlgorithms(PolicyPackageAlgorithms):
|
|
|
560
560
|
Handle scope extraction algorithms
|
|
561
561
|
"""
|
|
562
562
|
|
|
563
|
-
_algorithm_type = 'scope'
|
|
563
|
+
_algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL = 'scope'
|
|
564
564
|
|
|
565
565
|
def __init__(self, vo: str = DEFAULT_VO) -> None:
|
|
566
566
|
"""
|
rucio/core/config.py
CHANGED
|
@@ -330,17 +330,19 @@ def remove_section(section: str, *, session: "Session") -> bool:
|
|
|
330
330
|
return False
|
|
331
331
|
else:
|
|
332
332
|
stmt = select(
|
|
333
|
+
models.Config.opt,
|
|
333
334
|
models.Config.value
|
|
334
335
|
).where(
|
|
335
336
|
models.Config.section == section
|
|
336
337
|
)
|
|
337
|
-
for
|
|
338
|
-
old_option = models.ConfigHistory(
|
|
339
|
-
|
|
340
|
-
|
|
338
|
+
for option, value in session.execute(stmt).all():
|
|
339
|
+
old_option = models.ConfigHistory(
|
|
340
|
+
section=section,
|
|
341
|
+
opt=option,
|
|
342
|
+
value=value)
|
|
341
343
|
old_option.save(session=session)
|
|
342
|
-
delete_from_cache(key=CacheKey.has_option(
|
|
343
|
-
delete_from_cache(key=CacheKey.value(
|
|
344
|
+
delete_from_cache(key=CacheKey.has_option(section, option))
|
|
345
|
+
delete_from_cache(key=CacheKey.value(section, option))
|
|
344
346
|
|
|
345
347
|
stmt = delete(
|
|
346
348
|
models.Config
|
rucio/core/credential.py
CHANGED
|
@@ -27,7 +27,7 @@ from google.oauth2.service_account import Credentials
|
|
|
27
27
|
|
|
28
28
|
from rucio.common.cache import MemcacheRegion
|
|
29
29
|
from rucio.common.config import config_get, get_rse_credentials
|
|
30
|
-
from rucio.common.constants import RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS, RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, SUPPORTED_SIGN_URL_SERVICES, SUPPORTED_SIGN_URL_SERVICES_LITERAL, RseAttr
|
|
30
|
+
from rucio.common.constants import RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS, RSE_BASE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, SUPPORTED_SIGN_URL_SERVICES, SUPPORTED_SIGN_URL_SERVICES_LITERAL, HTTPMethod, RseAttr
|
|
31
31
|
from rucio.common.exception import UnsupportedOperation
|
|
32
32
|
from rucio.core.monitor import MetricManager
|
|
33
33
|
from rucio.core.rse import get_rse_attribute
|
|
@@ -51,7 +51,7 @@ def get_signed_url(
|
|
|
51
51
|
The signed URL will be valid for 1 hour but can be overridden.
|
|
52
52
|
|
|
53
53
|
:param rse_id: The ID of the RSE that the URL points to.
|
|
54
|
-
:param service: The service to
|
|
54
|
+
:param service: The service to authorize, either 'gcs', 's3' or 'swift'.
|
|
55
55
|
:param operation: The operation to sign, either 'read', 'write', or 'delete'.
|
|
56
56
|
:param url: The URL to sign.
|
|
57
57
|
:param lifetime: Lifetime of the signed URL in seconds.
|
|
@@ -69,6 +69,8 @@ def get_signed_url(
|
|
|
69
69
|
if url is None or url == '':
|
|
70
70
|
raise UnsupportedOperation('URL must not be empty')
|
|
71
71
|
|
|
72
|
+
operations_map = {'read': HTTPMethod.GET.value, 'write': HTTPMethod.PUT.value, 'delete': HTTPMethod.DELETE.value}
|
|
73
|
+
|
|
72
74
|
if lifetime:
|
|
73
75
|
if not isinstance(lifetime, int):
|
|
74
76
|
try:
|
|
@@ -88,25 +90,21 @@ def get_signed_url(
|
|
|
88
90
|
if lifetime is None:
|
|
89
91
|
lifetime = 0
|
|
90
92
|
else:
|
|
91
|
-
# GCS is timezone-sensitive, don't use UTC
|
|
92
|
-
# has to be converted to Unixtime
|
|
93
|
+
# GCS is timezone-sensitive, don't use UTC. Has to be converted to Unix time
|
|
93
94
|
lifetime_datetime = datetime.datetime.now() + datetime.timedelta(seconds=lifetime)
|
|
94
95
|
lifetime = int(time.mktime(lifetime_datetime.timetuple()))
|
|
95
96
|
|
|
96
97
|
# sign the path only
|
|
97
98
|
path = components.path
|
|
98
99
|
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# assemble message to sign
|
|
103
|
-
to_sign = "%s\n\n\n%s\n%s" % (operations[operation], lifetime, path)
|
|
100
|
+
# assemble a message to sign
|
|
101
|
+
to_sign = "%s\n\n\n%s\n%s" % (operations_map[operation], lifetime, path)
|
|
104
102
|
|
|
105
103
|
# create URL-capable signature
|
|
106
104
|
# first character is always a '=', remove it
|
|
107
105
|
signature = urlencode({'': base64.b64encode(CREDS_GCS.sign_bytes(to_sign))})[1:]
|
|
108
106
|
|
|
109
|
-
# assemble final signed URL
|
|
107
|
+
# assemble the final signed URL
|
|
110
108
|
signed_url = (
|
|
111
109
|
f'https://{host}{path}'
|
|
112
110
|
f'?GoogleAccessId={CREDS_GCS.service_account_email}'
|
|
@@ -127,18 +125,18 @@ def get_signed_url(
|
|
|
127
125
|
# split URL to get hostname, bucket and key
|
|
128
126
|
components = urlparse(url)
|
|
129
127
|
host = components.netloc
|
|
130
|
-
|
|
128
|
+
path_components = components.path.split('/')
|
|
131
129
|
if s3_url_style == "path":
|
|
132
|
-
if len(
|
|
130
|
+
if len(path_components) < 3:
|
|
133
131
|
raise UnsupportedOperation('Not a valid Path-Style S3 URL')
|
|
134
|
-
bucket =
|
|
135
|
-
key = '/'.join(
|
|
132
|
+
bucket = path_components[1]
|
|
133
|
+
key = '/'.join(path_components[2:])
|
|
136
134
|
elif s3_url_style == "host":
|
|
137
|
-
|
|
138
|
-
bucket =
|
|
139
|
-
if len(
|
|
135
|
+
host_components = host.split('.')
|
|
136
|
+
bucket = host_components[0]
|
|
137
|
+
if len(path_components) < 2:
|
|
140
138
|
raise UnsupportedOperation('Not a valid Host-Style S3 URL')
|
|
141
|
-
key = '/'.join(
|
|
139
|
+
key = '/'.join(path_components[1:])
|
|
142
140
|
else:
|
|
143
141
|
raise UnsupportedOperation('Not a valid RSE S3 URL style (allowed values: path|host)')
|
|
144
142
|
|
|
@@ -185,7 +183,7 @@ def get_signed_url(
|
|
|
185
183
|
s3op, Params={'Bucket': bucket, 'Key': key}, ExpiresIn=lifetime)
|
|
186
184
|
|
|
187
185
|
else: # service == 'swift'
|
|
188
|
-
# split URL to get hostname and path
|
|
186
|
+
# split URL to get the hostname and path
|
|
189
187
|
components = urlparse(url)
|
|
190
188
|
host = components.netloc
|
|
191
189
|
|
|
@@ -194,7 +192,7 @@ def get_signed_url(
|
|
|
194
192
|
if colon >= 0:
|
|
195
193
|
host = host[:colon]
|
|
196
194
|
|
|
197
|
-
# use RSE ID to look up key
|
|
195
|
+
# use RSE ID to look up the key
|
|
198
196
|
cred_name = rse_id
|
|
199
197
|
|
|
200
198
|
# look up tempurl signing key
|
|
@@ -205,12 +203,7 @@ def get_signed_url(
|
|
|
205
203
|
REGION.set('swift-%s' % cred_name, cred)
|
|
206
204
|
tempurl_key = cred['tempurl_key']
|
|
207
205
|
|
|
208
|
-
|
|
209
|
-
swiftop = 'GET'
|
|
210
|
-
elif operation == 'write':
|
|
211
|
-
swiftop = 'PUT'
|
|
212
|
-
else:
|
|
213
|
-
swiftop = 'DELETE'
|
|
206
|
+
swiftop = operations_map[operation]
|
|
214
207
|
|
|
215
208
|
expires = int(time.time() + lifetime) # type: ignore (lifetime could be None)
|
|
216
209
|
|
rucio/core/did.py
CHANGED
|
@@ -724,7 +724,7 @@ def __add_collections_to_container(
|
|
|
724
724
|
for row in session.execute(stmt):
|
|
725
725
|
|
|
726
726
|
if row.did_scope is None:
|
|
727
|
-
raise exception.DataIdentifierNotFound("Data identifier '
|
|
727
|
+
raise exception.DataIdentifierNotFound(f"Data identifier '{row.scope}:{row.name}' not found")
|
|
728
728
|
|
|
729
729
|
if row.did_type == DIDType.FILE:
|
|
730
730
|
raise exception.UnsupportedOperation("Adding a file (%s:%s) to a container (%s:%s) is forbidden" % (row.scope, row.name, parent_did.scope, parent_did.name))
|