rucio 38.1.0__py3-none-any.whl → 38.3.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.

Files changed (95) hide show
  1. rucio/cli/bin_legacy/rucio.py +26 -23
  2. rucio/cli/command.py +36 -26
  3. rucio/cli/config.py +22 -7
  4. rucio/cli/did.py +1 -1
  5. rucio/cli/download.py +1 -1
  6. rucio/cli/opendata.py +60 -9
  7. rucio/cli/utils.py +13 -1
  8. rucio/client/configclient.py +23 -0
  9. rucio/client/richclient.py +6 -0
  10. rucio/common/constants.py +5 -0
  11. rucio/common/exception.py +10 -0
  12. rucio/common/plugins.py +24 -8
  13. rucio/common/utils.py +3 -3
  14. rucio/core/config.py +8 -6
  15. rucio/core/did_meta_plugins/did_column_meta.py +226 -69
  16. rucio/core/replica.py +3 -4
  17. rucio/core/request.py +7 -2
  18. rucio/core/rule.py +6 -3
  19. rucio/core/rule_grouping.py +2 -2
  20. rucio/daemons/abacus/account.py +1 -1
  21. rucio/daemons/abacus/collection_replica.py +1 -1
  22. rucio/daemons/abacus/rse.py +1 -1
  23. rucio/daemons/common.py +1 -1
  24. rucio/daemons/hermes/hermes.py +14 -13
  25. rucio/daemons/judge/cleaner.py +2 -2
  26. rucio/daemons/judge/evaluator.py +2 -2
  27. rucio/daemons/judge/injector.py +5 -4
  28. rucio/daemons/judge/repairer.py +2 -2
  29. rucio/gateway/config.py +2 -37
  30. rucio/gateway/rule.py +2 -2
  31. rucio/rse/translation.py +3 -3
  32. rucio/transfertool/fts3_plugins.py +3 -3
  33. rucio/vcsversion.py +3 -3
  34. rucio/web/rest/flaskapi/v1/config.py +52 -14
  35. {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/METADATA +1 -1
  36. {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/RECORD +95 -95
  37. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  38. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  39. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  40. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
  41. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  42. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  43. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  44. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  45. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  46. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  47. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  48. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  49. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
  50. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
  51. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/requirements.server.txt +0 -0
  52. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/tools/bootstrap.py +0 -0
  53. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  54. {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/tools/reset_database.py +0 -0
  55. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio +0 -0
  56. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-abacus-account +0 -0
  57. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-abacus-collection-replica +0 -0
  58. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-abacus-rse +0 -0
  59. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-admin +0 -0
  60. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-atropos +0 -0
  61. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-auditor +0 -0
  62. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-automatix +0 -0
  63. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-bb8 +0 -0
  64. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-cache-client +0 -0
  65. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-cache-consumer +0 -0
  66. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-finisher +0 -0
  67. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-poller +0 -0
  68. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-preparer +0 -0
  69. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-receiver +0 -0
  70. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-stager +0 -0
  71. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-submitter +0 -0
  72. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-throttler +0 -0
  73. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-dark-reaper +0 -0
  74. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-dumper +0 -0
  75. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-follower +0 -0
  76. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-hermes +0 -0
  77. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-cleaner +0 -0
  78. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-evaluator +0 -0
  79. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-injector +0 -0
  80. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-repairer +0 -0
  81. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-kronos +0 -0
  82. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-minos +0 -0
  83. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
  84. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-necromancer +0 -0
  85. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-oauth-manager +0 -0
  86. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-reaper +0 -0
  87. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-replica-recoverer +0 -0
  88. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-rse-decommissioner +0 -0
  89. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-storage-consistency-actions +0 -0
  90. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-transmogrifier +0 -0
  91. {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-undertaker +0 -0
  92. {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/WHEEL +0 -0
  93. {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/licenses/AUTHORS.rst +0 -0
  94. {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/licenses/LICENSE +0 -0
  95. {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/top_level.txt +0 -0
@@ -92,12 +92,12 @@ def run_once(
92
92
  worker_number=worker_number,
93
93
  limit=did_limit,
94
94
  blocked_dids=[(InternalScope(key[0], from_external=False), key[1]) for key in paused_dids])
95
- logger(logging.DEBUG, 'index query time %f fetch size is %d (%d blocked)', time.time() - start, len(dids),
95
+ logger(logging.DEBUG, 'Index query time %f fetch size is %d (%d blocked)', time.time() - start, len(dids),
96
96
  len([(InternalScope(key[0], from_external=False), key[1]) for key in paused_dids]))
97
97
 
98
98
  # If the list is empty, sent the worker to sleep
99
99
  if not dids:
100
- logger(logging.DEBUG, 'did not get any work (paused_dids=%s)', str(len(paused_dids)))
100
+ logger(logging.DEBUG, 'Did not get any work (paused_dids=%s)', str(len(paused_dids)))
101
101
  return
102
102
 
103
103
  done_dids = {}
@@ -83,10 +83,10 @@ def run_once(
83
83
  worker_number=worker_number,
84
84
  limit=100,
85
85
  blocked_rules=[key for key in paused_rules])
86
- logger(logging.DEBUG, 'index query time %f fetch size is %d' % (time.time() - start, len(rules)))
86
+ logger(logging.DEBUG, 'Index query time %f fetch size is %d' % (time.time() - start, len(rules)))
87
87
 
88
88
  if not rules:
89
- logger(logging.DEBUG, 'did not get any work (paused_rules=%s)' % str(len(paused_rules)))
89
+ logger(logging.DEBUG, 'Did not get any work (paused_rules=%s)' % str(len(paused_rules)))
90
90
  return
91
91
 
92
92
  for rule_id in rules:
@@ -97,12 +97,13 @@ def run_once(
97
97
  try:
98
98
  start = time.time()
99
99
  inject_rule(rule_id=rule_id, logger=logger)
100
- logger(logging.DEBUG, 'injection of %s took %f' % (rule_id, time.time() - start))
100
+ logger(logging.DEBUG, 'Injection of %s took %f' % (rule_id, time.time() - start))
101
101
  except (DatabaseException, DatabaseError) as e:
102
102
  if match(ORACLE_RESOURCE_BUSY_REGEX, str(e.args[0])) or match(PSQL_PSYCOPG_LOCK_NOT_AVAILABLE_REGEX, str(e.args[0])) or match(MYSQL_LOCK_NOWAIT_REGEX, str(e.args[0])):
103
103
  paused_rules[rule_id] = datetime.utcnow() + timedelta(seconds=randint(60, 600)) # noqa: S311
104
104
  METRICS.counter('exceptions.{exception}').labels(exception='LocksDetected').inc()
105
- logger(logging.WARNING, 'Locks detected for %s' % rule_id)
105
+ logger(logging.WARNING,
106
+ 'Database locks detected for %s. Another instance might be processing this rule at the moment' % rule_id)
106
107
  elif match('.*QueuePool.*', str(e.args[0])):
107
108
  logger(logging.WARNING, 'DatabaseException', exc_info=True)
108
109
  METRICS.counter('exceptions.{exception}').labels(exception=e.__class__.__name__).inc()
@@ -90,10 +90,10 @@ def run_once(
90
90
  limit=100,
91
91
  blocked_rules=[key for key in paused_rules])
92
92
 
93
- logger(logging.DEBUG, 'index query time %f fetch size is %d' % (time.time() - start, len(rules)))
93
+ logger(logging.DEBUG, 'Index query time %f fetch size is %d' % (time.time() - start, len(rules)))
94
94
 
95
95
  if not rules:
96
- logger(logging.DEBUG, 'did not get any work (paused_rules=%s)' % (str(len(paused_rules))))
96
+ logger(logging.DEBUG, 'Did not get any work (paused_rules=%s)' % (str(len(paused_rules))))
97
97
  return
98
98
 
99
99
  for rule_id in rules:
rucio/gateway/config.py CHANGED
@@ -47,23 +47,6 @@ def sections(issuer: str, vo: str = DEFAULT_VO) -> list[str]:
47
47
  return config.sections(session=session)
48
48
 
49
49
 
50
- def add_section(section: str, issuer: str, vo: str = DEFAULT_VO) -> None:
51
- """
52
- Add a section to the configuration.
53
-
54
- :param section: The name of the section.
55
- :param issuer: The issuer account.
56
- :param vo: The VO to act on.
57
- """
58
-
59
- kwargs = {'issuer': issuer, 'section': section}
60
- with db_session(DatabaseOperationType.WRITE) as session:
61
- auth_result = permission.has_permission(issuer=issuer, vo=vo, action='config_add_section', kwargs=kwargs, session=session)
62
- if not auth_result.allowed:
63
- raise exception.AccessDenied('%s cannot add section %s. %s' % (issuer, section, auth_result.message))
64
- return config.add_section(section, session=session)
65
-
66
-
67
50
  def has_section(section: str, issuer: str, vo: str = DEFAULT_VO) -> bool:
68
51
  """
69
52
  Indicates whether the named section is present in the configuration.
@@ -79,25 +62,7 @@ def has_section(section: str, issuer: str, vo: str = DEFAULT_VO) -> bool:
79
62
  auth_result = permission.has_permission(issuer=issuer, vo=vo, action='config_has_section', kwargs=kwargs, session=session)
80
63
  if not auth_result.allowed:
81
64
  raise exception.AccessDenied('%s cannot check existence of section %s. %s' % (issuer, section, auth_result.message))
82
- return config.has_section(section, session=session)
83
-
84
-
85
- def options(section: str, issuer: str, vo: str = DEFAULT_VO) -> list[str]:
86
- """
87
- Returns a list of options available in the specified section.
88
-
89
- :param section: The name of the section.
90
- :param issuer: The issuer account.
91
- :param vo: The VO to act on.
92
- :returns: ['option', ...]
93
- """
94
-
95
- kwargs = {'issuer': issuer, 'section': section}
96
- with db_session(DatabaseOperationType.READ) as session:
97
- auth_result = permission.has_permission(issuer=issuer, vo=vo, action='config_options', kwargs=kwargs, session=session)
98
- if auth_result.allowed:
99
- raise exception.AccessDenied('%s cannot retrieve options from section %s. %s' % (issuer, section, auth_result.message))
100
- return config.options(section, session=session)
65
+ return config.has_section(section, session=session, use_cache=False)
101
66
 
102
67
 
103
68
  def has_option(section: str, option: str, issuer: str, vo: str = DEFAULT_VO) -> bool:
@@ -116,7 +81,7 @@ def has_option(section: str, option: str, issuer: str, vo: str = DEFAULT_VO) ->
116
81
  auth_result = permission.has_permission(issuer=issuer, vo=vo, action='config_has_option', kwargs=kwargs, session=session)
117
82
  if not auth_result.allowed:
118
83
  raise exception.AccessDenied('%s cannot check existence of option %s from section %s. %s' % (issuer, option, section, auth_result.message))
119
- return config.has_option(section, option, session=session)
84
+ return config.has_option(section, option, session=session, use_cache=False)
120
85
 
121
86
 
122
87
  def get(section: str, option: str, issuer: str, vo: str = DEFAULT_VO) -> Any:
rucio/gateway/rule.py CHANGED
@@ -15,7 +15,7 @@
15
15
  from typing import TYPE_CHECKING, Any, Literal, Optional
16
16
 
17
17
  from rucio.common.config import config_get_bool
18
- from rucio.common.constants import DEFAULT_VO
18
+ from rucio.common.constants import DEFAULT_ACTIVITY, DEFAULT_VO
19
19
  from rucio.common.exception import AccessDenied
20
20
  from rucio.common.schema import validate_schema
21
21
  from rucio.common.types import InternalAccount, InternalScope
@@ -97,7 +97,7 @@ def add_replication_rule(
97
97
  account = issuer
98
98
 
99
99
  if activity is None:
100
- activity = 'User Subscriptions'
100
+ activity = DEFAULT_ACTIVITY
101
101
 
102
102
  kwargs = {'dids': dids, 'copies': copies, 'rse_expression': rse_expression, 'weight': weight, 'lifetime': lifetime,
103
103
  'grouping': grouping, 'account': account, 'locked': locked, 'subscription_id': subscription_id,
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,
@@ -17,7 +17,7 @@ import sys
17
17
  from typing import TYPE_CHECKING, Any, Optional, TypeVar
18
18
 
19
19
  from rucio.common.config import config_get_int
20
- from rucio.common.constants import DEFAULT_VO
20
+ from rucio.common.constants import DEFAULT_VO, POLICY_ALGORITHM_TYPES_LITERAL
21
21
  from rucio.common.exception import InvalidRequest
22
22
  from rucio.common.plugins import PolicyPackageAlgorithms
23
23
 
@@ -33,8 +33,8 @@ class FTS3TapeMetadataPlugin(PolicyPackageAlgorithms):
33
33
  Plugins are registered during initialization and called during a transfer with FTS3
34
34
  """
35
35
 
36
- ALGORITHM_NAME = "fts3_tape_metadata_plugins"
37
- _INIT_FUNC_NAME = "fts3_plugins_init"
36
+ ALGORITHM_NAME: POLICY_ALGORITHM_TYPES_LITERAL = "fts3_tape_metadata_plugins"
37
+ _INIT_FUNC_NAME: POLICY_ALGORITHM_TYPES_LITERAL = "fts3_plugins_init"
38
38
  DEFAULT = "def"
39
39
 
40
40
  def __init__(self, policy_algorithm: str) -> None:
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.1.0',
7
+ 'version': '38.3.0',
8
8
  'branch_nick': 'release-38-LTS',
9
- 'revision_id': 'f63f25c84c254eac1701a9640e3cb658a7bbdd8e',
10
- 'revno': 13947
9
+ 'revision_id': '5eebc4e67a544dcb2d689bd45260cb73fb10066a',
10
+ 'revno': 14003
11
11
  }
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from typing import TYPE_CHECKING
16
+
15
17
  from flask import Flask, jsonify
16
18
  from flask import request as request
17
19
 
@@ -20,12 +22,15 @@ from rucio.gateway import config
20
22
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
21
23
  from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, json_parameters, response_headers
22
24
 
25
+ if TYPE_CHECKING:
26
+ from flask.typing import ResponseReturnValue
27
+
23
28
 
24
29
  class Config(ErrorHandlingMethodView):
25
30
  """ REST API for full configuration. """
26
31
 
27
32
  @check_accept_header_wrapper_flask(['application/json'])
28
- def get(self):
33
+ def get(self) -> 'ResponseReturnValue':
29
34
  """
30
35
  ---
31
36
  summary: List
@@ -53,7 +58,7 @@ class Config(ErrorHandlingMethodView):
53
58
 
54
59
  return jsonify(res), 200
55
60
 
56
- def post(self):
61
+ def post(self) -> 'ResponseReturnValue':
57
62
  """
58
63
  ---
59
64
  summary: Create
@@ -97,7 +102,7 @@ class Section(ErrorHandlingMethodView):
97
102
  """ REST API for the sections in the configuration. """
98
103
 
99
104
  @check_accept_header_wrapper_flask(['application/json'])
100
- def get(self, section):
105
+ def get(self, section: str) -> 'ResponseReturnValue':
101
106
  """
102
107
  ---
103
108
  summary: List Sections
@@ -136,25 +141,55 @@ class Section(ErrorHandlingMethodView):
136
141
  406:
137
142
  description: "Not acceptable"
138
143
  """
139
- res = {}
140
- for item in config.items(section, issuer=request.environ['issuer'], vo=request.environ['vo']):
141
- res[item[0]] = item[1]
144
+ if config.has_section(section, issuer=request.environ['issuer'], vo=request.environ['vo']):
145
+ res = {}
146
+ for item in config.items(section, issuer=request.environ['issuer'], vo=request.environ['vo']):
147
+ res[item[0]] = item[1]
148
+ return jsonify(res), 200
142
149
 
143
- if res == {}:
150
+ else:
144
151
  return generate_http_error_flask(
145
152
  status_code=404,
146
153
  exc=ConfigNotFound.__name__,
147
154
  exc_msg=f"No configuration found for section '{section}'"
148
155
  )
149
156
 
150
- return jsonify(res), 200
157
+ def delete(self, section: str) -> 'ResponseReturnValue':
158
+ """
159
+ ---
160
+ summary: Remove an existing section
161
+ tags:
162
+ - Config
163
+ parameters:
164
+ - name: section
165
+ in: path
166
+ description: "The section."
167
+ responses:
168
+ 200:
169
+ description: "OK"
170
+ 401:
171
+ description: "Invalid Auth Token"
172
+ 404:
173
+ description: "Config not found"
174
+ 406:
175
+ description: "Not acceptable"
176
+ """
177
+ if config.has_section(section, issuer=request.environ['issuer'], vo=request.environ['vo']):
178
+ config.remove_section(section, issuer=request.environ['issuer'], vo=request.environ['vo'])
179
+ return '', 200
180
+ else:
181
+ return generate_http_error_flask(
182
+ status_code=404,
183
+ exc=ConfigNotFound.__name__,
184
+ exc_msg=f"No configuration found for section '{section}'"
185
+ )
151
186
 
152
187
 
153
188
  class OptionGetDel(ErrorHandlingMethodView):
154
189
  """ REST API for reading or deleting the options in the configuration. """
155
190
 
156
191
  @check_accept_header_wrapper_flask(['application/json'])
157
- def get(self, section, option):
192
+ def get(self, section: str, option: str) -> 'ResponseReturnValue':
158
193
  """
159
194
  ---
160
195
  summary: Get option
@@ -197,7 +232,7 @@ class OptionGetDel(ErrorHandlingMethodView):
197
232
  except ConfigNotFound as error:
198
233
  return generate_http_error_flask(404, error, f"No configuration found for section '{section}' option '{option}'")
199
234
 
200
- def delete(self, section, option):
235
+ def delete(self, section: str, option: str) -> 'ResponseReturnValue':
201
236
  """
202
237
  ---
203
238
  summary: Delete option
@@ -223,14 +258,17 @@ class OptionGetDel(ErrorHandlingMethodView):
223
258
  401:
224
259
  description: "Invalid Auth Token"
225
260
  """
226
- config.remove_option(section=section, option=option, issuer=request.environ['issuer'], vo=request.environ['vo'])
227
- return '', 200
261
+ if config.has_option(section, option, issuer=request.environ['issuer'], vo=request.environ['vo']):
262
+ config.remove_option(section=section, option=option, issuer=request.environ['issuer'], vo=request.environ['vo'])
263
+ return '', 200
264
+ else:
265
+ return generate_http_error_flask(404, ConfigNotFound.__name__, f"No configuration found for section '{section}' option '{option}'")
228
266
 
229
267
 
230
268
  class OptionSet(ErrorHandlingMethodView):
231
269
  """ REST API for setting the options in the configuration. """
232
270
 
233
- def put(self, section, option, value):
271
+ def put(self, section: str, option: str, value: str) -> 'ResponseReturnValue':
234
272
  """
235
273
  ---
236
274
  summary: Create value
@@ -289,7 +327,7 @@ def blueprint() -> AuthenticatedBlueprint:
289
327
  option_get_del_view = OptionGetDel.as_view('option_get_del')
290
328
  bp.add_url_rule('/<section>/<option>', view_func=option_get_del_view, methods=['get', 'delete'])
291
329
  section_view = Section.as_view('section')
292
- bp.add_url_rule('/<section>', view_func=section_view, methods=['get', ])
330
+ bp.add_url_rule('/<section>', view_func=section_view, methods=['get', 'delete'])
293
331
  config_view = Config.as_view('config')
294
332
  bp.add_url_rule('', view_func=config_view, methods=['get', 'post'])
295
333
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rucio
3
- Version: 38.1.0
3
+ Version: 38.3.0
4
4
  Summary: Rucio Package
5
5
  Home-page: https://rucio.cern.ch/
6
6
  Author: Rucio