rucio 38.4.0__py3-none-any.whl → 38.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 might be problematic. Click here for more details.

Files changed (89) hide show
  1. rucio/cli/bin_legacy/rucio.py +12 -7
  2. rucio/cli/bin_legacy/rucio_admin.py +9 -2
  3. rucio/cli/replica.py +6 -2
  4. rucio/cli/rule.py +0 -1
  5. rucio/cli/scope.py +9 -0
  6. rucio/cli/utils.py +11 -0
  7. rucio/client/downloadclient.py +3 -1
  8. rucio/client/scopeclient.py +40 -1
  9. rucio/common/didtype.py +18 -11
  10. rucio/core/did_meta_plugins/__init__.py +2 -1
  11. rucio/core/did_meta_plugins/did_column_meta.py +2 -10
  12. rucio/core/did_meta_plugins/did_meta_plugin_interface.py +39 -25
  13. rucio/core/did_meta_plugins/elasticsearch_meta.py +3 -11
  14. rucio/core/did_meta_plugins/json_meta.py +2 -8
  15. rucio/core/did_meta_plugins/mongo_meta.py +3 -12
  16. rucio/core/did_meta_plugins/postgres_meta.py +7 -14
  17. rucio/core/dirac.py +1 -1
  18. rucio/core/rse.py +6 -2
  19. rucio/core/scope.py +47 -7
  20. rucio/daemons/automatix/automatix.py +2 -0
  21. rucio/db/sqla/models.py +22 -0
  22. rucio/gateway/did.py +1 -1
  23. rucio/gateway/dirac.py +1 -1
  24. rucio/gateway/scope.py +35 -3
  25. rucio/vcsversion.py +3 -3
  26. rucio/web/rest/flaskapi/v1/opendata.py +21 -21
  27. rucio/web/rest/flaskapi/v1/opendata_public.py +8 -8
  28. rucio/web/rest/flaskapi/v1/scopes.py +63 -11
  29. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/rucio.cfg.template +2 -3
  30. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +2 -3
  31. {rucio-38.4.0.dist-info → rucio-38.5.0.dist-info}/METADATA +1 -1
  32. {rucio-38.4.0.dist-info → rucio-38.5.0.dist-info}/RECORD +89 -89
  33. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  34. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  35. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  36. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
  37. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  38. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  39. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  40. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  41. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  42. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  43. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  44. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  45. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/requirements.server.txt +0 -0
  46. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/tools/bootstrap.py +0 -0
  47. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  48. {rucio-38.4.0.data → rucio-38.5.0.data}/data/rucio/tools/reset_database.py +0 -0
  49. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio +0 -0
  50. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-abacus-account +0 -0
  51. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-abacus-collection-replica +0 -0
  52. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-abacus-rse +0 -0
  53. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-admin +0 -0
  54. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-atropos +0 -0
  55. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-auditor +0 -0
  56. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-automatix +0 -0
  57. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-bb8 +0 -0
  58. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-cache-client +0 -0
  59. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-cache-consumer +0 -0
  60. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-finisher +0 -0
  61. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-poller +0 -0
  62. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-preparer +0 -0
  63. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-receiver +0 -0
  64. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-stager +0 -0
  65. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-submitter +0 -0
  66. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-conveyor-throttler +0 -0
  67. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-dark-reaper +0 -0
  68. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-dumper +0 -0
  69. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-follower +0 -0
  70. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-hermes +0 -0
  71. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-judge-cleaner +0 -0
  72. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-judge-evaluator +0 -0
  73. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-judge-injector +0 -0
  74. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-judge-repairer +0 -0
  75. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-kronos +0 -0
  76. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-minos +0 -0
  77. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
  78. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-necromancer +0 -0
  79. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-oauth-manager +0 -0
  80. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-reaper +0 -0
  81. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-replica-recoverer +0 -0
  82. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-rse-decommissioner +0 -0
  83. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-storage-consistency-actions +0 -0
  84. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-transmogrifier +0 -0
  85. {rucio-38.4.0.data → rucio-38.5.0.data}/scripts/rucio-undertaker +0 -0
  86. {rucio-38.4.0.dist-info → rucio-38.5.0.dist-info}/WHEEL +0 -0
  87. {rucio-38.4.0.dist-info → rucio-38.5.0.dist-info}/licenses/AUTHORS.rst +0 -0
  88. {rucio-38.4.0.dist-info → rucio-38.5.0.dist-info}/licenses/LICENSE +0 -0
  89. {rucio-38.4.0.dist-info → rucio-38.5.0.dist-info}/top_level.txt +0 -0
rucio/core/scope.py CHANGED
@@ -14,21 +14,24 @@
14
14
 
15
15
  from re import match
16
16
  from traceback import format_exc
17
- from typing import TYPE_CHECKING, Any, Optional
17
+ from typing import TYPE_CHECKING, Any, Literal, Optional
18
18
 
19
- from sqlalchemy import and_, select
19
+ from sqlalchemy import and_, select, update
20
20
  from sqlalchemy.exc import IntegrityError
21
21
 
22
- from rucio.common.exception import AccountNotFound, Duplicate, RucioException, VONotFound
22
+ import rucio.core.account as account_core
23
+ from rucio.common.exception import AccountNotFound, Duplicate, RucioException, ScopeNotFound, VONotFound
23
24
  from rucio.core.vo import vo_exists
24
25
  from rucio.db.sqla import models
25
26
  from rucio.db.sqla.constants import AccountStatus, ScopeStatus
26
27
  from rucio.db.sqla.session import read_session, transactional_session
27
28
 
28
29
  if TYPE_CHECKING:
30
+ from collections.abc import Iterable
31
+
29
32
  from sqlalchemy.orm import Session
30
33
 
31
- from rucio.common.types import InternalScope
34
+ from rucio.common.types import InternalAccount, InternalScope
32
35
 
33
36
 
34
37
  @transactional_session
@@ -87,7 +90,7 @@ def bulk_add_scopes(scopes, account, skip_existing=False, *, session: "Session")
87
90
 
88
91
 
89
92
  @read_session
90
- def list_scopes(filter_: Optional[dict[str, Any]] = None, *, session: "Session") -> list["InternalScope"]:
93
+ def list_scopes(filter_: Optional[dict[str, Any]] = None, *, session: "Session") -> "Iterable[dict[Literal['scope', 'account'], Any]]":
91
94
  """
92
95
  Lists all scopes.
93
96
  :param filter_: Dictionary of attributes by which the input data should be filtered
@@ -97,7 +100,8 @@ def list_scopes(filter_: Optional[dict[str, Any]] = None, *, session: "Session")
97
100
  """
98
101
  filter_ = filter_ or {}
99
102
  stmt = select(
100
- models.Scope.scope
103
+ models.Scope.scope,
104
+ models.Scope.account
101
105
  ).where(
102
106
  models.Scope.status != ScopeStatus.DELETED
103
107
  )
@@ -112,8 +116,14 @@ def list_scopes(filter_: Optional[dict[str, Any]] = None, *, session: "Session")
112
116
  stmt = stmt.where(
113
117
  models.Scope.scope == filter_['scope']
114
118
  )
119
+ scopes = []
120
+ for scope, account in session.execute(stmt):
121
+ scopes.append({
122
+ "scope": scope,
123
+ "account": account
124
+ })
115
125
 
116
- return list(session.execute(stmt).scalars().all())
126
+ return scopes
117
127
 
118
128
 
119
129
  @read_session
@@ -179,3 +189,33 @@ def is_scope_owner(scope, account, *, session: "Session"):
179
189
  models.Scope.account == account)
180
190
  )
181
191
  return bool(session.execute(stmt).scalar())
192
+
193
+
194
+ @transactional_session
195
+ def update_scope(scope: "InternalScope", account: "InternalAccount", *, session: "Session") -> None:
196
+ """ Give the scope a new owner
197
+
198
+ :param scope: the name for the existing scope.
199
+ :param account: the account to add the scope to.
200
+ :param session: The database session in use.
201
+ """
202
+
203
+ if not vo_exists(vo=scope.vo, session=session):
204
+ raise VONotFound('VO {} not found'.format(scope.vo))
205
+
206
+ # Verify both the scope and account exist
207
+ account_core.get_account(account, session=session)
208
+ if not check_scope(scope):
209
+ raise ScopeNotFound
210
+
211
+ stmt = update(
212
+ models.Scope
213
+ ).where(
214
+ models.Scope.scope == scope
215
+ ).values({
216
+ models.Scope.account: account
217
+ })
218
+ try:
219
+ session.execute(stmt)
220
+ except Exception:
221
+ raise RucioException(str(format_exc()))
@@ -177,6 +177,8 @@ def run_once(heartbeat_handler: HeartbeatHandler, inputfile: str, **_kwargs) ->
177
177
  vo = map_vo(client.vo) # type: ignore
178
178
  filters = {"scope": InternalScope("*", vo=vo)}
179
179
  scopes = list_scopes(filter_=filters)
180
+ if not isinstance(scopes[0], str): # TODO Backwards Compat - Remove in v40, #8125
181
+ scopes = [scope['scope'] for scope in scopes]
180
182
  if InternalScope(scope, vo=vo) not in scopes:
181
183
  logger(logging.ERROR, "Scope %s does not exist. Exiting", scope)
182
184
  return True
rucio/db/sqla/models.py CHANGED
@@ -877,6 +877,28 @@ class RSE(BASE, SoftModelBase):
877
877
  CheckConstraint('RSE_TYPE IS NOT NULL', name='RSES_TYPE_NN'),
878
878
  ForeignKeyConstraint(['vo'], ['vos.vo'], name='RSES_VOS_FK'), )
879
879
 
880
+ @staticmethod
881
+ def from_str(key: str, value: str) -> Any:
882
+ """Parses str value to key-specific database type."""
883
+
884
+ if key in ['deterministic', 'volatile', 'staging_area', 'availability_read', 'availability_write', 'availability_delete']:
885
+ # boolean types
886
+ if value == '1' or value.casefold() == 'true'.casefold():
887
+ return True
888
+ elif value == '0' or value.casefold() == 'false'.casefold():
889
+ return False
890
+ else:
891
+ raise ValueError(f'Invalid boolean value: {value}')
892
+
893
+ elif key == 'rse_type':
894
+ try:
895
+ return RSEType[value]
896
+ except KeyError:
897
+ raise ValueError(f'Invalid rse_type: {value}')
898
+
899
+ else:
900
+ return value
901
+
880
902
 
881
903
  class RSELimit(BASE, ModelBase):
882
904
  """Represents RSE limits"""
rucio/gateway/did.py CHANGED
@@ -62,7 +62,7 @@ def list_dids(
62
62
  if 'account' in or_group:
63
63
  or_group['account'] = InternalAccount(or_group['account'], vo=vo)
64
64
  if 'scope' in or_group:
65
- or_group['account'] = InternalScope(or_group['scope'], vo=vo)
65
+ or_group['scope'] = InternalScope(or_group['scope'], vo=vo)
66
66
 
67
67
  with db_session(DatabaseOperationType.READ) as session:
68
68
  result = did.list_dids(scope=internal_scope, filters=filters, did_type=did_type, ignore_case=ignore_case,
rucio/gateway/dirac.py CHANGED
@@ -52,7 +52,7 @@ def add_files(
52
52
 
53
53
  with db_session(DatabaseOperationType.WRITE) as session:
54
54
  filter_ = {'scope': InternalScope(scope='*', vo=vo)}
55
- scopes = [scope.external for scope in list_scopes(filter_=filter_, session=session)]
55
+ scopes = [scope['scope'].external for scope in list_scopes(filter_=filter_, session=session)]
56
56
  dids = []
57
57
  rses = {}
58
58
  for lfn in lfns:
rucio/gateway/scope.py CHANGED
@@ -12,19 +12,23 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, Optional
15
+ from typing import TYPE_CHECKING, Any, Optional
16
16
 
17
17
  import rucio.gateway.permission
18
18
  from rucio.common.constants import 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
22
+ from rucio.common.utils import gateway_update_return_dict
22
23
  from rucio.core import scope as core_scope
23
24
  from rucio.db.sqla.constants import DatabaseOperationType
24
25
  from rucio.db.sqla.session import db_session
25
26
 
27
+ if TYPE_CHECKING:
28
+ from collections.abc import Generator
26
29
 
27
- def list_scopes(filter_: Optional[dict[str, Any]] = None, vo: str = DEFAULT_VO) -> list[str]:
30
+
31
+ def list_scopes(filter_: Optional[dict[str, Any]] = None, vo: str = DEFAULT_VO) -> 'Generator[dict[str, Any]]':
28
32
  """
29
33
  Lists all scopes.
30
34
 
@@ -42,7 +46,9 @@ def list_scopes(filter_: Optional[dict[str, Any]] = None, vo: str = DEFAULT_VO)
42
46
  filter_['scope'] = InternalScope(scope='*', vo=vo)
43
47
 
44
48
  with db_session(DatabaseOperationType.READ) as session:
45
- return [scope.external for scope in core_scope.list_scopes(filter_=filter_, session=session)]
49
+ scopes = core_scope.list_scopes(filter_=filter_, session=session)
50
+ for scope in scopes:
51
+ yield gateway_update_return_dict(scope, session=session)
46
52
 
47
53
 
48
54
  def add_scope(
@@ -92,3 +98,29 @@ def get_scopes(
92
98
 
93
99
  with db_session(DatabaseOperationType.READ) as session:
94
100
  return [scope.external for scope in core_scope.get_scopes(internal_account, session=session)]
101
+
102
+
103
+ def update_scope(
104
+ scope: str,
105
+ account: str,
106
+ issuer: str,
107
+ vo: str = DEFAULT_VO
108
+ ) -> None:
109
+ """
110
+ Change ownership of a scope
111
+
112
+ :param account: New owner for the scope
113
+ :param scope: Scope to change
114
+ param issuer: User making the request
115
+ :param vo: VO to act on
116
+
117
+ """
118
+ kwargs = {'scope': scope, 'account': account}
119
+ with db_session(DatabaseOperationType.WRITE) as session:
120
+ auth_result = rucio.gateway.permission.has_permission(issuer=issuer, vo=vo, action='update_scope', kwargs=kwargs, session=session)
121
+ if not auth_result.allowed:
122
+ raise AccessDenied('Account %s can not add scope. %s' % (issuer, auth_result.message))
123
+
124
+ internal_account = InternalAccount(account, vo=vo)
125
+ internal_scope = InternalScope(scope, vo=vo)
126
+ core_scope.update_scope(internal_scope, internal_account, session=session)
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.4.0',
7
+ 'version': '38.5.0',
8
8
  'branch_nick': 'release-38-LTS',
9
- 'revision_id': '945ab71be90243fe96148bb3bd13c1c3ae410765',
10
- 'revno': 14030
9
+ 'revision_id': '430fd3dd8f4dc5103e1e932c9515421e1c515c3e',
10
+ 'revno': 14073
11
11
  }
@@ -48,10 +48,10 @@ class OpenDataView(ErrorHandlingMethodView):
48
48
  def get(self) -> "Response":
49
49
  """
50
50
  ---
51
- summary: List Opendata DIDs
52
- description: "Retrieves a list of Opendata Data Identifiers (DIDs). Supports optional query parameters for pagination and filtering by state."
51
+ summary: List Open Data DIDs
52
+ description: "Retrieves a list of Open Data DIDs. Supports optional query parameters for pagination and filtering by state."
53
53
  tags:
54
- - Opendata
54
+ - Open Data
55
55
  parameters:
56
56
  - name: limit
57
57
  in: query
@@ -76,7 +76,7 @@ class OpenDataView(ErrorHandlingMethodView):
76
76
  style: form
77
77
  responses:
78
78
  200:
79
- description: "Successful retrieval of the list of Opendata DIDs."
79
+ description: "Successful retrieval of the list of Open Data DIDs."
80
80
  content:
81
81
  application/json:
82
82
  schema:
@@ -126,10 +126,10 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
126
126
  def get(self, scope: str, name: str) -> "Response":
127
127
  """
128
128
  ---
129
- summary: Get Opendata DID Information
130
- description: "Retrieves detailed Opendata information for the given scope and name. Supports optional query parameters to control the inclusion of files, metadata, and DOI information."
129
+ summary: Get Open Data DID Information
130
+ description: "Retrieves detailed Open Data information for the given scope and name. Supports optional query parameters to control the inclusion of files, metadata, and DOI information."
131
131
  tags:
132
- - Opendata
132
+ - Open Data
133
133
  parameters:
134
134
  - name: scope
135
135
  in: path
@@ -178,7 +178,7 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
178
178
  style: form
179
179
  responses:
180
180
  200:
181
- description: "Successful retrieval of Opendata DID information."
181
+ description: "Successful retrieval of Open Data DID information."
182
182
  content:
183
183
  application/json:
184
184
  schema:
@@ -195,10 +195,10 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
195
195
  def post(self, scope: str, name: str) -> "Response":
196
196
  """
197
197
  ---
198
- summary: Register Opendata DID
199
- description: "Registers an existing DID as Opendata."
198
+ summary: Register Open Data DID
199
+ description: "Registers an existing DID as Open Data."
200
200
  tags:
201
- - Opendata
201
+ - Open Data
202
202
  parameters:
203
203
  - name: scope
204
204
  in: path
@@ -216,7 +216,7 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
216
216
  style: simple
217
217
  responses:
218
218
  201:
219
- description: "Opendata DID successfully registered."
219
+ description: "Open Data DID successfully registered."
220
220
  content:
221
221
  application/json:
222
222
  schema:
@@ -229,7 +229,7 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
229
229
  404:
230
230
  description: "Data Identifier not found."
231
231
  409:
232
- description: "Data Identifier already exists in the Opendata catalog."
232
+ description: "Data Identifier already exists in the Open Data catalog."
233
233
  """
234
234
  vo = request.environ.get("vo", DEFAULT_VO)
235
235
  try:
@@ -249,10 +249,10 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
249
249
  def put(self, scope: str, name: str) -> "Response":
250
250
  """
251
251
  ---
252
- summary: Update Opendata DID
253
- description: "Updates the properties of an existing Opendata DID."
252
+ summary: Update Open Data DID
253
+ description: "Updates the properties of an existing Open Data DID."
254
254
  tags:
255
- - Opendata
255
+ - Open Data
256
256
  parameters:
257
257
  - name: scope
258
258
  in: path
@@ -291,7 +291,7 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
291
291
  example: '10.1234/abcd.efgh'."
292
292
  responses:
293
293
  200:
294
- description: "Opendata DID successfully updated."
294
+ description: "Open Data DID successfully updated."
295
295
  content:
296
296
  application/json:
297
297
  schema:
@@ -329,10 +329,10 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
329
329
  def delete(self, scope: str, name: str) -> "Response":
330
330
  """
331
331
  ---
332
- summary: Delete Opendata DID
333
- description: "Deletes an entry in the Opendata catalog."
332
+ summary: Delete Open Data DID
333
+ description: "Deletes an entry in the Open Data catalog."
334
334
  tags:
335
- - Opendata
335
+ - Open Data
336
336
  parameters:
337
337
  - name: scope
338
338
  in: path
@@ -350,7 +350,7 @@ class OpenDataDIDsView(ErrorHandlingMethodView):
350
350
  style: simple
351
351
  responses:
352
352
  204:
353
- description: "Opendata DID successfully deleted. No content is returned."
353
+ description: "Open Data DID successfully deleted. No content is returned."
354
354
  400:
355
355
  description: "Invalid input: The provided scope/name is not valid."
356
356
  401:
@@ -25,10 +25,10 @@ class OpenDataPublicView(ErrorHandlingMethodView):
25
25
  def get(self) -> "Response":
26
26
  """
27
27
  ---
28
- summary: List Opendata DIDs marked as public
29
- description: "Retrieves a list of public Opendata Data Identifiers (DIDs). Supports optional query parameters for pagination."
28
+ summary: List Open Data DIDs marked as public
29
+ description: "Retrieves a list of public Open Data DIDs. Supports optional query parameters for pagination."
30
30
  tags:
31
- - Opendata Public
31
+ - Open Data Public
32
32
  parameters:
33
33
  - name: limit
34
34
  in: query
@@ -46,7 +46,7 @@ class OpenDataPublicView(ErrorHandlingMethodView):
46
46
  style: form
47
47
  responses:
48
48
  200:
49
- description: "Successful retrieval of the list of Opendata DIDs."
49
+ description: "Successful retrieval of the list of Open Data DIDs."
50
50
  content:
51
51
  application/json:
52
52
  schema:
@@ -65,10 +65,10 @@ class OpenDataPublicDIDsView(ErrorHandlingMethodView):
65
65
  def get(self, scope: str, name: str) -> "Response":
66
66
  """
67
67
  ---
68
- summary: Get Opendata DID Information for public Opendata DIDs
69
- description: "Retrieves detailed Opendata information for the given scope and name. Only works for public opendata DIDs. Supports optional query parameters to control the inclusion of files, metadata, and DOI information."
68
+ summary: Get Open Data DID Information for public Open Data DIDs
69
+ description: "Retrieves detailed Open Data information for the given scope and name. Only works for public opendata DIDs. Supports optional query parameters to control the inclusion of files, metadata, and DOI information."
70
70
  tags:
71
- - Opendata Public
71
+ - Open Data Public
72
72
  parameters:
73
73
  - name: scope
74
74
  in: path
@@ -110,7 +110,7 @@ class OpenDataPublicDIDsView(ErrorHandlingMethodView):
110
110
  style: form
111
111
  responses:
112
112
  200:
113
- description: "Successful retrieval of Opendata DID information."
113
+ description: "Successful retrieval of Open Data DID information."
114
114
  content:
115
115
  application/json:
116
116
  schema:
@@ -12,11 +12,12 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from flask import Flask, jsonify, request
15
+
16
+ from flask import Flask, Response, jsonify, request
16
17
 
17
18
  from rucio.common.constants import HTTPMethod
18
- from rucio.common.exception import AccountNotFound, Duplicate, ScopeNotFound
19
- from rucio.gateway.scope import add_scope, get_scopes, list_scopes
19
+ from rucio.common.exception import AccountNotFound, Duplicate, ScopeNotFound, VONotFound
20
+ from rucio.gateway.scope import add_scope, get_scopes, list_scopes, update_scope
20
21
  from rucio.web.rest.flaskapi.authenticated_bp import AuthenticatedBlueprint
21
22
  from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_accept_header_wrapper_flask, generate_http_error_flask, response_headers
22
23
 
@@ -24,7 +25,7 @@ from rucio.web.rest.flaskapi.v1.common import ErrorHandlingMethodView, check_acc
24
25
  class Scope(ErrorHandlingMethodView):
25
26
 
26
27
  @check_accept_header_wrapper_flask(['application/json'])
27
- def get(self):
28
+ def get(self) -> Response:
28
29
  """
29
30
  ---
30
31
  summary: List Scopes
@@ -40,16 +41,27 @@ class Scope(ErrorHandlingMethodView):
40
41
  description: "All scopes."
41
42
  type: array
42
43
  items:
43
- description: "A scope."
44
- type: string
44
+ type: object
45
+ properties:
46
+ scope:
47
+ description: "A scope."
48
+ type: string
49
+ account:
50
+ description: "The owner account."
51
+ type: string
52
+
45
53
  401:
46
54
  description: "Invalid Auth Token"
47
55
  406:
48
56
  description: "Not acceptable"
49
57
  """
50
- return jsonify(list_scopes(vo=request.environ['vo']))
58
+ scopes = list_scopes(vo=request.environ['vo'])
59
+ res = []
60
+ for dictionary in scopes:
61
+ res.append(dictionary)
62
+ return jsonify(res)
51
63
 
52
- def post(self, account, scope):
64
+ def post(self, account: str, scope: str) -> Response:
53
65
  """
54
66
  ---
55
67
  summary: Add Scope
@@ -91,13 +103,53 @@ class Scope(ErrorHandlingMethodView):
91
103
  except AccountNotFound as error:
92
104
  return generate_http_error_flask(404, error)
93
105
 
94
- return 'Created', 201
106
+ return Response('Created', 201)
107
+
108
+ def put(self, account: str, scope: str) -> Response:
109
+ """
110
+ ---
111
+ summary: Change ownership of a scope.
112
+ description: "Changes ownership of a scope.."
113
+ tags:
114
+ - Scopes
115
+ parameters:
116
+ - name: account
117
+ in: path
118
+ description: "The new owner account"
119
+ schema:
120
+ type: string
121
+ style: simple
122
+ - name: scope
123
+ in: path
124
+ description: "The name of the scope."
125
+ schema:
126
+ type: string
127
+ style: simple
128
+ responses:
129
+ 201:
130
+ description: "OK"
131
+ content:
132
+ application/json:
133
+ schema:
134
+ type: string
135
+ enum: ['']
136
+ 401:
137
+ description: "Invalid Auth Token"
138
+ 403:
139
+ description: "Scope or already exists"
140
+ """
141
+ try:
142
+ update_scope(scope=scope, account=account, issuer=request.environ['issuer'], vo=request.environ['vo'])
143
+ except (ScopeNotFound, AccountNotFound, VONotFound) as error:
144
+ return generate_http_error_flask(404, error)
145
+
146
+ return Response("", 200)
95
147
 
96
148
 
97
149
  class AccountScopeList(ErrorHandlingMethodView):
98
150
 
99
151
  @check_accept_header_wrapper_flask(['application/json'])
100
- def get(self, account):
152
+ def get(self, account: str) -> Response:
101
153
  """
102
154
  ---
103
155
  summary: List Account Scopes
@@ -145,7 +197,7 @@ def blueprint() -> AuthenticatedBlueprint:
145
197
 
146
198
  scope_view = Scope.as_view('scope')
147
199
  bp.add_url_rule('/', view_func=scope_view, methods=[HTTPMethod.GET.value])
148
- bp.add_url_rule('/<account>/<scope>', view_func=scope_view, methods=[HTTPMethod.POST.value])
200
+ bp.add_url_rule('/<account>/<scope>', view_func=scope_view, methods=[HTTPMethod.POST.value, HTTPMethod.PUT.value])
149
201
  account_scope_list_view = AccountScopeList.as_view('account_scope_list')
150
202
  bp.add_url_rule('/<account>/scopes', view_func=account_scope_list_view, methods=[HTTPMethod.GET.value])
151
203
 
@@ -121,8 +121,8 @@ usercert = /opt/rucio/tools/x509up
121
121
 
122
122
  [messaging-fts3]
123
123
  port = 61123
124
- ssl_key_file = /home/mario/.ssh/hostkey.pem
125
- ssl_cert_file = /home/mario/.ssh/hostcert.pem
124
+ ssl_key_file = /etc/grid-security/hostkey.pem
125
+ ssl_cert_file = /etc/grid-security/hostcert.pem
126
126
  destination = /topic/transfer.fts_monitoring_queue_state
127
127
  brokers = dashb-test-mb.cern.ch
128
128
  voname = atlas
@@ -199,7 +199,6 @@ account = cache_mb
199
199
  cacert = /opt/rucio/etc/web/ca.crt
200
200
  #cacert = /etc/pki/tls/certs/CERN-bundle.pem
201
201
  usercert = /opt/rucio/etc/web/usercert.pem
202
- #usercert = /home/mario/.ssh/usercert_with_key.pem
203
202
 
204
203
  [nagios]
205
204
  proxy = /opt/rucio/etc/ddmadmin.proxy.nagios
@@ -102,8 +102,8 @@ usercert = /opt/rucio/tools/x509up
102
102
 
103
103
  [messaging-fts3]
104
104
  port = 61123
105
- ssl_key_file = /home/mario/.ssh/hostkey.pem
106
- ssl_cert_file = /home/mario/.ssh/hostcert.pem
105
+ ssl_key_file = /etc/grid-security/hostkey.pem
106
+ ssl_cert_file = /etc/grid-security/hostcert.pem
107
107
  destination = /topic/transfer.fts_monitoring_queue_state
108
108
  brokers = dashb-test-mb.cern.ch
109
109
  voname = atlas
@@ -179,7 +179,6 @@ account = cache_mb
179
179
  cacert = /opt/rucio/etc/web/ca.crt
180
180
  #cacert = /etc/pki/tls/certs/CERN-bundle.pem
181
181
  usercert = /opt/rucio/etc/web/usercert.pem
182
- #usercert = /home/mario/.ssh/usercert_with_key.pem
183
182
 
184
183
  [nagios]
185
184
  proxy = /opt/rucio/etc/ddmadmin.proxy.nagios
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rucio
3
- Version: 38.4.0
3
+ Version: 38.5.0
4
4
  Summary: Rucio Package
5
5
  Home-page: https://rucio.cern.ch/
6
6
  Author: Rucio