rucio 35.7.0__py3-none-any.whl → 37.0.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/alembicrevision.py +1 -1
- rucio/{daemons/c3po/collectors → cli}/__init__.py +1 -0
- rucio/cli/account.py +216 -0
- rucio-35.7.0.data/scripts/rucio → rucio/cli/bin_legacy/rucio.py +769 -486
- rucio-35.7.0.data/scripts/rucio-admin → rucio/cli/bin_legacy/rucio_admin.py +476 -423
- rucio/cli/command.py +272 -0
- rucio/cli/config.py +72 -0
- rucio/cli/did.py +191 -0
- rucio/cli/download.py +128 -0
- rucio/cli/lifetime_exception.py +33 -0
- rucio/cli/replica.py +162 -0
- rucio/cli/rse.py +293 -0
- rucio/cli/rule.py +158 -0
- rucio/cli/scope.py +40 -0
- rucio/cli/subscription.py +73 -0
- rucio/cli/upload.py +60 -0
- rucio/cli/utils.py +226 -0
- rucio/client/accountclient.py +0 -1
- rucio/client/baseclient.py +33 -24
- rucio/client/client.py +45 -1
- rucio/client/didclient.py +5 -3
- rucio/client/downloadclient.py +6 -8
- rucio/client/replicaclient.py +0 -2
- rucio/client/richclient.py +317 -0
- rucio/client/rseclient.py +4 -4
- rucio/client/uploadclient.py +26 -12
- rucio/common/bittorrent.py +234 -0
- rucio/common/cache.py +66 -29
- rucio/common/checksum.py +168 -0
- rucio/common/client.py +122 -0
- rucio/common/config.py +22 -35
- rucio/common/constants.py +61 -3
- rucio/common/didtype.py +72 -24
- rucio/common/dumper/__init__.py +45 -38
- rucio/common/dumper/consistency.py +75 -30
- rucio/common/dumper/data_models.py +63 -19
- rucio/common/dumper/path_parsing.py +19 -8
- rucio/common/exception.py +65 -8
- rucio/common/extra.py +5 -10
- rucio/common/logging.py +13 -13
- rucio/common/pcache.py +8 -7
- rucio/common/plugins.py +59 -27
- rucio/common/policy.py +12 -3
- rucio/common/schema/__init__.py +84 -34
- rucio/common/schema/generic.py +0 -17
- rucio/common/schema/generic_multi_vo.py +0 -17
- rucio/common/test_rucio_server.py +12 -6
- rucio/common/types.py +132 -52
- rucio/common/utils.py +93 -643
- rucio/core/account_limit.py +14 -12
- rucio/core/authentication.py +2 -2
- rucio/core/config.py +23 -42
- rucio/core/credential.py +14 -15
- rucio/core/did.py +5 -1
- rucio/core/did_meta_plugins/elasticsearch_meta.py +407 -0
- rucio/core/did_meta_plugins/filter_engine.py +62 -3
- rucio/core/did_meta_plugins/json_meta.py +2 -2
- rucio/core/did_meta_plugins/mongo_meta.py +43 -30
- rucio/core/did_meta_plugins/postgres_meta.py +75 -39
- rucio/core/identity.py +6 -5
- rucio/core/importer.py +4 -3
- rucio/core/lifetime_exception.py +2 -2
- rucio/core/lock.py +8 -7
- rucio/core/message.py +6 -0
- rucio/core/monitor.py +30 -29
- rucio/core/naming_convention.py +2 -2
- rucio/core/nongrid_trace.py +2 -2
- rucio/core/oidc.py +11 -9
- rucio/core/permission/__init__.py +79 -37
- rucio/core/permission/generic.py +1 -7
- rucio/core/permission/generic_multi_vo.py +1 -7
- rucio/core/quarantined_replica.py +4 -3
- rucio/core/replica.py +464 -139
- rucio/core/replica_sorter.py +55 -59
- rucio/core/request.py +34 -32
- rucio/core/rse.py +301 -97
- rucio/core/rse_counter.py +1 -2
- rucio/core/rse_expression_parser.py +7 -7
- rucio/core/rse_selector.py +9 -7
- rucio/core/rule.py +41 -40
- rucio/core/rule_grouping.py +42 -40
- rucio/core/scope.py +5 -4
- rucio/core/subscription.py +26 -28
- rucio/core/topology.py +11 -11
- rucio/core/trace.py +2 -2
- rucio/core/transfer.py +29 -15
- rucio/core/volatile_replica.py +4 -3
- rucio/daemons/atropos/atropos.py +1 -1
- rucio/daemons/auditor/__init__.py +2 -2
- rucio/daemons/auditor/srmdumps.py +6 -6
- rucio/daemons/automatix/automatix.py +32 -21
- rucio/daemons/badreplicas/necromancer.py +2 -2
- rucio/daemons/bb8/nuclei_background_rebalance.py +1 -1
- rucio/daemons/bb8/t2_background_rebalance.py +1 -1
- rucio/daemons/common.py +15 -25
- rucio/daemons/conveyor/finisher.py +2 -2
- rucio/daemons/conveyor/poller.py +18 -28
- rucio/daemons/conveyor/receiver.py +2 -2
- rucio/daemons/conveyor/stager.py +1 -0
- rucio/daemons/conveyor/submitter.py +3 -3
- rucio/daemons/hermes/hermes.py +91 -30
- rucio/daemons/judge/evaluator.py +2 -2
- rucio/daemons/oauthmanager/oauthmanager.py +3 -3
- rucio/daemons/reaper/dark_reaper.py +7 -3
- rucio/daemons/reaper/reaper.py +12 -16
- rucio/daemons/rsedecommissioner/config.py +1 -1
- rucio/daemons/rsedecommissioner/profiles/generic.py +5 -4
- rucio/daemons/rsedecommissioner/profiles/types.py +7 -6
- rucio/daemons/rsedecommissioner/rse_decommissioner.py +1 -1
- rucio/daemons/storage/consistency/actions.py +8 -6
- rucio/daemons/tracer/kronos.py +4 -4
- rucio/db/sqla/constants.py +5 -0
- rucio/db/sqla/migrate_repo/versions/1677d4d803c8_split_rse_availability_into_multiple.py +4 -4
- rucio/db/sqla/migrate_repo/versions/30d5206e9cad_increase_oauthrequest_redirect_msg_.py +37 -0
- rucio/db/sqla/models.py +157 -154
- rucio/db/sqla/session.py +58 -27
- rucio/db/sqla/types.py +2 -2
- rucio/db/sqla/util.py +2 -2
- rucio/gateway/account.py +18 -12
- rucio/gateway/account_limit.py +137 -60
- rucio/gateway/authentication.py +18 -12
- rucio/gateway/config.py +30 -20
- rucio/gateway/credential.py +9 -10
- rucio/gateway/did.py +70 -53
- rucio/gateway/dirac.py +6 -4
- rucio/gateway/exporter.py +3 -2
- rucio/gateway/heartbeat.py +6 -4
- rucio/gateway/identity.py +36 -51
- rucio/gateway/importer.py +3 -2
- rucio/gateway/lifetime_exception.py +3 -2
- rucio/gateway/meta_conventions.py +17 -6
- rucio/gateway/permission.py +4 -1
- rucio/gateway/quarantined_replica.py +3 -2
- rucio/gateway/replica.py +31 -22
- rucio/gateway/request.py +27 -18
- rucio/gateway/rse.py +69 -37
- rucio/gateway/rule.py +46 -26
- rucio/gateway/scope.py +3 -2
- rucio/gateway/subscription.py +14 -11
- rucio/gateway/vo.py +12 -8
- rucio/rse/__init__.py +3 -3
- rucio/rse/protocols/bittorrent.py +11 -1
- rucio/rse/protocols/cache.py +0 -11
- rucio/rse/protocols/dummy.py +0 -11
- rucio/rse/protocols/gfal.py +14 -9
- rucio/rse/protocols/globus.py +1 -1
- rucio/rse/protocols/http_cache.py +1 -1
- rucio/rse/protocols/posix.py +2 -2
- rucio/rse/protocols/protocol.py +84 -317
- rucio/rse/protocols/rclone.py +2 -1
- rucio/rse/protocols/rfio.py +10 -1
- rucio/rse/protocols/ssh.py +2 -1
- rucio/rse/protocols/storm.py +2 -13
- rucio/rse/protocols/webdav.py +74 -30
- rucio/rse/protocols/xrootd.py +2 -1
- rucio/rse/rsemanager.py +170 -53
- rucio/rse/translation.py +260 -0
- rucio/tests/common.py +23 -13
- rucio/tests/common_server.py +26 -9
- rucio/transfertool/bittorrent.py +15 -14
- rucio/transfertool/bittorrent_driver.py +5 -7
- rucio/transfertool/bittorrent_driver_qbittorrent.py +9 -8
- rucio/transfertool/fts3.py +20 -16
- rucio/transfertool/mock.py +2 -3
- rucio/vcsversion.py +4 -4
- rucio/version.py +7 -0
- rucio/web/rest/flaskapi/v1/accounts.py +17 -3
- rucio/web/rest/flaskapi/v1/auth.py +5 -5
- rucio/web/rest/flaskapi/v1/credentials.py +3 -2
- rucio/web/rest/flaskapi/v1/dids.py +21 -15
- rucio/web/rest/flaskapi/v1/identities.py +33 -9
- rucio/web/rest/flaskapi/v1/redirect.py +5 -4
- rucio/web/rest/flaskapi/v1/replicas.py +12 -8
- rucio/web/rest/flaskapi/v1/rses.py +15 -4
- rucio/web/rest/flaskapi/v1/traces.py +56 -19
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio.cfg.template +3 -19
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/requirements.server.txt +97 -68
- rucio-37.0.0.data/scripts/rucio +133 -0
- rucio-37.0.0.data/scripts/rucio-admin +97 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-atropos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-auditor +2 -1
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-automatix +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-client +17 -10
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-receiver +1 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-kronos +1 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-minos-temporary-expiration +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-necromancer +2 -2
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-reaper +6 -6
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-transmogrifier +2 -2
- rucio-37.0.0.dist-info/METADATA +92 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/RECORD +237 -243
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/AUTHORS.rst +3 -0
- rucio/common/schema/atlas.py +0 -413
- rucio/common/schema/belleii.py +0 -408
- rucio/common/schema/domatpc.py +0 -401
- rucio/common/schema/escape.py +0 -426
- rucio/common/schema/icecube.py +0 -406
- rucio/core/permission/atlas.py +0 -1348
- rucio/core/permission/belleii.py +0 -1077
- rucio/core/permission/escape.py +0 -1078
- rucio/daemons/c3po/algorithms/__init__.py +0 -13
- rucio/daemons/c3po/algorithms/simple.py +0 -134
- rucio/daemons/c3po/algorithms/t2_free_space.py +0 -128
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop.py +0 -130
- rucio/daemons/c3po/algorithms/t2_free_space_only_pop_with_network.py +0 -294
- rucio/daemons/c3po/c3po.py +0 -371
- rucio/daemons/c3po/collectors/agis.py +0 -108
- rucio/daemons/c3po/collectors/free_space.py +0 -81
- rucio/daemons/c3po/collectors/jedi_did.py +0 -57
- rucio/daemons/c3po/collectors/mock_did.py +0 -51
- rucio/daemons/c3po/collectors/network_metrics.py +0 -71
- rucio/daemons/c3po/collectors/workload.py +0 -112
- rucio/daemons/c3po/utils/__init__.py +0 -13
- rucio/daemons/c3po/utils/dataset_cache.py +0 -50
- rucio/daemons/c3po/utils/expiring_dataset_cache.py +0 -56
- rucio/daemons/c3po/utils/expiring_list.py +0 -62
- rucio/daemons/c3po/utils/popularity.py +0 -85
- rucio/daemons/c3po/utils/timeseries.py +0 -89
- rucio/rse/protocols/gsiftp.py +0 -92
- rucio-35.7.0.data/scripts/rucio-c3po +0 -85
- rucio-35.7.0.dist-info/METADATA +0 -72
- /rucio/{daemons/c3po → cli/bin_legacy}/__init__.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-account +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-bb8 +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-dumper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-follower +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-hermes +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-injector +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-35.7.0.data → rucio-37.0.0.data}/scripts/rucio-undertaker +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/WHEEL +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/licenses/LICENSE +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,407 @@
|
|
|
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
|
+
|
|
16
|
+
'''
|
|
17
|
+
Elasticsearch based metadata plugin.
|
|
18
|
+
'''
|
|
19
|
+
|
|
20
|
+
import datetime
|
|
21
|
+
import operator
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
23
|
+
|
|
24
|
+
from elasticsearch import Elasticsearch
|
|
25
|
+
from elasticsearch import exceptions as elastic_exceptions
|
|
26
|
+
|
|
27
|
+
from rucio.common import config, exception
|
|
28
|
+
from rucio.core.did_meta_plugins.did_meta_plugin_interface import DidMetaPlugin
|
|
29
|
+
from rucio.core.did_meta_plugins.filter_engine import FilterEngine
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from collections.abc import Iterator
|
|
33
|
+
|
|
34
|
+
from sqlalchemy.orm import Session
|
|
35
|
+
|
|
36
|
+
from rucio.common.types import InternalScope
|
|
37
|
+
|
|
38
|
+
IMMUTABLE_KEYS = [
|
|
39
|
+
'scope', # generated on insert
|
|
40
|
+
'name', # generated on insert
|
|
41
|
+
'vo' # generated on insert
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ElasticDidMeta(DidMetaPlugin):
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
hosts: Optional[list[str]] = None,
|
|
49
|
+
user: Optional[str] = None,
|
|
50
|
+
password: Optional[str] = None,
|
|
51
|
+
index: Optional[str] = None,
|
|
52
|
+
archive_index: Optional[str] = None,
|
|
53
|
+
use_ssl: Optional[bool] = False,
|
|
54
|
+
verify_certs: bool = True,
|
|
55
|
+
ca_certs: Optional[str] = None,
|
|
56
|
+
client_cert: Optional[str] = None,
|
|
57
|
+
client_key: Optional[str] = None,
|
|
58
|
+
request_timeout: int = 100,
|
|
59
|
+
max_retries: int = 3,
|
|
60
|
+
retry_on_timeout: bool = False
|
|
61
|
+
) -> None:
|
|
62
|
+
super(ElasticDidMeta, self).__init__()
|
|
63
|
+
hosts = hosts or [config.config_get('metadata', 'elastic_service_hosts')]
|
|
64
|
+
user = user or config.config_get('metadata', 'elastic_user', False, None)
|
|
65
|
+
password = password or config.config_get('metadata', 'elastic_password', False, None)
|
|
66
|
+
self.index = index or config.config_get('metadata', 'meta_index', False, 'rucio_did_meta')
|
|
67
|
+
self.archive_index = archive_index or config.config_get('metadata', 'archive_index', False, 'archive_meta')
|
|
68
|
+
use_ssl = use_ssl or config.config_get_bool('metadata', 'use_ssl', False, False)
|
|
69
|
+
ca_certs = ca_certs or config.config_get('metadata', 'ca_certs', False, None)
|
|
70
|
+
client_cert = client_cert or config.config_get('metadata', 'client_cert', False, None)
|
|
71
|
+
client_key = client_key or config.config_get('metadata', 'client_key', False, None)
|
|
72
|
+
|
|
73
|
+
self.es_config = {
|
|
74
|
+
'hosts': hosts,
|
|
75
|
+
'timeout': request_timeout,
|
|
76
|
+
'max_retries': max_retries,
|
|
77
|
+
'retry_on_timeout': retry_on_timeout
|
|
78
|
+
}
|
|
79
|
+
if user and password:
|
|
80
|
+
self.es_config['basic_auth'] = (user, password)
|
|
81
|
+
|
|
82
|
+
if use_ssl:
|
|
83
|
+
self.es_config.update({
|
|
84
|
+
'ca_certs': ca_certs,
|
|
85
|
+
'verify_certs': verify_certs,
|
|
86
|
+
})
|
|
87
|
+
if client_cert and client_key:
|
|
88
|
+
self.es_config.update({
|
|
89
|
+
'client_cert': client_cert,
|
|
90
|
+
'client_key': client_key
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
self.client = Elasticsearch(**self.es_config)
|
|
94
|
+
self.plugin_name = "ELASTIC"
|
|
95
|
+
|
|
96
|
+
def drop_index(self) -> None:
|
|
97
|
+
self.client.indices.delete(index=self.index)
|
|
98
|
+
|
|
99
|
+
def get_metadata(
|
|
100
|
+
self,
|
|
101
|
+
scope: "InternalScope",
|
|
102
|
+
name: str,
|
|
103
|
+
*,
|
|
104
|
+
session: "Optional[Session]" = None
|
|
105
|
+
) -> dict[str, Any]:
|
|
106
|
+
"""
|
|
107
|
+
Get data identifier metadata.
|
|
108
|
+
|
|
109
|
+
:param scope: The scope name
|
|
110
|
+
:param name: The data identifier name
|
|
111
|
+
:param session: The database session in use
|
|
112
|
+
:returns: The metadata for the did
|
|
113
|
+
:raises DataIdentifierNotFound: If the DID metadata is not found.
|
|
114
|
+
:raises RucioException: If another error occurs during the process.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
doc_id = f"{scope.internal}{name}"
|
|
118
|
+
try:
|
|
119
|
+
doc = self.client.get(index=self.index, id=doc_id)["_source"]
|
|
120
|
+
except elastic_exceptions.NotFoundError as err:
|
|
121
|
+
raise exception.DataIdentifierNotFound(f"No metadata found for DID '{scope}:{name}' not found") from err
|
|
122
|
+
except Exception as err:
|
|
123
|
+
raise exception.RucioException(err)
|
|
124
|
+
return doc
|
|
125
|
+
|
|
126
|
+
def set_metadata(
|
|
127
|
+
self,
|
|
128
|
+
scope: "InternalScope",
|
|
129
|
+
name: str,
|
|
130
|
+
key: str,
|
|
131
|
+
value: str,
|
|
132
|
+
recursive: bool = False,
|
|
133
|
+
*,
|
|
134
|
+
session: "Optional[Session]" = None
|
|
135
|
+
) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Set single metadata key.
|
|
138
|
+
|
|
139
|
+
:param scope: the scope of did
|
|
140
|
+
:param name: the name of the did
|
|
141
|
+
:param key: the key to be added
|
|
142
|
+
:param value: the value of the key to be added
|
|
143
|
+
:param recursive: recurse into DIDs (not supported)
|
|
144
|
+
:param session: The database session in use
|
|
145
|
+
:raises DataIdentifierNotFound: If the DID is not found.
|
|
146
|
+
:raises RucioException: If an error occurs while setting the metadata.
|
|
147
|
+
"""
|
|
148
|
+
self.set_metadata_bulk(scope=scope, name=name, meta={key: value}, recursive=recursive, session=session)
|
|
149
|
+
|
|
150
|
+
def set_metadata_bulk(
|
|
151
|
+
self,
|
|
152
|
+
scope: "InternalScope",
|
|
153
|
+
name: str,
|
|
154
|
+
meta: dict[str, Any],
|
|
155
|
+
recursive: bool = False,
|
|
156
|
+
*,
|
|
157
|
+
session: "Optional[Session]" = None
|
|
158
|
+
) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Bulk set metadata keys.
|
|
161
|
+
|
|
162
|
+
:param scope: the scope of did
|
|
163
|
+
:param name: the name of the did
|
|
164
|
+
:param meta: dictionary of metadata keypairs to be added
|
|
165
|
+
:param recursive: recurse into DIDs (not supported)
|
|
166
|
+
:param session: The database session in use
|
|
167
|
+
:raises DataIdentifierNotFound: If the DID is not found.
|
|
168
|
+
:raises UnsupportedOperation: If recursive inserts are requested (currently unsupported).
|
|
169
|
+
:raises RucioException: If an error occurs while setting the metadata.
|
|
170
|
+
"""
|
|
171
|
+
doc_id = f"{scope.internal}{name}"
|
|
172
|
+
try:
|
|
173
|
+
# Try to get existing metadata
|
|
174
|
+
existing_meta = self.get_metadata(scope, name)
|
|
175
|
+
except exception.DataIdentifierNotFound:
|
|
176
|
+
existing_meta = {
|
|
177
|
+
'scope': str(scope.external),
|
|
178
|
+
'name': name,
|
|
179
|
+
'vo': scope.vo
|
|
180
|
+
}
|
|
181
|
+
for key, value in meta.items():
|
|
182
|
+
if key not in IMMUTABLE_KEYS:
|
|
183
|
+
existing_meta[key] = value
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
self.client.index(index=self.index, body=existing_meta, id=doc_id, refresh="true")
|
|
187
|
+
except Exception as err:
|
|
188
|
+
raise exception.RucioException(err)
|
|
189
|
+
|
|
190
|
+
if recursive:
|
|
191
|
+
raise exception.UnsupportedOperation(f"'{self.plugin_name.lower()}' metadata module does not currently support recursive inserts of metadata")
|
|
192
|
+
|
|
193
|
+
def delete_metadata(
|
|
194
|
+
self,
|
|
195
|
+
scope: "InternalScope",
|
|
196
|
+
name: str,
|
|
197
|
+
key: str,
|
|
198
|
+
*,
|
|
199
|
+
session: "Optional[Session]" = None
|
|
200
|
+
) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Delete a key from metadata.
|
|
203
|
+
|
|
204
|
+
:param scope: the scope of did
|
|
205
|
+
:param name: the name of the did
|
|
206
|
+
:param key: the key to be deleted
|
|
207
|
+
:raises DataIdentifierNotFound: If the DID is not found.
|
|
208
|
+
:raises RucioException: If an error occurs while setting the metadata.
|
|
209
|
+
"""
|
|
210
|
+
doc_id = f"{scope.internal}{name}"
|
|
211
|
+
try:
|
|
212
|
+
# First, get the current document
|
|
213
|
+
doc = self.client.get(index=self.index, id=doc_id)
|
|
214
|
+
|
|
215
|
+
# Check if the key exists in the document
|
|
216
|
+
if key in doc['_source']:
|
|
217
|
+
# Use script to remove the field
|
|
218
|
+
script = {
|
|
219
|
+
"script": {
|
|
220
|
+
"source": f"ctx._source.remove('{key}')",
|
|
221
|
+
"lang": "painless"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
self.client.update(index=self.index, id=doc_id, body=script)
|
|
225
|
+
except elastic_exceptions.NotFoundError as err:
|
|
226
|
+
raise exception.DataIdentifierNotFound(f"No metadata found for DID '{scope}:{name}' not found") from err
|
|
227
|
+
except Exception as err:
|
|
228
|
+
raise exception.RucioException(err)
|
|
229
|
+
|
|
230
|
+
def list_dids(
|
|
231
|
+
self,
|
|
232
|
+
scope: "InternalScope",
|
|
233
|
+
filters: Union[list[dict[str, Any]], dict[str, Any]],
|
|
234
|
+
did_type: Literal['all', 'collection', 'dataset', 'container', 'file'] = 'collection',
|
|
235
|
+
ignore_case: bool = False,
|
|
236
|
+
limit: Optional[int] = None,
|
|
237
|
+
offset: Optional[int] = None,
|
|
238
|
+
long: bool = False,
|
|
239
|
+
recursive: bool = False,
|
|
240
|
+
ignore_dids: Optional[list] = None,
|
|
241
|
+
*,
|
|
242
|
+
session: "Optional[Session]" = None
|
|
243
|
+
) -> "Iterator[dict[str, Any]]":
|
|
244
|
+
"""
|
|
245
|
+
List DIDs (Data Identifier).
|
|
246
|
+
|
|
247
|
+
:param scope: The scope of the DIDs to search.
|
|
248
|
+
:param filters: The filters to apply to the DID search.
|
|
249
|
+
:param did_type: The type of DID (default is 'collection').
|
|
250
|
+
:param ignore_case: Whether to ignore case (default is False).
|
|
251
|
+
:param limit: The maximum number of DIDs to return.
|
|
252
|
+
:param offset: The starting point for the search (used for pagination).
|
|
253
|
+
:param long: Whether to return extended information (scope, name, did_type, bytes, length) (default is False).
|
|
254
|
+
:param recursive: Whether to search recursively (currently unsupported).
|
|
255
|
+
:param ignore_dids: A list of DIDs to ignore (default is an empty list).
|
|
256
|
+
:param session: The database session in use.
|
|
257
|
+
:returns: A generator yielding DIDs as strings (when `long` is False) or dictionaries (when `long` is True).
|
|
258
|
+
:raises UnsupportedOperation: If recursive searches are requested (currently unsupported).
|
|
259
|
+
:raises RucioException: If an error occurs during the search.
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
if not ignore_dids:
|
|
263
|
+
ignore_dids = []
|
|
264
|
+
|
|
265
|
+
# backwards compatibility for filters as single {}.
|
|
266
|
+
if isinstance(filters, dict):
|
|
267
|
+
filters = [filters]
|
|
268
|
+
|
|
269
|
+
# Create Elasticsearch query
|
|
270
|
+
fe = FilterEngine(filters, model_class=None, strict_coerce=False)
|
|
271
|
+
elastic_query_str = fe.create_elastic_query(
|
|
272
|
+
additional_filters=[
|
|
273
|
+
('scope', operator.eq, str(scope.external)),
|
|
274
|
+
('vo', operator.eq, scope.vo)
|
|
275
|
+
]
|
|
276
|
+
)
|
|
277
|
+
pit = self.client.open_point_in_time(index=self.index, keep_alive="2m")
|
|
278
|
+
pit_id = pit["id"]
|
|
279
|
+
# Base query with point in time (pit) paramter.
|
|
280
|
+
# sort is needed for search_after, so we use scope sort (random choice)
|
|
281
|
+
query = {
|
|
282
|
+
"query": elastic_query_str,
|
|
283
|
+
"sort": [{"scope.keyword": "asc"}],
|
|
284
|
+
"_source": ["scope", "name"] if not long else ["scope", "name", "did_type", "bytes", "length"],
|
|
285
|
+
"pit": {"id": pit_id, "keep_alive": "2m"}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
# Add sorting and pagination
|
|
289
|
+
if offset:
|
|
290
|
+
query["from"] = offset
|
|
291
|
+
size = limit if limit else 10000
|
|
292
|
+
query["size"] = size
|
|
293
|
+
search_after = None
|
|
294
|
+
total_processed = 0
|
|
295
|
+
try:
|
|
296
|
+
while True:
|
|
297
|
+
if search_after:
|
|
298
|
+
query["search_after"] = search_after
|
|
299
|
+
query.pop("from", None)
|
|
300
|
+
# Execute search
|
|
301
|
+
results = self.client.search(body=query)
|
|
302
|
+
hits = results['hits']['hits']
|
|
303
|
+
if not hits:
|
|
304
|
+
break
|
|
305
|
+
|
|
306
|
+
for hit in hits:
|
|
307
|
+
did_full = f"{hit['_source']['scope']}:{hit['_source']['name']}"
|
|
308
|
+
if did_full not in ignore_dids:
|
|
309
|
+
ignore_dids.append(did_full)
|
|
310
|
+
if long:
|
|
311
|
+
yield {
|
|
312
|
+
'scope': (hit['_source']['scope']),
|
|
313
|
+
'name': hit['_source']['name'],
|
|
314
|
+
'did_type': hit['_source'].get('did_type', 'N/A'),
|
|
315
|
+
'bytes': hit['_source'].get('bytes', 'N/A'),
|
|
316
|
+
'length': hit['_source'].get('length', 'N/A')
|
|
317
|
+
}
|
|
318
|
+
else:
|
|
319
|
+
yield hit['_source']['name']
|
|
320
|
+
|
|
321
|
+
total_processed += 1
|
|
322
|
+
if limit and total_processed >= limit:
|
|
323
|
+
break
|
|
324
|
+
|
|
325
|
+
# Update search_after for the next iteration
|
|
326
|
+
search_after = hits[-1]["sort"]
|
|
327
|
+
|
|
328
|
+
finally:
|
|
329
|
+
# Always delete the point in time when done
|
|
330
|
+
self.client.close_point_in_time(body={"id": pit_id})
|
|
331
|
+
|
|
332
|
+
if recursive:
|
|
333
|
+
raise exception.UnsupportedOperation(f"'{self.plugin_name.lower()}' metadata module does not currently support recursive searches")
|
|
334
|
+
|
|
335
|
+
def on_delete(
|
|
336
|
+
self,
|
|
337
|
+
scope: "InternalScope",
|
|
338
|
+
name: str,
|
|
339
|
+
archive: bool = False,
|
|
340
|
+
session: "Optional[Session]" = None
|
|
341
|
+
) -> None:
|
|
342
|
+
"""
|
|
343
|
+
Delete a document and optionally archive it.
|
|
344
|
+
|
|
345
|
+
:param scope: The scope of the document
|
|
346
|
+
:param name: The name of the document
|
|
347
|
+
:param archive: Whether to archive the document before deletion
|
|
348
|
+
:raises DataIdentifierNotFound: If the DID is not found.
|
|
349
|
+
:raises RucioException: If an error occurs while setting the metadata.
|
|
350
|
+
"""
|
|
351
|
+
doc_id = f"{scope}{name}"
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
doc = self.client.get(index=self.index, id=doc_id)
|
|
355
|
+
|
|
356
|
+
if archive:
|
|
357
|
+
archived_doc = doc['_source']
|
|
358
|
+
archived_doc['deleted_at'] = datetime.datetime.now(datetime.timezone.utc).isoformat()
|
|
359
|
+
self.client.index(index=self.archive_index, id=doc_id, body=archived_doc)
|
|
360
|
+
|
|
361
|
+
self.client.delete(index=self.index, id=doc_id)
|
|
362
|
+
|
|
363
|
+
except elastic_exceptions.NotFoundError as err:
|
|
364
|
+
raise exception.DataIdentifierNotFound(f"No metadata found for DID '{scope}:{name}' not found") from err
|
|
365
|
+
except Exception as err:
|
|
366
|
+
raise exception.RucioException(err)
|
|
367
|
+
|
|
368
|
+
def get_metadata_archived(
|
|
369
|
+
self,
|
|
370
|
+
scope: "InternalScope",
|
|
371
|
+
name: str,
|
|
372
|
+
session: "Optional[Session]" = None
|
|
373
|
+
) -> None:
|
|
374
|
+
"""
|
|
375
|
+
Retrieve archived metadata for a given scope and name.
|
|
376
|
+
|
|
377
|
+
:param scope: The scope of the document
|
|
378
|
+
:param name: The name of the document
|
|
379
|
+
:return: The archived metadata or None if not found
|
|
380
|
+
:raises DataIdentifierNotFound: If the DID is not found.
|
|
381
|
+
:raises RucioException: If an error occurs while setting the metadata.
|
|
382
|
+
"""
|
|
383
|
+
doc_id = f"{scope}{name}"
|
|
384
|
+
|
|
385
|
+
try:
|
|
386
|
+
doc = self.client.get(index=self.archive_index, id=doc_id)["_source"]
|
|
387
|
+
return doc
|
|
388
|
+
except elastic_exceptions.NotFoundError as err:
|
|
389
|
+
raise exception.DataIdentifierNotFound(f"No metadata found for DID '{scope}:{name}' not found") from err
|
|
390
|
+
except Exception as err:
|
|
391
|
+
raise exception.RucioException(err)
|
|
392
|
+
|
|
393
|
+
def manages_key(
|
|
394
|
+
self,
|
|
395
|
+
key: str,
|
|
396
|
+
*,
|
|
397
|
+
session: "Optional[Session]" = None
|
|
398
|
+
) -> bool:
|
|
399
|
+
return True
|
|
400
|
+
|
|
401
|
+
def get_plugin_name(self) -> str:
|
|
402
|
+
"""
|
|
403
|
+
Returns a unique identifier for this plugin. This can be later used for filtering down results to this plugin only.
|
|
404
|
+
|
|
405
|
+
:returns: The name of the plugin
|
|
406
|
+
"""
|
|
407
|
+
return self.plugin_name
|
|
@@ -49,6 +49,15 @@ OPERATORS_CONVERSION_LUT = {
|
|
|
49
49
|
"": operator.eq
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
ELASTIC_OP_MAP = {
|
|
53
|
+
operator.eq: "=",
|
|
54
|
+
operator.ne: "!",
|
|
55
|
+
operator.gt: "gt",
|
|
56
|
+
operator.lt: "lt",
|
|
57
|
+
operator.ge: "gte",
|
|
58
|
+
operator.le: "lte"
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
# lookup table converting pythonic operators to oracle operators
|
|
53
62
|
ORACLE_OP_MAP = {
|
|
54
63
|
operator.eq: "==",
|
|
@@ -348,6 +357,56 @@ class FilterEngine:
|
|
|
348
357
|
|
|
349
358
|
return query_str
|
|
350
359
|
|
|
360
|
+
def create_elastic_query(
|
|
361
|
+
self,
|
|
362
|
+
additional_filters: Optional["Iterable[FilterTuple]"] = None
|
|
363
|
+
) -> dict[str, Any]:
|
|
364
|
+
"""
|
|
365
|
+
Returns a single elastic query dictionary describing the filters expression.
|
|
366
|
+
|
|
367
|
+
:param additional_filters: additional filters to be applied to all clauses.
|
|
368
|
+
:returns: an elastic query dictionary describing the filters expression.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
additional_filters = additional_filters or []
|
|
372
|
+
for or_group in self._filters:
|
|
373
|
+
for _filter in additional_filters:
|
|
374
|
+
or_group.append(list(_filter)) # type: ignore
|
|
375
|
+
|
|
376
|
+
should_clauses = []
|
|
377
|
+
for or_group in self._filters:
|
|
378
|
+
bool_query = {
|
|
379
|
+
"must": [],
|
|
380
|
+
"must_not": []
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
for and_group in or_group:
|
|
384
|
+
key, oper, value = and_group
|
|
385
|
+
key = str(key)
|
|
386
|
+
|
|
387
|
+
if isinstance(value, str) and any(char in value for char in ['*', '%']):
|
|
388
|
+
if value in ('*', '%', '*', '%'):
|
|
389
|
+
bool_query["must"].append({"wildcard": {key: value}})
|
|
390
|
+
else:
|
|
391
|
+
wildcard_query = {"wildcard": {key: value}}
|
|
392
|
+
if oper == operator.eq:
|
|
393
|
+
bool_query["must"].append(wildcard_query)
|
|
394
|
+
elif oper == operator.ne:
|
|
395
|
+
bool_query["must_not"].append(wildcard_query)
|
|
396
|
+
else:
|
|
397
|
+
if oper in [operator.lt, operator.gt, operator.ge, operator.le]:
|
|
398
|
+
elsop = ELASTIC_OP_MAP[oper]
|
|
399
|
+
bool_query["must"].append({"range": {key: {elsop: value}}})
|
|
400
|
+
elif oper == operator.eq:
|
|
401
|
+
bool_query["must"].append({"term": {key: value}})
|
|
402
|
+
elif oper == operator.ne:
|
|
403
|
+
bool_query["must_not"].append({"term": {key: value}})
|
|
404
|
+
|
|
405
|
+
should_clauses.append({"bool": bool_query})
|
|
406
|
+
|
|
407
|
+
query_expression = {"bool": {"should": should_clauses}}
|
|
408
|
+
return query_expression
|
|
409
|
+
|
|
351
410
|
def create_postgres_query(
|
|
352
411
|
self,
|
|
353
412
|
additional_filters: Optional["Iterable[FilterTuple]"] = None,
|
|
@@ -551,7 +610,7 @@ class FilterEngine:
|
|
|
551
610
|
"""
|
|
552
611
|
A (more) human readable format of <filters>.
|
|
553
612
|
"""
|
|
554
|
-
|
|
613
|
+
operators_conversion_lut_inv = {op2: op1 for op1, op2 in OPERATORS_CONVERSION_LUT.items()}
|
|
555
614
|
|
|
556
615
|
filters = '\n'
|
|
557
616
|
for or_group in self._filters:
|
|
@@ -559,10 +618,10 @@ class FilterEngine:
|
|
|
559
618
|
key, oper, value = and_group
|
|
560
619
|
if isinstance(key, InstrumentedAttribute):
|
|
561
620
|
key = and_group[0].key
|
|
562
|
-
if
|
|
621
|
+
if operators_conversion_lut_inv[oper] == "":
|
|
563
622
|
oper = "eq"
|
|
564
623
|
else:
|
|
565
|
-
oper =
|
|
624
|
+
oper = operators_conversion_lut_inv[oper]
|
|
566
625
|
if isinstance(value, InstrumentedAttribute):
|
|
567
626
|
value = and_group[2].key # type: ignore
|
|
568
627
|
elif isinstance(value, DIDType):
|
|
@@ -100,9 +100,9 @@ class JSONDidMeta(DidMetaPlugin):
|
|
|
100
100
|
if row_did_meta.meta:
|
|
101
101
|
if session.bind.dialect.name in ['oracle', 'sqlite']:
|
|
102
102
|
# Oracle and sqlite returns a string instead of a dict
|
|
103
|
-
existing_meta = json_lib.loads(cast(str, row_did_meta.meta))
|
|
103
|
+
existing_meta = json_lib.loads(cast("str", row_did_meta.meta))
|
|
104
104
|
else:
|
|
105
|
-
existing_meta = cast(dict[str, Any], row_did_meta.meta)
|
|
105
|
+
existing_meta = cast("dict[str, Any]", row_did_meta.meta)
|
|
106
106
|
|
|
107
107
|
for key, value in metadata.items():
|
|
108
108
|
existing_meta[key] = value
|
|
@@ -28,10 +28,10 @@ if TYPE_CHECKING:
|
|
|
28
28
|
from sqlalchemy.orm import Session
|
|
29
29
|
|
|
30
30
|
IMMUTABLE_KEYS = [
|
|
31
|
-
'_id',
|
|
32
|
-
'scope',
|
|
33
|
-
'name',
|
|
34
|
-
'vo'
|
|
31
|
+
'_id', # index key
|
|
32
|
+
'scope', # generated on insert
|
|
33
|
+
'name', # generated on insert
|
|
34
|
+
'vo' # generated on insert
|
|
35
35
|
]
|
|
36
36
|
|
|
37
37
|
|
|
@@ -46,28 +46,40 @@ class MongoDidMeta(DidMetaPlugin):
|
|
|
46
46
|
password: "Optional[str]" = None,
|
|
47
47
|
):
|
|
48
48
|
super(MongoDidMeta, self).__init__()
|
|
49
|
-
if host is None:
|
|
50
|
-
host = config.config_get('metadata', 'mongo_service_host')
|
|
51
|
-
if port is None:
|
|
52
|
-
port = config.config_get_int('metadata', 'mongo_service_port')
|
|
53
|
-
if db is None:
|
|
54
|
-
db = config.config_get('metadata', 'mongo_db')
|
|
55
|
-
if collection is None:
|
|
56
|
-
collection = config.config_get('metadata', 'mongo_collection')
|
|
57
|
-
|
|
58
|
-
if user is None and config.config_has_section("metadata"):
|
|
59
|
-
user = config.config_get("metadata", "mongo_user", default=None)
|
|
60
|
-
|
|
61
|
-
if user is not None:
|
|
62
|
-
if password is None:
|
|
63
|
-
password = config.config_get("metadata", "mongo_password")
|
|
64
|
-
auth = "{user}:{password}@".format(user=user, password=password)
|
|
65
|
-
else:
|
|
66
|
-
auth = ""
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
50
|
+
# Validate required parameters.
|
|
51
|
+
con_params = {
|
|
52
|
+
'mongo_service_host': host,
|
|
53
|
+
'mongo_service_port': port,
|
|
54
|
+
'mongo_db': db,
|
|
55
|
+
'mongo_collection': collection,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for param in con_params:
|
|
59
|
+
if con_params[param] is None:
|
|
60
|
+
if config.config_has_option('metadata', param):
|
|
61
|
+
con_params[param] = (
|
|
62
|
+
config.config_get_int('metadata', param)
|
|
63
|
+
if param == 'mongo_service_port'
|
|
64
|
+
else config.config_get('metadata', param)
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
raise exception.ConnectionParameterNotFound(param)
|
|
68
|
+
|
|
69
|
+
if user is None and config.config_has_option('metadata', 'mongo_user'):
|
|
70
|
+
user = config.config_get('metadata', 'mongo_user', default=None)
|
|
71
|
+
|
|
72
|
+
if password is None and config.config_has_option('metadata', 'mongo_password'):
|
|
73
|
+
password = config.config_get('metadata', 'mongo_password')
|
|
74
|
+
|
|
75
|
+
# Set the auth (fallback to an anonymous connection if either user or password is not defined).
|
|
76
|
+
auth = "" if not user or not password else f"{user}:{password}@"
|
|
77
|
+
|
|
78
|
+
self.client = pymongo.MongoClient(
|
|
79
|
+
f"mongodb://{auth}{con_params['mongo_service_host']}:{con_params['mongo_service_port']}/"
|
|
80
|
+
)
|
|
81
|
+
self.db = self.client[con_params['mongo_db']]
|
|
82
|
+
self.col = self.db[con_params['mongo_collection']]
|
|
71
83
|
|
|
72
84
|
self.plugin_name = "MONGO"
|
|
73
85
|
|
|
@@ -175,9 +187,10 @@ class MongoDidMeta(DidMetaPlugin):
|
|
|
175
187
|
|
|
176
188
|
if recursive:
|
|
177
189
|
# TODO: possible, but requires retrieving the results of a concurrent sqla query to call list_content on for datasets and containers
|
|
178
|
-
raise exception.UnsupportedOperation(
|
|
179
|
-
|
|
180
|
-
|
|
190
|
+
raise exception.UnsupportedOperation(
|
|
191
|
+
"'{}' metadata module does not currently support recursive searches".format(
|
|
192
|
+
self.plugin_name.lower()
|
|
193
|
+
))
|
|
181
194
|
|
|
182
195
|
if long:
|
|
183
196
|
query_result = self.col.find(mongo_query_str)
|
|
@@ -185,7 +198,7 @@ class MongoDidMeta(DidMetaPlugin):
|
|
|
185
198
|
query_result = query_result.limit(limit)
|
|
186
199
|
for did in query_result:
|
|
187
200
|
did_full = "{}:{}".format(did['scope'], did['name'])
|
|
188
|
-
if did_full not in ignore_dids:
|
|
201
|
+
if did_full not in ignore_dids: # aggregating recursive queries may contain duplicate DIDs
|
|
189
202
|
ignore_dids.add(did_full)
|
|
190
203
|
yield {
|
|
191
204
|
'scope': InternalScope(did['scope']),
|
|
@@ -200,7 +213,7 @@ class MongoDidMeta(DidMetaPlugin):
|
|
|
200
213
|
query_result = query_result.limit(limit)
|
|
201
214
|
for did in query_result:
|
|
202
215
|
did_full = "{}:{}".format(did['scope'], did['name'])
|
|
203
|
-
if did_full not in ignore_dids:
|
|
216
|
+
if did_full not in ignore_dids: # aggregating recursive queries may contain duplicate DIDs
|
|
204
217
|
ignore_dids.add(did_full)
|
|
205
218
|
yield did['name']
|
|
206
219
|
|