kaqing 2.0.172__py3-none-any.whl → 2.0.174__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 kaqing might be problematic. Click here for more details.
- adam/commands/__init__.py +6 -2
- adam/commands/alter_tables.py +24 -35
- adam/commands/cat.py +3 -13
- adam/commands/cd.py +6 -8
- adam/commands/check.py +9 -17
- adam/commands/command.py +39 -5
- adam/commands/cp.py +24 -32
- adam/commands/cql/cql_completions.py +3 -4
- adam/commands/cql/utils_cql.py +0 -2
- adam/commands/devices/device_app.py +2 -2
- adam/commands/devices/device_cass.py +2 -2
- adam/commands/devices/device_export.py +7 -9
- adam/commands/devices/device_postgres.py +2 -2
- adam/commands/export/clean_up_all_export_sessions.py +3 -3
- adam/commands/export/clean_up_export_sessions.py +8 -15
- adam/commands/export/drop_export_database.py +6 -22
- adam/commands/export/drop_export_databases.py +3 -9
- adam/commands/export/export.py +1 -1
- adam/commands/export/export_databases.py +70 -15
- adam/commands/export/export_select.py +12 -23
- adam/commands/export/export_select_x.py +3 -3
- adam/commands/export/export_sessions.py +124 -0
- adam/commands/export/export_use.py +0 -6
- adam/commands/export/exporter.py +61 -120
- adam/commands/export/import_session.py +4 -4
- adam/commands/export/importer.py +12 -5
- adam/commands/export/importer_athena.py +17 -13
- adam/commands/export/importer_sqlite.py +20 -17
- adam/commands/export/show_column_counts.py +5 -14
- adam/commands/export/show_export_databases.py +2 -1
- adam/commands/export/show_export_session.py +5 -13
- adam/commands/export/show_export_sessions.py +2 -2
- adam/commands/export/utils_export.py +5 -3
- adam/commands/medusa/medusa_backup.py +1 -2
- adam/commands/medusa/medusa_restore.py +21 -19
- adam/commands/param_get.py +8 -9
- adam/commands/param_set.py +7 -10
- adam/commands/postgres/postgres.py +13 -21
- adam/commands/postgres/utils_postgres.py +5 -1
- adam/commands/preview_table.py +1 -1
- adam/commands/reaper/reaper_run_abort.py +4 -10
- adam/commands/reaper/reaper_schedule_activate.py +6 -10
- adam/commands/reaper/reaper_schedule_start.py +6 -10
- adam/commands/reaper/reaper_schedule_stop.py +6 -10
- adam/repl.py +5 -3
- adam/utils_k8s/k8s.py +5 -5
- adam/utils_sqlite.py +13 -13
- adam/version.py +1 -1
- {kaqing-2.0.172.dist-info → kaqing-2.0.174.dist-info}/METADATA +1 -1
- {kaqing-2.0.172.dist-info → kaqing-2.0.174.dist-info}/RECORD +53 -53
- adam/commands/export/export_handlers.py +0 -71
- {kaqing-2.0.172.dist-info → kaqing-2.0.174.dist-info}/WHEEL +0 -0
- {kaqing-2.0.172.dist-info → kaqing-2.0.174.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.172.dist-info → kaqing-2.0.174.dist-info}/top_level.txt +0 -0
|
@@ -2,12 +2,13 @@ from collections.abc import Callable
|
|
|
2
2
|
import os
|
|
3
3
|
import boto3
|
|
4
4
|
|
|
5
|
-
from adam.commands.export.
|
|
5
|
+
from adam.commands.export.export_sessions import ExportSessions
|
|
6
|
+
from adam.commands.export.importer import Importer
|
|
6
7
|
from adam.config import Config
|
|
7
8
|
from adam.repl_session import ReplSession
|
|
9
|
+
from adam.repl_state import ReplState
|
|
8
10
|
from adam.utils import debug, lines_to_tabular, log, log2, ing, log_exc
|
|
9
11
|
from adam.utils_athena import Athena
|
|
10
|
-
from adam.utils_k8s.statefulsets import StatefulSets
|
|
11
12
|
from adam.utils_sqlite import SQLite
|
|
12
13
|
|
|
13
14
|
LIKE = 'e%_%'
|
|
@@ -90,7 +91,7 @@ class ExportDatabases:
|
|
|
90
91
|
if not export_session:
|
|
91
92
|
return
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
ExportDatabases.clear_cache()
|
|
94
95
|
|
|
95
96
|
keyspaces = {}
|
|
96
97
|
for table in ExportDatabases.table_names(export_session):
|
|
@@ -102,17 +103,6 @@ class ExportDatabases:
|
|
|
102
103
|
|
|
103
104
|
log(lines_to_tabular([f'{k},{v}' for k, v in keyspaces.items()], header='SCHEMA,# of TABLES', separator=','))
|
|
104
105
|
|
|
105
|
-
def disply_export_session(sts: str, pod: str, namespace: str, session: str):
|
|
106
|
-
if not pod:
|
|
107
|
-
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
108
|
-
|
|
109
|
-
if not pod:
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
tables, _ = ExportTableStatus.from_session(sts, pod, namespace, session)
|
|
113
|
-
log()
|
|
114
|
-
log(lines_to_tabular([f'{table.keyspace}\t{table.table}\t{table.target_table}\t{"export_completed_pending_import" if table.status == "pending_import" else table.status}' for table in tables], header='KEYSPACE\tTABLE\tTARGET_TABLE\tSTATUS', separator='\t'))
|
|
115
|
-
|
|
116
106
|
def database_names():
|
|
117
107
|
return ExportDatabases.copy_database_names() + ExportDatabases.export_database_names()
|
|
118
108
|
|
|
@@ -171,4 +161,69 @@ class ExportDatabases:
|
|
|
171
161
|
if db.startswith('s'):
|
|
172
162
|
return SQLite.database_names(prefix=f'{eprefix}_')
|
|
173
163
|
else:
|
|
174
|
-
return Athena.database_names(like=f'{eprefix}_%')
|
|
164
|
+
return Athena.database_names(like=f'{eprefix}_%')
|
|
165
|
+
|
|
166
|
+
def drop_databases(sts: str, pod: str, namespace: str, database: str = None):
|
|
167
|
+
importer = None
|
|
168
|
+
if database:
|
|
169
|
+
importer = Importer.importer_from_session(database)
|
|
170
|
+
|
|
171
|
+
sessions_done = ExportSessions.export_session_names(sts, pod, namespace, importer=importer, export_state='done')
|
|
172
|
+
sessions = ExportDatabases.sessions_from_dbs(ExportDatabases.drop_export_dbs(database))
|
|
173
|
+
if sessions_done and sessions:
|
|
174
|
+
intersects = list(set(sessions_done) & set(sessions))
|
|
175
|
+
with ing(f'Cleaning up {len(intersects)} completed sessions'):
|
|
176
|
+
ExportSessions.clean_up_sessions(sts, pod, namespace, list(intersects))
|
|
177
|
+
ExportSessions.clear_export_session_cache()
|
|
178
|
+
|
|
179
|
+
def clear_cache(database: str = None):
|
|
180
|
+
if not database or database.startswith('s'):
|
|
181
|
+
SQLite.clear_cache()
|
|
182
|
+
if not database or database.startswith('e'):
|
|
183
|
+
Athena.clear_cache()
|
|
184
|
+
|
|
185
|
+
def show_databases(importer: str = None):
|
|
186
|
+
lines = [f'{k}\t{v}' for k, v in ExportDatabases.database_names_with_keyspace_cnt(importer).items()]
|
|
187
|
+
log(lines_to_tabular(lines, 'NAME\tKEYSPACES', separator='\t'))
|
|
188
|
+
|
|
189
|
+
class ExportDatabaseService:
|
|
190
|
+
def __init__(self, handler: 'ExportDatabaseHandler'):
|
|
191
|
+
self.handler = handler
|
|
192
|
+
|
|
193
|
+
def sql(self, query: str, database: str = None):
|
|
194
|
+
if not database:
|
|
195
|
+
database = self.handler.state.export_session
|
|
196
|
+
|
|
197
|
+
ExportDatabases.run_query(query, database, show_query = True)
|
|
198
|
+
|
|
199
|
+
def drop(self, database: str):
|
|
200
|
+
state = self.handler.state
|
|
201
|
+
|
|
202
|
+
ExportDatabases.drop_databases(state.sts, state.pod, state.namespace, database)
|
|
203
|
+
ExportDatabases.clear_cache(database)
|
|
204
|
+
if state.export_session == database:
|
|
205
|
+
state.export_session = None
|
|
206
|
+
|
|
207
|
+
def drop_all(self):
|
|
208
|
+
state = self.handler.state
|
|
209
|
+
|
|
210
|
+
ExportDatabases.drop_databases(state.sts, state.pod, state.namespace)
|
|
211
|
+
ExportDatabases.clear_cache()
|
|
212
|
+
|
|
213
|
+
state.export_session = None
|
|
214
|
+
|
|
215
|
+
def show_databases(self, importer: str = None):
|
|
216
|
+
ExportDatabases.show_databases(importer)
|
|
217
|
+
|
|
218
|
+
class ExportDatabaseHandler:
|
|
219
|
+
def __init__(self, state: ReplState = None):
|
|
220
|
+
self.state = state
|
|
221
|
+
|
|
222
|
+
def __enter__(self):
|
|
223
|
+
return ExportDatabaseService(self)
|
|
224
|
+
|
|
225
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
def export_db(state: ReplState = None):
|
|
229
|
+
return ExportDatabaseHandler(state)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from adam.commands import extract_trailing_options
|
|
3
|
-
from adam.commands.command import Command
|
|
2
|
+
from adam.commands import extract_trailing_options, validate_args
|
|
3
|
+
from adam.commands.command import Command, InvalidArgumentsException
|
|
4
4
|
from adam.commands.export.export_databases import ExportDatabases
|
|
5
5
|
from adam.config import Config
|
|
6
6
|
from adam.repl_state import ReplState, RequiredState
|
|
@@ -43,32 +43,21 @@ class ExportSelect(Command):
|
|
|
43
43
|
|
|
44
44
|
Command.display_help()
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
raise InvalidArgumentsException()
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
log2('* SQL statement is missing.')
|
|
53
|
-
|
|
54
|
-
Command.display_help()
|
|
55
|
-
|
|
56
|
-
return 'command-missing'
|
|
57
|
-
|
|
58
|
-
query = ' '.join(args)
|
|
59
|
-
|
|
60
|
-
def output(out: str):
|
|
61
|
-
log_prefix = Config().get('export.log-prefix', '/tmp/qing')
|
|
62
|
-
log_file = f'{log_prefix}-{datetime.now().strftime("%d%H%M%S")}-sqlite.log'
|
|
48
|
+
with validate_args(args, state, name='SQL statement') as query:
|
|
49
|
+
def output(out: str):
|
|
50
|
+
log_prefix = Config().get('export.log-prefix', '/tmp/qing')
|
|
51
|
+
log_file = f'{log_prefix}-{datetime.now().strftime("%d%H%M%S")}-sqlite.log'
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|
|
53
|
+
with open(log_file, 'w') as f:
|
|
54
|
+
f.write(out)
|
|
66
55
|
|
|
67
|
-
|
|
56
|
+
return log_file
|
|
68
57
|
|
|
69
|
-
|
|
58
|
+
ExportDatabases.run_query(f'select {query}', database=state.export_session, output=output if backgrounded else None)
|
|
70
59
|
|
|
71
|
-
|
|
60
|
+
return state
|
|
72
61
|
|
|
73
62
|
def completion(self, state: ReplState):
|
|
74
63
|
if not state.export_session:
|
|
@@ -38,15 +38,15 @@ class ExportSelectX(Command):
|
|
|
38
38
|
)}
|
|
39
39
|
|
|
40
40
|
if state.export_session:
|
|
41
|
-
completions |= {
|
|
41
|
+
completions |= {dml: SqlCompleter(
|
|
42
42
|
lambda: ExportDatabases.table_names(state.export_session),
|
|
43
|
-
dml=
|
|
43
|
+
dml=dml,
|
|
44
44
|
expandables={
|
|
45
45
|
'export-dbs': lambda: ExportDatabases.database_names(),
|
|
46
46
|
'columns':lambda _: Athena.column_names(database=state.export_session, function='export'),
|
|
47
47
|
},
|
|
48
48
|
variant=SqlVariant.ATHENA
|
|
49
|
-
)}
|
|
49
|
+
) for dml in ['select', 'preview']}
|
|
50
50
|
|
|
51
51
|
return completions
|
|
52
52
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from adam.commands.export.importer import Importer
|
|
5
|
+
from adam.commands.export.utils_export import ExportTableStatus, csv_dir, find_files
|
|
6
|
+
from adam.config import Config
|
|
7
|
+
from adam.utils import lines_to_tabular, log, parallelize
|
|
8
|
+
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
9
|
+
from adam.utils_k8s.pods import log_prefix
|
|
10
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
11
|
+
|
|
12
|
+
class ExportSessions:
|
|
13
|
+
def clear_export_session_cache():
|
|
14
|
+
ExportSessions.find_export_sessions.cache_clear()
|
|
15
|
+
ExportSessions.export_session_names.cache_clear()
|
|
16
|
+
|
|
17
|
+
@functools.lru_cache()
|
|
18
|
+
def export_session_names(sts: str, pod: str, namespace: str, importer: str = None, export_state = None):
|
|
19
|
+
if not sts or not namespace:
|
|
20
|
+
return []
|
|
21
|
+
|
|
22
|
+
if not pod:
|
|
23
|
+
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
24
|
+
|
|
25
|
+
if not pod:
|
|
26
|
+
return []
|
|
27
|
+
|
|
28
|
+
return [session for session, state in ExportSessions.find_export_sessions(pod, namespace, importer).items() if not export_state or state == export_state]
|
|
29
|
+
|
|
30
|
+
@functools.lru_cache()
|
|
31
|
+
def find_export_sessions(pod: str, namespace: str, importer: str = None, limit = 100):
|
|
32
|
+
sessions: dict[str, str] = {}
|
|
33
|
+
|
|
34
|
+
prefix = Importer.prefix_from_importer(importer)
|
|
35
|
+
|
|
36
|
+
log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{prefix}*_*.log*')
|
|
37
|
+
|
|
38
|
+
if not log_files:
|
|
39
|
+
return {}
|
|
40
|
+
|
|
41
|
+
for log_file in log_files[:limit]:
|
|
42
|
+
m = re.match(f'{log_prefix()}-(.*?)_.*\.log?(.*)', log_file)
|
|
43
|
+
if m:
|
|
44
|
+
s = m.group(1)
|
|
45
|
+
state = m.group(2) # '', '.pending_import', '.done'
|
|
46
|
+
if state:
|
|
47
|
+
state = state.strip('.')
|
|
48
|
+
else:
|
|
49
|
+
state = 'in_export'
|
|
50
|
+
|
|
51
|
+
if s not in sessions:
|
|
52
|
+
sessions[s] = state
|
|
53
|
+
elif sessions[s] == 'done' and state != 'done':
|
|
54
|
+
sessions[s] = state
|
|
55
|
+
|
|
56
|
+
return sessions
|
|
57
|
+
|
|
58
|
+
def clean_up_all_sessions(sts: str, pod: str, namespace: str):
|
|
59
|
+
if not sts or not namespace:
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
if not pod:
|
|
63
|
+
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
64
|
+
|
|
65
|
+
CassandraNodes.exec(pod, namespace, f'rm -rf {csv_dir()}/*', show_out=Config().is_debug(), shell='bash')
|
|
66
|
+
CassandraNodes.exec(pod, namespace, f'rm -rf {log_prefix()}-*.log*', show_out=Config().is_debug(), shell='bash')
|
|
67
|
+
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
def clean_up_sessions(sts: str, pod: str, namespace: str, sessions: list[str], max_workers = 0):
|
|
71
|
+
if not sessions:
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
if not max_workers:
|
|
75
|
+
max_workers = Config().action_workers('export', 8)
|
|
76
|
+
|
|
77
|
+
with parallelize(sessions, max_workers, msg='Cleaning|Cleaned up {size} export sessions') as exec:
|
|
78
|
+
cnt_tuples = exec.map(lambda session: ExportSessions.clean_up_session(sts, pod, namespace, session, True))
|
|
79
|
+
csv_cnt = 0
|
|
80
|
+
log_cnt = 0
|
|
81
|
+
for (csv, log) in cnt_tuples:
|
|
82
|
+
csv_cnt += csv
|
|
83
|
+
log_cnt += log
|
|
84
|
+
|
|
85
|
+
return csv_cnt, log_cnt
|
|
86
|
+
|
|
87
|
+
def clean_up_session(sts: str, pod: str, namespace: str, session: str, multi_tables = True):
|
|
88
|
+
if not sts or not namespace:
|
|
89
|
+
return 0, 0
|
|
90
|
+
|
|
91
|
+
if not pod:
|
|
92
|
+
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
93
|
+
|
|
94
|
+
if not pod:
|
|
95
|
+
return 0, 0
|
|
96
|
+
|
|
97
|
+
csv_cnt = 0
|
|
98
|
+
log_cnt = 0
|
|
99
|
+
|
|
100
|
+
log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{session}_*.log*')
|
|
101
|
+
|
|
102
|
+
for log_file in log_files:
|
|
103
|
+
m = re.match(f'{log_prefix()}-{session}_(.*?)\.(.*?)\.log.*', log_file)
|
|
104
|
+
if m:
|
|
105
|
+
table = m.group(2)
|
|
106
|
+
|
|
107
|
+
CassandraNodes.exec(pod, namespace, f'rm -rf {csv_dir()}/{session}_{table}', show_out=not multi_tables, shell='bash')
|
|
108
|
+
csv_cnt += 1
|
|
109
|
+
|
|
110
|
+
CassandraNodes.exec(pod, namespace, f'rm -rf {log_file}', show_out=not multi_tables, shell='bash')
|
|
111
|
+
log_cnt += 1
|
|
112
|
+
|
|
113
|
+
return csv_cnt, log_cnt
|
|
114
|
+
|
|
115
|
+
def disply_export_session(sts: str, pod: str, namespace: str, session: str):
|
|
116
|
+
if not pod:
|
|
117
|
+
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
118
|
+
|
|
119
|
+
if not pod:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
tables, _ = ExportTableStatus.from_session(sts, pod, namespace, session)
|
|
123
|
+
log()
|
|
124
|
+
log(lines_to_tabular([f'{table.keyspace}\t{table.table}\t{table.target_table}\t{"export_completed_pending_import" if table.status == "pending_import" else table.status}' for table in tables], header='KEYSPACE\tTABLE\tTARGET_TABLE\tSTATUS', separator='\t'))
|
|
@@ -2,8 +2,6 @@ from adam.commands.command import Command
|
|
|
2
2
|
from adam.commands.export.export_databases import ExportDatabases
|
|
3
3
|
from adam.repl_state import ReplState
|
|
4
4
|
from adam.utils import log2
|
|
5
|
-
from adam.utils_athena import Athena
|
|
6
|
-
from adam.utils_sqlite import SQLite
|
|
7
5
|
|
|
8
6
|
class ExportUse(Command):
|
|
9
7
|
COMMAND = 'use'
|
|
@@ -36,10 +34,6 @@ class ExportUse(Command):
|
|
|
36
34
|
return state
|
|
37
35
|
|
|
38
36
|
state.export_session = args[0]
|
|
39
|
-
if state.export_session.startswith('e'):
|
|
40
|
-
Athena.clear_cache()
|
|
41
|
-
else:
|
|
42
|
-
SQLite.clear_cache()
|
|
43
37
|
|
|
44
38
|
ExportDatabases.display_export_db(state.export_session)
|
|
45
39
|
|
adam/commands/export/exporter.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
import functools
|
|
3
|
-
import re
|
|
4
2
|
import time
|
|
5
3
|
|
|
4
|
+
from adam.commands import validate_args
|
|
5
|
+
from adam.commands.command import Command
|
|
6
6
|
from adam.commands.cql.utils_cql import cassandra_table_names, run_cql, table_spec
|
|
7
7
|
from adam.commands.export.export_databases import ExportDatabases
|
|
8
|
+
from adam.commands.export.export_sessions import ExportSessions
|
|
8
9
|
from adam.commands.export.importer import Importer
|
|
9
10
|
from adam.commands.export.importer_athena import AthenaImporter
|
|
10
11
|
from adam.commands.export.importer_sqlite import SqliteImporter
|
|
11
|
-
from adam.commands.export.utils_export import ExportSpec, ExportTableStatus, ExportTableSpec, ImportSpec, csv_dir, find_files
|
|
12
|
+
from adam.commands.export.utils_export import ExportSpec, ExportTableStatus, ExportTableSpec, ImportSpec, csv_dir, find_files, state_with_pod
|
|
12
13
|
from adam.config import Config
|
|
13
14
|
from adam.pod_exec_result import PodExecResult
|
|
14
15
|
from adam.repl_state import ReplState
|
|
15
|
-
from adam.utils import debug, parallelize, log2, ing, log_exc
|
|
16
|
+
from adam.utils import debug, log, parallelize, log2, ing, log_exc
|
|
16
17
|
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
17
18
|
from adam.utils_k8s.pods import log_prefix
|
|
18
|
-
from adam.utils_k8s.statefulsets import StatefulSets
|
|
19
19
|
|
|
20
20
|
class Exporter:
|
|
21
21
|
def export_tables(args: list[str], state: ReplState, export_only: bool = False, max_workers = 0) -> tuple[list[str], ExportSpec]:
|
|
@@ -211,109 +211,7 @@ class Exporter:
|
|
|
211
211
|
|
|
212
212
|
def import_from_csv(spec: ExportTableSpec, state: ReplState, session: str, importer: str, table: str, target_table: str, columns: str, multi_tables = True, create_db = False):
|
|
213
213
|
im = AthenaImporter() if importer == 'athena' else SqliteImporter()
|
|
214
|
-
return im.import_from_csv(state
|
|
215
|
-
|
|
216
|
-
def clear_export_session_cache():
|
|
217
|
-
Exporter.find_export_sessions.cache_clear()
|
|
218
|
-
Exporter.export_session_names.cache_clear()
|
|
219
|
-
|
|
220
|
-
@functools.lru_cache()
|
|
221
|
-
def export_session_names(sts: str, pod: str, namespace: str, importer: str = None, export_state = None):
|
|
222
|
-
if not sts or not namespace:
|
|
223
|
-
return []
|
|
224
|
-
|
|
225
|
-
if not pod:
|
|
226
|
-
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
227
|
-
|
|
228
|
-
if not pod:
|
|
229
|
-
return []
|
|
230
|
-
|
|
231
|
-
return [session for session, state in Exporter.find_export_sessions(pod, namespace, importer).items() if not export_state or state == export_state]
|
|
232
|
-
|
|
233
|
-
@functools.lru_cache()
|
|
234
|
-
def find_export_sessions(pod: str, namespace: str, importer: str = None, limit = 100):
|
|
235
|
-
sessions: dict[str, str] = {}
|
|
236
|
-
|
|
237
|
-
prefix = Importer.prefix_from_importer(importer)
|
|
238
|
-
|
|
239
|
-
log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{prefix}*_*.log*')
|
|
240
|
-
|
|
241
|
-
if not log_files:
|
|
242
|
-
return {}
|
|
243
|
-
|
|
244
|
-
for log_file in log_files[:limit]:
|
|
245
|
-
m = re.match(f'{log_prefix()}-(.*?)_.*\.log?(.*)', log_file)
|
|
246
|
-
if m:
|
|
247
|
-
s = m.group(1)
|
|
248
|
-
state = m.group(2) # '', '.pending_import', '.done'
|
|
249
|
-
if state:
|
|
250
|
-
state = state.strip('.')
|
|
251
|
-
else:
|
|
252
|
-
state = 'in_export'
|
|
253
|
-
|
|
254
|
-
if s not in sessions:
|
|
255
|
-
sessions[s] = state
|
|
256
|
-
elif sessions[s] == 'done' and state != 'done':
|
|
257
|
-
sessions[s] = state
|
|
258
|
-
|
|
259
|
-
return sessions
|
|
260
|
-
|
|
261
|
-
def clean_up_all_sessions(sts: str, pod: str, namespace: str):
|
|
262
|
-
if not sts or not namespace:
|
|
263
|
-
return False
|
|
264
|
-
|
|
265
|
-
if not pod:
|
|
266
|
-
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
267
|
-
|
|
268
|
-
CassandraNodes.exec(pod, namespace, f'rm -rf {csv_dir()}/*', show_out=Config().is_debug(), shell='bash')
|
|
269
|
-
CassandraNodes.exec(pod, namespace, f'rm -rf {log_prefix()}-*.log*', show_out=Config().is_debug(), shell='bash')
|
|
270
|
-
|
|
271
|
-
return True
|
|
272
|
-
|
|
273
|
-
def clean_up_sessions(sts: str, pod: str, namespace: str, sessions: list[str], max_workers = 0):
|
|
274
|
-
if not sessions:
|
|
275
|
-
return []
|
|
276
|
-
|
|
277
|
-
if not max_workers:
|
|
278
|
-
max_workers = Config().action_workers('export', 8)
|
|
279
|
-
|
|
280
|
-
with parallelize(sessions, max_workers, msg='Cleaning|Cleaned up {size} export sessions') as exec:
|
|
281
|
-
cnt_tuples = exec.map(lambda session: Exporter.clean_up_session(sts, pod, namespace, session, True))
|
|
282
|
-
csv_cnt = 0
|
|
283
|
-
log_cnt = 0
|
|
284
|
-
for (csv, log) in cnt_tuples:
|
|
285
|
-
csv_cnt += csv
|
|
286
|
-
log_cnt += log
|
|
287
|
-
|
|
288
|
-
return csv_cnt, log_cnt
|
|
289
|
-
|
|
290
|
-
def clean_up_session(sts: str, pod: str, namespace: str, session: str, multi_tables = True):
|
|
291
|
-
if not sts or not namespace:
|
|
292
|
-
return 0, 0
|
|
293
|
-
|
|
294
|
-
if not pod:
|
|
295
|
-
pod = StatefulSets.pod_names(sts, namespace)[0]
|
|
296
|
-
|
|
297
|
-
if not pod:
|
|
298
|
-
return 0, 0
|
|
299
|
-
|
|
300
|
-
csv_cnt = 0
|
|
301
|
-
log_cnt = 0
|
|
302
|
-
|
|
303
|
-
log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{session}_*.log*')
|
|
304
|
-
|
|
305
|
-
for log_file in log_files:
|
|
306
|
-
m = re.match(f'{log_prefix()}-{session}_(.*?)\.(.*?)\.log.*', log_file)
|
|
307
|
-
if m:
|
|
308
|
-
table = m.group(2)
|
|
309
|
-
|
|
310
|
-
CassandraNodes.exec(pod, namespace, f'rm -rf {csv_dir()}/{session}_{table}', show_out=not multi_tables, shell='bash')
|
|
311
|
-
csv_cnt += 1
|
|
312
|
-
|
|
313
|
-
CassandraNodes.exec(pod, namespace, f'rm -rf {log_file}', show_out=not multi_tables, shell='bash')
|
|
314
|
-
log_cnt += 1
|
|
315
|
-
|
|
316
|
-
return csv_cnt, log_cnt
|
|
214
|
+
return im.import_from_csv(state, session if session else state.export_session, spec.keyspace, table, target_table, columns, multi_tables, create_db)
|
|
317
215
|
|
|
318
216
|
def resove_table_n_columns(spec: ExportTableSpec, state: ReplState, include_ks_in_target = False, importer = 'sqlite'):
|
|
319
217
|
table = spec.table
|
|
@@ -339,15 +237,58 @@ class Exporter:
|
|
|
339
237
|
|
|
340
238
|
return table, target_table, columns
|
|
341
239
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
with
|
|
352
|
-
|
|
353
|
-
Exporter.
|
|
240
|
+
class ExportService:
|
|
241
|
+
def __init__(self, handler: 'ExporterHandler'):
|
|
242
|
+
self.handler = handler
|
|
243
|
+
|
|
244
|
+
def export(self, args: list[str], export_only=False):
|
|
245
|
+
state = self.handler.state
|
|
246
|
+
export_session = state.export_session
|
|
247
|
+
spec: ExportSpec = None
|
|
248
|
+
try:
|
|
249
|
+
with state_with_pod(state) as state:
|
|
250
|
+
# --export-only for testing only
|
|
251
|
+
statuses, spec = Exporter.export_tables(args, state, export_only=export_only)
|
|
252
|
+
if not statuses:
|
|
253
|
+
return state
|
|
254
|
+
|
|
255
|
+
ExportSessions.clear_export_session_cache()
|
|
256
|
+
|
|
257
|
+
if spec.importer == 'csv' or export_only:
|
|
258
|
+
ExportSessions.disply_export_session(state.sts, state.pod, state.namespace, spec.session)
|
|
259
|
+
else:
|
|
260
|
+
log()
|
|
261
|
+
ExportDatabases.display_export_db(state.export_session)
|
|
262
|
+
finally:
|
|
263
|
+
# if exporting to csv, do not bind the new session id to repl state
|
|
264
|
+
if spec and spec.importer == 'csv':
|
|
265
|
+
state.export_session = export_session
|
|
266
|
+
|
|
267
|
+
return state
|
|
268
|
+
|
|
269
|
+
def import_sesion(self, args: list[str]):
|
|
270
|
+
state = self.handler.state
|
|
271
|
+
|
|
272
|
+
with validate_args(args, state, name='export session') as args_str:
|
|
273
|
+
with state_with_pod(state) as state:
|
|
274
|
+
tables, _ = Exporter.import_session(args_str, state)
|
|
275
|
+
if tables:
|
|
276
|
+
ExportSessions.clear_export_session_cache()
|
|
277
|
+
|
|
278
|
+
log()
|
|
279
|
+
ExportDatabases.display_export_db(state.export_session)
|
|
280
|
+
|
|
281
|
+
return state
|
|
282
|
+
|
|
283
|
+
class ExporterHandler:
|
|
284
|
+
def __init__(self, state: ReplState):
|
|
285
|
+
self.state = state
|
|
286
|
+
|
|
287
|
+
def __enter__(self):
|
|
288
|
+
return ExportService(self)
|
|
289
|
+
|
|
290
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
291
|
+
return False
|
|
292
|
+
|
|
293
|
+
def export(state: ReplState):
|
|
294
|
+
return ExporterHandler(state)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from adam.commands.command import Command
|
|
2
|
-
from adam.commands.export.
|
|
3
|
-
from adam.commands.export.exporter import
|
|
2
|
+
from adam.commands.export.export_sessions import ExportSessions
|
|
3
|
+
from adam.commands.export.exporter import export
|
|
4
4
|
from adam.repl_state import ReplState, RequiredState
|
|
5
5
|
|
|
6
6
|
class ImportSession(Command):
|
|
@@ -31,8 +31,8 @@ class ImportSession(Command):
|
|
|
31
31
|
|
|
32
32
|
def completion(self, state: ReplState):
|
|
33
33
|
# warm up cache
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
ExportSessions.export_session_names(state.sts, state.pod, state.namespace)
|
|
35
|
+
ExportSessions.export_session_names(state.sts, state.pod, state.namespace, export_state='pending_import')
|
|
36
36
|
|
|
37
37
|
return {}
|
|
38
38
|
|
adam/commands/export/importer.py
CHANGED
|
@@ -2,6 +2,7 @@ from abc import abstractmethod
|
|
|
2
2
|
|
|
3
3
|
from adam.commands.export.utils_export import csv_dir
|
|
4
4
|
from adam.config import Config
|
|
5
|
+
from adam.repl_state import ReplState
|
|
5
6
|
from adam.utils import ing
|
|
6
7
|
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
7
8
|
from adam.utils_k8s.pods import log_prefix
|
|
@@ -12,10 +13,13 @@ class Importer:
|
|
|
12
13
|
pass
|
|
13
14
|
|
|
14
15
|
@abstractmethod
|
|
15
|
-
def import_from_csv(self,
|
|
16
|
+
def import_from_csv(self, state: ReplState, from_session: str, keyspace: str, table: str, target_table: str, columns: str, multi_tables = True, create_db = False):
|
|
16
17
|
pass
|
|
17
18
|
|
|
18
|
-
def move_to_done(self,
|
|
19
|
+
def move_to_done(self, state: ReplState, from_session: str, keyspace: str, target_table: str):
|
|
20
|
+
pod = state.pod
|
|
21
|
+
namespace = state.namespace
|
|
22
|
+
to_session = state.export_session
|
|
19
23
|
log_file = f'{log_prefix()}-{from_session}_{keyspace}.{target_table}.log.pending_import'
|
|
20
24
|
|
|
21
25
|
to = f'{log_prefix()}-{to_session}_{keyspace}.{target_table}.log.done'
|
|
@@ -30,9 +34,12 @@ class Importer:
|
|
|
30
34
|
|
|
31
35
|
return session
|
|
32
36
|
|
|
33
|
-
def remove_csv(self,
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
def remove_csv(self, state: ReplState, from_session: str, table: str, target_table: str, multi_tables = True):
|
|
38
|
+
pod = state.pod
|
|
39
|
+
namespace = state.namespace
|
|
40
|
+
|
|
41
|
+
with ing(f'[{from_session}] Cleaning up temporary files', suppress_log=multi_tables):
|
|
42
|
+
CassandraNodes.exec(pod, namespace, f'rm -rf {self.csv_file(from_session, table, target_table)}', show_out=Config().is_debug(), shell='bash')
|
|
36
43
|
|
|
37
44
|
def db(self, session: str, keyspace: str):
|
|
38
45
|
return f'{session}_{keyspace}'
|