db-sync-tool-kmi 2.11.6__py3-none-any.whl → 3.0.2__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.
- db_sync_tool/__main__.py +7 -252
- db_sync_tool/cli.py +733 -0
- db_sync_tool/database/process.py +94 -111
- db_sync_tool/database/utility.py +339 -121
- db_sync_tool/info.py +1 -1
- db_sync_tool/recipes/drupal.py +87 -12
- db_sync_tool/recipes/laravel.py +7 -6
- db_sync_tool/recipes/parsing.py +102 -0
- db_sync_tool/recipes/symfony.py +17 -28
- db_sync_tool/recipes/typo3.py +33 -54
- db_sync_tool/recipes/wordpress.py +13 -12
- db_sync_tool/remote/client.py +206 -71
- db_sync_tool/remote/file_transfer.py +303 -0
- db_sync_tool/remote/rsync.py +18 -15
- db_sync_tool/remote/system.py +2 -3
- db_sync_tool/remote/transfer.py +51 -47
- db_sync_tool/remote/utility.py +29 -30
- db_sync_tool/sync.py +52 -28
- db_sync_tool/utility/config.py +367 -0
- db_sync_tool/utility/config_resolver.py +573 -0
- db_sync_tool/utility/console.py +779 -0
- db_sync_tool/utility/exceptions.py +32 -0
- db_sync_tool/utility/helper.py +155 -148
- db_sync_tool/utility/info.py +53 -20
- db_sync_tool/utility/log.py +55 -31
- db_sync_tool/utility/logging_config.py +410 -0
- db_sync_tool/utility/mode.py +85 -150
- db_sync_tool/utility/output.py +122 -51
- db_sync_tool/utility/parser.py +33 -53
- db_sync_tool/utility/pure.py +93 -0
- db_sync_tool/utility/security.py +79 -0
- db_sync_tool/utility/system.py +277 -194
- db_sync_tool/utility/validation.py +2 -9
- db_sync_tool_kmi-3.0.2.dist-info/METADATA +99 -0
- db_sync_tool_kmi-3.0.2.dist-info/RECORD +44 -0
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/WHEEL +1 -1
- db_sync_tool_kmi-2.11.6.dist-info/METADATA +0 -276
- db_sync_tool_kmi-2.11.6.dist-info/RECORD +0 -34
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/entry_points.txt +0 -0
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info/licenses}/LICENSE +0 -0
- {db_sync_tool_kmi-2.11.6.dist-info → db_sync_tool_kmi-3.0.2.dist-info}/top_level.txt +0 -0
db_sync_tool/utility/mode.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: future_fstrings -*-
|
|
3
2
|
|
|
4
3
|
"""
|
|
5
4
|
Mode script
|
|
6
5
|
"""
|
|
7
6
|
|
|
8
7
|
import subprocess
|
|
9
|
-
import sys
|
|
10
8
|
|
|
11
9
|
from db_sync_tool.utility import system, output, helper
|
|
10
|
+
from db_sync_tool.utility.exceptions import DbSyncError
|
|
11
|
+
from db_sync_tool.utility.security import sanitize_command_for_logging # noqa: F401 (re-export)
|
|
12
12
|
from db_sync_tool.remote import system as remote_system
|
|
13
13
|
|
|
14
14
|
|
|
@@ -38,140 +38,72 @@ class SyncMode:
|
|
|
38
38
|
SYNC_LOCAL = 'SYNC_LOCAL'
|
|
39
39
|
|
|
40
40
|
@staticmethod
|
|
41
|
-
def is_dump_local():
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"""
|
|
46
|
-
return SyncMode.is_full_local() and SyncMode.is_same_host() and not SyncMode.is_sync_local()
|
|
47
|
-
|
|
48
|
-
@staticmethod
|
|
49
|
-
def is_dump_remote():
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
:return: boolean
|
|
53
|
-
"""
|
|
54
|
-
return SyncMode.is_full_remote() and SyncMode.is_same_host() and \
|
|
55
|
-
not SyncMode.is_sync_remote()
|
|
56
|
-
|
|
57
|
-
@staticmethod
|
|
58
|
-
def is_receiver():
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
:return: boolean
|
|
62
|
-
"""
|
|
63
|
-
return 'host' in system.config[Client.ORIGIN] and not SyncMode.is_proxy() and \
|
|
64
|
-
not SyncMode.is_sync_remote()
|
|
65
|
-
|
|
66
|
-
@staticmethod
|
|
67
|
-
def is_sender():
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
:return: boolean
|
|
71
|
-
"""
|
|
72
|
-
return 'host' in system.config[Client.TARGET] and not SyncMode.is_proxy() and \
|
|
73
|
-
not SyncMode.is_sync_remote()
|
|
41
|
+
def is_dump_local() -> bool:
|
|
42
|
+
cfg = system.get_typed_config()
|
|
43
|
+
both_local = not cfg.origin.is_remote and not cfg.target.is_remote
|
|
44
|
+
return both_local and SyncMode.is_same_host() and not SyncMode.is_sync_local()
|
|
74
45
|
|
|
75
46
|
@staticmethod
|
|
76
|
-
def
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"""
|
|
81
|
-
return SyncMode.is_full_remote()
|
|
47
|
+
def is_dump_remote() -> bool:
|
|
48
|
+
cfg = system.get_typed_config()
|
|
49
|
+
both_remote = cfg.origin.is_remote and cfg.target.is_remote
|
|
50
|
+
return both_remote and SyncMode.is_same_host() and not SyncMode.is_sync_remote()
|
|
82
51
|
|
|
83
52
|
@staticmethod
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
:return: boolean
|
|
88
|
-
"""
|
|
89
|
-
return system.config['import'] != '' and 'host' not in system.config[Client.TARGET]
|
|
53
|
+
def is_receiver() -> bool:
|
|
54
|
+
cfg = system.get_typed_config()
|
|
55
|
+
return cfg.origin.is_remote and not SyncMode.is_proxy() and not SyncMode.is_sync_remote()
|
|
90
56
|
|
|
91
57
|
@staticmethod
|
|
92
|
-
def
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
:return: boolean
|
|
96
|
-
"""
|
|
97
|
-
return system.config['import'] != '' and 'host' in system.config[Client.TARGET]
|
|
58
|
+
def is_sender() -> bool:
|
|
59
|
+
cfg = system.get_typed_config()
|
|
60
|
+
return cfg.target.is_remote and not SyncMode.is_proxy() and not SyncMode.is_sync_remote()
|
|
98
61
|
|
|
99
62
|
@staticmethod
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
:return: boolean
|
|
104
|
-
"""
|
|
105
|
-
return SyncMode.is_full_local() and SyncMode.is_same_host() and SyncMode.is_same_sync()
|
|
63
|
+
def is_proxy() -> bool:
|
|
64
|
+
cfg = system.get_typed_config()
|
|
65
|
+
return cfg.origin.is_remote and cfg.target.is_remote
|
|
106
66
|
|
|
107
67
|
@staticmethod
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
:return: boolean
|
|
112
|
-
"""
|
|
113
|
-
return SyncMode.is_full_remote() and SyncMode.is_same_host() and SyncMode.is_same_sync()
|
|
68
|
+
def is_import_local() -> bool:
|
|
69
|
+
cfg = system.get_typed_config()
|
|
70
|
+
return cfg.import_file != '' and not cfg.target.is_remote
|
|
114
71
|
|
|
115
72
|
@staticmethod
|
|
116
|
-
def
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
:return: boolean
|
|
120
|
-
"""
|
|
121
|
-
return ((SyncMode.is_available_configuration('path') and
|
|
122
|
-
not SyncMode.is_same_configuration('path')) or
|
|
123
|
-
(SyncMode.is_available_configuration('db') and
|
|
124
|
-
not SyncMode.is_same_configuration('db')))
|
|
73
|
+
def is_import_remote() -> bool:
|
|
74
|
+
cfg = system.get_typed_config()
|
|
75
|
+
return cfg.import_file != '' and cfg.target.is_remote
|
|
125
76
|
|
|
126
77
|
@staticmethod
|
|
127
|
-
def
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"""
|
|
132
|
-
return SyncMode.is_available_configuration('host')
|
|
78
|
+
def is_sync_local() -> bool:
|
|
79
|
+
cfg = system.get_typed_config()
|
|
80
|
+
return (not cfg.origin.is_remote and not cfg.target.is_remote and
|
|
81
|
+
SyncMode.is_same_host() and SyncMode.is_same_sync())
|
|
133
82
|
|
|
134
83
|
@staticmethod
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"""
|
|
140
|
-
return SyncMode.is_unavailable_configuration('host')
|
|
84
|
+
def is_sync_remote() -> bool:
|
|
85
|
+
cfg = system.get_typed_config()
|
|
86
|
+
return (cfg.origin.is_remote and cfg.target.is_remote and
|
|
87
|
+
SyncMode.is_same_host() and SyncMode.is_same_sync())
|
|
141
88
|
|
|
142
89
|
@staticmethod
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
:return: boolean
|
|
155
|
-
"""
|
|
156
|
-
return key in system.config[Client.ORIGIN] and key in system.config[Client.TARGET]
|
|
157
|
-
|
|
158
|
-
@staticmethod
|
|
159
|
-
def is_unavailable_configuration(key):
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
:return: boolean
|
|
163
|
-
"""
|
|
164
|
-
return key not in system.config[Client.ORIGIN] and key not in system.config[Client.TARGET]
|
|
90
|
+
def is_same_sync() -> bool:
|
|
91
|
+
cfg = system.get_typed_config()
|
|
92
|
+
# Different paths on same host
|
|
93
|
+
if cfg.origin.path and cfg.target.path and cfg.origin.path != cfg.target.path:
|
|
94
|
+
return True
|
|
95
|
+
# Different databases on same host
|
|
96
|
+
if cfg.origin.db.name and cfg.target.db.name:
|
|
97
|
+
if (cfg.origin.db.name, cfg.origin.db.host) != (cfg.target.db.name, cfg.target.db.host):
|
|
98
|
+
return True
|
|
99
|
+
return False
|
|
165
100
|
|
|
166
101
|
@staticmethod
|
|
167
|
-
def
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return (SyncMode.is_available_configuration(key) and
|
|
173
|
-
system.config[Client.ORIGIN][key] == system.config[Client.TARGET][key]) or \
|
|
174
|
-
SyncMode.is_unavailable_configuration(key)
|
|
102
|
+
def is_same_host() -> bool:
|
|
103
|
+
cfg = system.get_typed_config()
|
|
104
|
+
return (cfg.origin.host == cfg.target.host and
|
|
105
|
+
cfg.origin.port == cfg.target.port and
|
|
106
|
+
cfg.origin.user == cfg.target.user)
|
|
175
107
|
|
|
176
108
|
|
|
177
109
|
# Default sync mode
|
|
@@ -181,7 +113,7 @@ sync_mode = SyncMode.RECEIVER
|
|
|
181
113
|
#
|
|
182
114
|
# FUNCTIONS
|
|
183
115
|
#
|
|
184
|
-
def get_sync_mode():
|
|
116
|
+
def get_sync_mode() -> str:
|
|
185
117
|
"""
|
|
186
118
|
Returning the sync mode
|
|
187
119
|
:return: String sync_mode
|
|
@@ -189,10 +121,9 @@ def get_sync_mode():
|
|
|
189
121
|
return sync_mode
|
|
190
122
|
|
|
191
123
|
|
|
192
|
-
def check_sync_mode():
|
|
124
|
+
def check_sync_mode() -> None:
|
|
193
125
|
"""
|
|
194
126
|
Checking the sync_mode based on the given configuration
|
|
195
|
-
:return: String subject
|
|
196
127
|
"""
|
|
197
128
|
global sync_mode
|
|
198
129
|
_description = ''
|
|
@@ -214,14 +145,15 @@ def check_sync_mode():
|
|
|
214
145
|
sync_mode = _mode
|
|
215
146
|
_description = _desc
|
|
216
147
|
|
|
148
|
+
cfg = system.get_typed_config()
|
|
217
149
|
if is_import():
|
|
218
150
|
output.message(
|
|
219
151
|
output.Subject.INFO,
|
|
220
|
-
f'Import file {output.CliFormat.BLACK}{
|
|
152
|
+
f'Import file {output.CliFormat.BLACK}{cfg.import_file}{output.CliFormat.ENDC}',
|
|
221
153
|
True
|
|
222
154
|
)
|
|
223
155
|
|
|
224
|
-
system.
|
|
156
|
+
system.set_is_same_client(SyncMode.is_same_host())
|
|
225
157
|
|
|
226
158
|
output.message(
|
|
227
159
|
output.Subject.INFO,
|
|
@@ -232,23 +164,19 @@ def check_sync_mode():
|
|
|
232
164
|
check_for_protection()
|
|
233
165
|
|
|
234
166
|
|
|
235
|
-
def is_remote(client):
|
|
167
|
+
def is_remote(client: str) -> bool:
|
|
236
168
|
"""
|
|
237
169
|
Check if given client is remote client
|
|
238
|
-
:param client:
|
|
170
|
+
:param client: Client identifier
|
|
239
171
|
:return: Boolean
|
|
240
172
|
"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
elif client == Client.LOCAL:
|
|
246
|
-
return False
|
|
247
|
-
else:
|
|
248
|
-
return False
|
|
173
|
+
return {
|
|
174
|
+
Client.ORIGIN: is_origin_remote,
|
|
175
|
+
Client.TARGET: is_target_remote,
|
|
176
|
+
}.get(client, lambda: False)()
|
|
249
177
|
|
|
250
178
|
|
|
251
|
-
def is_target_remote():
|
|
179
|
+
def is_target_remote() -> bool:
|
|
252
180
|
"""
|
|
253
181
|
Check if target is remote client
|
|
254
182
|
:return: Boolean
|
|
@@ -257,7 +185,7 @@ def is_target_remote():
|
|
|
257
185
|
SyncMode.IMPORT_REMOTE, SyncMode.SYNC_REMOTE)
|
|
258
186
|
|
|
259
187
|
|
|
260
|
-
def is_origin_remote():
|
|
188
|
+
def is_origin_remote() -> bool:
|
|
261
189
|
"""
|
|
262
190
|
Check if origin is remote client
|
|
263
191
|
:return: Boolean
|
|
@@ -266,7 +194,7 @@ def is_origin_remote():
|
|
|
266
194
|
SyncMode.IMPORT_REMOTE, SyncMode.SYNC_REMOTE)
|
|
267
195
|
|
|
268
196
|
|
|
269
|
-
def is_import():
|
|
197
|
+
def is_import() -> bool:
|
|
270
198
|
"""
|
|
271
199
|
Check if sync mode is import
|
|
272
200
|
:return: Boolean
|
|
@@ -274,15 +202,16 @@ def is_import():
|
|
|
274
202
|
return sync_mode in (SyncMode.IMPORT_LOCAL, SyncMode.IMPORT_REMOTE)
|
|
275
203
|
|
|
276
204
|
|
|
277
|
-
def is_dump():
|
|
205
|
+
def is_dump() -> bool:
|
|
278
206
|
"""
|
|
279
|
-
Check if sync mode is
|
|
207
|
+
Check if sync mode is dump
|
|
280
208
|
:return: Boolean
|
|
281
209
|
"""
|
|
282
210
|
return sync_mode in (SyncMode.DUMP_LOCAL, SyncMode.DUMP_REMOTE)
|
|
283
211
|
|
|
284
212
|
|
|
285
|
-
def run_command(command, client, force_output=
|
|
213
|
+
def run_command(command: str, client: str, force_output: bool = False,
|
|
214
|
+
allow_fail: bool = False, skip_dry_run: bool = False) -> str | None:
|
|
286
215
|
"""
|
|
287
216
|
Run command depending on the given client
|
|
288
217
|
:param command: String
|
|
@@ -290,17 +219,20 @@ def run_command(command, client, force_output=False, allow_fail=False, skip_dry_
|
|
|
290
219
|
:param force_output: Boolean
|
|
291
220
|
:param allow_fail: Boolean
|
|
292
221
|
:param skip_dry_run: Boolean
|
|
293
|
-
:return:
|
|
222
|
+
:return: Command output or None
|
|
294
223
|
"""
|
|
295
|
-
|
|
224
|
+
cfg = system.get_typed_config()
|
|
225
|
+
if cfg.verbose:
|
|
226
|
+
# Sanitize command to prevent credentials from appearing in logs
|
|
227
|
+
_safe_command = sanitize_command_for_logging(command)
|
|
296
228
|
output.message(
|
|
297
229
|
output.host_to_subject(client),
|
|
298
|
-
output.CliFormat.BLACK +
|
|
230
|
+
output.CliFormat.BLACK + _safe_command + output.CliFormat.ENDC,
|
|
299
231
|
debug=True
|
|
300
232
|
)
|
|
301
233
|
|
|
302
|
-
if
|
|
303
|
-
return
|
|
234
|
+
if cfg.dry_run and skip_dry_run:
|
|
235
|
+
return None
|
|
304
236
|
|
|
305
237
|
if is_remote(client):
|
|
306
238
|
if force_output:
|
|
@@ -314,22 +246,25 @@ def run_command(command, client, force_output=False, allow_fail=False, skip_dry_
|
|
|
314
246
|
|
|
315
247
|
if res.wait() != 0 and err.decode() != '' and not allow_fail:
|
|
316
248
|
helper.run_script(script='error')
|
|
317
|
-
|
|
249
|
+
raise DbSyncError(err.decode())
|
|
318
250
|
|
|
319
251
|
if force_output:
|
|
320
252
|
return out.decode().strip()
|
|
321
253
|
|
|
254
|
+
return None
|
|
255
|
+
|
|
322
256
|
|
|
323
|
-
def check_for_protection():
|
|
257
|
+
def check_for_protection() -> None:
|
|
324
258
|
"""
|
|
325
|
-
Check if the target system is protected
|
|
326
|
-
:return: Boolean
|
|
259
|
+
Check if the target system is protected and exit if so.
|
|
327
260
|
"""
|
|
261
|
+
cfg = system.get_typed_config()
|
|
328
262
|
if sync_mode in (SyncMode.RECEIVER, SyncMode.SENDER, SyncMode.PROXY, SyncMode.SYNC_LOCAL,
|
|
329
263
|
SyncMode.SYNC_REMOTE, SyncMode.IMPORT_LOCAL, SyncMode.IMPORT_REMOTE) and \
|
|
330
|
-
|
|
264
|
+
cfg.target.protect:
|
|
331
265
|
_host = helper.get_ssh_host_name(Client.TARGET)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
266
|
+
raise DbSyncError(
|
|
267
|
+
f'The host {_host} is protected against the import of a database dump. '
|
|
268
|
+
'Please check synchronisation target or adjust the host configuration.'
|
|
269
|
+
)
|
|
335
270
|
|
db_sync_tool/utility/output.py
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: future_fstrings -*-
|
|
3
2
|
|
|
4
3
|
"""
|
|
5
4
|
Output script
|
|
5
|
+
|
|
6
|
+
This module provides the legacy output interface using Rich and structured logging.
|
|
7
|
+
For new code, prefer using the logging_config module directly:
|
|
8
|
+
|
|
9
|
+
from db_sync_tool.utility.logging_config import get_sync_logger
|
|
10
|
+
|
|
11
|
+
logger = get_sync_logger("origin", remote=True)
|
|
12
|
+
logger.info("Creating database dump")
|
|
6
13
|
"""
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
from db_sync_tool.utility import
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from db_sync_tool.utility import mode, system
|
|
17
|
+
from db_sync_tool.utility.console import get_output_manager
|
|
18
|
+
from db_sync_tool.utility.logging_config import get_sync_logger
|
|
10
19
|
|
|
11
20
|
|
|
12
21
|
class CliFormat:
|
|
22
|
+
"""ANSI color codes for CLI formatting (legacy compatibility)."""
|
|
13
23
|
BEIGE = '\033[96m'
|
|
14
24
|
PURPLE = '\033[95m'
|
|
15
25
|
BLUE = '\033[94m'
|
|
@@ -23,6 +33,7 @@ class CliFormat:
|
|
|
23
33
|
|
|
24
34
|
|
|
25
35
|
class Subject:
|
|
36
|
+
"""Subject prefixes for messages (legacy compatibility)."""
|
|
26
37
|
INFO = CliFormat.GREEN + '[INFO]' + CliFormat.ENDC
|
|
27
38
|
LOCAL = CliFormat.BEIGE + '[LOCAL]' + CliFormat.ENDC
|
|
28
39
|
TARGET = CliFormat.BLUE + '[TARGET]' + CliFormat.ENDC
|
|
@@ -32,57 +43,113 @@ class Subject:
|
|
|
32
43
|
DEBUG = CliFormat.BLACK + '[DEBUG]' + CliFormat.ENDC
|
|
33
44
|
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
# Mapping from Subject constants to subject strings for structured logging
|
|
47
|
+
_SUBJECT_MAP = {
|
|
48
|
+
Subject.INFO: "INFO",
|
|
49
|
+
Subject.LOCAL: "LOCAL",
|
|
50
|
+
Subject.TARGET: "TARGET",
|
|
51
|
+
Subject.ORIGIN: "ORIGIN",
|
|
52
|
+
Subject.ERROR: "INFO", # Error level handled separately
|
|
53
|
+
Subject.WARNING: "INFO", # Warning level handled separately
|
|
54
|
+
Subject.DEBUG: "INFO", # Debug level handled separately
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def message(
|
|
59
|
+
header: str,
|
|
60
|
+
message: str,
|
|
61
|
+
do_print: bool = True,
|
|
62
|
+
do_log: bool = False,
|
|
63
|
+
debug: bool = False,
|
|
64
|
+
verbose_only: bool = False,
|
|
65
|
+
) -> str | None:
|
|
36
66
|
"""
|
|
37
|
-
Formatting a message for print or log
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
:
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
Formatting a message for print or log.
|
|
68
|
+
|
|
69
|
+
This function maintains backward compatibility while using structured logging
|
|
70
|
+
and delegating console output to the Rich-based OutputManager.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
header: Subject prefix (e.g., Subject.ORIGIN)
|
|
74
|
+
message: Message text
|
|
75
|
+
do_print: Whether to print to console
|
|
76
|
+
do_log: Whether to log the message
|
|
77
|
+
debug: Whether this is a debug message
|
|
78
|
+
verbose_only: Only show in verbose mode
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
String message if do_print is False, None otherwise
|
|
45
82
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
cfg = system.get_typed_config()
|
|
84
|
+
output_manager = get_output_manager()
|
|
85
|
+
|
|
86
|
+
# Clean ANSI codes from message for logging and structured output
|
|
87
|
+
clean_message = remove_multiple_elements_from_string([
|
|
88
|
+
CliFormat.BEIGE, CliFormat.PURPLE, CliFormat.BLUE,
|
|
89
|
+
CliFormat.YELLOW, CliFormat.GREEN, CliFormat.RED,
|
|
90
|
+
CliFormat.BLACK, CliFormat.ENDC, CliFormat.BOLD,
|
|
91
|
+
CliFormat.UNDERLINE
|
|
92
|
+
], message)
|
|
93
|
+
|
|
94
|
+
# Get subject and remote status for structured logging
|
|
95
|
+
subject_str = _SUBJECT_MAP.get(header, "INFO")
|
|
96
|
+
is_remote = _is_remote_for_header(header)
|
|
97
|
+
|
|
98
|
+
# Structured logging if explicitly forced or verbose option is active
|
|
99
|
+
if do_log or cfg.verbose:
|
|
100
|
+
logger = get_sync_logger(subject=subject_str, remote=is_remote)
|
|
101
|
+
|
|
59
102
|
if debug:
|
|
60
|
-
|
|
103
|
+
logger.debug(clean_message)
|
|
61
104
|
elif header == Subject.WARNING:
|
|
62
|
-
|
|
105
|
+
logger.warning(clean_message)
|
|
63
106
|
elif header == Subject.ERROR:
|
|
64
|
-
|
|
107
|
+
logger.error(clean_message)
|
|
65
108
|
else:
|
|
66
|
-
|
|
109
|
+
logger.info(clean_message)
|
|
67
110
|
|
|
68
|
-
#
|
|
69
|
-
if (
|
|
111
|
+
# Console output if mute option is inactive
|
|
112
|
+
if (cfg.mute and header == Subject.ERROR) or (not cfg.mute):
|
|
70
113
|
if do_print:
|
|
71
|
-
if not verbose_only or (verbose_only and
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
114
|
+
if not verbose_only or (verbose_only and cfg.verbose):
|
|
115
|
+
# Use new OutputManager for console display
|
|
116
|
+
if header == Subject.ERROR:
|
|
117
|
+
output_manager.error(clean_message)
|
|
118
|
+
elif header == Subject.WARNING:
|
|
119
|
+
output_manager.warning(clean_message)
|
|
120
|
+
elif debug:
|
|
121
|
+
output_manager.debug(clean_message)
|
|
122
|
+
else:
|
|
123
|
+
# Legacy API: messages are logged after completion
|
|
124
|
+
# Set up step context for success() to use, then show completed
|
|
125
|
+
output_manager._setup_step(clean_message, subject=subject_str, remote=is_remote)
|
|
126
|
+
output_manager.success()
|
|
127
|
+
return None
|
|
75
128
|
else:
|
|
76
129
|
return header + extend_output_by_sync_mode(header, debug) + ' ' + message
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _is_remote_for_header(header) -> bool:
|
|
134
|
+
"""Determine if the operation is remote based on header."""
|
|
135
|
+
if header in (Subject.INFO, Subject.LOCAL, Subject.WARNING, Subject.ERROR):
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
host = subject_to_host(header)
|
|
139
|
+
if host is None:
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
return mode.is_remote(host)
|
|
77
143
|
|
|
78
144
|
|
|
79
145
|
def extend_output_by_sync_mode(header, debug=False):
|
|
80
146
|
"""
|
|
81
|
-
Extending the output by a client information (LOCAL|REMOTE)
|
|
82
|
-
|
|
83
|
-
:
|
|
147
|
+
Extending the output by a client information (LOCAL|REMOTE).
|
|
148
|
+
|
|
149
|
+
:param header: Subject prefix
|
|
150
|
+
:param debug: Whether to include debug tag
|
|
151
|
+
:return: String extension
|
|
84
152
|
"""
|
|
85
|
-
_sync_mode = mode.get_sync_mode()
|
|
86
153
|
_debug = ''
|
|
87
154
|
|
|
88
155
|
if debug:
|
|
@@ -103,9 +170,10 @@ def extend_output_by_sync_mode(header, debug=False):
|
|
|
103
170
|
|
|
104
171
|
def host_to_subject(host):
|
|
105
172
|
"""
|
|
106
|
-
Converting the client to the according subject
|
|
107
|
-
|
|
108
|
-
:
|
|
173
|
+
Converting the client to the according subject.
|
|
174
|
+
|
|
175
|
+
:param host: Client constant
|
|
176
|
+
:return: Subject prefix
|
|
109
177
|
"""
|
|
110
178
|
if host == mode.Client.ORIGIN:
|
|
111
179
|
return Subject.ORIGIN
|
|
@@ -113,13 +181,15 @@ def host_to_subject(host):
|
|
|
113
181
|
return Subject.TARGET
|
|
114
182
|
elif host == mode.Client.LOCAL:
|
|
115
183
|
return Subject.LOCAL
|
|
184
|
+
return None
|
|
116
185
|
|
|
117
186
|
|
|
118
187
|
def subject_to_host(subject):
|
|
119
188
|
"""
|
|
120
|
-
Converting the subject to the according host
|
|
121
|
-
|
|
122
|
-
:
|
|
189
|
+
Converting the subject to the according host.
|
|
190
|
+
|
|
191
|
+
:param subject: Subject prefix
|
|
192
|
+
:return: Client constant
|
|
123
193
|
"""
|
|
124
194
|
if subject == Subject.ORIGIN:
|
|
125
195
|
return mode.Client.ORIGIN
|
|
@@ -127,17 +197,18 @@ def subject_to_host(subject):
|
|
|
127
197
|
return mode.Client.TARGET
|
|
128
198
|
elif subject == Subject.LOCAL:
|
|
129
199
|
return mode.Client.LOCAL
|
|
200
|
+
return None
|
|
130
201
|
|
|
131
202
|
|
|
132
203
|
def remove_multiple_elements_from_string(elements, string):
|
|
133
204
|
"""
|
|
134
|
-
Removing multiple elements from a string
|
|
135
|
-
|
|
136
|
-
:param
|
|
137
|
-
:
|
|
205
|
+
Removing multiple elements from a string.
|
|
206
|
+
|
|
207
|
+
:param elements: List of strings to remove
|
|
208
|
+
:param string: Input string
|
|
209
|
+
:return: Cleaned string
|
|
138
210
|
"""
|
|
139
211
|
for element in elements:
|
|
140
212
|
if element in string:
|
|
141
213
|
string = string.replace(element, '')
|
|
142
214
|
return string
|
|
143
|
-
|