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.
- rucio/cli/bin_legacy/rucio.py +26 -23
- rucio/cli/command.py +36 -26
- rucio/cli/config.py +22 -7
- rucio/cli/did.py +1 -1
- rucio/cli/download.py +1 -1
- rucio/cli/opendata.py +60 -9
- rucio/cli/utils.py +13 -1
- rucio/client/configclient.py +23 -0
- rucio/client/richclient.py +6 -0
- rucio/common/constants.py +5 -0
- rucio/common/exception.py +10 -0
- rucio/common/plugins.py +24 -8
- rucio/common/utils.py +3 -3
- rucio/core/config.py +8 -6
- rucio/core/did_meta_plugins/did_column_meta.py +226 -69
- rucio/core/replica.py +3 -4
- rucio/core/request.py +7 -2
- rucio/core/rule.py +6 -3
- rucio/core/rule_grouping.py +2 -2
- rucio/daemons/abacus/account.py +1 -1
- rucio/daemons/abacus/collection_replica.py +1 -1
- rucio/daemons/abacus/rse.py +1 -1
- rucio/daemons/common.py +1 -1
- rucio/daemons/hermes/hermes.py +14 -13
- rucio/daemons/judge/cleaner.py +2 -2
- rucio/daemons/judge/evaluator.py +2 -2
- rucio/daemons/judge/injector.py +5 -4
- rucio/daemons/judge/repairer.py +2 -2
- rucio/gateway/config.py +2 -37
- rucio/gateway/rule.py +2 -2
- rucio/rse/translation.py +3 -3
- rucio/transfertool/fts3_plugins.py +3 -3
- rucio/vcsversion.py +3 -3
- rucio/web/rest/flaskapi/v1/config.py +52 -14
- {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/METADATA +1 -1
- {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/RECORD +95 -95
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/alembic.ini.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/alembic_offline.ini.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rucio.cfg.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/requirements.server.txt +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-abacus-account +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-admin +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-atropos +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-auditor +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-automatix +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-bb8 +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-cache-client +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-receiver +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-dumper +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-follower +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-hermes +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-injector +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-kronos +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-minos +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-minos-temporary-expiration +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-necromancer +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-reaper +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-transmogrifier +0 -0
- {rucio-38.1.0.data → rucio-38.3.0.data}/scripts/rucio-undertaker +0 -0
- {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/WHEEL +0 -0
- {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-38.1.0.dist-info → rucio-38.3.0.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import operator
|
|
16
16
|
from datetime import datetime, timedelta
|
|
17
|
-
from typing import TYPE_CHECKING
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
18
|
|
|
19
19
|
from sqlalchemy import inspect, update
|
|
20
20
|
from sqlalchemy.exc import CompileError, InvalidRequestError, NoResultFound
|
|
@@ -30,46 +30,95 @@ from rucio.db.sqla.constants import DIDType
|
|
|
30
30
|
from rucio.db.sqla.session import read_session, stream_session, transactional_session
|
|
31
31
|
|
|
32
32
|
if TYPE_CHECKING:
|
|
33
|
-
from
|
|
33
|
+
from collections.abc import Iterator
|
|
34
|
+
from typing import Literal, Optional, Union
|
|
34
35
|
|
|
35
36
|
from sqlalchemy.orm import Session
|
|
36
37
|
|
|
38
|
+
from rucio.common.types import InternalScope
|
|
39
|
+
|
|
37
40
|
|
|
38
41
|
class DidColumnMeta(DidMetaPlugin):
|
|
39
42
|
"""
|
|
40
43
|
A metadata plugin to interact with the base DID table metadata.
|
|
41
44
|
"""
|
|
42
|
-
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
"""Initialize the DID column metadata plugin."""
|
|
43
48
|
super(DidColumnMeta, self).__init__()
|
|
44
49
|
self.plugin_name = "DID_COLUMN"
|
|
45
50
|
|
|
46
51
|
@read_session
|
|
47
|
-
def get_metadata(
|
|
52
|
+
def get_metadata(
|
|
53
|
+
self,
|
|
54
|
+
scope: "InternalScope",
|
|
55
|
+
name: str,
|
|
56
|
+
*,
|
|
57
|
+
session: "Session",
|
|
58
|
+
) -> dict[str, Any]:
|
|
48
59
|
"""
|
|
49
|
-
Get data identifier
|
|
60
|
+
Get all the metadata of some data identifier.
|
|
50
61
|
|
|
51
|
-
:param scope: The scope
|
|
52
|
-
:param name: The
|
|
62
|
+
:param scope: The scope of the DID.
|
|
63
|
+
:param name: The name of the DID.
|
|
53
64
|
:param session: The database session in use.
|
|
65
|
+
:returns: DID metadata as a dictionary.
|
|
54
66
|
"""
|
|
55
67
|
try:
|
|
56
|
-
row = session.query(models.DataIdentifier).filter_by(scope=scope, name=name)
|
|
68
|
+
row = session.query(models.DataIdentifier).filter_by(scope=scope, name=name). \
|
|
57
69
|
with_hint(models.DataIdentifier, "INDEX(DIDS DIDS_PK)", 'oracle').one()
|
|
58
70
|
return row.to_dict()
|
|
59
71
|
except NoResultFound:
|
|
60
72
|
raise exception.DataIdentifierNotFound(f"Data identifier '{scope}:{name}' not found")
|
|
61
73
|
|
|
62
74
|
@transactional_session
|
|
63
|
-
def set_metadata(
|
|
75
|
+
def set_metadata(
|
|
76
|
+
self,
|
|
77
|
+
scope: "InternalScope",
|
|
78
|
+
name: str,
|
|
79
|
+
key: str,
|
|
80
|
+
value: Any,
|
|
81
|
+
recursive: bool = False,
|
|
82
|
+
*,
|
|
83
|
+
session: "Session",
|
|
84
|
+
) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Add a single key-value metadata pair to a data identifier.
|
|
87
|
+
|
|
88
|
+
:param scope: The scope of the DID.
|
|
89
|
+
:param name: The name of the DID.
|
|
90
|
+
:param key: The metadata key.
|
|
91
|
+
:param value: The metadata value.
|
|
92
|
+
:param recursive: Option to propagate the metadata updates to child content.
|
|
93
|
+
:param session: The database session in use.
|
|
94
|
+
"""
|
|
64
95
|
self.set_metadata_bulk(scope=scope, name=name, metadata={key: value}, recursive=recursive, session=session)
|
|
65
96
|
|
|
66
97
|
@transactional_session
|
|
67
|
-
def set_metadata_bulk(
|
|
68
|
-
|
|
98
|
+
def set_metadata_bulk(
|
|
99
|
+
self,
|
|
100
|
+
scope: "InternalScope",
|
|
101
|
+
name: str,
|
|
102
|
+
metadata: dict[str, Any],
|
|
103
|
+
recursive: bool = False,
|
|
104
|
+
*,
|
|
105
|
+
session: "Session",
|
|
106
|
+
) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Add multiple key-value metadata pairs to a data identifier.
|
|
109
|
+
|
|
110
|
+
:param scope: The scope of the DID.
|
|
111
|
+
:param name: The name of the DID.
|
|
112
|
+
:param metadata: All key-value metadata pairs to set.
|
|
113
|
+
:param recursive: Option to propagate the metadata updates to child content.
|
|
114
|
+
:param session: The database session in use.
|
|
115
|
+
"""
|
|
116
|
+
did_query = session.query(models.DataIdentifier).with_hint(models.DataIdentifier, "INDEX(DIDS DIDS_PK)",
|
|
117
|
+
'oracle').filter_by(scope=scope, name=name)
|
|
69
118
|
if did_query.one_or_none() is None:
|
|
70
119
|
raise exception.DataIdentifierNotFound("Data identifier '%s:%s' not found" % (scope, name))
|
|
71
120
|
|
|
72
|
-
remainder = {}
|
|
121
|
+
remainder: dict[Any, Any] = {}
|
|
73
122
|
for key, value in metadata.items():
|
|
74
123
|
if key == 'eol_at' and isinstance(value, str):
|
|
75
124
|
try:
|
|
@@ -92,51 +141,106 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
92
141
|
# check for DID presence
|
|
93
142
|
raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
|
|
94
143
|
elif key in ['guid', 'events']:
|
|
95
|
-
rowcount = did_query
|
|
144
|
+
rowcount = did_query \
|
|
145
|
+
.filter_by(did_type=DIDType.FILE) \
|
|
146
|
+
.update({key: value}, synchronize_session=False)
|
|
96
147
|
if not rowcount:
|
|
97
148
|
# check for DID presence
|
|
98
149
|
raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
|
|
99
150
|
|
|
100
|
-
session.query(models.DataIdentifierAssociation)
|
|
151
|
+
session.query(models.DataIdentifierAssociation) \
|
|
152
|
+
.filter_by(child_scope=scope, child_name=name, child_type=DIDType.FILE) \
|
|
153
|
+
.update({key: value}, synchronize_session=False)
|
|
101
154
|
if key == 'events':
|
|
102
|
-
for parent_scope, parent_name
|
|
103
|
-
|
|
104
|
-
|
|
155
|
+
for parent_scope, parent_name \
|
|
156
|
+
in session.query(models.DataIdentifierAssociation.scope,
|
|
157
|
+
models.DataIdentifierAssociation.name
|
|
158
|
+
).filter_by(child_scope=scope, child_name=name):
|
|
159
|
+
events = session.query(func.sum(models.DataIdentifierAssociation.events)) \
|
|
160
|
+
.filter_by(scope=parent_scope, name=parent_name).one()[0]
|
|
161
|
+
session.query(models.DataIdentifier) \
|
|
162
|
+
.filter_by(scope=parent_scope, name=parent_name) \
|
|
163
|
+
.update({'events': events}, synchronize_session=False)
|
|
105
164
|
elif key == 'adler32':
|
|
106
|
-
rowcount = did_query
|
|
165
|
+
rowcount = did_query \
|
|
166
|
+
.filter_by(did_type=DIDType.FILE) \
|
|
167
|
+
.update({key: value}, synchronize_session=False)
|
|
107
168
|
if not rowcount:
|
|
108
169
|
# check for DID presence
|
|
109
170
|
raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
|
|
110
171
|
|
|
111
|
-
session.query(models.DataIdentifierAssociation)
|
|
112
|
-
|
|
113
|
-
|
|
172
|
+
session.query(models.DataIdentifierAssociation) \
|
|
173
|
+
.filter_by(child_scope=scope, child_name=name, child_type=DIDType.FILE) \
|
|
174
|
+
.update({key: value}, synchronize_session=False)
|
|
175
|
+
session.query(models.Request) \
|
|
176
|
+
.filter_by(scope=scope, name=name) \
|
|
177
|
+
.update({key: value}, synchronize_session=False)
|
|
178
|
+
session.query(models.RSEFileAssociation) \
|
|
179
|
+
.filter_by(scope=scope, name=name) \
|
|
180
|
+
.update({key: value}, synchronize_session=False)
|
|
114
181
|
elif key == 'bytes':
|
|
115
|
-
rowcount = did_query
|
|
182
|
+
rowcount = did_query \
|
|
183
|
+
.filter_by(did_type=DIDType.FILE) \
|
|
184
|
+
.update({key: value}, synchronize_session=False)
|
|
116
185
|
if not rowcount:
|
|
117
186
|
# check for DID presence
|
|
118
187
|
raise exception.UnsupportedOperation('%s for %s:%s cannot be updated' % (key, scope, name))
|
|
119
188
|
|
|
120
|
-
session.query(models.DataIdentifierAssociation)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
189
|
+
session.query(models.DataIdentifierAssociation) \
|
|
190
|
+
.filter_by(child_scope=scope, child_name=name, child_type=DIDType.FILE) \
|
|
191
|
+
.update({key: value}, synchronize_session=False)
|
|
192
|
+
session.query(models.Request) \
|
|
193
|
+
.filter_by(scope=scope, name=name) \
|
|
194
|
+
.update({key: value}, synchronize_session=False)
|
|
195
|
+
|
|
196
|
+
for account, bytes_, rse_id, rule_id \
|
|
197
|
+
in session.query(models.ReplicaLock.account,
|
|
198
|
+
models.ReplicaLock.bytes,
|
|
199
|
+
models.ReplicaLock.rse_id,
|
|
200
|
+
models.ReplicaLock.rule_id
|
|
201
|
+
).filter_by(scope=scope, name=name):
|
|
202
|
+
session.query(models.ReplicaLock) \
|
|
203
|
+
.filter_by(scope=scope, name=name, rule_id=rule_id, rse_id=rse_id) \
|
|
204
|
+
.update({key: value}, synchronize_session=False)
|
|
125
205
|
account_counter.decrease(rse_id=rse_id, account=account, files=1, bytes_=bytes_, session=session)
|
|
126
206
|
account_counter.increase(rse_id=rse_id, account=account, files=1, bytes_=value, session=session)
|
|
127
207
|
|
|
128
|
-
for bytes_, rse_id
|
|
129
|
-
|
|
208
|
+
for bytes_, rse_id \
|
|
209
|
+
in session.query(models.RSEFileAssociation.bytes,
|
|
210
|
+
models.RSEFileAssociation.rse_id
|
|
211
|
+
).filter_by(scope=scope, name=name):
|
|
212
|
+
session.query(models.RSEFileAssociation) \
|
|
213
|
+
.filter_by(scope=scope, name=name, rse_id=rse_id) \
|
|
214
|
+
.update({key: value}, synchronize_session=False)
|
|
130
215
|
rse_counter.decrease(rse_id=rse_id, files=1, bytes_=bytes_, session=session)
|
|
131
216
|
rse_counter.increase(rse_id=rse_id, files=1, bytes_=value, session=session)
|
|
132
217
|
|
|
133
|
-
for parent_scope, parent_name
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
218
|
+
for parent_scope, parent_name \
|
|
219
|
+
in session.query(models.DataIdentifierAssociation.scope,
|
|
220
|
+
models.DataIdentifierAssociation.name
|
|
221
|
+
).filter_by(child_scope=scope, child_name=name):
|
|
222
|
+
values: dict[Any, Any] = {
|
|
223
|
+
'length': (session
|
|
224
|
+
.query(func.count(models.DataIdentifierAssociation.scope),
|
|
225
|
+
func.sum(models.DataIdentifierAssociation.bytes),
|
|
226
|
+
func.sum(models.DataIdentifierAssociation.events))
|
|
227
|
+
.filter_by(scope=parent_scope, name=parent_name).one())[0],
|
|
228
|
+
'bytes': (session
|
|
229
|
+
.query(func.count(models.DataIdentifierAssociation.scope),
|
|
230
|
+
func.sum(models.DataIdentifierAssociation.bytes),
|
|
231
|
+
func.sum(models.DataIdentifierAssociation.events))
|
|
232
|
+
.filter_by(scope=parent_scope, name=parent_name).one())[1],
|
|
233
|
+
'events': (session
|
|
234
|
+
.query(func.count(models.DataIdentifierAssociation.scope),
|
|
235
|
+
func.sum(models.DataIdentifierAssociation.bytes),
|
|
236
|
+
func.sum(models.DataIdentifierAssociation.events))
|
|
237
|
+
.filter_by(scope=parent_scope, name=parent_name).one())[2]}
|
|
238
|
+
session.query(models.DataIdentifier) \
|
|
239
|
+
.filter_by(scope=parent_scope, name=parent_name) \
|
|
240
|
+
.update(values, synchronize_session=False)
|
|
241
|
+
session.query(models.DatasetLock) \
|
|
242
|
+
.filter_by(scope=parent_scope, name=parent_name) \
|
|
243
|
+
.update({'length': values['length'], 'bytes': values['bytes']}, synchronize_session=False)
|
|
140
244
|
else:
|
|
141
245
|
remainder[key] = value
|
|
142
246
|
|
|
@@ -148,42 +252,63 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
148
252
|
except InvalidRequestError:
|
|
149
253
|
raise exception.InvalidMetadata("Some of the keys are not accepted: " + str(list(remainder.keys())))
|
|
150
254
|
if not rowcount:
|
|
151
|
-
raise exception.UnsupportedOperation(
|
|
255
|
+
raise exception.UnsupportedOperation(
|
|
256
|
+
'Some of the keys for %s:%s cannot be updated: %s' % (scope, name, str(list(remainder.keys()))))
|
|
152
257
|
|
|
153
258
|
# propagate metadata updates to child content
|
|
154
259
|
if recursive:
|
|
155
|
-
content_query = session.query(models.DataIdentifierAssociation.child_scope,
|
|
156
|
-
|
|
260
|
+
content_query = session.query(models.DataIdentifierAssociation.child_scope,
|
|
261
|
+
models.DataIdentifierAssociation.child_name)
|
|
262
|
+
content_query = content_query.with_hint(models.DataIdentifierAssociation, "INDEX(CONTENTS CONTENTS_PK)",
|
|
263
|
+
'oracle').filter_by(scope=scope, name=name)
|
|
157
264
|
|
|
158
265
|
for child_scope, child_name in content_query:
|
|
159
266
|
try:
|
|
160
|
-
stmt = update(models.DataIdentifier)\
|
|
161
|
-
.prefix_with("/*+ INDEX(DIDS DIDS_PK) */", dialect='oracle')\
|
|
162
|
-
.filter_by(scope=child_scope, name=child_name)\
|
|
163
|
-
.execution_options(synchronize_session='fetch')\
|
|
267
|
+
stmt = update(models.DataIdentifier) \
|
|
268
|
+
.prefix_with("/*+ INDEX(DIDS DIDS_PK) */", dialect='oracle') \
|
|
269
|
+
.filter_by(scope=child_scope, name=child_name) \
|
|
270
|
+
.execution_options(synchronize_session='fetch') \
|
|
164
271
|
.values(remainder)
|
|
165
272
|
session.execute(stmt)
|
|
166
273
|
except CompileError as error:
|
|
167
274
|
raise exception.InvalidMetadata(error)
|
|
168
275
|
except InvalidRequestError:
|
|
169
|
-
raise exception.InvalidMetadata(
|
|
276
|
+
raise exception.InvalidMetadata(
|
|
277
|
+
"Some of the keys are not accepted recursively: " + str(list(remainder.keys())))
|
|
170
278
|
|
|
171
279
|
@stream_session
|
|
172
|
-
def list_dids(
|
|
173
|
-
|
|
280
|
+
def list_dids(
|
|
281
|
+
self,
|
|
282
|
+
scope: "InternalScope",
|
|
283
|
+
filters: "Union[dict[str, Any], list[dict[str, Any]]]",
|
|
284
|
+
did_type: "Literal['all', 'collection', 'dataset', 'container', 'file']" = 'collection',
|
|
285
|
+
ignore_case: bool = False,
|
|
286
|
+
limit: "Optional[int]" = None,
|
|
287
|
+
offset: "Optional[int]" = None,
|
|
288
|
+
long: bool = False,
|
|
289
|
+
recursive: bool = False,
|
|
290
|
+
ignore_dids: "Optional[set[str]]" = None,
|
|
291
|
+
*,
|
|
292
|
+
session: "Session",
|
|
293
|
+
) -> "Iterator[Union[str, dict[str, Any]]]":
|
|
174
294
|
"""
|
|
175
295
|
Search data identifiers.
|
|
176
296
|
|
|
177
|
-
:param scope:
|
|
178
|
-
:param filters:
|
|
179
|
-
|
|
180
|
-
:param
|
|
181
|
-
|
|
182
|
-
:param
|
|
183
|
-
:param
|
|
297
|
+
:param scope: The scope of the DIDs to list.
|
|
298
|
+
:param filters: A single dict or a list of dicts representing OR groups (disjunction).
|
|
299
|
+
Each group can include a semantic 'type' expanded into did_type filters.
|
|
300
|
+
:param did_type: Option to filter by a specific DID type:
|
|
301
|
+
all(container, dataset, file), collection(dataset or container), dataset, container, file.
|
|
302
|
+
:param ignore_case: Has no effect.
|
|
303
|
+
:param limit: Option to limit the number of returned results.
|
|
304
|
+
:param offset: Has no effect.
|
|
305
|
+
:param long: Option to display more information for each DID.
|
|
306
|
+
:param recursive: Option to recursively list child-DIDs content.
|
|
307
|
+
:param ignore_dids: A set of 'scope:name' strings to de-duplicate results across OR groups and recursion.
|
|
184
308
|
:param session: The database session in use.
|
|
185
|
-
:
|
|
186
|
-
|
|
309
|
+
:yields:
|
|
310
|
+
- If long is False: DID names (str).
|
|
311
|
+
- If long is True: dicts with keys: {'scope', 'name', 'did_type', 'bytes', 'length'}.
|
|
187
312
|
"""
|
|
188
313
|
if not ignore_dids:
|
|
189
314
|
ignore_dids = set()
|
|
@@ -202,7 +327,8 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
202
327
|
filters = [filters]
|
|
203
328
|
|
|
204
329
|
# for each or_group, make sure there is a mapped "did_type" filter.
|
|
205
|
-
# if type maps to many DIDTypes, the corresponding or_group will be copied the
|
|
330
|
+
# if type maps to many DIDTypes, the corresponding or_group will be copied the
|
|
331
|
+
# required number of times to satisfy all the logical possibilities.
|
|
206
332
|
filters_tmp = []
|
|
207
333
|
for or_group in filters:
|
|
208
334
|
if 'type' not in or_group:
|
|
@@ -210,7 +336,8 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
210
336
|
else:
|
|
211
337
|
or_group_type = or_group.pop('type').lower()
|
|
212
338
|
if or_group_type not in type_to_did_type_mapping.keys():
|
|
213
|
-
raise exception.UnsupportedOperation(
|
|
339
|
+
raise exception.UnsupportedOperation(
|
|
340
|
+
'{} is not a valid type. Valid types are {}'.format(or_group_type, type_to_did_type_mapping.keys()))
|
|
214
341
|
|
|
215
342
|
for mapped_did_type in type_to_did_type_mapping[or_group_type]:
|
|
216
343
|
or_group['did_type'] = mapped_did_type
|
|
@@ -245,24 +372,32 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
245
372
|
if recursive:
|
|
246
373
|
from rucio.core.did import list_content
|
|
247
374
|
|
|
248
|
-
# Get attached DIDs and save in list because query has to be finished before starting a new one in the recursion
|
|
375
|
+
# Get attached DIDs and save in a list because the query has to be finished before starting a new one in the recursion
|
|
249
376
|
collections_content = []
|
|
250
377
|
for did in session.execute(stmt).yield_per(100):
|
|
251
|
-
if
|
|
378
|
+
if did.did_type == DIDType.CONTAINER or did.did_type == DIDType.DATASET:
|
|
252
379
|
collections_content += [d for d in list_content(scope=did.scope, name=did.name)]
|
|
253
380
|
|
|
254
381
|
# Replace any name filtering with recursed DID names.
|
|
255
382
|
for did in collections_content:
|
|
256
383
|
for or_group in filters:
|
|
257
384
|
or_group['name'] = did['name']
|
|
258
|
-
for result in self.list_dids(scope=did['scope'],
|
|
259
|
-
|
|
385
|
+
for result in self.list_dids(scope=did['scope'],
|
|
386
|
+
filters=filters,
|
|
387
|
+
recursive=True,
|
|
388
|
+
did_type=did_type,
|
|
389
|
+
limit=limit,
|
|
390
|
+
offset=offset,
|
|
391
|
+
long=long,
|
|
392
|
+
ignore_dids=ignore_dids,
|
|
393
|
+
session=session):
|
|
260
394
|
yield result
|
|
261
395
|
|
|
262
|
-
for did in session.execute(stmt).yield_per(
|
|
396
|
+
for did in session.execute(stmt).yield_per(
|
|
397
|
+
5): # don't unpack this as it makes it dependent on query return order!
|
|
263
398
|
if long:
|
|
264
399
|
did_full = "{}:{}".format(did.scope, did.name)
|
|
265
|
-
if did_full not in ignore_dids:
|
|
400
|
+
if did_full not in ignore_dids: # concatenating results of OR clauses may contain duplicate DIDs if the query result sets not mutually exclusive.
|
|
266
401
|
ignore_dids.add(did_full)
|
|
267
402
|
yield {
|
|
268
403
|
'scope': did.scope,
|
|
@@ -273,21 +408,41 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
273
408
|
}
|
|
274
409
|
else:
|
|
275
410
|
did_full = "{}:{}".format(did.scope, did.name)
|
|
276
|
-
if did_full not in ignore_dids:
|
|
411
|
+
if did_full not in ignore_dids: # concatenating results of OR clauses may contain duplicate DIDs if the query result sets not mutually exclusive.
|
|
277
412
|
ignore_dids.add(did_full)
|
|
278
413
|
yield did.name
|
|
279
414
|
|
|
280
|
-
def delete_metadata(
|
|
415
|
+
def delete_metadata(
|
|
416
|
+
self,
|
|
417
|
+
scope: "InternalScope",
|
|
418
|
+
name: str,
|
|
419
|
+
key: str,
|
|
420
|
+
*,
|
|
421
|
+
session: "Optional[Session]" = None,
|
|
422
|
+
) -> None:
|
|
281
423
|
"""
|
|
282
|
-
Deletes the metadata stored for the given key.
|
|
424
|
+
Deletes the metadata stored for the given key. (Currently not implemented)
|
|
283
425
|
|
|
284
426
|
:param scope: The scope of the DID.
|
|
285
427
|
:param name: The name of the DID.
|
|
286
428
|
:param key: Key of the metadata.
|
|
429
|
+
:param session: The database session in use.
|
|
287
430
|
"""
|
|
288
431
|
raise NotImplementedError('The DidColumnMeta plugin does not currently support deleting metadata.')
|
|
289
432
|
|
|
290
|
-
def manages_key(
|
|
433
|
+
def manages_key(
|
|
434
|
+
self,
|
|
435
|
+
key: str,
|
|
436
|
+
*,
|
|
437
|
+
session: "Optional[Session]" = None,
|
|
438
|
+
) -> bool:
|
|
439
|
+
"""
|
|
440
|
+
Return whether a metadata key is managed by this plugin.
|
|
441
|
+
|
|
442
|
+
:param key: Key of the metadata.
|
|
443
|
+
:param session: Unused; accepted for interface compatibility.
|
|
444
|
+
:returns: ``True`` if the key is managed by this plugin, else ``False``.
|
|
445
|
+
"""
|
|
291
446
|
# Build list of which keys are managed by this plugin.
|
|
292
447
|
#
|
|
293
448
|
all_did_table_columns = []
|
|
@@ -323,9 +478,11 @@ class DidColumnMeta(DidMetaPlugin):
|
|
|
323
478
|
|
|
324
479
|
return key in hardcoded_keys
|
|
325
480
|
|
|
326
|
-
def get_plugin_name(
|
|
481
|
+
def get_plugin_name(
|
|
482
|
+
self
|
|
483
|
+
) -> str:
|
|
327
484
|
"""
|
|
328
|
-
|
|
485
|
+
Return a unique identifier for this plugin.
|
|
329
486
|
:returns: The name of the plugin.
|
|
330
487
|
"""
|
|
331
488
|
return self.plugin_name
|
rucio/core/replica.py
CHANGED
|
@@ -22,7 +22,6 @@ from curses.ascii import isprint
|
|
|
22
22
|
from datetime import datetime, timedelta
|
|
23
23
|
from hashlib import sha256
|
|
24
24
|
from itertools import groupby
|
|
25
|
-
from json import dumps
|
|
26
25
|
from re import match
|
|
27
26
|
from struct import unpack
|
|
28
27
|
from traceback import format_exc
|
|
@@ -2304,9 +2303,9 @@ def __cleanup_after_replica_deletion(
|
|
|
2304
2303
|
for scope, name, did_type in session.execute(stmt):
|
|
2305
2304
|
if did_type == DIDType.DATASET:
|
|
2306
2305
|
messages.append({'event_type': 'ERASE',
|
|
2307
|
-
'payload':
|
|
2308
|
-
|
|
2309
|
-
|
|
2306
|
+
'payload': {'scope': scope.external,
|
|
2307
|
+
'name': name,
|
|
2308
|
+
'account': 'root'}})
|
|
2310
2309
|
dids_to_delete.add(ScopeName(scope=scope, name=name))
|
|
2311
2310
|
|
|
2312
2311
|
# Remove Archive Constituents
|
rucio/core/request.py
CHANGED
|
@@ -22,6 +22,7 @@ import threading
|
|
|
22
22
|
import traceback
|
|
23
23
|
from abc import ABCMeta, abstractmethod
|
|
24
24
|
from collections import defaultdict, namedtuple
|
|
25
|
+
from collections.abc import Sized
|
|
25
26
|
from dataclasses import dataclass
|
|
26
27
|
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
27
28
|
|
|
@@ -310,7 +311,11 @@ def queue_requests(
|
|
|
310
311
|
:param logger: Optional decorated logger that can be passed from the calling daemons or servers.
|
|
311
312
|
:returns: List of Request-IDs as 32 character hex strings.
|
|
312
313
|
"""
|
|
313
|
-
|
|
314
|
+
|
|
315
|
+
if isinstance(requests, Sized):
|
|
316
|
+
logger(logging.DEBUG, "Queuing %d requests", len(requests))
|
|
317
|
+
else:
|
|
318
|
+
logger(logging.DEBUG, "Queuing requests")
|
|
314
319
|
|
|
315
320
|
request_clause = []
|
|
316
321
|
rses = {}
|
|
@@ -1508,7 +1513,7 @@ class TransferStatsManager:
|
|
|
1508
1513
|
def __enter__(self) -> "TransferStatsManager":
|
|
1509
1514
|
self.record_stats = config_get_bool('transfers', 'stats_enabled', default=self.record_stats)
|
|
1510
1515
|
downsample_period = config_get_int('transfers', 'stats_downsample_period', default=self.downsample_period)
|
|
1511
|
-
# Introduce some voluntary jitter to reduce the
|
|
1516
|
+
# Introduce some voluntary jitter to reduce the likelihood of performing this database
|
|
1512
1517
|
# operation multiple times in parallel.
|
|
1513
1518
|
self.downsample_period = random.randint(downsample_period * 3 // 4, math.ceil(downsample_period * 5 / 4)) # noqa: S311
|
|
1514
1519
|
if self.record_stats:
|
rucio/core/rule.py
CHANGED
|
@@ -37,7 +37,7 @@ import rucio.core.lock # import get_replica_locks, get_files_and_replica_locks_
|
|
|
37
37
|
import rucio.core.replica # import get_and_lock_file_replicas, get_and_lock_file_replicas_for_dataset
|
|
38
38
|
from rucio.common.cache import MemcacheRegion
|
|
39
39
|
from rucio.common.config import config_get
|
|
40
|
-
from rucio.common.constants import DEFAULT_VO, RseAttr
|
|
40
|
+
from rucio.common.constants import DEFAULT_ACTIVITY, DEFAULT_VO, POLICY_ALGORITHM_TYPES_LITERAL, RseAttr
|
|
41
41
|
from rucio.common.exception import (
|
|
42
42
|
DataIdentifierNotFound,
|
|
43
43
|
DuplicateRule,
|
|
@@ -98,7 +98,7 @@ class AutoApprove(PolicyPackageAlgorithms):
|
|
|
98
98
|
Handle automatic approval algorithms for replication rules
|
|
99
99
|
"""
|
|
100
100
|
|
|
101
|
-
_algorithm_type = 'auto_approve'
|
|
101
|
+
_algorithm_type: POLICY_ALGORITHM_TYPES_LITERAL = 'auto_approve'
|
|
102
102
|
|
|
103
103
|
def __init__(self, rule: models.ReplicationRule, did: models.DataIdentifier, session: 'Session', vo: str = DEFAULT_VO) -> None:
|
|
104
104
|
super().__init__()
|
|
@@ -180,7 +180,7 @@ def add_rule(
|
|
|
180
180
|
locked: bool,
|
|
181
181
|
subscription_id: Optional[str],
|
|
182
182
|
source_replica_expression: Optional[str] = None,
|
|
183
|
-
activity: str =
|
|
183
|
+
activity: Optional[str] = None,
|
|
184
184
|
notify: Optional[Literal['Y', 'N', 'C', 'P']] = None,
|
|
185
185
|
purge_replicas: bool = False,
|
|
186
186
|
ignore_availability: bool = False,
|
|
@@ -232,6 +232,9 @@ def add_rule(
|
|
|
232
232
|
if copies <= 0:
|
|
233
233
|
raise InvalidValueForKey("The number of copies for a replication rule should be greater than 0.")
|
|
234
234
|
|
|
235
|
+
if not activity:
|
|
236
|
+
activity = DEFAULT_ACTIVITY
|
|
237
|
+
|
|
235
238
|
rule_ids = []
|
|
236
239
|
|
|
237
240
|
grouping_value = {'ALL': RuleGrouping.ALL, 'NONE': RuleGrouping.NONE}.get(grouping, RuleGrouping.DATASET)
|
rucio/core/rule_grouping.py
CHANGED
|
@@ -921,8 +921,8 @@ def __is_retry_required(lock, activity):
|
|
|
921
921
|
:param activity: The activity of the rule.
|
|
922
922
|
"""
|
|
923
923
|
|
|
924
|
-
created_at_diff = (datetime.utcnow() - lock.created_at).
|
|
925
|
-
updated_at_diff = (datetime.utcnow() - lock.updated_at).
|
|
924
|
+
created_at_diff = (datetime.utcnow() - lock.created_at).total_seconds()
|
|
925
|
+
updated_at_diff = (datetime.utcnow() - lock.updated_at).total_seconds()
|
|
926
926
|
|
|
927
927
|
if activity == 'Express':
|
|
928
928
|
if updated_at_diff > 3600 * 2:
|
rucio/daemons/abacus/account.py
CHANGED
|
@@ -66,7 +66,7 @@ def run_once(
|
|
|
66
66
|
|
|
67
67
|
# If the list is empty, sent the worker to sleep
|
|
68
68
|
if not updated_account_counters:
|
|
69
|
-
logger(logging.INFO, '
|
|
69
|
+
logger(logging.INFO, 'Did not get any work')
|
|
70
70
|
return
|
|
71
71
|
|
|
72
72
|
for account_counter in updated_account_counters:
|
|
@@ -71,7 +71,7 @@ def run_once(
|
|
|
71
71
|
logger(logging.DEBUG, 'Index query time %f size=%d' % (time.time() - start, len(replicas)))
|
|
72
72
|
# If the list is empty, sent the worker to sleep
|
|
73
73
|
if not replicas:
|
|
74
|
-
logger(logging.INFO, '
|
|
74
|
+
logger(logging.INFO, 'Did not get any work')
|
|
75
75
|
must_sleep = True
|
|
76
76
|
return must_sleep
|
|
77
77
|
|
rucio/daemons/abacus/rse.py
CHANGED
rucio/daemons/common.py
CHANGED
|
@@ -200,7 +200,7 @@ def db_workqueue(
|
|
|
200
200
|
|
|
201
201
|
with HeartbeatHandler(executable=executable, renewal_interval=sleep_time - 1) as heartbeat_handler:
|
|
202
202
|
logger = heartbeat_handler.logger
|
|
203
|
-
logger(logging.INFO, 'started')
|
|
203
|
+
logger(logging.INFO, 'Daemon started')
|
|
204
204
|
|
|
205
205
|
if partition_wait_time:
|
|
206
206
|
graceful_stop.wait(partition_wait_time)
|
rucio/daemons/hermes/hermes.py
CHANGED
|
@@ -799,19 +799,20 @@ def run_once(heartbeat_handler: "HeartbeatHandler", bulk: int, **_kwargs) -> boo
|
|
|
799
799
|
except Exception as error:
|
|
800
800
|
logger(logging.ERROR, "Error sending to ActiveMQ : %s", str(error))
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
802
|
+
logger(logging.INFO, "Deleting %s messages", len(to_delete))
|
|
803
|
+
to_delete = [
|
|
804
|
+
{
|
|
805
|
+
"id": message["id"],
|
|
806
|
+
"created_at": message["created_at"],
|
|
807
|
+
"updated_at": message["created_at"],
|
|
808
|
+
"payload": str(message["payload"]),
|
|
809
|
+
"event_type": message["event_type"],
|
|
810
|
+
"services": message["services"]
|
|
811
|
+
}
|
|
812
|
+
for message in to_delete
|
|
813
|
+
]
|
|
814
|
+
delete_messages(messages=to_delete)
|
|
815
|
+
|
|
815
816
|
must_sleep = True
|
|
816
817
|
return must_sleep
|
|
817
818
|
|
rucio/daemons/judge/cleaner.py
CHANGED
|
@@ -85,10 +85,10 @@ def run_once(
|
|
|
85
85
|
worker_number=worker_number,
|
|
86
86
|
limit=200,
|
|
87
87
|
blocked_rules=[key for key in paused_rules])
|
|
88
|
-
logger(logging.DEBUG, '
|
|
88
|
+
logger(logging.DEBUG, 'Index query time %f fetch size is %d' % (time.time() - start, len(rules)))
|
|
89
89
|
|
|
90
90
|
if not rules:
|
|
91
|
-
logger(logging.DEBUG, '
|
|
91
|
+
logger(logging.DEBUG, 'Did not get any work (paused_rules=%s)' % str(len(paused_rules)))
|
|
92
92
|
return
|
|
93
93
|
|
|
94
94
|
for rule in rules:
|