rucio-clients 37.3.0__py3-none-any.whl → 37.5.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/rule.py +1 -1
- rucio/client/accountclient.py +205 -60
- rucio/client/accountlimitclient.py +84 -25
- rucio/client/baseclient.py +85 -48
- rucio/client/client.py +49 -41
- rucio/client/configclient.py +36 -13
- rucio/client/credentialclient.py +16 -6
- rucio/client/didclient.py +321 -133
- rucio/client/diracclient.py +13 -6
- rucio/client/downloadclient.py +435 -165
- rucio/client/exportclient.py +8 -2
- rucio/client/fileclient.py +10 -3
- rucio/client/importclient.py +4 -1
- rucio/client/lifetimeclient.py +48 -31
- rucio/client/lockclient.py +22 -7
- rucio/client/metaconventionsclient.py +59 -21
- rucio/client/pingclient.py +3 -1
- rucio/client/replicaclient.py +213 -96
- rucio/client/requestclient.py +124 -16
- rucio/client/rseclient.py +385 -160
- rucio/client/ruleclient.py +147 -51
- rucio/client/scopeclient.py +35 -10
- rucio/client/subscriptionclient.py +60 -27
- rucio/client/touchclient.py +16 -7
- rucio/common/constants.py +14 -17
- rucio/common/utils.py +18 -2
- rucio/rse/rsemanager.py +2 -2
- rucio/vcsversion.py +3 -3
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/data/etc/rucio.cfg.template +0 -1
- {rucio_clients-37.3.0.dist-info → rucio_clients-37.5.0.dist-info}/METADATA +1 -1
- {rucio_clients-37.3.0.dist-info → rucio_clients-37.5.0.dist-info}/RECORD +41 -41
- {rucio_clients-37.3.0.dist-info → rucio_clients-37.5.0.dist-info}/WHEEL +1 -1
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/data/etc/rse-accounts.cfg.template +0 -0
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/data/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/data/requirements.client.txt +0 -0
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/data/rucio_client/merge_rucio_configs.py +0 -0
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/scripts/rucio +0 -0
- {rucio_clients-37.3.0.data → rucio_clients-37.5.0.data}/scripts/rucio-admin +0 -0
- {rucio_clients-37.3.0.dist-info → rucio_clients-37.5.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {rucio_clients-37.3.0.dist-info → rucio_clients-37.5.0.dist-info}/licenses/LICENSE +0 -0
- {rucio_clients-37.3.0.dist-info → rucio_clients-37.5.0.dist-info}/top_level.txt +0 -0
rucio/client/replicaclient.py
CHANGED
|
@@ -33,9 +33,14 @@ class ReplicaClient(BaseClient):
|
|
|
33
33
|
"""
|
|
34
34
|
Add quaratined replicas for RSE.
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
replicas :
|
|
39
|
+
List of replica infos: {'scope': <scope> (optional), 'name': <name> (optional), 'path':<path> (required)}.
|
|
40
|
+
rse :
|
|
41
|
+
RSE name.
|
|
42
|
+
rse_id :
|
|
43
|
+
RSE id. Either RSE name or RSE id must be specified, but not both.
|
|
39
44
|
"""
|
|
40
45
|
|
|
41
46
|
if (rse is None) == (rse_id is None):
|
|
@@ -54,10 +59,19 @@ class ReplicaClient(BaseClient):
|
|
|
54
59
|
"""
|
|
55
60
|
Declare a list of bad replicas.
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
replicas :
|
|
65
|
+
Either a list of PFNs (string) or a list of dicts {'scope': <scope>, 'name': <name>, 'rse_id': <rse_id> or 'rse': <rse_name>}
|
|
66
|
+
reason :
|
|
67
|
+
The reason of the loss.
|
|
68
|
+
force :
|
|
69
|
+
Tell the server to ignore existing replica status in the bad_replicas table. Default: False
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
|
|
74
|
+
Dictionary of the form {"rse_name": ["did: error",...]} - list of strings for DIDs failed to declare, by RSE
|
|
61
75
|
"""
|
|
62
76
|
|
|
63
77
|
out = {} # {rse: ["did: error text",...]}
|
|
@@ -79,9 +93,14 @@ class ReplicaClient(BaseClient):
|
|
|
79
93
|
"""
|
|
80
94
|
Declare a list of bad replicas.
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
rse :
|
|
99
|
+
The RSE where the bad replicas reside.
|
|
100
|
+
dids :
|
|
101
|
+
The DIDs of the bad replicas.
|
|
102
|
+
reason :
|
|
103
|
+
The reason of the loss.
|
|
85
104
|
"""
|
|
86
105
|
data = {'reason': reason, 'rse': rse, 'dids': dids}
|
|
87
106
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'bad/dids']))
|
|
@@ -96,8 +115,13 @@ class ReplicaClient(BaseClient):
|
|
|
96
115
|
"""
|
|
97
116
|
Declare a list of bad replicas.
|
|
98
117
|
|
|
99
|
-
|
|
100
|
-
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
pfns:
|
|
121
|
+
The list of PFNs.
|
|
122
|
+
reason:
|
|
123
|
+
The reason of the loss.
|
|
124
|
+
|
|
101
125
|
"""
|
|
102
126
|
data = {'reason': reason, 'pfns': pfns}
|
|
103
127
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'suspicious']))
|
|
@@ -110,11 +134,14 @@ class ReplicaClient(BaseClient):
|
|
|
110
134
|
|
|
111
135
|
def get_did_from_pfns(self, pfns, rse=None):
|
|
112
136
|
"""
|
|
113
|
-
Get the DIDs associated to a PFN on one given RSE
|
|
137
|
+
Get the DIDs associated to a PFN on one given RSE.
|
|
114
138
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
pfns :
|
|
142
|
+
The list of PFNs.
|
|
143
|
+
rse :
|
|
144
|
+
The RSE name.
|
|
118
145
|
"""
|
|
119
146
|
data = {'rse': rse, 'pfns': pfns}
|
|
120
147
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'dids']))
|
|
@@ -134,24 +161,44 @@ class ReplicaClient(BaseClient):
|
|
|
134
161
|
"""
|
|
135
162
|
List file replicas for a list of data identifiers (DIDs).
|
|
136
163
|
|
|
137
|
-
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
dids:
|
|
167
|
+
The list of data identifiers (DIDs) like :
|
|
138
168
|
[{'scope': <scope1>, 'name': <name1>}, {'scope': <scope2>, 'name': <name2>}, ...]
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
:
|
|
142
|
-
|
|
143
|
-
:
|
|
144
|
-
|
|
145
|
-
:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
:
|
|
149
|
-
|
|
150
|
-
:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
169
|
+
schemes:
|
|
170
|
+
A list of schemes to filter the replicas. (e.g. file, http, ...)
|
|
171
|
+
ignore_availability:
|
|
172
|
+
Also include replicas from blocked RSEs into the list
|
|
173
|
+
all_states:
|
|
174
|
+
Include all states of the replicas. Default: False
|
|
175
|
+
metalink:
|
|
176
|
+
``False`` (default) retrieves as JSON,
|
|
177
|
+
``True`` retrieves as metalink4+xml.
|
|
178
|
+
rse_expression:
|
|
179
|
+
The RSE expression to restrict replicas on a set of RSEs.
|
|
180
|
+
client_location:
|
|
181
|
+
Client location dictionary for PFN modification {'ip', 'fqdn', 'site', 'latitude', 'longitude'}
|
|
182
|
+
sort:
|
|
183
|
+
Sort the replicas: ``geoip`` - based on src/dst IP topographical distance
|
|
184
|
+
domain:
|
|
185
|
+
Define the domain. None is fallback to 'wan', otherwise 'wan, 'lan', or 'all'
|
|
186
|
+
signature_lifetime:
|
|
187
|
+
If supported, in seconds, restrict the lifetime of the signed PFN.
|
|
188
|
+
nrandom:
|
|
189
|
+
pick N random replicas. If the initial number of replicas is smaller than N, returns all replicas.
|
|
190
|
+
resolve_archives:
|
|
191
|
+
When set to True, find archives which contain the replicas.
|
|
192
|
+
resolve_parents:
|
|
193
|
+
When set to True, find all parent datasets which contain the replicas.
|
|
194
|
+
updated_after:
|
|
195
|
+
epoch timestamp or datetime object (UTC time), only return replicas updated after this time
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
|
|
201
|
+
A list of dictionaries with replica information.
|
|
155
202
|
"""
|
|
156
203
|
data = {'dids': dids,
|
|
157
204
|
'domain': domain}
|
|
@@ -208,11 +255,16 @@ class ReplicaClient(BaseClient):
|
|
|
208
255
|
"""
|
|
209
256
|
List file replicas tagged as suspicious.
|
|
210
257
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
:
|
|
214
|
-
|
|
215
|
-
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
rse_expression:
|
|
261
|
+
The RSE expression to restrict replicas on a set of RSEs.
|
|
262
|
+
younger_than:
|
|
263
|
+
Datetime object to select the replicas which were declared since younger_than date. Default value = 10 days ago.
|
|
264
|
+
nattempts:
|
|
265
|
+
The minimum number of replica appearances in the bad_replica DB table from younger_than date. Default value = 0.
|
|
266
|
+
state:
|
|
267
|
+
State of the replica, either 'BAD' or 'SUSPICIOUS'. No value returns replicas with either state.
|
|
216
268
|
"""
|
|
217
269
|
params = {}
|
|
218
270
|
if rse_expression:
|
|
@@ -246,17 +298,29 @@ class ReplicaClient(BaseClient):
|
|
|
246
298
|
"""
|
|
247
299
|
Add file replicas to a RSE.
|
|
248
300
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
301
|
+
Parameters
|
|
302
|
+
----------
|
|
303
|
+
rse :
|
|
304
|
+
The RSE name.
|
|
305
|
+
scope :
|
|
306
|
+
The scope of the file.
|
|
307
|
+
name :
|
|
308
|
+
The name of the file.
|
|
309
|
+
bytes_ :
|
|
310
|
+
The size in bytes.
|
|
311
|
+
adler32 :
|
|
312
|
+
adler32 checksum.
|
|
313
|
+
pfn :
|
|
314
|
+
PFN of the file for non deterministic RSE.
|
|
315
|
+
md5 :
|
|
316
|
+
md5 checksum.
|
|
317
|
+
meta :
|
|
318
|
+
Metadata attributes.
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
|
|
323
|
+
True if files were created successfully.
|
|
260
324
|
"""
|
|
261
325
|
meta = meta or {}
|
|
262
326
|
dict_ = {'scope': scope, 'name': name, 'bytes': bytes_, 'meta': meta, 'adler32': adler32}
|
|
@@ -270,13 +334,19 @@ class ReplicaClient(BaseClient):
|
|
|
270
334
|
"""
|
|
271
335
|
Bulk add file replicas to a RSE.
|
|
272
336
|
|
|
273
|
-
|
|
274
|
-
|
|
337
|
+
Parameters
|
|
338
|
+
----------
|
|
339
|
+
rse:
|
|
340
|
+
the RSE name
|
|
341
|
+
files:
|
|
342
|
+
The list of files. This is a list of DIDs like :
|
|
275
343
|
[{'scope': <scope1>, 'name': <name1>}, {'scope': <scope2>, 'name': <name2>}, ...]
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
:return: True if files were created successfully.
|
|
344
|
+
ignore_availability:
|
|
345
|
+
Ignore the RSE blocklist
|
|
279
346
|
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
True if files were created successfully.
|
|
280
350
|
"""
|
|
281
351
|
url = build_url(choice(self.list_hosts), path=self.REPLICAS_BASEURL)
|
|
282
352
|
data = {'rse': rse, 'files': files, 'ignore_availability': ignore_availability}
|
|
@@ -289,14 +359,19 @@ class ReplicaClient(BaseClient):
|
|
|
289
359
|
def delete_replicas(self, rse, files, ignore_availability=True):
|
|
290
360
|
"""
|
|
291
361
|
Bulk delete file replicas from a RSE.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
:
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
rse:
|
|
365
|
+
the RSE name
|
|
366
|
+
files:
|
|
367
|
+
The list of files. This is a list of DIDs like :
|
|
295
368
|
[{'scope': <scope1>, 'name': <name1>}, {'scope': <scope2>, 'name': <name2>}, ...]
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
:return: True if files have been deleted successfully.
|
|
369
|
+
ignore_availability:
|
|
370
|
+
Ignore the RSE blocklist
|
|
299
371
|
|
|
372
|
+
Returns
|
|
373
|
+
-------
|
|
374
|
+
True if files have been deleted successfully.
|
|
300
375
|
"""
|
|
301
376
|
url = build_url(choice(self.list_hosts), path=self.REPLICAS_BASEURL)
|
|
302
377
|
data = {'rse': rse, 'files': files, 'ignore_availability': ignore_availability}
|
|
@@ -307,21 +382,29 @@ class ReplicaClient(BaseClient):
|
|
|
307
382
|
raise exc_cls(exc_msg)
|
|
308
383
|
|
|
309
384
|
def update_replicas_states(self, rse, files):
|
|
385
|
+
|
|
310
386
|
"""
|
|
387
|
+
|
|
311
388
|
Bulk update the file replicas states from a RSE.
|
|
312
389
|
|
|
313
|
-
|
|
314
|
-
|
|
390
|
+
Parameters
|
|
391
|
+
----------
|
|
392
|
+
rse :
|
|
393
|
+
The RSE name.
|
|
394
|
+
files :
|
|
395
|
+
The list of files. This is a list of DIDs like :
|
|
315
396
|
[{'scope': <scope1>, 'name': <name1>, 'state': <state1>}, {'scope': <scope2>, 'name': <name2>, 'state': <state2>}, ...],
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
397
|
+
Where a state value can be any of:
|
|
398
|
+
* 'A' (AVAILABLE)
|
|
399
|
+
* 'U' (UNAVAILABLE)
|
|
400
|
+
* 'C' (COPYING)
|
|
401
|
+
* 'B' (BEING_DELETED)
|
|
402
|
+
* 'D' (BAD)
|
|
403
|
+
* 'T' (TEMPORARY_UNAVAILABLE)
|
|
404
|
+
|
|
405
|
+
Returns
|
|
406
|
+
-------
|
|
407
|
+
True if replica states have been updated successfully, otherwise an exception is raised.
|
|
325
408
|
"""
|
|
326
409
|
url = build_url(choice(self.list_hosts), path=self.REPLICAS_BASEURL)
|
|
327
410
|
data = {'rse': rse, 'files': files}
|
|
@@ -335,12 +418,18 @@ class ReplicaClient(BaseClient):
|
|
|
335
418
|
"""
|
|
336
419
|
List dataset replicas for a did (scope:name).
|
|
337
420
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
:
|
|
341
|
-
|
|
342
|
-
:
|
|
421
|
+
Parameters
|
|
422
|
+
----------
|
|
423
|
+
scope:
|
|
424
|
+
The scope of the dataset.
|
|
425
|
+
name:
|
|
426
|
+
The name of the dataset.
|
|
427
|
+
deep:
|
|
428
|
+
Lookup at the file level.
|
|
343
429
|
|
|
430
|
+
Returns
|
|
431
|
+
-------
|
|
432
|
+
A list of dict dataset replicas
|
|
344
433
|
"""
|
|
345
434
|
payload = {}
|
|
346
435
|
if deep:
|
|
@@ -359,9 +448,14 @@ class ReplicaClient(BaseClient):
|
|
|
359
448
|
"""
|
|
360
449
|
List dataset replicas for a did (scope:name).
|
|
361
450
|
|
|
362
|
-
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
dids:
|
|
454
|
+
The list of DIDs of the datasets
|
|
363
455
|
|
|
364
|
-
|
|
456
|
+
Returns
|
|
457
|
+
-------
|
|
458
|
+
A list of dict dataset replicas
|
|
365
459
|
"""
|
|
366
460
|
payload = {'dids': list(dids)}
|
|
367
461
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'datasets_bulk']))
|
|
@@ -378,11 +472,19 @@ class ReplicaClient(BaseClient):
|
|
|
378
472
|
|
|
379
473
|
NOTICE: This is an RnD function and might change or go away at any time.
|
|
380
474
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
:
|
|
475
|
+
Parameters
|
|
476
|
+
----------
|
|
477
|
+
scope:
|
|
478
|
+
The scope of the dataset.
|
|
479
|
+
name:
|
|
480
|
+
The name of the dataset.
|
|
481
|
+
deep:
|
|
482
|
+
Lookup at the file level.
|
|
483
|
+
|
|
484
|
+
Returns
|
|
485
|
+
-------
|
|
486
|
+
If VP exists a list of dicts of sites
|
|
384
487
|
|
|
385
|
-
:returns: If VP exists a list of dicts of sites
|
|
386
488
|
"""
|
|
387
489
|
payload = {}
|
|
388
490
|
if deep:
|
|
@@ -401,12 +503,17 @@ class ReplicaClient(BaseClient):
|
|
|
401
503
|
"""
|
|
402
504
|
List datasets at a RSE.
|
|
403
505
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
:
|
|
407
|
-
|
|
408
|
-
:
|
|
409
|
-
|
|
506
|
+
Parameters
|
|
507
|
+
----------
|
|
508
|
+
rse:
|
|
509
|
+
The RSE name.
|
|
510
|
+
filters:
|
|
511
|
+
dictionary of attributes by which the results should be filtered.
|
|
512
|
+
limit:
|
|
513
|
+
limit number.
|
|
514
|
+
Returns
|
|
515
|
+
-------
|
|
516
|
+
A list of dict dataset replicas
|
|
410
517
|
"""
|
|
411
518
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'rse', rse]))
|
|
412
519
|
r = self._send_request(url, type_='GET')
|
|
@@ -420,13 +527,20 @@ class ReplicaClient(BaseClient):
|
|
|
420
527
|
"""
|
|
421
528
|
Declare a list of bad replicas.
|
|
422
529
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
:
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
530
|
+
Parameters
|
|
531
|
+
----------
|
|
532
|
+
pfns:
|
|
533
|
+
The list of PFNs.
|
|
534
|
+
reason:
|
|
535
|
+
The reason of the loss.
|
|
536
|
+
state:
|
|
537
|
+
The state of the replica. Either BAD, SUSPICIOUS, TEMPORARY_UNAVAILABLE
|
|
538
|
+
expires_at:
|
|
539
|
+
Specify a timeout for the TEMPORARY_UNAVAILABLE replicas. None for BAD files.
|
|
540
|
+
|
|
541
|
+
Returns
|
|
542
|
+
-------
|
|
543
|
+
True if PFNs were created successfully.
|
|
430
544
|
"""
|
|
431
545
|
data = {'reason': reason, 'pfns': pfns, 'state': state, 'expires_at': expires_at}
|
|
432
546
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'bad/pfns']))
|
|
@@ -441,7 +555,10 @@ class ReplicaClient(BaseClient):
|
|
|
441
555
|
"""
|
|
442
556
|
Set a tombstone on a list of replicas.
|
|
443
557
|
|
|
444
|
-
|
|
558
|
+
Parameters
|
|
559
|
+
----------
|
|
560
|
+
replicas:
|
|
561
|
+
list of replicas.
|
|
445
562
|
"""
|
|
446
563
|
url = build_url(self.host, path='/'.join([self.REPLICAS_BASEURL, 'tombstone']))
|
|
447
564
|
data = {'replicas': replicas}
|
rucio/client/requestclient.py
CHANGED
|
@@ -12,12 +12,14 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from json import dumps
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional
|
|
16
17
|
from urllib.parse import quote_plus
|
|
17
18
|
|
|
18
19
|
from requests.status_codes import codes
|
|
19
20
|
|
|
20
21
|
from rucio.client.baseclient import BaseClient, choice
|
|
22
|
+
from rucio.common.constants import TransferLimitDirection
|
|
21
23
|
from rucio.common.utils import build_url
|
|
22
24
|
|
|
23
25
|
if TYPE_CHECKING:
|
|
@@ -36,7 +38,9 @@ class RequestClient(BaseClient):
|
|
|
36
38
|
) -> 'Iterator[dict[str, Any]]':
|
|
37
39
|
"""Return latest request details
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
request information
|
|
40
44
|
"""
|
|
41
45
|
path = '/'.join([self.REQUEST_BASEURL, 'list']) + '?' + '&'.join(['src_rse={}'.format(src_rse), 'dst_rse={}'.format(
|
|
42
46
|
dst_rse), 'request_states={}'.format(request_states)])
|
|
@@ -59,7 +63,9 @@ class RequestClient(BaseClient):
|
|
|
59
63
|
) -> 'Iterator[dict[str, Any]]':
|
|
60
64
|
"""Return historical request details
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
request information
|
|
63
69
|
"""
|
|
64
70
|
path = '/'.join([self.REQUEST_BASEURL, 'history', 'list']) + '?' + '&'.join(['src_rse={}'.format(src_rse), 'dst_rse={}'.format(
|
|
65
71
|
dst_rse), 'request_states={}'.format(request_states), 'offset={}'.format(offset), 'limit={}'.format(limit)])
|
|
@@ -79,12 +85,22 @@ class RequestClient(BaseClient):
|
|
|
79
85
|
scope: Optional[str] = None
|
|
80
86
|
) -> 'Iterator[dict[str, Any]]':
|
|
81
87
|
"""Return latest request details for a DID
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
:
|
|
85
|
-
|
|
86
|
-
:
|
|
87
|
-
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
name:
|
|
91
|
+
DID
|
|
92
|
+
rse:
|
|
93
|
+
Destination RSE name
|
|
94
|
+
scope:
|
|
95
|
+
rucio scope, defaults to None
|
|
96
|
+
|
|
97
|
+
Raises
|
|
98
|
+
-------
|
|
99
|
+
exc_cls: from BaseClient._get_exception
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
request information
|
|
88
104
|
"""
|
|
89
105
|
|
|
90
106
|
if scope is not None:
|
|
@@ -104,13 +120,25 @@ class RequestClient(BaseClient):
|
|
|
104
120
|
rse: str,
|
|
105
121
|
scope: Optional[str] = None
|
|
106
122
|
) -> 'Iterator[dict[str, Any]]':
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
:
|
|
113
|
-
|
|
123
|
+
"""
|
|
124
|
+
Return latest request details for a DID
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
name:
|
|
129
|
+
DID
|
|
130
|
+
rse:
|
|
131
|
+
Destination RSE name
|
|
132
|
+
scope:
|
|
133
|
+
rucio scope, defaults to None
|
|
134
|
+
|
|
135
|
+
Raises
|
|
136
|
+
-------
|
|
137
|
+
exc_cls: from BaseClient._get_exception
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
request information
|
|
114
142
|
"""
|
|
115
143
|
|
|
116
144
|
if scope is not None:
|
|
@@ -123,3 +151,83 @@ class RequestClient(BaseClient):
|
|
|
123
151
|
else:
|
|
124
152
|
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
125
153
|
raise exc_cls(exc_msg)
|
|
154
|
+
|
|
155
|
+
def list_transfer_limits(
|
|
156
|
+
self
|
|
157
|
+
) -> 'Iterator[dict[str, Any]]':
|
|
158
|
+
"""Returns all the transfer limits
|
|
159
|
+
|
|
160
|
+
:returns: transfer limits
|
|
161
|
+
"""
|
|
162
|
+
path = '/'.join([self.REQUEST_BASEURL, 'transfer_limits'])
|
|
163
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
164
|
+
r = self._send_request(url, type_='GET')
|
|
165
|
+
|
|
166
|
+
if r.status_code == codes.ok:
|
|
167
|
+
return self._load_json_data(r)
|
|
168
|
+
else:
|
|
169
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
170
|
+
raise exc_cls(exc_msg)
|
|
171
|
+
|
|
172
|
+
def set_transfer_limit(
|
|
173
|
+
self,
|
|
174
|
+
rse_expression: str,
|
|
175
|
+
activity: Optional[str] = None,
|
|
176
|
+
direction: TransferLimitDirection = TransferLimitDirection.DESTINATION,
|
|
177
|
+
max_transfers: Optional[int] = None,
|
|
178
|
+
volume: Optional[int] = None,
|
|
179
|
+
deadline: Optional[int] = None,
|
|
180
|
+
strategy: Optional[str] = None,
|
|
181
|
+
transfers: Optional[int] = None,
|
|
182
|
+
waitings: Optional[int] = None,
|
|
183
|
+
) -> Literal[True]:
|
|
184
|
+
"""Set the transfer limit for a given RSE
|
|
185
|
+
|
|
186
|
+
:param rse_expression: RSE expression string.
|
|
187
|
+
:param activity: The activity.
|
|
188
|
+
:param direction: The direction in which this limit applies (source/destination)
|
|
189
|
+
:param max_transfers: Maximum transfers.
|
|
190
|
+
:param volume: Maximum transfer volume in bytes.
|
|
191
|
+
:param deadline: Maximum waiting time in hours until a datasets gets released.
|
|
192
|
+
:param strategy: defines how to handle datasets: `fifo` (each file released separately) or `grouped_fifo` (wait for the entire dataset to fit)
|
|
193
|
+
:param transfers: Current number of active transfers
|
|
194
|
+
:param waitings: Current number of waiting transfers
|
|
195
|
+
|
|
196
|
+
:returns: True if the transfer limit was deleted
|
|
197
|
+
"""
|
|
198
|
+
path = '/'.join([self.REQUEST_BASEURL, 'transfer_limits'])
|
|
199
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
200
|
+
data = dumps({'rse_expression': rse_expression, 'activity': activity,
|
|
201
|
+
'direction': direction.value, 'max_transfers': max_transfers,
|
|
202
|
+
'volume': volume, 'deadline': deadline, 'strategy': strategy,
|
|
203
|
+
'transfers': transfers, 'waitings': waitings})
|
|
204
|
+
r = self._send_request(url, type_='PUT', data=data)
|
|
205
|
+
|
|
206
|
+
if r.status_code == codes.created:
|
|
207
|
+
return True
|
|
208
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
209
|
+
raise exc_cls(exc_msg)
|
|
210
|
+
|
|
211
|
+
def delete_transfer_limit(
|
|
212
|
+
self,
|
|
213
|
+
rse_expression: str,
|
|
214
|
+
activity: Optional[str] = None,
|
|
215
|
+
direction: TransferLimitDirection = TransferLimitDirection.DESTINATION
|
|
216
|
+
) -> Literal[True]:
|
|
217
|
+
"""Delete the transfer limit for a given RSE
|
|
218
|
+
|
|
219
|
+
:param rse_expression: RSE expression string.
|
|
220
|
+
:param activity: The activity.
|
|
221
|
+
:param direction: The direction in which this limit applies (source/destination)
|
|
222
|
+
|
|
223
|
+
:returns: True if the transfer limit was deleted
|
|
224
|
+
"""
|
|
225
|
+
path = '/'.join([self.REQUEST_BASEURL, 'transfer_limits'])
|
|
226
|
+
url = build_url(choice(self.list_hosts), path=path)
|
|
227
|
+
data = dumps({'rse_expression': rse_expression, 'activity': activity, 'direction': direction.value})
|
|
228
|
+
r = self._send_request(url, type_='DEL', data=data)
|
|
229
|
+
|
|
230
|
+
if r.status_code == codes.ok:
|
|
231
|
+
return True
|
|
232
|
+
exc_cls, exc_msg = self._get_exception(headers=r.headers, status_code=r.status_code, data=r.content)
|
|
233
|
+
raise exc_cls(exc_msg)
|