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
rucio/daemons/tracer/kronos.py
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
"""
|
|
16
16
|
This daemon consumes tracer messages from ActiveMQ and updates the atime for replicas.
|
|
17
17
|
"""
|
|
18
|
-
|
|
19
18
|
import functools
|
|
20
19
|
import logging
|
|
21
20
|
import re
|
|
@@ -26,13 +25,13 @@ from json import loads as jloads
|
|
|
26
25
|
from queue import Queue
|
|
27
26
|
from threading import Event, Thread
|
|
28
27
|
from time import time
|
|
29
|
-
from typing import TYPE_CHECKING
|
|
28
|
+
from typing import TYPE_CHECKING
|
|
30
29
|
|
|
31
30
|
import rucio.db.sqla.util
|
|
32
|
-
from rucio.common.config import config_get,
|
|
31
|
+
from rucio.common.config import config_get, config_get_int, config_get_list
|
|
33
32
|
from rucio.common.exception import DatabaseException, RSENotFound
|
|
34
|
-
from rucio.common.logging import setup_logging
|
|
35
|
-
from rucio.common.stomp_utils import StompConnectionManager
|
|
33
|
+
from rucio.common.logging import formatted_logger, setup_logging
|
|
34
|
+
from rucio.common.stomp_utils import ListenerBase, StompConnectionManager
|
|
36
35
|
from rucio.common.stopwatch import Stopwatch
|
|
37
36
|
from rucio.common.types import InternalAccount, InternalScope, LoggerFunction
|
|
38
37
|
from rucio.core.did import list_parent_dids, touch_dids
|
|
@@ -47,9 +46,10 @@ if TYPE_CHECKING:
|
|
|
47
46
|
from collections.abc import Set
|
|
48
47
|
from types import FrameType
|
|
49
48
|
|
|
50
|
-
from stomp import Connection
|
|
51
49
|
from stomp.utils import Frame
|
|
52
50
|
|
|
51
|
+
from rucio.common.stomp_utils import Connection
|
|
52
|
+
|
|
53
53
|
|
|
54
54
|
logging.getLogger("stomp").setLevel(logging.CRITICAL)
|
|
55
55
|
|
|
@@ -57,23 +57,20 @@ METRICS = MetricManager(module=__name__)
|
|
|
57
57
|
graceful_stop = Event()
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
class AMQConsumer:
|
|
60
|
+
class AMQConsumer(ListenerBase):
|
|
61
61
|
"""ActiveMQ message consumer"""
|
|
62
62
|
|
|
63
|
-
def __init__(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
):
|
|
75
|
-
self.__broker = broker
|
|
76
|
-
self.__conn = conn
|
|
63
|
+
def __init__(self,
|
|
64
|
+
conn: "Connection",
|
|
65
|
+
queue: str,
|
|
66
|
+
chunksize: int,
|
|
67
|
+
subscription_id: str,
|
|
68
|
+
excluded_usrdns: "Set[str]",
|
|
69
|
+
dataset_queue: Queue,
|
|
70
|
+
bad_files_patterns: list[re.Pattern],
|
|
71
|
+
logger: LoggerFunction = logging.log,
|
|
72
|
+
**kwargs: dict) -> None:
|
|
73
|
+
super().__init__(conn=conn, logger=logger, **kwargs)
|
|
77
74
|
self.__queue = queue
|
|
78
75
|
self.__reports = []
|
|
79
76
|
self.__ids = []
|
|
@@ -85,15 +82,6 @@ class AMQConsumer:
|
|
|
85
82
|
self.__excluded_usrdns = excluded_usrdns
|
|
86
83
|
self.__dataset_queue = dataset_queue
|
|
87
84
|
self.__bad_files_patterns = bad_files_patterns
|
|
88
|
-
self.__logger = logger
|
|
89
|
-
|
|
90
|
-
@METRICS.count_it
|
|
91
|
-
def on_heartbeat_timeout(self) -> None:
|
|
92
|
-
self.__conn.disconnect()
|
|
93
|
-
|
|
94
|
-
@METRICS.count_it
|
|
95
|
-
def on_error(self, frame: "Frame") -> None:
|
|
96
|
-
self.__logger(logging.ERROR, 'Message receive error: [%s] %s' % (self.__broker, frame.body))
|
|
97
85
|
|
|
98
86
|
@METRICS.count_it
|
|
99
87
|
def on_message(self, frame: "Frame") -> None:
|
|
@@ -107,7 +95,7 @@ class AMQConsumer:
|
|
|
107
95
|
|
|
108
96
|
try:
|
|
109
97
|
if appversion == 'dq2':
|
|
110
|
-
self.
|
|
98
|
+
self._conn.ack(msg_id, self.__subscription_id)
|
|
111
99
|
return
|
|
112
100
|
else:
|
|
113
101
|
report = jloads(frame.body) # type: ignore
|
|
@@ -115,22 +103,26 @@ class AMQConsumer:
|
|
|
115
103
|
# message is corrupt, not much to do here
|
|
116
104
|
# send count to graphite, send ack to broker and return
|
|
117
105
|
METRICS.counter('json_error').inc()
|
|
118
|
-
self.
|
|
119
|
-
self.
|
|
106
|
+
self._logger(logging.ERROR, 'json error', exc_info=True)
|
|
107
|
+
self._conn.ack(msg_id, self.__subscription_id)
|
|
120
108
|
return
|
|
121
109
|
|
|
122
110
|
self.__ids.append(msg_id)
|
|
123
111
|
self.__reports.append(report)
|
|
124
112
|
|
|
125
113
|
try:
|
|
126
|
-
self.
|
|
114
|
+
self._logger(logging.DEBUG,
|
|
115
|
+
"message received: %s %s %s",
|
|
116
|
+
str(report['eventType']),
|
|
117
|
+
report['filename'],
|
|
118
|
+
report['remoteSite'])
|
|
127
119
|
except Exception:
|
|
128
120
|
pass
|
|
129
121
|
|
|
130
122
|
if len(self.__ids) >= self.__chunksize:
|
|
131
123
|
self.__update_atime()
|
|
132
124
|
for msg_id in self.__ids:
|
|
133
|
-
self.
|
|
125
|
+
self._conn.ack(msg_id, self.__subscription_id)
|
|
134
126
|
|
|
135
127
|
self.__reports = []
|
|
136
128
|
self.__ids = []
|
|
@@ -153,16 +145,19 @@ class AMQConsumer:
|
|
|
153
145
|
if 'stateReason' in report and report['stateReason'] and isinstance(report['stateReason'], str) and pattern.match(report['stateReason']):
|
|
154
146
|
reason = report['stateReason'][:255]
|
|
155
147
|
if 'url' not in report or not report['url']:
|
|
156
|
-
self.
|
|
148
|
+
self._logger(logging.ERROR, 'Missing url in the following trace : ' + str(report))
|
|
157
149
|
else:
|
|
158
150
|
try:
|
|
159
|
-
|
|
160
|
-
declare_bad_file_replicas([
|
|
161
|
-
self.
|
|
151
|
+
surl = report['url']
|
|
152
|
+
declare_bad_file_replicas([surl, ], reason=reason, issuer=InternalAccount('root', vo=report['vo']), status=BadFilesStatus.SUSPICIOUS)
|
|
153
|
+
self._logger(logging.INFO,
|
|
154
|
+
"Declare suspicious file %s with reason %s",
|
|
155
|
+
report['url'],
|
|
156
|
+
reason)
|
|
162
157
|
except Exception as error:
|
|
163
|
-
self.
|
|
158
|
+
self._logger(logging.ERROR, 'Failed to declare suspicious file' + str(error))
|
|
164
159
|
except Exception as error:
|
|
165
|
-
self.
|
|
160
|
+
self._logger(logging.ERROR, 'Problem with bad trace : %s . Error %s', str(report), str(error))
|
|
166
161
|
|
|
167
162
|
# check if scope in report. if not skip this one.
|
|
168
163
|
if 'scope' not in report:
|
|
@@ -225,7 +220,7 @@ class AMQConsumer:
|
|
|
225
220
|
try:
|
|
226
221
|
rse_id = get_rse_id(rse=rse, vo=report['vo'])
|
|
227
222
|
except RSENotFound:
|
|
228
|
-
self.
|
|
223
|
+
self._logger(logging.WARNING, "Cannot lookup rse_id for %s. Will skip this report.", rse)
|
|
229
224
|
METRICS.counter('rse_not_found').inc()
|
|
230
225
|
continue
|
|
231
226
|
replicas.append({'name': report['filename'], 'scope': report['scope'], 'rse': rse, 'rse_id': rse_id, 'accessed_at': datetime.utcfromtimestamp(report['traceTimeentryUnix']),
|
|
@@ -241,7 +236,7 @@ class AMQConsumer:
|
|
|
241
236
|
try:
|
|
242
237
|
rse_id = get_rse_id(rse=rse, vo=report['vo'])
|
|
243
238
|
except RSENotFound:
|
|
244
|
-
self.
|
|
239
|
+
self._logger(logging.WARNING, "Cannot lookup rse_id for %s.", rse)
|
|
245
240
|
METRICS.counter('rse_not_found').inc()
|
|
246
241
|
if 'datasetScope' in report:
|
|
247
242
|
self.__dataset_queue.put({'scope': InternalScope(report['datasetScope'], vo=report['vo']),
|
|
@@ -259,11 +254,11 @@ class AMQConsumer:
|
|
|
259
254
|
'accessed_at': datetime.utcfromtimestamp(report['traceTimeentryUnix'])})
|
|
260
255
|
|
|
261
256
|
except (KeyError, AttributeError):
|
|
262
|
-
self.
|
|
257
|
+
self._logger(logging.ERROR, "Cannot handle report.", exc_info=True)
|
|
263
258
|
METRICS.counter('report_error').inc()
|
|
264
259
|
continue
|
|
265
260
|
except Exception:
|
|
266
|
-
self.
|
|
261
|
+
self._logger(logging.ERROR, "Exception", exc_info=True)
|
|
267
262
|
continue
|
|
268
263
|
|
|
269
264
|
for did in list_parent_dids(report['scope'], report['filename']):
|
|
@@ -276,15 +271,15 @@ class AMQConsumer:
|
|
|
276
271
|
try:
|
|
277
272
|
rse_id = get_rse_id(rse=rse, vo=report['vo'])
|
|
278
273
|
except RSENotFound:
|
|
279
|
-
self.
|
|
274
|
+
self._logger(logging.WARNING, "Cannot lookup rse_id for %s. Will skip this report.", rse)
|
|
280
275
|
METRICS.counter('rse_not_found').inc()
|
|
281
276
|
continue
|
|
282
277
|
self.__dataset_queue.put({'scope': did['scope'], 'name': did['name'], 'did_type': did['type'], 'rse_id': rse_id, 'accessed_at': datetime.utcfromtimestamp(report['traceTimeentryUnix'])})
|
|
283
278
|
|
|
284
|
-
if not
|
|
279
|
+
if not replicas:
|
|
285
280
|
return
|
|
286
281
|
|
|
287
|
-
self.
|
|
282
|
+
self._logger(logging.DEBUG, "trying to update replicas: %s", replicas)
|
|
288
283
|
|
|
289
284
|
stopwatch = Stopwatch()
|
|
290
285
|
try:
|
|
@@ -301,25 +296,50 @@ class AMQConsumer:
|
|
|
301
296
|
'eventVersion': replica['eventVersion']}
|
|
302
297
|
if replica['scope'].vo != 'def':
|
|
303
298
|
resubmit['vo'] = replica['scope'].vo
|
|
304
|
-
self.
|
|
299
|
+
self._conn.send(body=jdumps(resubmit), destination=self.__queue, headers={'appversion': 'rucio', 'resubmitted': '1'})
|
|
305
300
|
METRICS.counter('sent_resubmitted').inc()
|
|
306
301
|
METRICS.timer('update_atime').observe(stopwatch.elapsed)
|
|
307
302
|
except Exception:
|
|
308
|
-
self.
|
|
303
|
+
self._logger(logging.ERROR, "Cannot update replicas.", exc_info=True)
|
|
309
304
|
METRICS.counter('update_error').inc()
|
|
310
305
|
|
|
311
306
|
METRICS.counter('updated_replicas').inc()
|
|
312
307
|
|
|
313
308
|
|
|
314
|
-
def kronos_file(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
) -> None:
|
|
309
|
+
def kronos_file(once: bool = False,
|
|
310
|
+
dataset_queue: "Queue | None" = None,
|
|
311
|
+
sleep_time: int = 60,
|
|
312
|
+
logger: LoggerFunction = logging.log) -> None:
|
|
319
313
|
"""
|
|
320
314
|
Main loop to consume tracer reports.
|
|
321
315
|
"""
|
|
322
|
-
|
|
316
|
+
bad_files_patterns = []
|
|
317
|
+
try:
|
|
318
|
+
patterns = config_get_list(section='kronos', option='bad_files_patterns', session=None)
|
|
319
|
+
for pat in patterns:
|
|
320
|
+
bad_files_patterns.append(re.compile(pat.strip()))
|
|
321
|
+
except (NoOptionError, NoSectionError, RuntimeError):
|
|
322
|
+
logger(logging.ERROR, "Failed to get bad_file_patterns")
|
|
323
|
+
bad_files_patterns.clear()
|
|
324
|
+
except Exception as error:
|
|
325
|
+
logger(logging.ERROR, f'Failed to get bad_file_patterns {str(error)}')
|
|
326
|
+
bad_files_patterns.clear()
|
|
327
|
+
|
|
328
|
+
excluded_usrdns = set(config_get('tracer-kronos', 'excluded_usrdns').split(','))
|
|
329
|
+
|
|
330
|
+
subscription_id = config_get('tracer-kronos', 'subscription_id')
|
|
331
|
+
stomp_conn_mngr = StompConnectionManager(config_section='tracer-kronos',
|
|
332
|
+
logger=logger)
|
|
333
|
+
stomp_conn_mngr.set_listener_factory('rucio-tracer-kronos',
|
|
334
|
+
AMQConsumer,
|
|
335
|
+
queue=config_get('tracer-kronos', 'queue'),
|
|
336
|
+
chunksize=config_get_int('tracer-kronos', 'chunksize'),
|
|
337
|
+
subscription_id=subscription_id,
|
|
338
|
+
excluded_usrdns=excluded_usrdns,
|
|
339
|
+
dataset_queue=dataset_queue,
|
|
340
|
+
bad_files_patterns=bad_files_patterns,
|
|
341
|
+
heartbeats=stomp_conn_mngr.config.heartbeats)
|
|
342
|
+
|
|
323
343
|
run_daemon(
|
|
324
344
|
once=once,
|
|
325
345
|
graceful_stop=graceful_stop,
|
|
@@ -336,74 +356,20 @@ def kronos_file(
|
|
|
336
356
|
stomp_conn_mngr.disconnect()
|
|
337
357
|
|
|
338
358
|
|
|
339
|
-
def run_once_kronos_file(heartbeat_handler: HeartbeatHandler,
|
|
359
|
+
def run_once_kronos_file(heartbeat_handler: HeartbeatHandler,
|
|
360
|
+
stomp_conn_mngr: StompConnectionManager,
|
|
361
|
+
dataset_queue: Queue,
|
|
362
|
+
sleep_time: int,
|
|
363
|
+
**kwargs: dict) -> None:
|
|
340
364
|
"""
|
|
341
365
|
Run the amq consumer once.
|
|
342
366
|
"""
|
|
343
367
|
_, _, logger = heartbeat_handler.live()
|
|
344
368
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
try:
|
|
350
|
-
bad_files_patterns = []
|
|
351
|
-
pattern = config_get(section='kronos', option='bad_files_patterns', session=None)
|
|
352
|
-
pattern = str(pattern)
|
|
353
|
-
patterns = pattern.split(",")
|
|
354
|
-
for pat in patterns:
|
|
355
|
-
bad_files_patterns.append(re.compile(pat.strip()))
|
|
356
|
-
except (NoOptionError, NoSectionError, RuntimeError):
|
|
357
|
-
bad_files_patterns = []
|
|
358
|
-
except Exception as error:
|
|
359
|
-
logger.error(f'Failed to get bad_file_patterns {str(error)}')
|
|
360
|
-
bad_files_patterns = []
|
|
361
|
-
|
|
362
|
-
use_ssl = config_get_bool('tracer-kronos', 'use_ssl', default=True, raise_exception=False)
|
|
363
|
-
if not use_ssl:
|
|
364
|
-
username = config_get('tracer-kronos', 'username')
|
|
365
|
-
password = config_get('tracer-kronos', 'password')
|
|
366
|
-
|
|
367
|
-
excluded_usrdns = set(config_get('tracer-kronos', 'excluded_usrdns').split(','))
|
|
368
|
-
vhost = config_get('tracer-kronos', 'broker_virtual_host', raise_exception=False)
|
|
369
|
-
|
|
370
|
-
brokers_alias = config_get_list('tracer-kronos', 'brokers')
|
|
371
|
-
port = config_get_int('tracer-kronos', 'port')
|
|
372
|
-
reconnect_attempts = config_get_int('tracer-kronos', 'reconnect_attempts')
|
|
373
|
-
ssl_key_file = config_get('tracer-kronos', 'ssl_key_file', raise_exception=False)
|
|
374
|
-
ssl_cert_file = config_get('tracer-kronos', 'ssl_cert_file', raise_exception=False)
|
|
375
|
-
|
|
376
|
-
created_conns, _ = stomp_conn_mngr.re_configure(
|
|
377
|
-
brokers=brokers_alias,
|
|
378
|
-
port=port,
|
|
379
|
-
use_ssl=use_ssl,
|
|
380
|
-
vhost=vhost,
|
|
381
|
-
reconnect_attempts=reconnect_attempts,
|
|
382
|
-
ssl_key_file=ssl_key_file,
|
|
383
|
-
ssl_cert_file=ssl_cert_file,
|
|
384
|
-
timeout=sleep_time,
|
|
385
|
-
heartbeats=(0, 5000),
|
|
386
|
-
logger=logger,
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
for conn in created_conns:
|
|
390
|
-
if not conn.is_connected():
|
|
391
|
-
logger(logging.INFO, 'connecting to %s' % str(conn.transport._Transport__host_and_ports[0]))
|
|
392
|
-
METRICS.counter('reconnect.{host}').labels(host=conn.transport._Transport__host_and_ports[0][0]).inc()
|
|
393
|
-
conn.set_listener('rucio-tracer-kronos', AMQConsumer(broker=conn.transport._Transport__host_and_ports[0],
|
|
394
|
-
conn=conn,
|
|
395
|
-
queue=config_get('tracer-kronos', 'queue'),
|
|
396
|
-
chunksize=chunksize,
|
|
397
|
-
subscription_id=subscription_id,
|
|
398
|
-
excluded_usrdns=excluded_usrdns,
|
|
399
|
-
dataset_queue=dataset_queue,
|
|
400
|
-
bad_files_patterns=bad_files_patterns,
|
|
401
|
-
logger=logger))
|
|
402
|
-
if not use_ssl:
|
|
403
|
-
conn.connect(username, password)
|
|
404
|
-
else:
|
|
405
|
-
conn.connect()
|
|
406
|
-
conn.subscribe(destination=config_get('tracer-kronos', 'queue'), ack='client-individual', id=subscription_id, headers={'activemq.prefetchSize': prefetch_size})
|
|
369
|
+
stomp_conn_mngr.subscribe(id_=config_get('tracer-kronos', 'subscription_id'),
|
|
370
|
+
ack='client-individual',
|
|
371
|
+
destination=config_get('tracer-kronos', 'queue'),
|
|
372
|
+
headers={'activemq.prefetchSize': config_get_int('tracer-kronos', 'prefetch_size')})
|
|
407
373
|
|
|
408
374
|
|
|
409
375
|
def kronos_dataset(dataset_queue: Queue, once: bool = False, sleep_time: int = 60) -> None:
|
|
@@ -422,10 +388,16 @@ def kronos_dataset(dataset_queue: Queue, once: bool = False, sleep_time: int = 6
|
|
|
422
388
|
)
|
|
423
389
|
|
|
424
390
|
# once again for potential backlog
|
|
425
|
-
run_once_kronos_dataset(dataset_queue=dataset_queue,
|
|
391
|
+
run_once_kronos_dataset(dataset_queue=dataset_queue,
|
|
392
|
+
return_values=return_values,
|
|
393
|
+
heartbeat_handler=return_values['heartbeat_handler'],
|
|
394
|
+
sleep_time=sleep_time)
|
|
426
395
|
|
|
427
396
|
|
|
428
|
-
def run_once_kronos_dataset(dataset_queue: Queue,
|
|
397
|
+
def run_once_kronos_dataset(dataset_queue: Queue,
|
|
398
|
+
return_values: dict,
|
|
399
|
+
heartbeat_handler: "HeartbeatHandler | None",
|
|
400
|
+
**kwargs) -> None:
|
|
429
401
|
if heartbeat_handler is None:
|
|
430
402
|
if "heartbeat_handler" not in return_values.keys():
|
|
431
403
|
return_values["heartbeat_handler"] = HeartbeatHandler("kronos-dataset", 10)
|
|
@@ -458,7 +430,7 @@ def run_once_kronos_dataset(dataset_queue: Queue, return_values: dict, heartbeat
|
|
|
458
430
|
total, failed, start = 0, 0, time()
|
|
459
431
|
for did, accessed_at in datasets.items():
|
|
460
432
|
scope, name = did.split(':')
|
|
461
|
-
scope = InternalScope(scope,
|
|
433
|
+
scope = InternalScope(scope, from_external=False)
|
|
462
434
|
update_did = {'scope': scope, 'name': name, 'type': DIDType.DATASET, 'accessed_at': accessed_at}
|
|
463
435
|
# if update fails, put back in queue and retry next time
|
|
464
436
|
if not touch_dids((update_did,)):
|
|
@@ -471,7 +443,7 @@ def run_once_kronos_dataset(dataset_queue: Queue, return_values: dict, heartbeat
|
|
|
471
443
|
total, failed, start = 0, 0, time()
|
|
472
444
|
for did, rses in dslocks.items():
|
|
473
445
|
scope, name = did.split(':')
|
|
474
|
-
scope = InternalScope(scope,
|
|
446
|
+
scope = InternalScope(scope, from_external=False)
|
|
475
447
|
for rse, accessed_at in rses.items():
|
|
476
448
|
update_dslock = {'scope': scope, 'name': name, 'rse_id': rse, 'accessed_at': accessed_at}
|
|
477
449
|
# if update fails, put back in queue and retry next time
|
|
@@ -484,7 +456,7 @@ def run_once_kronos_dataset(dataset_queue: Queue, return_values: dict, heartbeat
|
|
|
484
456
|
total, failed, start = 0, 0, time()
|
|
485
457
|
for did, rses in dslocks.items():
|
|
486
458
|
scope, name = did.split(':')
|
|
487
|
-
scope = InternalScope(scope,
|
|
459
|
+
scope = InternalScope(scope, from_external=False)
|
|
488
460
|
for rse, accessed_at in rses.items():
|
|
489
461
|
update_dslock = {'scope': scope, 'name': name, 'rse_id': rse, 'accessed_at': accessed_at}
|
|
490
462
|
# if update fails, put back in queue and retry next time
|
|
@@ -495,7 +467,7 @@ def run_once_kronos_dataset(dataset_queue: Queue, return_values: dict, heartbeat
|
|
|
495
467
|
logger(logging.INFO, 'update done for %d collection replicas, %d failed (%ds)' % (total, failed, time() - start))
|
|
496
468
|
|
|
497
469
|
|
|
498
|
-
def stop(signum:
|
|
470
|
+
def stop(signum: "int | None" = None, frame: "FrameType | None" = None) -> None:
|
|
499
471
|
"""
|
|
500
472
|
Graceful exit.
|
|
501
473
|
"""
|
|
@@ -506,31 +478,34 @@ def run(
|
|
|
506
478
|
once: bool = False,
|
|
507
479
|
threads: int = 1,
|
|
508
480
|
sleep_time_datasets: int = 60,
|
|
509
|
-
sleep_time_files: int = 60
|
|
510
|
-
) -> None:
|
|
481
|
+
sleep_time_files: int = 60) -> None:
|
|
511
482
|
"""
|
|
512
483
|
Starts up the consumer threads
|
|
513
484
|
"""
|
|
514
485
|
setup_logging(process_name='tracer-kronos')
|
|
486
|
+
logger = formatted_logger(logging.log, 'Kronos %s')
|
|
515
487
|
|
|
516
488
|
if rucio.db.sqla.util.is_old_db():
|
|
517
489
|
raise DatabaseException('Database was not updated, daemon won\'t start')
|
|
518
490
|
|
|
519
491
|
dataset_queue = Queue()
|
|
520
|
-
logging.
|
|
492
|
+
logger(logging.INFO, 'starting tracer consumer threads')
|
|
521
493
|
|
|
522
494
|
thread_list = []
|
|
523
|
-
for _ in range(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
495
|
+
for _ in range(threads):
|
|
496
|
+
krf_thread = Thread(target=kronos_file, kwargs={'once': once,
|
|
497
|
+
'logger': logger,
|
|
498
|
+
'sleep_time': sleep_time_files,
|
|
499
|
+
'dataset_queue': dataset_queue})
|
|
500
|
+
krf_thread.start()
|
|
501
|
+
krd_thread = Thread(target=kronos_dataset, kwargs={'once': once,
|
|
502
|
+
'sleep_time': sleep_time_datasets,
|
|
503
|
+
'dataset_queue': dataset_queue})
|
|
504
|
+
krd_thread.start()
|
|
505
|
+
thread_list.append(krf_thread)
|
|
506
|
+
thread_list.append(krd_thread)
|
|
507
|
+
|
|
508
|
+
logger(logging.INFO, 'waiting for interrupts')
|
|
509
|
+
|
|
510
|
+
while [thread.join(timeout=3.) for thread in thread_list if thread.is_alive()]:
|
|
511
|
+
pass
|
rucio/db/sqla/constants.py
CHANGED
|
@@ -38,7 +38,7 @@ def upgrade():
|
|
|
38
38
|
add_column("rses", sa.Column("availability_write", sa.Boolean, server_default=true()), schema=schema)
|
|
39
39
|
add_column("rses", sa.Column("availability_delete", sa.Boolean, server_default=true()), schema=schema)
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
rse = sa.sql.table(
|
|
42
42
|
"rses",
|
|
43
43
|
sa.Column("id", GUID()),
|
|
44
44
|
sa.Column("availability", sa.Integer),
|
|
@@ -50,9 +50,9 @@ def upgrade():
|
|
|
50
50
|
|
|
51
51
|
conn = get_bind()
|
|
52
52
|
|
|
53
|
-
conn.execute(
|
|
54
|
-
conn.execute(
|
|
55
|
-
conn.execute(
|
|
53
|
+
conn.execute(rse.update().where(rse.c.availability.in_([0, 1, 2, 3])).values({"availability_read": False}))
|
|
54
|
+
conn.execute(rse.update().where(rse.c.availability.in_([0, 1, 4, 5])).values({"availability_write": False}))
|
|
55
|
+
conn.execute(rse.update().where(rse.c.availability.in_([0, 2, 4, 6])).values({"availability_delete": False}))
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def downgrade():
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
"""Increase OAuthRequest.redirect_msg length""" # noqa: D400, D415
|
|
16
|
+
|
|
17
|
+
import sqlalchemy as sa
|
|
18
|
+
from alembic import context
|
|
19
|
+
from alembic.op import alter_column
|
|
20
|
+
|
|
21
|
+
# Alembic revision identifiers
|
|
22
|
+
revision = '30d5206e9cad'
|
|
23
|
+
down_revision = 'b0070f3695c8'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def upgrade():
|
|
27
|
+
"""Upgrade the database to this revision."""
|
|
28
|
+
if context.get_context().dialect.name in ['oracle', 'mysql', 'postgresql']:
|
|
29
|
+
schema = context.get_context().version_table_schema if context.get_context().version_table_schema else ''
|
|
30
|
+
alter_column('oauth_requests', 'redirect_msg', existing_type=sa.String(2048), type_=sa.String(4000), schema=schema)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def downgrade():
|
|
34
|
+
"""Downgrade the database to the previous revision."""
|
|
35
|
+
if context.get_context().dialect.name in ['oracle', 'mysql', 'postgresql']:
|
|
36
|
+
schema = context.get_context().version_table_schema if context.get_context().version_table_schema else ''
|
|
37
|
+
alter_column('oauth_requests', 'redirect_msg', existing_type=sa.String(4000), type_=sa.String(2048), schema=schema)
|