rucio 37.3.0__py3-none-any.whl → 37.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/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 +123 -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/core/permission/generic.py +37 -1
- rucio/core/replica.py +5 -5
- rucio/core/rule.py +5 -3
- rucio/daemons/judge/evaluator.py +1 -1
- rucio/gateway/replica.py +129 -41
- rucio/gateway/request.py +176 -103
- rucio/gateway/subscription.py +90 -108
- rucio/vcsversion.py +3 -3
- rucio/web/rest/flaskapi/v1/redirect.py +1 -1
- rucio/web/rest/flaskapi/v1/replicas.py +1 -1
- rucio/web/rest/flaskapi/v1/requests.py +211 -20
- rucio/web/rest/flaskapi/v1/subscriptions.py +9 -9
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/rucio.cfg.template +0 -1
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -1
- {rucio-37.3.0.dist-info → rucio-37.4.0.dist-info}/METADATA +1 -1
- {rucio-37.3.0.dist-info → rucio-37.4.0.dist-info}/RECORD +97 -97
- {rucio-37.3.0.dist-info → rucio-37.4.0.dist-info}/WHEEL +1 -1
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/alembic.ini.template +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/requirements.server.txt +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-abacus-account +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-admin +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-atropos +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-auditor +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-automatix +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-bb8 +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-cache-client +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-receiver +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-dumper +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-follower +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-hermes +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-judge-injector +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-kronos +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-minos +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-necromancer +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-reaper +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-transmogrifier +0 -0
- {rucio-37.3.0.data → rucio-37.4.0.data}/scripts/rucio-undertaker +0 -0
- {rucio-37.3.0.dist-info → rucio-37.4.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {rucio-37.3.0.dist-info → rucio-37.4.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-37.3.0.dist-info → rucio-37.4.0.dist-info}/top_level.txt +0 -0
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import json
|
|
16
|
-
from typing import TYPE_CHECKING
|
|
16
|
+
from typing import TYPE_CHECKING, Union, cast
|
|
17
17
|
|
|
18
18
|
import flask
|
|
19
19
|
from flask import Flask, Response
|
|
20
20
|
|
|
21
|
-
from rucio.common.exception import RequestNotFound
|
|
21
|
+
from rucio.common.exception import AccessDenied, RequestNotFound
|
|
22
22
|
from rucio.common.utils import APIEncoder, render_json
|
|
23
23
|
from rucio.core.rse import get_rses_with_attribute_value
|
|
24
|
-
from rucio.db.sqla.constants import RequestState
|
|
24
|
+
from rucio.db.sqla.constants import RequestState, TransferLimitDirection
|
|
25
25
|
from rucio.gateway import request
|
|
26
26
|
from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
|
|
27
|
-
from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, parse_scope_name, response_headers, try_stream
|
|
27
|
+
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
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
30
30
|
from collections.abc import Iterator
|
|
@@ -178,7 +178,7 @@ class RequestGet(ErrorHandlingMethodView):
|
|
|
178
178
|
description: Not acceptable
|
|
179
179
|
"""
|
|
180
180
|
try:
|
|
181
|
-
scope, name = parse_scope_name(scope_name, flask.request.environ
|
|
181
|
+
scope, name = parse_scope_name(scope_name, flask.request.environ['vo'])
|
|
182
182
|
except ValueError as error:
|
|
183
183
|
return generate_http_error_flask(400, error)
|
|
184
184
|
|
|
@@ -187,8 +187,8 @@ class RequestGet(ErrorHandlingMethodView):
|
|
|
187
187
|
scope=scope,
|
|
188
188
|
name=name,
|
|
189
189
|
rse=rse,
|
|
190
|
-
issuer=flask.request.environ
|
|
191
|
-
vo=flask.request.environ
|
|
190
|
+
issuer=flask.request.environ['issuer'],
|
|
191
|
+
vo=flask.request.environ['vo'],
|
|
192
192
|
)
|
|
193
193
|
return Response(json.dumps(request_data, cls=APIEncoder), content_type='application/json')
|
|
194
194
|
except RequestNotFound as error:
|
|
@@ -343,7 +343,7 @@ class RequestHistoryGet(ErrorHandlingMethodView):
|
|
|
343
343
|
description: Not acceptable
|
|
344
344
|
"""
|
|
345
345
|
try:
|
|
346
|
-
scope, name = parse_scope_name(scope_name, flask.request.environ
|
|
346
|
+
scope, name = parse_scope_name(scope_name, flask.request.environ['vo'])
|
|
347
347
|
except ValueError as error:
|
|
348
348
|
return generate_http_error_flask(400, error)
|
|
349
349
|
|
|
@@ -352,8 +352,8 @@ class RequestHistoryGet(ErrorHandlingMethodView):
|
|
|
352
352
|
scope=scope,
|
|
353
353
|
name=name,
|
|
354
354
|
rse=rse,
|
|
355
|
-
issuer=flask.request.environ
|
|
356
|
-
vo=flask.request.environ
|
|
355
|
+
issuer=flask.request.environ['issuer'],
|
|
356
|
+
vo=flask.request.environ['vo'],
|
|
357
357
|
)
|
|
358
358
|
return Response(json.dumps(request_data, cls=APIEncoder), content_type='application/json')
|
|
359
359
|
except RequestNotFound as error:
|
|
@@ -564,11 +564,11 @@ class RequestList(ErrorHandlingMethodView):
|
|
|
564
564
|
src_rses = []
|
|
565
565
|
dst_rses = []
|
|
566
566
|
if src_site:
|
|
567
|
-
src_rses = get_rses_with_attribute_value(key='site', value=src_site, vo=flask.request.environ
|
|
567
|
+
src_rses = get_rses_with_attribute_value(key='site', value=src_site, vo=flask.request.environ['vo'])
|
|
568
568
|
if not src_rses:
|
|
569
569
|
return generate_http_error_flask(404, 'NotFound', f'Could not resolve site name {src_site} to RSE')
|
|
570
570
|
src_rses = [rse['rse_name'] for rse in src_rses]
|
|
571
|
-
dst_rses = get_rses_with_attribute_value(key='site', value=dst_site, vo=flask.request.environ
|
|
571
|
+
dst_rses = get_rses_with_attribute_value(key='site', value=dst_site, vo=flask.request.environ['vo'])
|
|
572
572
|
if not dst_rses:
|
|
573
573
|
return generate_http_error_flask(404, 'NotFound', f'Could not resolve site name {dst_site} to RSE')
|
|
574
574
|
dst_rses = [rse['rse_name'] for rse in dst_rses]
|
|
@@ -576,11 +576,15 @@ class RequestList(ErrorHandlingMethodView):
|
|
|
576
576
|
dst_rses = [dst_rse]
|
|
577
577
|
src_rses = [src_rse]
|
|
578
578
|
|
|
579
|
+
# Manual cast to list[str] as static code analysis erroneously sees these as list[Optional[str]]
|
|
580
|
+
src_rses = cast("list[str]", src_rses)
|
|
581
|
+
dst_rses = cast("list[str]", dst_rses)
|
|
582
|
+
|
|
579
583
|
def generate(issuer, vo):
|
|
580
584
|
for result in request.list_requests(src_rses, dst_rses, states, issuer=issuer, vo=vo):
|
|
581
585
|
yield render_json(**result) + '\n'
|
|
582
586
|
|
|
583
|
-
return try_stream(generate(issuer=flask.request.environ
|
|
587
|
+
return try_stream(generate(issuer=flask.request.environ['issuer'], vo=flask.request.environ['vo']))
|
|
584
588
|
|
|
585
589
|
|
|
586
590
|
class RequestHistoryList(ErrorHandlingMethodView):
|
|
@@ -779,8 +783,8 @@ class RequestHistoryList(ErrorHandlingMethodView):
|
|
|
779
783
|
src_site = flask.request.args.get('src_site', default=None)
|
|
780
784
|
dst_site = flask.request.args.get('dst_site', default=None)
|
|
781
785
|
request_states = flask.request.args.get('request_states', default=None)
|
|
782
|
-
offset = flask.request.args.get('offset', default=0)
|
|
783
|
-
limit = flask.request.args.get('limit', default=100)
|
|
786
|
+
offset = flask.request.args.get('offset', type=int, default=0)
|
|
787
|
+
limit = flask.request.args.get('limit', type=int, default=100)
|
|
784
788
|
|
|
785
789
|
if not request_states:
|
|
786
790
|
return generate_http_error_flask(400, 'MissingParameter', 'Request state is missing')
|
|
@@ -801,11 +805,11 @@ class RequestHistoryList(ErrorHandlingMethodView):
|
|
|
801
805
|
src_rses = []
|
|
802
806
|
dst_rses = []
|
|
803
807
|
if src_site:
|
|
804
|
-
src_rses = get_rses_with_attribute_value(key='site', value=src_site, vo=flask.request.environ
|
|
808
|
+
src_rses = get_rses_with_attribute_value(key='site', value=src_site, vo=flask.request.environ['vo'])
|
|
805
809
|
if not src_rses:
|
|
806
810
|
return generate_http_error_flask(404, 'NotFound', f'Could not resolve site name {src_site} to RSE')
|
|
807
811
|
src_rses = [rse['rse_name'] for rse in src_rses]
|
|
808
|
-
dst_rses = get_rses_with_attribute_value(key='site', value=dst_site, vo=flask.request.environ
|
|
812
|
+
dst_rses = get_rses_with_attribute_value(key='site', value=dst_site, vo=flask.request.environ['vo'])
|
|
809
813
|
if not dst_rses:
|
|
810
814
|
return generate_http_error_flask(404, 'NotFound', f'Could not resolve site name {dst_site} to RSE')
|
|
811
815
|
dst_rses = [rse['rse_name'] for rse in dst_rses]
|
|
@@ -813,11 +817,15 @@ class RequestHistoryList(ErrorHandlingMethodView):
|
|
|
813
817
|
dst_rses = [dst_rse]
|
|
814
818
|
src_rses = [src_rse]
|
|
815
819
|
|
|
820
|
+
# Manual cast to list[str] as static code analysis erroneously sees these as list[Optional[str]]
|
|
821
|
+
src_rses = cast("list[str]", src_rses)
|
|
822
|
+
dst_rses = cast("list[str]", dst_rses)
|
|
823
|
+
|
|
816
824
|
def generate(issuer, vo):
|
|
817
825
|
for result in request.list_requests_history(src_rses, dst_rses, states, issuer=issuer, vo=vo, offset=offset, limit=limit):
|
|
818
826
|
yield render_json(**result) + '\n'
|
|
819
827
|
|
|
820
|
-
return try_stream(generate(issuer=flask.request.environ
|
|
828
|
+
return try_stream(generate(issuer=flask.request.environ['issuer'], vo=flask.request.environ['vo']))
|
|
821
829
|
|
|
822
830
|
|
|
823
831
|
class RequestMetricsGet(ErrorHandlingMethodView):
|
|
@@ -960,8 +968,8 @@ class RequestMetricsGet(ErrorHandlingMethodView):
|
|
|
960
968
|
src_rse=src_rse,
|
|
961
969
|
activity=activity,
|
|
962
970
|
group_by_rse_attribute=group_by_rse_attribute,
|
|
963
|
-
issuer=flask.request.environ
|
|
964
|
-
vo=flask.request.environ
|
|
971
|
+
issuer=flask.request.environ['issuer'],
|
|
972
|
+
vo=flask.request.environ['vo']
|
|
965
973
|
)
|
|
966
974
|
|
|
967
975
|
if format == 'panda':
|
|
@@ -972,6 +980,187 @@ class RequestMetricsGet(ErrorHandlingMethodView):
|
|
|
972
980
|
yield render_json(**result) + '\n'
|
|
973
981
|
return try_stream(generate())
|
|
974
982
|
|
|
983
|
+
class TransferLimits(ErrorHandlingMethodView):
|
|
984
|
+
""" REST API to get, set or delete transfer limits. """
|
|
985
|
+
|
|
986
|
+
@check_accept_header_wrapper_flask(['application/x-json-stream'])
|
|
987
|
+
def get(self) -> flask.Response:
|
|
988
|
+
"""
|
|
989
|
+
---
|
|
990
|
+
summary: Get Transfer Limits
|
|
991
|
+
description: Get all the transfer limits.
|
|
992
|
+
tags:
|
|
993
|
+
- Requests
|
|
994
|
+
responses:
|
|
995
|
+
200:
|
|
996
|
+
description: OK
|
|
997
|
+
content:
|
|
998
|
+
application/x-json-stream:
|
|
999
|
+
schema:
|
|
1000
|
+
description: All the transfer limits
|
|
1001
|
+
type: array
|
|
1002
|
+
items:
|
|
1003
|
+
type: object
|
|
1004
|
+
properties:
|
|
1005
|
+
id:
|
|
1006
|
+
description: The transfer limit id.
|
|
1007
|
+
type: string
|
|
1008
|
+
rse_expression:
|
|
1009
|
+
description: The RSE expression for which the limit applies.
|
|
1010
|
+
type: string
|
|
1011
|
+
direction:
|
|
1012
|
+
description: The direction in which this limit applies (source/destination)
|
|
1013
|
+
type: string
|
|
1014
|
+
max_transfers:
|
|
1015
|
+
description: Maximum number of transfers allowed.
|
|
1016
|
+
type: integer
|
|
1017
|
+
volume:
|
|
1018
|
+
description: Maximum transfer volume in bytes.
|
|
1019
|
+
type: integer
|
|
1020
|
+
deadline:
|
|
1021
|
+
description: Maximum waiting time in hours until a datasets gets released.
|
|
1022
|
+
type: integer
|
|
1023
|
+
strategy:
|
|
1024
|
+
description: defines how to handle datasets: `fifo` (each file released separately) or `grouped_fifo` (wait for the entire dataset to fit)
|
|
1025
|
+
type: string
|
|
1026
|
+
transfers:
|
|
1027
|
+
description: Current number of active transfers
|
|
1028
|
+
type: integer
|
|
1029
|
+
waitings:
|
|
1030
|
+
description: Current number of waiting transfers
|
|
1031
|
+
type: integer
|
|
1032
|
+
updated_at:
|
|
1033
|
+
description: Datetime of the last update.
|
|
1034
|
+
type: string
|
|
1035
|
+
created_at:
|
|
1036
|
+
description: Datetime of the creation of the transfer limit.
|
|
1037
|
+
type: string
|
|
1038
|
+
401:
|
|
1039
|
+
description: Invalid Auth Token
|
|
1040
|
+
"""
|
|
1041
|
+
transfer_limits = request.list_transfer_limits(issuer=flask.request.environ['issuer'], vo=flask.request.environ['vo'])
|
|
1042
|
+
def generate() -> "Iterator[str]":
|
|
1043
|
+
for limit in transfer_limits:
|
|
1044
|
+
yield json.dumps(limit, cls=APIEncoder) + '\n'
|
|
1045
|
+
return try_stream(generate())
|
|
1046
|
+
|
|
1047
|
+
def put(self) -> Union[flask.Response, tuple[str, int]]:
|
|
1048
|
+
"""
|
|
1049
|
+
---
|
|
1050
|
+
summary: Set Transfer Limit
|
|
1051
|
+
description: Create or update a transfer limit for a specific RSE expression and activity.
|
|
1052
|
+
tags:
|
|
1053
|
+
- Requests
|
|
1054
|
+
requestBody:
|
|
1055
|
+
content:
|
|
1056
|
+
application/json:
|
|
1057
|
+
schema:
|
|
1058
|
+
type: object
|
|
1059
|
+
required:
|
|
1060
|
+
- rse_expression
|
|
1061
|
+
- max_transfers
|
|
1062
|
+
properties:
|
|
1063
|
+
rse_expression:
|
|
1064
|
+
type: string
|
|
1065
|
+
description: The RSE expression for which the transfer limit is being set.
|
|
1066
|
+
activity:
|
|
1067
|
+
type: string
|
|
1068
|
+
description: The activity to which the transfer limit applies.
|
|
1069
|
+
max_transfers:
|
|
1070
|
+
type: integer
|
|
1071
|
+
description: The maximum number of transfers allowed.
|
|
1072
|
+
direction:
|
|
1073
|
+
type: string
|
|
1074
|
+
description: The direction of the transfer limit (source or destination).
|
|
1075
|
+
enum: ["SOURCE", "DESTINATION"]
|
|
1076
|
+
default: "DESTINATION"
|
|
1077
|
+
volume:
|
|
1078
|
+
type: integer
|
|
1079
|
+
description: The maximum transfer volume in bytes.
|
|
1080
|
+
deadline:
|
|
1081
|
+
type: integer
|
|
1082
|
+
description: The maximum waiting time in hours until a dataset is released.
|
|
1083
|
+
strategy:
|
|
1084
|
+
type: string
|
|
1085
|
+
description: The strategy for handling datasets (e.g., `fifo` or `grouped_fifo`).
|
|
1086
|
+
transfers:
|
|
1087
|
+
type: integer
|
|
1088
|
+
description: The current number of active transfers.
|
|
1089
|
+
waitings:
|
|
1090
|
+
type: integer
|
|
1091
|
+
description: The current number of waiting transfers.
|
|
1092
|
+
responses:
|
|
1093
|
+
201:
|
|
1094
|
+
description: Transfer limit set successfully.
|
|
1095
|
+
400:
|
|
1096
|
+
description: Invalid input data.
|
|
1097
|
+
401:
|
|
1098
|
+
description: Invalid Auth Token.
|
|
1099
|
+
500:
|
|
1100
|
+
description: Internal server error.
|
|
1101
|
+
"""
|
|
1102
|
+
parameters = json_parameters()
|
|
1103
|
+
rse_expression = param_get(parameters, 'rse_expression')
|
|
1104
|
+
max_transfers = param_get(parameters, 'max_transfers')
|
|
1105
|
+
|
|
1106
|
+
try:
|
|
1107
|
+
request.set_transfer_limit(
|
|
1108
|
+
rse_expression=rse_expression,
|
|
1109
|
+
max_transfers=max_transfers,
|
|
1110
|
+
activity=param_get(parameters, 'activity', default=None),
|
|
1111
|
+
direction=param_get(parameters, 'direction', default=TransferLimitDirection.DESTINATION),
|
|
1112
|
+
volume=param_get(parameters, 'volume', default=None),
|
|
1113
|
+
deadline=param_get(parameters, 'deadline', default=None),
|
|
1114
|
+
strategy=param_get(parameters, 'strategy', default=None),
|
|
1115
|
+
transfers=param_get(parameters, 'transfers', default=None),
|
|
1116
|
+
waitings=param_get(parameters, 'waitings', default=None),
|
|
1117
|
+
issuer=flask.request.environ['issuer'],
|
|
1118
|
+
vo=flask.request.environ['vo']
|
|
1119
|
+
)
|
|
1120
|
+
except AccessDenied as error:
|
|
1121
|
+
return generate_http_error_flask(401, error)
|
|
1122
|
+
|
|
1123
|
+
return '', 201
|
|
1124
|
+
|
|
1125
|
+
def delete(self) -> Union[flask.Response, tuple[str, int]]:
|
|
1126
|
+
"""
|
|
1127
|
+
---
|
|
1128
|
+
summary: Delete Transfer Limit
|
|
1129
|
+
description: Delete a transfer limit for an RSE expression.
|
|
1130
|
+
tags:
|
|
1131
|
+
- Requests
|
|
1132
|
+
parameters:
|
|
1133
|
+
- name: rse_expression
|
|
1134
|
+
in: query
|
|
1135
|
+
description: The RSE expression to delete the limit for.
|
|
1136
|
+
required: true
|
|
1137
|
+
schema:
|
|
1138
|
+
type: string
|
|
1139
|
+
responses:
|
|
1140
|
+
200:
|
|
1141
|
+
description: Transfer limit deleted successfully.
|
|
1142
|
+
400:
|
|
1143
|
+
description: Invalid input data.
|
|
1144
|
+
401:
|
|
1145
|
+
description: Invalid Auth Token.
|
|
1146
|
+
500:
|
|
1147
|
+
description: Internal server error.
|
|
1148
|
+
"""
|
|
1149
|
+
parameters = json_parameters()
|
|
1150
|
+
rse_expression = param_get(parameters, 'rse_expression')
|
|
1151
|
+
|
|
1152
|
+
try:
|
|
1153
|
+
request.delete_transfer_limit(
|
|
1154
|
+
rse_expression=rse_expression,
|
|
1155
|
+
activity=param_get(parameters, 'activity', default=None),
|
|
1156
|
+
direction=param_get(parameters, 'direction', default=TransferLimitDirection.DESTINATION),
|
|
1157
|
+
issuer=flask.request.environ['issuer'],
|
|
1158
|
+
vo=flask.request.environ['vo']
|
|
1159
|
+
)
|
|
1160
|
+
except AccessDenied as error:
|
|
1161
|
+
return generate_http_error_flask(401, error)
|
|
1162
|
+
|
|
1163
|
+
return '', 200
|
|
975
1164
|
|
|
976
1165
|
def blueprint():
|
|
977
1166
|
bp = AuthenticatedBlueprint('requests', __name__, url_prefix='/requests')
|
|
@@ -986,6 +1175,8 @@ def blueprint():
|
|
|
986
1175
|
bp.add_url_rule('/history/list', view_func=request_history_list_view, methods=['get', ])
|
|
987
1176
|
request_metrics_view = RequestMetricsGet.as_view('request_metrics_get')
|
|
988
1177
|
bp.add_url_rule('/metrics', view_func=request_metrics_view, methods=['get', ])
|
|
1178
|
+
transfer_limits_view = TransferLimits.as_view('transfer_limits_get')
|
|
1179
|
+
bp.add_url_rule('/transfer_limits', view_func=transfer_limits_view, methods=['get', 'put', 'delete'])
|
|
989
1180
|
|
|
990
1181
|
bp.after_request(response_headers)
|
|
991
1182
|
return bp
|
|
@@ -112,7 +112,7 @@ class Subscription(ErrorHandlingMethodView):
|
|
|
112
112
|
for subscription in list_subscriptions(name=name, account=account, vo=vo):
|
|
113
113
|
yield render_json(**subscription) + '\n'
|
|
114
114
|
|
|
115
|
-
return try_stream(generate(vo=request.environ
|
|
115
|
+
return try_stream(generate(vo=request.environ['vo']))
|
|
116
116
|
except SubscriptionNotFound as error:
|
|
117
117
|
return generate_http_error_flask(404, error)
|
|
118
118
|
|
|
@@ -193,7 +193,7 @@ class Subscription(ErrorHandlingMethodView):
|
|
|
193
193
|
metadata[keyword] = param_get(options, keyword, default=metadata[keyword])
|
|
194
194
|
|
|
195
195
|
try:
|
|
196
|
-
update_subscription(name=name, account=account, metadata=metadata, issuer=request.environ
|
|
196
|
+
update_subscription(name=name, account=account, metadata=metadata, issuer=request.environ['issuer'], vo=request.environ['vo'])
|
|
197
197
|
except (InvalidObject, TypeError) as error:
|
|
198
198
|
return generate_http_error_flask(400, InvalidObject.__name__, error.args[0])
|
|
199
199
|
except AccessDenied as error:
|
|
@@ -306,8 +306,8 @@ class Subscription(ErrorHandlingMethodView):
|
|
|
306
306
|
retroactive=retroactive,
|
|
307
307
|
dry_run=dry_run,
|
|
308
308
|
priority=priority,
|
|
309
|
-
issuer=request.environ
|
|
310
|
-
vo=request.environ
|
|
309
|
+
issuer=request.environ['issuer'],
|
|
310
|
+
vo=request.environ['vo'],
|
|
311
311
|
)
|
|
312
312
|
except (InvalidObject, TypeError) as error:
|
|
313
313
|
return generate_http_error_flask(400, InvalidObject.__name__, error.args[0])
|
|
@@ -400,7 +400,7 @@ class SubscriptionName(ErrorHandlingMethodView):
|
|
|
400
400
|
for subscription in list_subscriptions(name=name, vo=vo):
|
|
401
401
|
yield render_json(**subscription) + '\n'
|
|
402
402
|
|
|
403
|
-
return try_stream(generate(vo=request.environ
|
|
403
|
+
return try_stream(generate(vo=request.environ['vo']))
|
|
404
404
|
except SubscriptionNotFound as error:
|
|
405
405
|
return generate_http_error_flask(404, error)
|
|
406
406
|
|
|
@@ -454,7 +454,7 @@ class Rules(ErrorHandlingMethodView):
|
|
|
454
454
|
"""
|
|
455
455
|
state = request.args.get('state', default=None)
|
|
456
456
|
try:
|
|
457
|
-
subscriptions = [subscription['id'] for subscription in list_subscriptions(name=name, account=account, vo=request.environ
|
|
457
|
+
subscriptions = [subscription['id'] for subscription in list_subscriptions(name=name, account=account, vo=request.environ['vo'])]
|
|
458
458
|
|
|
459
459
|
def generate(vo):
|
|
460
460
|
if len(subscriptions) > 0:
|
|
@@ -465,7 +465,7 @@ class Rules(ErrorHandlingMethodView):
|
|
|
465
465
|
for rule in list_replication_rules({'subscription_id': subscriptions[0]}, vo=vo):
|
|
466
466
|
yield render_json(**rule) + '\n'
|
|
467
467
|
|
|
468
|
-
return try_stream(generate(vo=request.environ
|
|
468
|
+
return try_stream(generate(vo=request.environ['vo']))
|
|
469
469
|
except (RuleNotFound, SubscriptionNotFound) as error:
|
|
470
470
|
return generate_http_error_flask(404, error)
|
|
471
471
|
|
|
@@ -529,7 +529,7 @@ class States(ErrorHandlingMethodView):
|
|
|
529
529
|
for row in list_subscription_rule_states(name=name, account=account, vo=vo):
|
|
530
530
|
yield dumps(row, cls=APIEncoder) + '\n'
|
|
531
531
|
|
|
532
|
-
return try_stream(generate(vo=request.environ
|
|
532
|
+
return try_stream(generate(vo=request.environ['vo']))
|
|
533
533
|
|
|
534
534
|
|
|
535
535
|
class SubscriptionId(ErrorHandlingMethodView):
|
|
@@ -607,7 +607,7 @@ class SubscriptionId(ErrorHandlingMethodView):
|
|
|
607
607
|
description: Not acceptable
|
|
608
608
|
"""
|
|
609
609
|
try:
|
|
610
|
-
subscription = get_subscription_by_id(subscription_id, vo=request.environ
|
|
610
|
+
subscription = get_subscription_by_id(subscription_id, vo=request.environ['vo'])
|
|
611
611
|
except SubscriptionNotFound as error:
|
|
612
612
|
return generate_http_error_flask(404, error)
|
|
613
613
|
|
|
@@ -116,7 +116,6 @@ metrics_port = 8080
|
|
|
116
116
|
[conveyor]
|
|
117
117
|
scheme = srm,gsiftp,root,http,https
|
|
118
118
|
transfertool = fts3
|
|
119
|
-
ftshosts = https://fts3-pilot.cern.ch:8446, https://fts3-pilot.cern.ch:8446
|
|
120
119
|
cacert = /opt/rucio/etc/web/ca.crt
|
|
121
120
|
usercert = /opt/rucio/tools/x509up
|
|
122
121
|
|