rucio 37.2.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.

Files changed (127) hide show
  1. rucio/cli/rule.py +1 -1
  2. rucio/client/accountclient.py +205 -60
  3. rucio/client/accountlimitclient.py +84 -25
  4. rucio/client/baseclient.py +85 -48
  5. rucio/client/client.py +49 -41
  6. rucio/client/configclient.py +36 -13
  7. rucio/client/credentialclient.py +16 -6
  8. rucio/client/didclient.py +321 -133
  9. rucio/client/diracclient.py +13 -6
  10. rucio/client/downloadclient.py +435 -165
  11. rucio/client/exportclient.py +8 -2
  12. rucio/client/fileclient.py +10 -3
  13. rucio/client/importclient.py +4 -1
  14. rucio/client/lifetimeclient.py +48 -31
  15. rucio/client/lockclient.py +22 -7
  16. rucio/client/metaconventionsclient.py +59 -21
  17. rucio/client/pingclient.py +3 -1
  18. rucio/client/replicaclient.py +213 -96
  19. rucio/client/requestclient.py +123 -16
  20. rucio/client/rseclient.py +385 -160
  21. rucio/client/ruleclient.py +147 -51
  22. rucio/client/scopeclient.py +35 -10
  23. rucio/client/subscriptionclient.py +60 -27
  24. rucio/client/touchclient.py +16 -7
  25. rucio/common/plugins.py +1 -1
  26. rucio/core/did.py +2 -3
  27. rucio/core/permission/generic.py +37 -1
  28. rucio/core/replica.py +6 -6
  29. rucio/core/rule.py +5 -3
  30. rucio/daemons/judge/evaluator.py +1 -1
  31. rucio/db/sqla/util.py +1 -1
  32. rucio/gateway/authentication.py +58 -88
  33. rucio/gateway/config.py +63 -75
  34. rucio/gateway/did.py +245 -329
  35. rucio/gateway/dirac.py +33 -34
  36. rucio/gateway/exporter.py +27 -30
  37. rucio/gateway/importer.py +12 -14
  38. rucio/gateway/lifetime_exception.py +16 -24
  39. rucio/gateway/lock.py +27 -40
  40. rucio/gateway/replica.py +334 -249
  41. rucio/gateway/request.py +176 -103
  42. rucio/gateway/rse.py +191 -218
  43. rucio/gateway/rule.py +115 -146
  44. rucio/gateway/scope.py +18 -25
  45. rucio/gateway/subscription.py +90 -108
  46. rucio/gateway/trace.py +48 -0
  47. rucio/vcsversion.py +3 -3
  48. rucio/web/rest/flaskapi/v1/accounts.py +2 -2
  49. rucio/web/rest/flaskapi/v1/auth.py +15 -0
  50. rucio/web/rest/flaskapi/v1/common.py +3 -0
  51. rucio/web/rest/flaskapi/v1/config.py +7 -7
  52. rucio/web/rest/flaskapi/v1/dids.py +55 -55
  53. rucio/web/rest/flaskapi/v1/dirac.py +2 -2
  54. rucio/web/rest/flaskapi/v1/export.py +1 -1
  55. rucio/web/rest/flaskapi/v1/import.py +1 -1
  56. rucio/web/rest/flaskapi/v1/lifetime_exceptions.py +5 -5
  57. rucio/web/rest/flaskapi/v1/locks.py +4 -4
  58. rucio/web/rest/flaskapi/v1/main.py +17 -10
  59. rucio/web/rest/flaskapi/v1/redirect.py +1 -1
  60. rucio/web/rest/flaskapi/v1/replicas.py +30 -29
  61. rucio/web/rest/flaskapi/v1/requests.py +211 -20
  62. rucio/web/rest/flaskapi/v1/rses.py +37 -37
  63. rucio/web/rest/flaskapi/v1/rules.py +15 -15
  64. rucio/web/rest/flaskapi/v1/scopes.py +3 -3
  65. rucio/web/rest/flaskapi/v1/subscriptions.py +9 -9
  66. rucio/web/rest/flaskapi/v1/traces.py +75 -77
  67. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/rucio.cfg.template +0 -1
  68. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -1
  69. {rucio-37.2.0.dist-info → rucio-37.4.0.dist-info}/METADATA +1 -1
  70. {rucio-37.2.0.dist-info → rucio-37.4.0.dist-info}/RECORD +127 -126
  71. {rucio-37.2.0.dist-info → rucio-37.4.0.dist-info}/WHEEL +1 -1
  72. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/alembic.ini.template +0 -0
  73. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
  74. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
  75. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
  76. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
  77. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
  78. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
  79. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
  80. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
  81. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
  82. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
  83. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
  84. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/requirements.server.txt +0 -0
  85. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/tools/bootstrap.py +0 -0
  86. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
  87. {rucio-37.2.0.data → rucio-37.4.0.data}/data/rucio/tools/reset_database.py +0 -0
  88. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio +0 -0
  89. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-abacus-account +0 -0
  90. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-abacus-collection-replica +0 -0
  91. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-abacus-rse +0 -0
  92. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-admin +0 -0
  93. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-atropos +0 -0
  94. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-auditor +0 -0
  95. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-automatix +0 -0
  96. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-bb8 +0 -0
  97. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-cache-client +0 -0
  98. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-cache-consumer +0 -0
  99. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-finisher +0 -0
  100. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-poller +0 -0
  101. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-preparer +0 -0
  102. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-receiver +0 -0
  103. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-stager +0 -0
  104. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-submitter +0 -0
  105. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-conveyor-throttler +0 -0
  106. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-dark-reaper +0 -0
  107. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-dumper +0 -0
  108. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-follower +0 -0
  109. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-hermes +0 -0
  110. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-judge-cleaner +0 -0
  111. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-judge-evaluator +0 -0
  112. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-judge-injector +0 -0
  113. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-judge-repairer +0 -0
  114. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-kronos +0 -0
  115. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-minos +0 -0
  116. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
  117. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-necromancer +0 -0
  118. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-oauth-manager +0 -0
  119. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-reaper +0 -0
  120. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-replica-recoverer +0 -0
  121. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-rse-decommissioner +0 -0
  122. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-storage-consistency-actions +0 -0
  123. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-transmogrifier +0 -0
  124. {rucio-37.2.0.data → rucio-37.4.0.data}/scripts/rucio-undertaker +0 -0
  125. {rucio-37.2.0.dist-info → rucio-37.4.0.dist-info}/licenses/AUTHORS.rst +0 -0
  126. {rucio-37.2.0.dist-info → rucio-37.4.0.dist-info}/licenses/LICENSE +0 -0
  127. {rucio-37.2.0.dist-info → rucio-37.4.0.dist-info}/top_level.txt +0 -0
@@ -20,19 +20,17 @@ from rucio.common.exception import AccessDenied, InvalidObject
20
20
  from rucio.common.schema import validate_schema
21
21
  from rucio.common.types import InternalAccount, InternalScope
22
22
  from rucio.core import subscription
23
- from rucio.db.sqla.session import read_session, stream_session, transactional_session
23
+ from rucio.db.sqla.constants import DatabaseOperationType
24
+ from rucio.db.sqla.session import db_session
24
25
  from rucio.gateway.permission import has_permission
25
26
 
26
27
  if TYPE_CHECKING:
27
28
  from collections.abc import Iterator
28
29
 
29
- from sqlalchemy.orm import Session
30
-
31
30
 
32
31
  SubscriptionRuleState = namedtuple('SubscriptionRuleState', ['account', 'name', 'state', 'count'])
33
32
 
34
33
 
35
- @transactional_session
36
34
  def add_subscription(
37
35
  name: str,
38
36
  account: str,
@@ -42,11 +40,9 @@ def add_subscription(
42
40
  lifetime: Union[int, Literal[False]],
43
41
  retroactive: bool,
44
42
  dry_run: bool,
43
+ issuer: str,
45
44
  priority: Optional[int] = None,
46
- issuer: Optional[str] = None,
47
45
  vo: str = 'def',
48
- *,
49
- session: "Session"
50
46
  ) -> str:
51
47
  """
52
48
  Adds a new subscription which will be verified against every new added file and dataset
@@ -63,53 +59,50 @@ def add_subscription(
63
59
  :param priority: The priority of the subscription
64
60
  :param issuer: The account issuing this operation.
65
61
  :param vo: The VO to act on.
66
- :param session: The database session in use.
67
62
  :returns: subscription_id
68
63
  """
69
- auth_result = has_permission(issuer=issuer, vo=vo, action='add_subscription', kwargs={'account': account}, session=session)
70
- if not auth_result.allowed:
71
- raise AccessDenied('Account %s can not add subscription. %s' % (issuer, auth_result.message))
72
- try:
73
- if filter_:
74
- if not isinstance(filter_, dict):
75
- raise TypeError('filter should be a dict')
76
- validate_schema(name='subscription_filter', obj=filter_, vo=vo)
77
- if replication_rules:
78
- if not isinstance(replication_rules, list):
79
- raise TypeError('replication_rules should be a list')
80
- else:
81
- for rule in replication_rules:
82
- validate_schema(name='activity', obj=rule.get('activity', 'default'), vo=vo)
83
- else:
84
- raise InvalidObject('You must specify a rule')
85
- except ValueError as error:
86
- raise TypeError(error)
87
-
88
- internal_account = InternalAccount(account, vo=vo)
89
-
90
- keys = ['scope', 'account']
91
- types = [InternalScope, InternalAccount]
92
- for _key, _type in zip(keys, types):
93
- if _key in filter_:
94
- if isinstance(filter_[_key], list):
95
- filter_[_key] = [_type(val, vo=vo).internal for val in filter_[_key]]
64
+ with db_session(DatabaseOperationType.WRITE) as session:
65
+ auth_result = has_permission(issuer=issuer, vo=vo, action='add_subscription', kwargs={'account': account}, session=session)
66
+ if not auth_result.allowed:
67
+ raise AccessDenied('Account %s can not add subscription. %s' % (issuer, auth_result.message))
68
+ try:
69
+ if filter_:
70
+ if not isinstance(filter_, dict):
71
+ raise TypeError('filter should be a dict')
72
+ validate_schema(name='subscription_filter', obj=filter_, vo=vo)
73
+ if replication_rules:
74
+ if not isinstance(replication_rules, list):
75
+ raise TypeError('replication_rules should be a list')
76
+ else:
77
+ for rule in replication_rules:
78
+ validate_schema(name='activity', obj=rule.get('activity', 'default'), vo=vo)
96
79
  else:
97
- filter_[_key] = _type(filter_[_key], vo=vo).internal
80
+ raise InvalidObject('You must specify a rule')
81
+ except ValueError as error:
82
+ raise TypeError(error)
83
+
84
+ internal_account = InternalAccount(account, vo=vo)
85
+
86
+ keys = ['scope', 'account']
87
+ types = [InternalScope, InternalAccount]
88
+ for _key, _type in zip(keys, types):
89
+ if _key in filter_:
90
+ if isinstance(filter_[_key], list):
91
+ filter_[_key] = [_type(val, vo=vo).internal for val in filter_[_key]]
92
+ else:
93
+ filter_[_key] = _type(filter_[_key], vo=vo).internal
98
94
 
99
- return subscription.add_subscription(name=name, account=internal_account, filter_=dumps(filter_), replication_rules=dumps(replication_rules),
100
- comments=comments, lifetime=lifetime, retroactive=retroactive, dry_run=dry_run, priority=priority,
101
- session=session)
95
+ return subscription.add_subscription(name=name, account=internal_account, filter_=dumps(filter_), replication_rules=dumps(replication_rules),
96
+ comments=comments, lifetime=lifetime, retroactive=retroactive, dry_run=dry_run, priority=priority,
97
+ session=session)
102
98
 
103
99
 
104
- @transactional_session
105
100
  def update_subscription(
106
101
  name: str,
107
102
  account: str,
103
+ issuer: str,
108
104
  metadata: Optional[dict[str, Any]] = None,
109
- issuer: Optional[str] = None,
110
105
  vo: str = 'def',
111
- *,
112
- session: "Session"
113
106
  ) -> None:
114
107
  """
115
108
  Updates a subscription
@@ -119,53 +112,50 @@ def update_subscription(
119
112
  :param metadata: Dictionary of metadata to update. Supported keys : filter, replication_rules, comments, lifetime, retroactive, dry_run, priority, last_processed
120
113
  :param issuer: The account issuing this operation.
121
114
  :param vo: The VO to act on.
122
- :param session: The database session in use.
123
115
  :raises: SubscriptionNotFound if subscription is not found
124
116
  """
125
- auth_result = has_permission(issuer=issuer, vo=vo, action='update_subscription', kwargs={'account': account}, session=session)
126
- if not auth_result.allowed:
127
- raise AccessDenied('Account %s can not update subscription. %s' % (issuer, auth_result.message))
128
- try:
129
- if not isinstance(metadata, dict):
130
- raise TypeError('metadata should be a dict')
131
- if 'filter' in metadata and metadata['filter']:
132
- if not isinstance(metadata['filter'], dict):
133
- raise TypeError('filter should be a dict')
134
- validate_schema(name='subscription_filter', obj=metadata['filter'], vo=vo)
135
- if 'replication_rules' in metadata and metadata['replication_rules']:
136
- if not isinstance(metadata['replication_rules'], list):
137
- raise TypeError('replication_rules should be a list')
138
- else:
139
- for rule in metadata['replication_rules']:
140
- validate_schema(name='activity', obj=rule.get('activity', 'default'), vo=vo)
141
- except ValueError as error:
142
- raise TypeError(error)
117
+ with db_session(DatabaseOperationType.WRITE) as session:
118
+ auth_result = has_permission(issuer=issuer, vo=vo, action='update_subscription', kwargs={'account': account}, session=session)
119
+ if not auth_result.allowed:
120
+ raise AccessDenied('Account %s can not update subscription. %s' % (issuer, auth_result.message))
121
+ try:
122
+ if not isinstance(metadata, dict):
123
+ raise TypeError('metadata should be a dict')
124
+ if 'filter' in metadata and metadata['filter']:
125
+ if not isinstance(metadata['filter'], dict):
126
+ raise TypeError('filter should be a dict')
127
+ validate_schema(name='subscription_filter', obj=metadata['filter'], vo=vo)
128
+ if 'replication_rules' in metadata and metadata['replication_rules']:
129
+ if not isinstance(metadata['replication_rules'], list):
130
+ raise TypeError('replication_rules should be a list')
131
+ else:
132
+ for rule in metadata['replication_rules']:
133
+ validate_schema(name='activity', obj=rule.get('activity', 'default'), vo=vo)
134
+ except ValueError as error:
135
+ raise TypeError(error)
143
136
 
144
- internal_account = InternalAccount(account, vo=vo)
137
+ internal_account = InternalAccount(account, vo=vo)
145
138
 
146
- if 'filter' in metadata and metadata['filter'] is not None:
147
- filter_ = metadata['filter']
148
- keys = ['scope', 'account']
149
- types = [InternalScope, InternalAccount]
139
+ if 'filter' in metadata and metadata['filter'] is not None:
140
+ filter_ = metadata['filter']
141
+ keys = ['scope', 'account']
142
+ types = [InternalScope, InternalAccount]
150
143
 
151
- for _key, _type in zip(keys, types):
152
- if _key in filter_ and filter_[_key] is not None:
153
- if isinstance(filter_[_key], list):
154
- filter_[_key] = [_type(val, vo=vo).internal for val in filter_[_key]]
155
- else:
156
- filter_[_key] = _type(filter_[_key], vo=vo).internal
144
+ for _key, _type in zip(keys, types):
145
+ if _key in filter_ and filter_[_key] is not None:
146
+ if isinstance(filter_[_key], list):
147
+ filter_[_key] = [_type(val, vo=vo).internal for val in filter_[_key]]
148
+ else:
149
+ filter_[_key] = _type(filter_[_key], vo=vo).internal
157
150
 
158
- return subscription.update_subscription(name=name, account=internal_account, metadata=metadata, session=session)
151
+ return subscription.update_subscription(name=name, account=internal_account, metadata=metadata, session=session)
159
152
 
160
153
 
161
- @stream_session
162
154
  def list_subscriptions(
163
155
  name: Optional[str] = None,
164
156
  account: Optional[str] = None,
165
157
  state: Optional[str] = None,
166
158
  vo: str = 'def',
167
- *,
168
- session: "Session"
169
159
  ) -> 'Iterator[dict[str, Any]]':
170
160
  """
171
161
  Returns a dictionary with the subscription information :
@@ -175,7 +165,6 @@ def list_subscriptions(
175
165
  :param account: Account identifier
176
166
  :param state: Filter for subscription state
177
167
  :param vo: The VO to act on.
178
- :param session: The database session in use.
179
168
  :returns: Dictionary containing subscription parameter
180
169
  :raises: exception.NotFound if subscription is not found
181
170
  """
@@ -185,85 +174,78 @@ def list_subscriptions(
185
174
  else:
186
175
  internal_account = InternalAccount('*', vo=vo)
187
176
 
188
- subs = subscription.list_subscriptions(name, internal_account, state, session=session)
177
+ with db_session(DatabaseOperationType.READ) as session:
178
+ subs = subscription.list_subscriptions(name, internal_account, state, session=session)
189
179
 
190
- for sub in subs:
191
- sub['account'] = sub['account'].external
180
+ for sub in subs:
181
+ sub['account'] = sub['account'].external
192
182
 
193
- if 'filter' in sub:
194
- fil = loads(sub['filter'])
195
- if 'account' in fil:
196
- fil['account'] = [InternalAccount(acc, from_external=False).external for acc in fil['account']]
197
- if 'scope' in fil:
198
- fil['scope'] = [InternalScope(sco, from_external=False).external for sco in fil['scope']]
199
- sub['filter'] = dumps(fil)
183
+ if 'filter' in sub:
184
+ fil = loads(sub['filter'])
185
+ if 'account' in fil:
186
+ fil['account'] = [InternalAccount(acc, from_external=False).external for acc in fil['account']]
187
+ if 'scope' in fil:
188
+ fil['scope'] = [InternalScope(sco, from_external=False).external for sco in fil['scope']]
189
+ sub['filter'] = dumps(fil)
200
190
 
201
- yield sub
191
+ yield sub
202
192
 
203
193
 
204
- @stream_session
205
194
  def list_subscription_rule_states(
206
195
  name: Optional[str] = None,
207
196
  account: Optional[str] = None,
208
197
  vo: str = 'def',
209
- *,
210
- session: "Session"
211
198
  ) -> 'Iterator[SubscriptionRuleState]':
212
199
  """Returns a list of with the number of rules per state for a subscription.
213
200
 
214
201
  :param name: Name of the subscription
215
202
  :param account: Account identifier
216
203
  :param vo: The VO to act on.
217
- :param session: The database session in use.
218
204
  :returns: Sequence with SubscriptionRuleState named tuples (account, name, state, count)
219
205
  """
220
206
  if account is not None:
221
207
  internal_account = InternalAccount(account, vo=vo)
222
208
  else:
223
209
  internal_account = InternalAccount('*', vo=vo)
224
- subs = subscription.list_subscription_rule_states(name, internal_account, session=session)
225
- for sub in subs:
226
- # sub is an immutable Row so return new named tuple with edited entries
227
- d = sub._asdict()
228
- d['account'] = d['account'].external
229
- yield SubscriptionRuleState(**d)
210
+
211
+ with db_session(DatabaseOperationType.READ) as session:
212
+ subs = subscription.list_subscription_rule_states(name, internal_account, session=session)
213
+
214
+ for sub in subs:
215
+ # sub is an immutable Row so return new named tuple with edited entries
216
+ d = sub._asdict()
217
+ d['account'] = d['account'].external
218
+ yield SubscriptionRuleState(**d)
230
219
 
231
220
 
232
- @transactional_session
233
221
  def delete_subscription(
234
222
  subscription_id: str,
235
223
  vo: str = 'def',
236
- *,
237
- session: "Session"
238
224
  ) -> None:
239
225
  """
240
226
  Deletes a subscription
241
227
 
242
228
  :param subscription_id: Subscription identifier
243
229
  :param vo: The VO of the user issuing command
244
- :param session: The database session in use.
245
230
  """
246
231
 
247
232
  raise NotImplementedError
248
233
 
249
234
 
250
- @read_session
251
235
  def get_subscription_by_id(
252
236
  subscription_id: str,
253
237
  vo: str = 'def',
254
- *,
255
- session: "Session"
256
238
  ) -> dict[str, Any]:
257
239
  """
258
240
  Get a specific subscription by id.
259
241
 
260
242
  :param subscription_id: The subscription_id to select.
261
243
  :param vo: The VO of the user issuing command.
262
- :param session: The database session in use.
263
244
 
264
245
  :raises: SubscriptionNotFound if no Subscription can be found.
265
246
  """
266
- sub = subscription.get_subscription_by_id(subscription_id, session=session)
247
+ with db_session(DatabaseOperationType.READ) as session:
248
+ sub = subscription.get_subscription_by_id(subscription_id, session=session)
267
249
  if sub['account'].vo != vo:
268
250
  raise AccessDenied('Unable to get subscription')
269
251
 
rucio/gateway/trace.py ADDED
@@ -0,0 +1,48 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import datetime
16
+ import json
17
+ import uuid
18
+ from typing import Optional
19
+
20
+ from rucio.core.trace import trace as core_trace
21
+
22
+
23
+ def trace(request: bytes, trace_ip: Optional[str]) -> None:
24
+ """
25
+ Sends the trace data to trace broker after adding additional fields
26
+ Adds the following fields:
27
+ - 'traceTimeentry': The current UTC timestamp.
28
+ - 'traceTimeentryUnix': The Unix timestamp with microsecond precision.
29
+ - 'traceIp': The client's IP address, either from 'X-Forwarded-For' header or remote address.
30
+ - 'traceId': A unique identifier for the trace, generated as a UUID without hyphens.
31
+
32
+ Args:
33
+ request_data: Request json given by client
34
+ trace_ip: TraceIP, either the client's IP address, or IP from "X-Forwarded-For" header
35
+ """
36
+ request_data = json.loads(request)
37
+ if isinstance(request_data, dict):
38
+ request_data = [request_data]
39
+
40
+ for item in request_data:
41
+ item["traceIp"] = trace_ip
42
+ # generate entry timestamp
43
+ item["traceTimeentry"] = datetime.datetime.now(datetime.timezone.utc)
44
+ item["traceTimeentryUnix"] = item["traceTimeentry"].timestamp()
45
+ # generate unique ID
46
+ item["traceId"] = str(uuid.uuid4()).replace("-", "").lower()
47
+
48
+ core_trace(payload=item)
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': '37.2.0',
7
+ 'version': '37.4.0',
8
8
  'branch_nick': 'release-37',
9
- 'revision_id': 'ebd2ace1ae33234044fc2d70c35f6ae8f59127ce',
10
- 'revno': 13666
9
+ 'revision_id': '20eed71b1dd6d1d8e550966b7d40ea730a16c7d8',
10
+ 'revno': 13754
11
11
  }
@@ -217,7 +217,7 @@ class Scopes(ErrorHandlingMethodView):
217
217
  description: Not acceptable
218
218
  """
219
219
  try:
220
- scopes = get_scopes(account, vo=request.environ.get('vo'))
220
+ scopes = get_scopes(account, vo=request.environ['vo'])
221
221
  except AccountNotFound as error:
222
222
  return generate_http_error_flask(404, error)
223
223
 
@@ -264,7 +264,7 @@ class Scopes(ErrorHandlingMethodView):
264
264
  description: Scope already exists.
265
265
  """
266
266
  try:
267
- add_scope(scope, account, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
267
+ add_scope(scope, account, issuer=request.environ['issuer'], vo=request.environ['vo'])
268
268
  except InvalidObject as error:
269
269
  return generate_http_error_flask(400, error)
270
270
  except AccessDenied as error:
@@ -916,6 +916,9 @@ class GSS(ErrorHandlingMethodView):
916
916
  appid = request.headers.get('X-Rucio-AppID', default='unknown')
917
917
  ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
918
918
 
919
+ if not account or not gsscred:
920
+ return generate_http_error_flask(400, ValueError.__name__, 'Account and REMOTE_USER must be set.')
921
+
919
922
  try:
920
923
  result = get_auth_token_gss(account, gsscred, appid, ip, vo=vo)
921
924
  except AccessDenied:
@@ -1212,6 +1215,9 @@ class SSH(ErrorHandlingMethodView):
1212
1215
  appid = request.headers.get('X-Rucio-AppID', default='unknown')
1213
1216
  ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1214
1217
 
1218
+ if not account or not signature:
1219
+ return generate_http_error_flask(400, ValueError.__name__, 'Account and SSH signature must be set.')
1220
+
1215
1221
  try:
1216
1222
  result = get_auth_token_ssh(account, signature, appid, ip, vo=vo)
1217
1223
  except AccessDenied:
@@ -1332,6 +1338,9 @@ class SSHChallengeToken(ErrorHandlingMethodView):
1332
1338
  appid = request.headers.get('X-Rucio-AppID', default='unknown')
1333
1339
  ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1334
1340
 
1341
+ if not account:
1342
+ return generate_http_error_flask(400, ValueError.__name__, 'Account must be set.')
1343
+
1335
1344
  result = get_ssh_challenge_token(account, appid, ip, vo=vo)
1336
1345
 
1337
1346
  if not result:
@@ -1452,6 +1461,9 @@ class SAML(ErrorHandlingMethodView):
1452
1461
  appid = request.headers.get('X-Rucio-AppID', default='unknown')
1453
1462
  ip = request.headers.get('X-Forwarded-For', default=request.remote_addr)
1454
1463
 
1464
+ if not account:
1465
+ return generate_http_error_flask(400, ValueError.__name__, 'Account must be set.')
1466
+
1455
1467
  if saml_nameid:
1456
1468
  try:
1457
1469
  result = get_auth_token_saml(account, saml_nameid, appid, ip, vo=vo)
@@ -1592,6 +1604,9 @@ class Validate(ErrorHandlingMethodView):
1592
1604
 
1593
1605
  token = request.headers.get('X-Rucio-Auth-Token', default=None)
1594
1606
 
1607
+ if not token:
1608
+ return generate_http_error_flask(400, ValueError.__name__, 'Token must be set.')
1609
+
1595
1610
  result = validate_auth_token(token)
1596
1611
  if not result:
1597
1612
  return generate_http_error_flask(
@@ -148,6 +148,9 @@ def request_auth_env() -> Optional['ResponseReturnValue']:
148
148
 
149
149
  auth_token = flask.request.headers.get('X-Rucio-Auth-Token', default=None)
150
150
 
151
+ if not auth_token:
152
+ return generate_http_error_flask(400, ValueError.__name__, 'Token must be set.')
153
+
151
154
  try:
152
155
  auth = validate_auth_token(auth_token)
153
156
  except CannotAuthenticate:
@@ -46,9 +46,9 @@ class Config(ErrorHandlingMethodView):
46
46
  description: Not acceptable
47
47
  """
48
48
  res = {}
49
- for section in config.sections(issuer=request.environ.get('issuer'), vo=request.environ.get('vo')):
49
+ for section in config.sections(issuer=request.environ['issuer'], vo=request.environ['vo']):
50
50
  res[section] = {}
51
- for item in config.items(section, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')):
51
+ for item in config.items(section, issuer=request.environ['issuer'], vo=request.environ['vo']):
52
52
  res[section][item[0]] = item[1]
53
53
 
54
54
  return jsonify(res), 200
@@ -87,7 +87,7 @@ class Config(ErrorHandlingMethodView):
87
87
  return generate_http_error_flask(400, ValueError.__name__, '')
88
88
  for option, value in section_config.items():
89
89
  try:
90
- config.set(section=section, option=option, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
90
+ config.set(section=section, option=option, value=value, issuer=request.environ['issuer'], vo=request.environ['vo'])
91
91
  except ConfigurationError:
92
92
  return generate_http_error_flask(400, 'ConfigurationError', f"Could not set value '{value}' for section '{section}' option '{option}'")
93
93
  return 'Created', 201
@@ -137,7 +137,7 @@ class Section(ErrorHandlingMethodView):
137
137
  description: Not acceptable
138
138
  """
139
139
  res = {}
140
- for item in config.items(section, issuer=request.environ.get('issuer'), vo=request.environ.get('vo')):
140
+ for item in config.items(section, issuer=request.environ['issuer'], vo=request.environ['vo']):
141
141
  res[item[0]] = item[1]
142
142
 
143
143
  if res == {}:
@@ -190,7 +190,7 @@ class OptionGetDel(ErrorHandlingMethodView):
190
190
  description: Not acceptable
191
191
  """
192
192
  try:
193
- result = config.get(section=section, option=option, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
193
+ result = config.get(section=section, option=option, issuer=request.environ['issuer'], vo=request.environ['vo'])
194
194
  return jsonify(result), 200
195
195
  except AccessDenied as error:
196
196
  return generate_http_error_flask(401, error, f"Access to '{section}' option '{option}' denied")
@@ -223,7 +223,7 @@ class OptionGetDel(ErrorHandlingMethodView):
223
223
  401:
224
224
  description: Invalid Auth Token
225
225
  """
226
- config.remove_option(section=section, option=option, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
226
+ config.remove_option(section=section, option=option, issuer=request.environ['issuer'], vo=request.environ['vo'])
227
227
  return '', 200
228
228
 
229
229
 
@@ -275,7 +275,7 @@ class OptionSet(ErrorHandlingMethodView):
275
275
  enum: ['Could not set value {} for section {} option {}']
276
276
  """
277
277
  try:
278
- config.set(section=section, option=option, value=value, issuer=request.environ.get('issuer'), vo=request.environ.get('vo'))
278
+ config.set(section=section, option=option, value=value, issuer=request.environ['issuer'], vo=request.environ['vo'])
279
279
  return 'Created', 201
280
280
  except ConfigurationError as error:
281
281
  return generate_http_error_flask(500, error, f"Could not set value '{value}' for section '{section}' option '{option}'")