rucio 35.7.0__py3-none-any.whl → 37.0.0rc2__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/stomp_utils.py +383 -119
- 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/cache/consumer.py +26 -90
- 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 +53 -123
- rucio/daemons/conveyor/stager.py +1 -0
- rucio/daemons/conveyor/submitter.py +3 -3
- rucio/daemons/hermes/hermes.py +129 -369
- 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 +117 -142
- 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.0rc2.data}/data/rucio/etc/alembic.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/alembic_offline.ini.template +1 -1
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio.cfg.atlas.client.template +3 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio.cfg.template +3 -19
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rucio_multi_vo.cfg.template +1 -18
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/requirements.server.txt +97 -68
- rucio-37.0.0rc2.data/scripts/rucio +133 -0
- rucio-37.0.0rc2.data/scripts/rucio-admin +97 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-atropos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-auditor +2 -1
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-automatix +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-cache-client +17 -10
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-receiver +1 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-kronos +1 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-minos +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-minos-temporary-expiration +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-necromancer +2 -2
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-reaper +6 -6
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-transmogrifier +2 -2
- rucio-37.0.0rc2.dist-info/METADATA +92 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/RECORD +239 -245
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.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.0rc2.data}/data/rucio/etc/globus-config.yml.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/ldap.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approval_request.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approved_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_approved_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_denied_admin.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_denied_user.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/mail_templates/rule_ok_notification.tmpl +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/etc/rse-accounts.cfg.template +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/bootstrap.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/merge_rucio_configs.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/data/rucio/tools/reset_database.py +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-account +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-collection-replica +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-abacus-rse +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-bb8 +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-cache-consumer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-finisher +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-poller +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-preparer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-stager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-submitter +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-conveyor-throttler +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-dark-reaper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-dumper +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-follower +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-hermes +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-cleaner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-evaluator +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-injector +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-judge-repairer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-oauth-manager +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-replica-recoverer +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-rse-decommissioner +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-storage-consistency-actions +0 -0
- {rucio-35.7.0.data → rucio-37.0.0rc2.data}/scripts/rucio-undertaker +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/WHEEL +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/licenses/LICENSE +0 -0
- {rucio-35.7.0.dist-info → rucio-37.0.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -1,294 +0,0 @@
|
|
|
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 logging
|
|
16
|
-
from operator import itemgetter
|
|
17
|
-
from typing import TYPE_CHECKING, Any
|
|
18
|
-
|
|
19
|
-
from rucio.common.config import config_get, config_get_int
|
|
20
|
-
from rucio.common.constants import RseAttr
|
|
21
|
-
from rucio.common.exception import DataIdentifierNotFound
|
|
22
|
-
from rucio.core.did import get_did
|
|
23
|
-
from rucio.core.replica import list_dataset_replicas
|
|
24
|
-
from rucio.core.rse import get_rse, get_rse_name, list_rse_attributes
|
|
25
|
-
from rucio.core.rse_expression_parser import parse_expression
|
|
26
|
-
from rucio.daemons.c3po.collectors.free_space import FreeSpaceCollector
|
|
27
|
-
from rucio.daemons.c3po.collectors.network_metrics import NetworkMetricsCollector
|
|
28
|
-
from rucio.daemons.c3po.utils.dataset_cache import DatasetCache
|
|
29
|
-
from rucio.daemons.c3po.utils.expiring_dataset_cache import ExpiringDatasetCache
|
|
30
|
-
from rucio.daemons.c3po.utils.popularity import get_popularity
|
|
31
|
-
from rucio.daemons.c3po.utils.timeseries import RedisTimeSeries
|
|
32
|
-
from rucio.db.sqla.constants import ReplicaState
|
|
33
|
-
|
|
34
|
-
if TYPE_CHECKING:
|
|
35
|
-
from rucio.common.types import InternalScope
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class PlacementAlgorithm:
|
|
39
|
-
"""
|
|
40
|
-
Placement algorithm that focusses on free space on T2 DATADISK RSEs. It incorporates network metrics for placement decisions.
|
|
41
|
-
"""
|
|
42
|
-
def __init__(
|
|
43
|
-
self,
|
|
44
|
-
datatypes: str,
|
|
45
|
-
dest_rse_expr: str,
|
|
46
|
-
max_bytes_hour: int,
|
|
47
|
-
max_files_hour: int,
|
|
48
|
-
max_bytes_hour_rse: int,
|
|
49
|
-
max_files_hour_rse: int,
|
|
50
|
-
min_popularity: int,
|
|
51
|
-
min_recent_requests: int,
|
|
52
|
-
max_replicas: int
|
|
53
|
-
):
|
|
54
|
-
self._fsc = FreeSpaceCollector()
|
|
55
|
-
self._nmc = NetworkMetricsCollector()
|
|
56
|
-
self._added_cache = ExpiringDatasetCache(config_get('c3po', 'redis_host'), config_get_int('c3po', 'redis_port'), timeout=86400)
|
|
57
|
-
self._dc = DatasetCache(config_get('c3po', 'redis_host'), config_get_int('c3po', 'redis_port'), timeout=86400)
|
|
58
|
-
self._added_bytes = RedisTimeSeries(config_get('c3po', 'redis_host'), config_get_int('c3po', 'redis_port'), window=3600, prefix="added_bytes_")
|
|
59
|
-
self._added_files = RedisTimeSeries(config_get('c3po', 'redis_host'), config_get_int('c3po', 'redis_port'), window=3600, prefix="added_files_")
|
|
60
|
-
|
|
61
|
-
self._datatypes = datatypes.split(',')
|
|
62
|
-
self._dest_rse_expr = dest_rse_expr
|
|
63
|
-
self._max_bytes_hour = max_bytes_hour
|
|
64
|
-
self._max_files_hour = max_files_hour
|
|
65
|
-
self._max_bytes_hour_rse = max_bytes_hour_rse
|
|
66
|
-
self._max_files_hour_rse = max_files_hour_rse
|
|
67
|
-
self._min_popularity = min_popularity
|
|
68
|
-
self._min_recent_requests = min_recent_requests
|
|
69
|
-
self._max_replicas = max_replicas
|
|
70
|
-
|
|
71
|
-
rses = parse_expression(self._dest_rse_expr)
|
|
72
|
-
|
|
73
|
-
self._rses = {}
|
|
74
|
-
self._sites = {}
|
|
75
|
-
for rse in rses:
|
|
76
|
-
rse_attrs = list_rse_attributes(rse_id=rse['id'])
|
|
77
|
-
rse_attrs['rse_id'] = rse['id']
|
|
78
|
-
self._rses[rse['id']] = rse_attrs
|
|
79
|
-
self._sites[rse_attrs[RseAttr.SITE]] = rse_attrs
|
|
80
|
-
|
|
81
|
-
self._dst_penalties = {}
|
|
82
|
-
self._src_penalties = {}
|
|
83
|
-
|
|
84
|
-
self._print_params()
|
|
85
|
-
|
|
86
|
-
def _print_params(self) -> None:
|
|
87
|
-
logging.debug('Parameter Overview:')
|
|
88
|
-
logging.debug('Algorithm: t2_free_space_only_pop_with_network')
|
|
89
|
-
logging.debug('Datatypes: %s' % ','.join(self._datatypes))
|
|
90
|
-
logging.debug('Max bytes/files per hour: %d / %d' % (self._max_bytes_hour, self._max_files_hour))
|
|
91
|
-
logging.debug('Max bytes/files per hour per RSE: %d / %d' % (self._max_bytes_hour_rse, self._max_files_hour_rse))
|
|
92
|
-
logging.debug('Min recent requests / Min popularity: %d / %d' % (self._min_recent_requests, self._min_popularity))
|
|
93
|
-
logging.debug('Max existing replicas: %d' % self._max_replicas)
|
|
94
|
-
|
|
95
|
-
def __update_penalties(self) -> None:
|
|
96
|
-
for rse_id, penalty in self._dst_penalties.items():
|
|
97
|
-
if penalty < 100.0:
|
|
98
|
-
self._dst_penalties[rse_id] += 10.0
|
|
99
|
-
|
|
100
|
-
for rse_id, penalty in self._src_penalties.items():
|
|
101
|
-
if penalty < 100.0:
|
|
102
|
-
self._src_penalties[rse_id] += 10.0
|
|
103
|
-
|
|
104
|
-
def check_did(self, did: tuple['InternalScope', str]) -> dict[str, Any]:
|
|
105
|
-
decision: dict[str, Any] = {'did': '{}:{}'.format(did[0].internal, did[1])}
|
|
106
|
-
if (self._added_cache.check_dataset(decision['did'])):
|
|
107
|
-
decision['error_reason'] = 'already added replica for this did in the last 24h'
|
|
108
|
-
return decision
|
|
109
|
-
|
|
110
|
-
if (did[0].external is not None) and (not did[0].external.startswith('data')) and (not did[0].external.startswith('mc')):
|
|
111
|
-
decision['error_reason'] = 'not a data or mc dataset'
|
|
112
|
-
return decision
|
|
113
|
-
|
|
114
|
-
datatype = did[1].split('.')[4].split('_')[0]
|
|
115
|
-
|
|
116
|
-
if datatype not in self._datatypes:
|
|
117
|
-
decision['error_reason'] = 'wrong datatype'
|
|
118
|
-
return decision
|
|
119
|
-
|
|
120
|
-
try:
|
|
121
|
-
meta = get_did(did[0], did[1])
|
|
122
|
-
except DataIdentifierNotFound:
|
|
123
|
-
decision['error_reason'] = 'did does not exist'
|
|
124
|
-
return decision
|
|
125
|
-
if meta['length'] is None:
|
|
126
|
-
meta['length'] = 0
|
|
127
|
-
if meta['bytes'] is None:
|
|
128
|
-
meta['bytes'] = 0
|
|
129
|
-
logging.debug('got %s:%s, num_files: %d, bytes: %d' % (did[0], did[1], meta['length'], meta['bytes']))
|
|
130
|
-
|
|
131
|
-
decision['length'] = meta['length']
|
|
132
|
-
decision['bytes'] = meta['bytes']
|
|
133
|
-
|
|
134
|
-
total_added_bytes = sum(self._added_bytes.get_series('total'))
|
|
135
|
-
total_added_files = sum(self._added_files.get_series('total'))
|
|
136
|
-
|
|
137
|
-
logging.debug("total_added_bytes: %d" % total_added_bytes)
|
|
138
|
-
logging.debug("total_added_files: %d" % total_added_files)
|
|
139
|
-
|
|
140
|
-
if ((total_added_bytes + meta['bytes']) > self._max_bytes_hour):
|
|
141
|
-
decision['error_reason'] = 'above bytes limit of %d bytes' % self._max_bytes_hour
|
|
142
|
-
return decision
|
|
143
|
-
if ((total_added_files + meta['length']) > self._max_files_hour):
|
|
144
|
-
decision['error_reason'] = 'above files limit of %d files' % self._max_files_hour
|
|
145
|
-
return decision
|
|
146
|
-
|
|
147
|
-
last_accesses = self._dc.get_did(did)
|
|
148
|
-
self._dc.add_did(did)
|
|
149
|
-
|
|
150
|
-
decision['last_accesses'] = last_accesses
|
|
151
|
-
|
|
152
|
-
try:
|
|
153
|
-
pop = get_popularity(did)
|
|
154
|
-
decision['popularity'] = pop or 0.0
|
|
155
|
-
except Exception:
|
|
156
|
-
decision['error_reason'] = 'problems connecting to ES'
|
|
157
|
-
return decision
|
|
158
|
-
|
|
159
|
-
if (last_accesses < self._min_recent_requests) and (decision['popularity'] < self._min_popularity):
|
|
160
|
-
decision['error_reason'] = 'did not popular enough'
|
|
161
|
-
return decision
|
|
162
|
-
|
|
163
|
-
return decision
|
|
164
|
-
|
|
165
|
-
def place(self, did: tuple['InternalScope', str]) -> dict[str, Any]:
|
|
166
|
-
self.__update_penalties()
|
|
167
|
-
self._added_bytes.trim()
|
|
168
|
-
self._added_files.trim()
|
|
169
|
-
|
|
170
|
-
decision = self.check_did(did)
|
|
171
|
-
|
|
172
|
-
if 'error_reason' in decision:
|
|
173
|
-
return decision
|
|
174
|
-
|
|
175
|
-
meta = get_did(did[0], did[1])
|
|
176
|
-
available_reps = {}
|
|
177
|
-
reps = list_dataset_replicas(did[0], did[1])
|
|
178
|
-
num_reps = 0
|
|
179
|
-
space_info = self._fsc.get_rse_space()
|
|
180
|
-
max_mbps = 0.0
|
|
181
|
-
for rep in reps:
|
|
182
|
-
rse_attr = list_rse_attributes(rep['rse_id'])
|
|
183
|
-
src_rse_id = rep['rse_id']
|
|
184
|
-
if RseAttr.SITE not in rse_attr:
|
|
185
|
-
continue
|
|
186
|
-
|
|
187
|
-
src_site = rse_attr[RseAttr.SITE]
|
|
188
|
-
src_rse_info = get_rse(rse_id=src_rse_id)
|
|
189
|
-
|
|
190
|
-
if 'type' not in rse_attr:
|
|
191
|
-
continue
|
|
192
|
-
if rse_attr['type'] != 'DATADISK':
|
|
193
|
-
continue
|
|
194
|
-
if not src_rse_info['availability_read']:
|
|
195
|
-
continue
|
|
196
|
-
|
|
197
|
-
if rep['state'] == ReplicaState.AVAILABLE:
|
|
198
|
-
if rep['available_length'] == 0:
|
|
199
|
-
continue
|
|
200
|
-
net_metrics = {}
|
|
201
|
-
net_metrics_type = None
|
|
202
|
-
for metric_type in ('fts', 'fax', 'perfsonar', 'dashb'):
|
|
203
|
-
net_metrics_type = metric_type
|
|
204
|
-
net_metrics = self._nmc.getMbps(src_site, metric_type)
|
|
205
|
-
if net_metrics:
|
|
206
|
-
break
|
|
207
|
-
if len(net_metrics) == 0: # type: ignore
|
|
208
|
-
continue
|
|
209
|
-
available_reps[src_rse_id] = {}
|
|
210
|
-
for dst_site, mbps in net_metrics.items(): # type: ignore
|
|
211
|
-
if src_site == dst_site:
|
|
212
|
-
continue
|
|
213
|
-
if dst_site in self._sites:
|
|
214
|
-
if mbps > max_mbps:
|
|
215
|
-
max_mbps = mbps
|
|
216
|
-
dst_rse_id = self._sites[dst_site]['rse_id']
|
|
217
|
-
dst_rse_info = get_rse(rse_id=dst_rse_id)
|
|
218
|
-
|
|
219
|
-
if not dst_rse_info['availability_write']:
|
|
220
|
-
continue
|
|
221
|
-
|
|
222
|
-
site_added_bytes = sum(self._added_bytes.get_series(dst_rse_id))
|
|
223
|
-
site_added_files = sum(self._added_files.get_series(dst_rse_id))
|
|
224
|
-
|
|
225
|
-
if ((site_added_bytes + meta['bytes']) > self._max_bytes_hour_rse):
|
|
226
|
-
continue
|
|
227
|
-
if ((site_added_files + meta['length']) > self._max_files_hour_rse):
|
|
228
|
-
continue
|
|
229
|
-
|
|
230
|
-
queued = self._nmc.getQueuedFiles(src_site, dst_site)
|
|
231
|
-
|
|
232
|
-
# logging.debug('queued %s -> %s: %d' % (src_site, dst_site, queued))
|
|
233
|
-
if queued > 0:
|
|
234
|
-
continue
|
|
235
|
-
rse_space = space_info.get(dst_rse_id, {'free': 0, 'total': 1})
|
|
236
|
-
if src_rse_id not in self._src_penalties:
|
|
237
|
-
self._src_penalties[src_rse_id] = 100.0
|
|
238
|
-
src_penalty = self._src_penalties[src_rse_id]
|
|
239
|
-
if dst_rse_id not in self._dst_penalties:
|
|
240
|
-
self._dst_penalties[dst_rse_id] = 100.0
|
|
241
|
-
dst_penalty = self._dst_penalties[dst_rse_id]
|
|
242
|
-
|
|
243
|
-
free_space = float(rse_space['free']) / float(rse_space['total']) * 100.0
|
|
244
|
-
available_reps[src_rse_id][dst_rse_id] = {'free_space': free_space, 'src_penalty': src_penalty, 'dst_penalty': dst_penalty, 'mbps': float(mbps), 'metrics_type': net_metrics_type}
|
|
245
|
-
|
|
246
|
-
num_reps += 1
|
|
247
|
-
|
|
248
|
-
# decision['replica_rses'] = available_reps
|
|
249
|
-
decision['num_replicas'] = num_reps
|
|
250
|
-
|
|
251
|
-
if num_reps >= 5:
|
|
252
|
-
decision['error_reason'] = 'more than 4 replicas already exist'
|
|
253
|
-
return decision
|
|
254
|
-
|
|
255
|
-
src_dst_ratios = []
|
|
256
|
-
|
|
257
|
-
if max_mbps == 0.0:
|
|
258
|
-
decision['error_reason'] = 'could not find enough network metrics'
|
|
259
|
-
return decision
|
|
260
|
-
|
|
261
|
-
for src_id, dst_ids in available_reps.items():
|
|
262
|
-
for dst_id, metrics in dst_ids.items():
|
|
263
|
-
if dst_id in available_reps:
|
|
264
|
-
continue
|
|
265
|
-
bdw = (metrics['mbps'] / max_mbps) * 100.0
|
|
266
|
-
src_penalty = self._src_penalties[src_id]
|
|
267
|
-
dst_penalty = self._dst_penalties[dst_id]
|
|
268
|
-
|
|
269
|
-
ratio = ((metrics['free_space'] / 4.0) + bdw) * src_penalty * dst_penalty
|
|
270
|
-
src_dst_ratios.append((src_id, dst_id, ratio))
|
|
271
|
-
|
|
272
|
-
if len(src_dst_ratios) == 0:
|
|
273
|
-
decision['error_reason'] = 'found no suitable src/dst for replication'
|
|
274
|
-
return decision
|
|
275
|
-
|
|
276
|
-
sorted_ratios = sorted(src_dst_ratios, key=itemgetter(2), reverse=True)
|
|
277
|
-
logging.debug(sorted_ratios)
|
|
278
|
-
destination_rse = sorted_ratios[0][1]
|
|
279
|
-
source_rse = sorted_ratios[0][0]
|
|
280
|
-
decision['destination_rse'] = get_rse_name(rse_id=destination_rse)
|
|
281
|
-
decision['source_rse'] = get_rse_name(rse_id=source_rse)
|
|
282
|
-
# decision['rse_ratios'] = src_dst_ratios
|
|
283
|
-
self._dst_penalties[destination_rse] = 10.0
|
|
284
|
-
self._src_penalties[source_rse] = 10.0
|
|
285
|
-
|
|
286
|
-
self._added_cache.add_dataset(decision['did'])
|
|
287
|
-
|
|
288
|
-
self._added_bytes.add_point(destination_rse, meta['bytes'])
|
|
289
|
-
self._added_files.add_point(destination_rse, meta['length'])
|
|
290
|
-
|
|
291
|
-
self._added_bytes.add_point('total', meta['bytes'])
|
|
292
|
-
self._added_files.add_point('total', meta['length'])
|
|
293
|
-
|
|
294
|
-
return decision
|
rucio/daemons/c3po/c3po.py
DELETED
|
@@ -1,371 +0,0 @@
|
|
|
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
|
-
Dynamic data placement daemon.
|
|
17
|
-
'''
|
|
18
|
-
|
|
19
|
-
import logging
|
|
20
|
-
from datetime import datetime
|
|
21
|
-
from hashlib import md5
|
|
22
|
-
from json import dumps
|
|
23
|
-
from queue import Queue
|
|
24
|
-
from threading import Event, Thread
|
|
25
|
-
from time import sleep
|
|
26
|
-
from typing import TYPE_CHECKING, Optional
|
|
27
|
-
from uuid import uuid4
|
|
28
|
-
|
|
29
|
-
from requests import post
|
|
30
|
-
from requests.auth import HTTPBasicAuth
|
|
31
|
-
from requests.exceptions import RequestException
|
|
32
|
-
|
|
33
|
-
import rucio.db.sqla.util
|
|
34
|
-
from rucio.client import Client
|
|
35
|
-
from rucio.common import exception
|
|
36
|
-
from rucio.common.config import config_get, config_get_options
|
|
37
|
-
from rucio.common.logging import setup_logging
|
|
38
|
-
from rucio.common.types import InternalScope
|
|
39
|
-
from rucio.daemons.c3po.collectors.free_space import FreeSpaceCollector
|
|
40
|
-
from rucio.daemons.c3po.collectors.jedi_did import JediDIDCollector
|
|
41
|
-
from rucio.daemons.c3po.collectors.workload import WorkloadCollector
|
|
42
|
-
|
|
43
|
-
if TYPE_CHECKING:
|
|
44
|
-
from types import FrameType
|
|
45
|
-
|
|
46
|
-
GRACEFUL_STOP = Event()
|
|
47
|
-
DAEMON_NAME = 'c3po'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def read_free_space(
|
|
51
|
-
once: bool = False,
|
|
52
|
-
thread: int = 0,
|
|
53
|
-
waiting_time: int = 1800,
|
|
54
|
-
sleep_time: int = 10
|
|
55
|
-
) -> None:
|
|
56
|
-
"""
|
|
57
|
-
Thread to collect the space usage information for RSEs.
|
|
58
|
-
"""
|
|
59
|
-
free_space_collector = FreeSpaceCollector()
|
|
60
|
-
timer = waiting_time
|
|
61
|
-
while not GRACEFUL_STOP.is_set():
|
|
62
|
-
if timer < waiting_time:
|
|
63
|
-
timer += sleep_time
|
|
64
|
-
sleep(sleep_time)
|
|
65
|
-
continue
|
|
66
|
-
|
|
67
|
-
logging.info('collecting free space')
|
|
68
|
-
free_space_collector.collect_free_space()
|
|
69
|
-
timer = 0
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def read_workload(
|
|
73
|
-
once: bool = False,
|
|
74
|
-
thread: int = 0,
|
|
75
|
-
waiting_time: int = 1800,
|
|
76
|
-
sleep_time: int = 10
|
|
77
|
-
) -> None:
|
|
78
|
-
"""
|
|
79
|
-
Thread to collect the workload information from PanDA.
|
|
80
|
-
"""
|
|
81
|
-
workload_collector = WorkloadCollector()
|
|
82
|
-
timer = waiting_time
|
|
83
|
-
while not GRACEFUL_STOP.is_set():
|
|
84
|
-
if timer < waiting_time:
|
|
85
|
-
timer += sleep_time
|
|
86
|
-
sleep(sleep_time)
|
|
87
|
-
continue
|
|
88
|
-
|
|
89
|
-
logging.info('collecting workload')
|
|
90
|
-
workload_collector.collect_workload()
|
|
91
|
-
timer = 0
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def print_workload(
|
|
95
|
-
once: bool = False,
|
|
96
|
-
thread: int = 0,
|
|
97
|
-
waiting_time: int = 600,
|
|
98
|
-
sleep_time: int = 10
|
|
99
|
-
) -> None:
|
|
100
|
-
"""
|
|
101
|
-
Thread to regularly output the workload to logs for debugging.
|
|
102
|
-
"""
|
|
103
|
-
workload_collector = WorkloadCollector()
|
|
104
|
-
timer = waiting_time
|
|
105
|
-
while not GRACEFUL_STOP.is_set():
|
|
106
|
-
if timer < waiting_time:
|
|
107
|
-
timer += sleep_time
|
|
108
|
-
sleep(sleep_time)
|
|
109
|
-
continue
|
|
110
|
-
|
|
111
|
-
logging.info('Number of sites cached %d' % len(workload_collector.get_sites()))
|
|
112
|
-
for site in workload_collector.get_sites():
|
|
113
|
-
logging.info('%s: %d / %d / %d' % (site, workload_collector.get_cur_jobs(site), workload_collector.get_avg_jobs(site), workload_collector.get_max_jobs(site)))
|
|
114
|
-
timer = 0
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def read_dids(
|
|
118
|
-
once: bool = False,
|
|
119
|
-
thread: int = 0,
|
|
120
|
-
did_collector: Optional[JediDIDCollector] = None,
|
|
121
|
-
waiting_time: int = 60,
|
|
122
|
-
sleep_time: int = 10
|
|
123
|
-
) -> None:
|
|
124
|
-
"""
|
|
125
|
-
Thread to collect DIDs for the placement algorithm.
|
|
126
|
-
"""
|
|
127
|
-
timer = waiting_time
|
|
128
|
-
while not GRACEFUL_STOP.is_set():
|
|
129
|
-
if timer < waiting_time:
|
|
130
|
-
timer += sleep_time
|
|
131
|
-
sleep(sleep_time)
|
|
132
|
-
continue
|
|
133
|
-
|
|
134
|
-
if did_collector is not None:
|
|
135
|
-
did_collector.get_dids()
|
|
136
|
-
timer = 0
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def add_rule(
|
|
140
|
-
client: Client,
|
|
141
|
-
did: dict[str, str],
|
|
142
|
-
src_rse: str,
|
|
143
|
-
dst_rse: str
|
|
144
|
-
) -> None:
|
|
145
|
-
logging.debug('add rule for %s from %s to %s' % (did, src_rse, dst_rse))
|
|
146
|
-
r = client.add_replication_rule([did, ], 1, dst_rse, lifetime=604800, account='c3po', source_replica_expression=src_rse, activity='Data Brokering', asynchronous=True)
|
|
147
|
-
logging.debug(r)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def place_replica(
|
|
151
|
-
did_queue: Queue,
|
|
152
|
-
once: bool = False,
|
|
153
|
-
thread: int = 0,
|
|
154
|
-
waiting_time: int = 100,
|
|
155
|
-
dry_run: bool = False,
|
|
156
|
-
sampling: bool = False,
|
|
157
|
-
algorithms: str = 't2_free_space_only_pop_with_network',
|
|
158
|
-
datatypes: str = 'NTUP,DAOD',
|
|
159
|
-
dest_rse_expr: str = 'type=DATADISK',
|
|
160
|
-
max_bytes_hour: int = 100000000000000,
|
|
161
|
-
max_files_hour: int = 100000,
|
|
162
|
-
max_bytes_hour_rse: int = 50000000000000,
|
|
163
|
-
max_files_hour_rse: int = 10000,
|
|
164
|
-
min_popularity: int = 8,
|
|
165
|
-
min_recent_requests: int = 5,
|
|
166
|
-
max_replicas: int = 5,
|
|
167
|
-
sleep_time: int = 10
|
|
168
|
-
) -> None:
|
|
169
|
-
"""
|
|
170
|
-
Thread to run the placement algorithm to decide if and where to put new replicas.
|
|
171
|
-
"""
|
|
172
|
-
try:
|
|
173
|
-
c3po_options = config_get_options('c3po')
|
|
174
|
-
client = None
|
|
175
|
-
|
|
176
|
-
if 'algorithms' in c3po_options:
|
|
177
|
-
algorithms = config_get('c3po', 'algorithms')
|
|
178
|
-
|
|
179
|
-
algorithms_list = algorithms.split(',')
|
|
180
|
-
|
|
181
|
-
if not dry_run:
|
|
182
|
-
if len(algorithms_list) != 1:
|
|
183
|
-
logging.error('Multiple algorithms are only allowed in dry_run mode')
|
|
184
|
-
return
|
|
185
|
-
client = Client(auth_type='x509_proxy', account='c3po', creds={'client_proxy': '/opt/rucio/etc/ddmadmin.long.proxy'})
|
|
186
|
-
|
|
187
|
-
vo = client.vo
|
|
188
|
-
instances = {}
|
|
189
|
-
for algorithm in algorithms_list:
|
|
190
|
-
module_path = 'rucio.daemons.c3po.algorithms.' + algorithm
|
|
191
|
-
module = __import__(module_path, globals(), locals(), ['PlacementAlgorithm'])
|
|
192
|
-
instance = module.PlacementAlgorithm(datatypes, dest_rse_expr, max_bytes_hour, max_files_hour, max_bytes_hour_rse, max_files_hour_rse, min_popularity, min_recent_requests, max_replicas)
|
|
193
|
-
instances[algorithm] = instance
|
|
194
|
-
|
|
195
|
-
params = {
|
|
196
|
-
'dry_run': dry_run,
|
|
197
|
-
'sampling': sampling,
|
|
198
|
-
'datatypes': datatypes,
|
|
199
|
-
'dest_rse_expr': dest_rse_expr,
|
|
200
|
-
'max_bytes_hour': max_bytes_hour,
|
|
201
|
-
'max_files_hour': max_files_hour,
|
|
202
|
-
'max_bytes_hour_rse': max_bytes_hour_rse,
|
|
203
|
-
'max_files_hour_rse': max_files_hour_rse,
|
|
204
|
-
'min_recent_requests': min_recent_requests,
|
|
205
|
-
'min_popularity': min_popularity
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
instance_id = str(uuid4()).split('-')[0]
|
|
209
|
-
|
|
210
|
-
elastic_url = config_get('c3po', 'elastic_url')
|
|
211
|
-
elastic_index = config_get('c3po', 'elastic_index')
|
|
212
|
-
|
|
213
|
-
ca_cert = False
|
|
214
|
-
if 'ca_cert' in c3po_options:
|
|
215
|
-
ca_cert = config_get('c3po', 'ca_cert')
|
|
216
|
-
|
|
217
|
-
auth = False
|
|
218
|
-
if ('elastic_user' in c3po_options) and ('elastic_pass' in c3po_options):
|
|
219
|
-
auth = HTTPBasicAuth(config_get('c3po', 'elastic_user'), config_get('c3po', 'elastic_pass'))
|
|
220
|
-
|
|
221
|
-
w = waiting_time
|
|
222
|
-
while not GRACEFUL_STOP.is_set():
|
|
223
|
-
if w < waiting_time:
|
|
224
|
-
w += sleep_time
|
|
225
|
-
sleep(sleep_time)
|
|
226
|
-
continue
|
|
227
|
-
len_dids = did_queue.qsize()
|
|
228
|
-
|
|
229
|
-
if len_dids > 0:
|
|
230
|
-
logging.debug('(%s) %d did(s) in queue' % (instance_id, len_dids))
|
|
231
|
-
else:
|
|
232
|
-
logging.debug('(%s) no dids in queue' % (instance_id))
|
|
233
|
-
|
|
234
|
-
for _ in range(0, len_dids):
|
|
235
|
-
did = did_queue.get()
|
|
236
|
-
if isinstance(did[0], str):
|
|
237
|
-
did[0] = InternalScope(did[0], vo=vo)
|
|
238
|
-
for algorithm, instance in instances.items():
|
|
239
|
-
logging.info('(%s:%s) Retrieved %s:%s from queue. Run placement algorithm' % (algorithm, instance_id, did[0], did[1]))
|
|
240
|
-
decision = instance.place(did)
|
|
241
|
-
decision['@timestamp'] = datetime.utcnow().isoformat()
|
|
242
|
-
decision['algorithm'] = algorithm
|
|
243
|
-
decision['instance_id'] = instance_id
|
|
244
|
-
decision['params'] = params
|
|
245
|
-
|
|
246
|
-
create_rule = True
|
|
247
|
-
if sampling and 'error_reason' not in decision:
|
|
248
|
-
create_rule = bool(ord(md5(decision['did']).hexdigest()[-1]) & 1)
|
|
249
|
-
decision['create_rule'] = create_rule
|
|
250
|
-
# write the output to ES for further analysis
|
|
251
|
-
index_url = elastic_url + '/' + elastic_index + '-' + datetime.utcnow().strftime('%Y-%m') + '/record/'
|
|
252
|
-
try:
|
|
253
|
-
if ca_cert:
|
|
254
|
-
r = post(index_url, data=dumps(decision), verify=ca_cert, auth=auth)
|
|
255
|
-
else:
|
|
256
|
-
r = post(index_url, data=dumps(decision))
|
|
257
|
-
if r.status_code != 201:
|
|
258
|
-
logging.error(r)
|
|
259
|
-
logging.error('(%s:%s) could not write to ElasticSearch' % (algorithm, instance_id))
|
|
260
|
-
except RequestException as e:
|
|
261
|
-
logging.error('(%s:%s) could not write to ElasticSearch' % (algorithm, instance_id))
|
|
262
|
-
logging.error(e)
|
|
263
|
-
continue
|
|
264
|
-
|
|
265
|
-
logging.debug(decision)
|
|
266
|
-
if 'error_reason' in decision:
|
|
267
|
-
logging.error('(%s:%s) The placement algorithm ran into an error: %s' % (algorithm, instance_id, decision['error_reason']))
|
|
268
|
-
continue
|
|
269
|
-
|
|
270
|
-
logging.info('(%s:%s) Decided to place a new replica for %s on %s' % (algorithm, instance_id, decision['did'], decision['destination_rse']))
|
|
271
|
-
|
|
272
|
-
if (not dry_run) and create_rule:
|
|
273
|
-
# DO IT!
|
|
274
|
-
try:
|
|
275
|
-
add_rule(client, {'scope': did[0].external, 'name': did[1]}, decision.get('source_rse'), decision.get('destination_rse')) # type: ignore
|
|
276
|
-
except exception.RucioException as e:
|
|
277
|
-
logging.debug(e)
|
|
278
|
-
|
|
279
|
-
w = 0
|
|
280
|
-
except Exception as e:
|
|
281
|
-
logging.critical(e)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
def stop(signum: Optional[int] = None, frame: Optional['FrameType'] = None) -> None:
|
|
285
|
-
"""
|
|
286
|
-
Graceful exit.
|
|
287
|
-
"""
|
|
288
|
-
GRACEFUL_STOP.set()
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
def run(
|
|
292
|
-
once: bool = False,
|
|
293
|
-
threads: int = 1,
|
|
294
|
-
only_workload: bool = False,
|
|
295
|
-
dry_run: bool = False,
|
|
296
|
-
sampling: bool = False,
|
|
297
|
-
algorithms: str = 't2_free_space_only_pop_with_network',
|
|
298
|
-
datatypes: str = 'NTUP,DAOD',
|
|
299
|
-
dest_rse_expr: str = 'type=DATADISK',
|
|
300
|
-
max_bytes_hour: int = 100000000000000,
|
|
301
|
-
max_files_hour: int = 100000,
|
|
302
|
-
max_bytes_hour_rse: int = 50000000000000,
|
|
303
|
-
max_files_hour_rse: int = 10000,
|
|
304
|
-
min_popularity: int = 8,
|
|
305
|
-
min_recent_requests: int = 5,
|
|
306
|
-
max_replicas: int = 5,
|
|
307
|
-
waiting_time_read_free_space: int = 1800,
|
|
308
|
-
waiting_time_read_workload: int = 1800,
|
|
309
|
-
waiting_time_print_workload: int = 600,
|
|
310
|
-
waiting_time_read_dids: int = 60,
|
|
311
|
-
waiting_time_place_replica: int = 100,
|
|
312
|
-
sleep_time: int = 10
|
|
313
|
-
) -> None:
|
|
314
|
-
"""
|
|
315
|
-
Starts up the main thread
|
|
316
|
-
"""
|
|
317
|
-
setup_logging(process_name=DAEMON_NAME)
|
|
318
|
-
|
|
319
|
-
if rucio.db.sqla.util.is_old_db():
|
|
320
|
-
raise exception.DatabaseException('Database was not updated, daemon won\'t start')
|
|
321
|
-
|
|
322
|
-
logging.info('activating C-3PO')
|
|
323
|
-
|
|
324
|
-
thread_list = []
|
|
325
|
-
try:
|
|
326
|
-
if only_workload:
|
|
327
|
-
logging.info('running in workload-collector-only mode')
|
|
328
|
-
thread_list.append(Thread(target=read_workload, name='read_workload', kwargs={'thread': 0,
|
|
329
|
-
'waiting_time': waiting_time_read_workload,
|
|
330
|
-
'sleep_time': sleep_time}))
|
|
331
|
-
thread_list.append(Thread(target=print_workload, name='print_workload', kwargs={'thread': 0,
|
|
332
|
-
'waiting_time': waiting_time_print_workload,
|
|
333
|
-
'sleep_time': sleep_time}))
|
|
334
|
-
else:
|
|
335
|
-
logging.info('running in placement mode')
|
|
336
|
-
did_queue = Queue()
|
|
337
|
-
dc = JediDIDCollector(did_queue)
|
|
338
|
-
|
|
339
|
-
thread_list.append(Thread(target=read_free_space, name='read_free_space', kwargs={'thread': 0,
|
|
340
|
-
'waiting_time': waiting_time_read_free_space,
|
|
341
|
-
'sleep_time': sleep_time}))
|
|
342
|
-
thread_list.append(Thread(target=read_dids, name='read_dids', kwargs={'thread': 0,
|
|
343
|
-
'did_collector': dc,
|
|
344
|
-
'waiting_time': waiting_time_read_dids,
|
|
345
|
-
'sleep_time': sleep_time}))
|
|
346
|
-
thread_list.append(Thread(target=place_replica, name='place_replica', kwargs={'thread': 0,
|
|
347
|
-
'did_queue': did_queue,
|
|
348
|
-
'waiting_time': waiting_time_place_replica,
|
|
349
|
-
'algorithms': algorithms,
|
|
350
|
-
'dry_run': dry_run,
|
|
351
|
-
'sampling': sampling,
|
|
352
|
-
'datatypes': datatypes,
|
|
353
|
-
'dest_rse_expr': dest_rse_expr,
|
|
354
|
-
'max_bytes_hour': max_bytes_hour,
|
|
355
|
-
'max_files_hour': max_files_hour,
|
|
356
|
-
'max_bytes_hour_rse': max_bytes_hour_rse,
|
|
357
|
-
'max_files_hour_rse': max_files_hour_rse,
|
|
358
|
-
'min_popularity': min_popularity,
|
|
359
|
-
'min_recent_requests': min_recent_requests,
|
|
360
|
-
'max_replicas': max_replicas,
|
|
361
|
-
'sleep_time': sleep_time}))
|
|
362
|
-
|
|
363
|
-
for t in thread_list:
|
|
364
|
-
t.start()
|
|
365
|
-
|
|
366
|
-
logging.info('waiting for interrupts')
|
|
367
|
-
|
|
368
|
-
while len(thread_list) > 0:
|
|
369
|
-
[t.join(timeout=3) for t in thread_list if t and t.is_alive()]
|
|
370
|
-
except Exception as error:
|
|
371
|
-
logging.critical(error)
|