rucio-clients 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-clients 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/rse/protocols/webdav.py +5 -2
- rucio/rse/translation.py +3 -3
- rucio/vcsversion.py +3 -3
- {rucio_clients-38.2.0.dist-info → rucio_clients-38.4.0.dist-info}/METADATA +1 -1
- {rucio_clients-38.2.0.dist-info → rucio_clients-38.4.0.dist-info}/RECORD +48 -48
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/data/etc/rse-accounts.cfg.template +0 -0
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/data/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/data/etc/rucio.cfg.template +0 -0
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/data/requirements.client.txt +0 -0
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/data/rucio_client/merge_rucio_configs.py +0 -0
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/scripts/rucio +0 -0
- {rucio_clients-38.2.0.data → rucio_clients-38.4.0.data}/scripts/rucio-admin +0 -0
- {rucio_clients-38.2.0.dist-info → rucio_clients-38.4.0.dist-info}/WHEEL +0 -0
- {rucio_clients-38.2.0.dist-info → rucio_clients-38.4.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {rucio_clients-38.2.0.dist-info → rucio_clients-38.4.0.dist-info}/licenses/LICENSE +0 -0
- {rucio_clients-38.2.0.dist-info → rucio_clients-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/rse/protocols/webdav.py
CHANGED
|
@@ -25,6 +25,7 @@ from requests.adapters import HTTPAdapter
|
|
|
25
25
|
from urllib3.poolmanager import PoolManager
|
|
26
26
|
|
|
27
27
|
from rucio.common import exception
|
|
28
|
+
from rucio.common.constants import HTTPMethod
|
|
28
29
|
from rucio.rse.protocols import protocol
|
|
29
30
|
|
|
30
31
|
|
|
@@ -259,9 +260,11 @@ class Default(protocol.RSEProtocol):
|
|
|
259
260
|
try:
|
|
260
261
|
# use GET instead of HEAD for presigned urls
|
|
261
262
|
if not using_presigned_urls:
|
|
262
|
-
result = self.session.request(
|
|
263
|
+
result = self.session.request(HTTPMethod.HEAD.value, path, verify=False, timeout=self.timeout,
|
|
264
|
+
cert=self.cert)
|
|
263
265
|
else:
|
|
264
|
-
result = self.session.request(
|
|
266
|
+
result = self.session.request(HTTPMethod.GET.value, path, verify=False, timeout=self.timeout,
|
|
267
|
+
cert=self.cert)
|
|
265
268
|
if result.status_code == 200:
|
|
266
269
|
return True
|
|
267
270
|
elif result.status_code in [401, ]:
|
rucio/rse/translation.py
CHANGED
|
@@ -18,7 +18,7 @@ from configparser import NoOptionError, NoSectionError
|
|
|
18
18
|
from typing import TYPE_CHECKING, Any, Optional
|
|
19
19
|
|
|
20
20
|
from rucio.common import config
|
|
21
|
-
from rucio.common.constants import DEFAULT_VO, RseAttr
|
|
21
|
+
from rucio.common.constants import DEFAULT_VO, POLICY_ALGORITHM_TYPES_LITERAL, RseAttr
|
|
22
22
|
from rucio.common.exception import ConfigNotFound
|
|
23
23
|
from rucio.common.plugins import PolicyPackageAlgorithms
|
|
24
24
|
|
|
@@ -33,7 +33,7 @@ class RSEDeterministicScopeTranslation(PolicyPackageAlgorithms):
|
|
|
33
33
|
Translates a pfn dictionary into a scope and name
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
-
_algorithm_type = "pfn2lfn"
|
|
36
|
+
_algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL = "pfn2lfn"
|
|
37
37
|
|
|
38
38
|
def __init__(self, vo: str = DEFAULT_VO):
|
|
39
39
|
super().__init__()
|
|
@@ -111,7 +111,7 @@ class RSEDeterministicTranslation(PolicyPackageAlgorithms):
|
|
|
111
111
|
"""
|
|
112
112
|
|
|
113
113
|
_DEFAULT_LFN2PFN = "hash"
|
|
114
|
-
_algorithm_type = "lfn2pfn"
|
|
114
|
+
_algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL = "lfn2pfn"
|
|
115
115
|
|
|
116
116
|
def __init__(
|
|
117
117
|
self,
|
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': '38.
|
|
7
|
+
'version': '38.4.0',
|
|
8
8
|
'branch_nick': 'release-38-LTS',
|
|
9
|
-
'revision_id': '
|
|
10
|
-
'revno':
|
|
9
|
+
'revision_id': '945ab71be90243fe96148bb3bd13c1c3ae410765',
|
|
10
|
+
'revno': 14030
|
|
11
11
|
}
|