kaqing 2.0.145__py3-none-any.whl → 2.0.172__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/__init__.py +0 -2
- adam/app_session.py +8 -11
- adam/batch.py +3 -3
- adam/checks/check_utils.py +14 -46
- adam/checks/cpu.py +7 -1
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +2 -3
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +18 -0
- adam/commands/alter_tables.py +43 -47
- adam/commands/audit/audit.py +22 -23
- adam/commands/audit/audit_repair_tables.py +14 -17
- adam/commands/audit/audit_run.py +15 -23
- adam/commands/audit/show_last10.py +10 -13
- adam/commands/audit/show_slow10.py +10 -13
- adam/commands/audit/show_top10.py +10 -13
- adam/commands/audit/utils_show_top10.py +2 -3
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +7 -104
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cat.py +13 -19
- adam/commands/cd.py +8 -10
- adam/commands/check.py +20 -21
- adam/commands/cli_commands.py +2 -3
- adam/commands/code.py +20 -23
- adam/commands/command.py +120 -39
- adam/commands/commands_utils.py +8 -17
- adam/commands/cp.py +33 -39
- adam/commands/cql/cql_completions.py +9 -4
- adam/commands/cql/cqlsh.py +10 -30
- adam/commands/cql/{cql_utils.py → utils_cql.py} +149 -15
- adam/commands/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +3 -3
- adam/commands/deploy/deploy.py +4 -27
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +2 -5
- adam/commands/deploy/deploy_pod.py +64 -68
- adam/commands/deploy/undeploy.py +4 -27
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +4 -7
- adam/commands/deploy/undeploy_pod.py +9 -12
- adam/commands/devices/device.py +93 -2
- adam/commands/devices/device_app.py +37 -10
- adam/commands/devices/device_auit_log.py +8 -2
- adam/commands/devices/device_cass.py +47 -7
- adam/commands/devices/device_export.py +2 -2
- adam/commands/devices/device_postgres.py +41 -6
- adam/commands/exit.py +1 -4
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +18 -7
- adam/commands/export/drop_export_database.py +15 -18
- adam/commands/export/drop_export_databases.py +6 -9
- adam/commands/export/export.py +8 -38
- adam/commands/export/export_databases.py +16 -12
- adam/commands/export/export_handlers.py +71 -0
- adam/commands/export/export_select.py +33 -24
- adam/commands/export/export_use.py +12 -15
- adam/commands/export/exporter.py +37 -48
- adam/commands/export/import_session.py +4 -32
- adam/commands/export/importer_athena.py +4 -7
- adam/commands/export/importer_sqlite.py +19 -27
- adam/commands/export/show_column_counts.py +13 -22
- adam/commands/export/show_export_databases.py +3 -6
- adam/commands/export/show_export_session.py +10 -13
- adam/commands/export/show_export_sessions.py +8 -11
- adam/commands/export/utils_export.py +24 -1
- adam/commands/intermediate_command.py +49 -0
- adam/commands/issues.py +11 -43
- adam/commands/kubectl.py +3 -6
- adam/commands/login.py +22 -24
- adam/commands/logs.py +3 -6
- adam/commands/ls.py +8 -9
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -24
- adam/commands/medusa/medusa_restore.py +29 -33
- adam/commands/medusa/medusa_show_backupjobs.py +14 -18
- adam/commands/medusa/medusa_show_restorejobs.py +11 -18
- adam/commands/nodetool.py +6 -15
- adam/commands/param_get.py +11 -12
- adam/commands/param_set.py +9 -10
- adam/commands/postgres/postgres.py +29 -37
- adam/commands/postgres/postgres_context.py +47 -23
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/psql_completions.py +1 -1
- adam/commands/postgres/utils_postgres.py +66 -0
- adam/commands/preview_table.py +5 -44
- adam/commands/pwd.py +13 -16
- adam/commands/reaper/reaper.py +4 -27
- adam/commands/reaper/reaper_forward.py +48 -55
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +7 -14
- adam/commands/reaper/reaper_run_abort.py +11 -30
- adam/commands/reaper/reaper_runs.py +42 -57
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +11 -30
- adam/commands/reaper/reaper_schedule_start.py +10 -29
- adam/commands/reaper/reaper_schedule_stop.py +10 -29
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +196 -0
- adam/commands/repair/repair.py +4 -22
- adam/commands/repair/repair_log.py +5 -11
- adam/commands/repair/repair_run.py +27 -34
- adam/commands/repair/repair_scan.py +32 -38
- adam/commands/repair/repair_stop.py +5 -11
- adam/commands/report.py +27 -29
- adam/commands/restart.py +25 -26
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +10 -4
- adam/commands/show/show.py +10 -26
- adam/commands/show/show_cassandra_repairs.py +35 -0
- adam/commands/show/show_cassandra_status.py +32 -43
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_commands.py +19 -24
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +20 -27
- adam/commands/show/show_processes.py +15 -19
- adam/commands/show/show_storage.py +10 -20
- adam/commands/watch.py +26 -29
- adam/config.py +4 -16
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/pod_exec_result.py +3 -3
- adam/repl.py +29 -32
- adam/repl_commands.py +11 -11
- adam/repl_state.py +52 -26
- adam/sql/sql_completer.py +4 -6
- adam/sql/sql_state_machine.py +21 -14
- adam/sso/authn_ad.py +6 -8
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +3 -5
- adam/sso/idp.py +9 -12
- adam/utils.py +393 -33
- adam/utils_athena.py +14 -13
- adam/utils_audits.py +12 -12
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +13 -18
- adam/utils_k8s/app_pods.py +2 -0
- adam/utils_k8s/cassandra_clusters.py +21 -18
- adam/utils_k8s/custom_resources.py +16 -17
- adam/utils_k8s/ingresses.py +2 -2
- adam/utils_k8s/jobs.py +7 -11
- adam/utils_k8s/k8s.py +87 -0
- adam/utils_k8s/pods.py +14 -76
- adam/utils_k8s/secrets.py +4 -4
- adam/utils_k8s/service_accounts.py +5 -4
- adam/utils_k8s/services.py +2 -2
- adam/utils_k8s/statefulsets.py +1 -12
- adam/utils_repl/state_machine.py +3 -3
- adam/utils_sqlite.py +78 -42
- adam/version.py +1 -1
- {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/METADATA +1 -1
- kaqing-2.0.172.dist-info/RECORD +230 -0
- adam/commands/app.py +0 -67
- adam/commands/app_ping.py +0 -44
- adam/commands/export/clean_up_export_session.py +0 -53
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/show/show_app_actions.py +0 -56
- adam/commands/show/show_app_id.py +0 -47
- adam/commands/show/show_app_queues.py +0 -45
- adam/commands/show/show_repairs.py +0 -47
- kaqing-2.0.145.dist-info/RECORD +0 -227
- {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/WHEEL +0 -0
- {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.145.dist-info → kaqing-2.0.172.dist-info}/top_level.txt +0 -0
adam/utils_k8s/pods.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
|
-
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
3
2
|
from datetime import datetime
|
|
4
3
|
import sys
|
|
5
4
|
import time
|
|
6
|
-
from typing import TypeVar
|
|
5
|
+
from typing import TypeVar
|
|
7
6
|
from kubernetes import client
|
|
8
7
|
from kubernetes.stream import stream
|
|
9
8
|
from kubernetes.stream.ws_client import ERROR_CHANNEL, WSClient
|
|
@@ -11,7 +10,7 @@ from kubernetes.stream.ws_client import ERROR_CHANNEL, WSClient
|
|
|
11
10
|
from adam.config import Config
|
|
12
11
|
from adam.utils_k8s.volumes import ConfigMapMount
|
|
13
12
|
from adam.pod_exec_result import PodExecResult
|
|
14
|
-
from adam.utils import
|
|
13
|
+
from adam.utils import ParallelMapHandler, log2, debug, log_exc
|
|
15
14
|
from .kube_context import KubeContext
|
|
16
15
|
|
|
17
16
|
from websocket._core import WebSocket
|
|
@@ -30,11 +29,9 @@ class Pods:
|
|
|
30
29
|
return _TEST_POD_EXEC_OUTS
|
|
31
30
|
|
|
32
31
|
def delete(pod_name: str, namespace: str, grace_period_seconds: int = None):
|
|
33
|
-
|
|
32
|
+
with log_exc(lambda e: "Exception when calling CoreV1Api->delete_namespaced_pod: %s\n" % e):
|
|
34
33
|
v1 = client.CoreV1Api()
|
|
35
34
|
v1.delete_namespaced_pod(pod_name, namespace, grace_period_seconds=grace_period_seconds)
|
|
36
|
-
except Exception as e:
|
|
37
|
-
log2("Exception when calling CoreV1Api->delete_namespaced_pod: %s\n" % e)
|
|
38
35
|
|
|
39
36
|
def delete_with_selector(namespace: str, label_selector: str, grace_period_seconds: int = None):
|
|
40
37
|
v1 = client.CoreV1Api()
|
|
@@ -43,64 +40,13 @@ class Pods:
|
|
|
43
40
|
for i in ret.items:
|
|
44
41
|
v1.delete_namespaced_pod(name=i.metadata.name, namespace=namespace, grace_period_seconds=grace_period_seconds)
|
|
45
42
|
|
|
46
|
-
def
|
|
47
|
-
namespace: str,
|
|
48
|
-
body: Callable[[ThreadPoolExecutor, str, str, bool], T],
|
|
49
|
-
post: Callable[[T], T] = None,
|
|
50
|
-
action: str = 'action',
|
|
51
|
-
max_workers=0,
|
|
52
|
-
show_out=True,
|
|
53
|
-
on_any = False,
|
|
54
|
-
background = False) -> list[T]:
|
|
55
|
-
show_out = KubeContext.show_out(show_out)
|
|
56
|
-
|
|
43
|
+
def parallelize(collection: list, max_workers: int = 0, samples = sys.maxsize, msg: str = None, action: str = 'action'):
|
|
57
44
|
if not max_workers:
|
|
58
45
|
max_workers = Config().action_workers(action, 0)
|
|
59
|
-
if
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
start_time = time.time()
|
|
64
|
-
try:
|
|
65
|
-
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
66
|
-
# disable stdout from the pod_exec, then show the output in a for loop
|
|
67
|
-
futures = [body(executor, pod, namespace, show_out) for pod in pods]
|
|
68
|
-
if len(futures) == 0:
|
|
69
|
-
return cast(list[T], [])
|
|
70
|
-
|
|
71
|
-
rs = [future.result() for future in as_completed(futures)]
|
|
72
|
-
if post:
|
|
73
|
-
rs = [post(r, show_out=show_out) for r in rs]
|
|
74
|
-
|
|
75
|
-
return rs
|
|
76
|
-
finally:
|
|
77
|
-
if KubeContext.show_parallelism():
|
|
78
|
-
log2(f"Parallel {action} elapsed time: {elapsed_time(start_time)} with {max_workers} workers")
|
|
79
|
-
else:
|
|
80
|
-
results: list[T] = []
|
|
81
|
-
|
|
82
|
-
samples = 1 if on_any else Config().action_node_samples(action, sys.maxsize)
|
|
83
|
-
l = min(len(pods), samples)
|
|
84
|
-
adj = 'all'
|
|
85
|
-
if l < len(pods):
|
|
86
|
-
adj = f'{l} sample'
|
|
87
|
-
if show_out:
|
|
88
|
-
log2(f'Executing on {adj} nodes from statefulset...')
|
|
89
|
-
for pod_name in pods:
|
|
90
|
-
try:
|
|
91
|
-
# disable stdout from the pod_exec, then show the output in a for loop
|
|
92
|
-
result = body(None, pod_name, namespace, False)
|
|
93
|
-
if post:
|
|
94
|
-
result = post(result, show_out=show_out)
|
|
95
|
-
results.append(result)
|
|
96
|
-
if result:
|
|
97
|
-
l -= 1
|
|
98
|
-
if not l:
|
|
99
|
-
break
|
|
100
|
-
except Exception as e:
|
|
101
|
-
log2(e)
|
|
102
|
-
|
|
103
|
-
return results
|
|
46
|
+
if samples == sys.maxsize:
|
|
47
|
+
samples = Config().action_node_samples(action, sys.maxsize)
|
|
48
|
+
|
|
49
|
+
return ParallelMapHandler(collection, max_workers, samples = samples, msg = msg)
|
|
104
50
|
|
|
105
51
|
def exec(pod_name: str, container: str, namespace: str, command: str,
|
|
106
52
|
show_out = True, throw_err = False, shell = '/bin/sh',
|
|
@@ -138,8 +84,7 @@ class Pods:
|
|
|
138
84
|
exec_command = [shell, '-c', command]
|
|
139
85
|
|
|
140
86
|
k_command = f'kubectl exec {pod_name} -c {container} -n {namespace} -- {shell} -c "{command}"'
|
|
141
|
-
|
|
142
|
-
print(k_command)
|
|
87
|
+
debug(k_command)
|
|
143
88
|
|
|
144
89
|
resp: WSClient = stream(
|
|
145
90
|
api.connect_get_namespaced_pod_exec,
|
|
@@ -173,11 +118,9 @@ class Pods:
|
|
|
173
118
|
stderr.append(frag)
|
|
174
119
|
if show_out: print(frag, end="")
|
|
175
120
|
|
|
176
|
-
|
|
121
|
+
with log_exc():
|
|
177
122
|
# get the exit code from server
|
|
178
123
|
error_output = resp.read_channel(ERROR_CHANNEL)
|
|
179
|
-
except Exception as e:
|
|
180
|
-
pass
|
|
181
124
|
except Exception as e:
|
|
182
125
|
if throw_err:
|
|
183
126
|
raise e
|
|
@@ -186,10 +129,8 @@ class Pods:
|
|
|
186
129
|
finally:
|
|
187
130
|
resp.close()
|
|
188
131
|
if s and s.sock and Pods._TEST_POD_CLOSE_SOCKET:
|
|
189
|
-
|
|
132
|
+
with log_exc():
|
|
190
133
|
s.sock.close()
|
|
191
|
-
except:
|
|
192
|
-
pass
|
|
193
134
|
|
|
194
135
|
return PodExecResult("".join(stdout), "".join(stderr), k_command, error_output, pod=pod_name, log_file=log_file)
|
|
195
136
|
|
|
@@ -214,20 +155,17 @@ class Pods:
|
|
|
214
155
|
if resp.peek_stdout():
|
|
215
156
|
yield resp.read_stdout()
|
|
216
157
|
|
|
217
|
-
|
|
158
|
+
with log_exc():
|
|
218
159
|
# get the exit code from server
|
|
219
160
|
error_output = resp.read_channel(ERROR_CHANNEL)
|
|
220
|
-
except Exception as e:
|
|
221
|
-
pass
|
|
222
161
|
except Exception as e:
|
|
223
162
|
raise e
|
|
224
163
|
finally:
|
|
225
164
|
resp.close()
|
|
226
165
|
if s and s.sock and Pods._TEST_POD_CLOSE_SOCKET:
|
|
227
|
-
|
|
166
|
+
with log_exc():
|
|
228
167
|
s.sock.close()
|
|
229
|
-
|
|
230
|
-
pass
|
|
168
|
+
|
|
231
169
|
def get_container(namespace: str, pod_name: str, container_name: str):
|
|
232
170
|
pod = Pods.get(namespace, pod_name)
|
|
233
171
|
if not pod:
|
adam/utils_k8s/secrets.py
CHANGED
|
@@ -6,13 +6,13 @@ from kubernetes import client
|
|
|
6
6
|
from kubernetes.client import V1Secret
|
|
7
7
|
|
|
8
8
|
from adam.config import Config
|
|
9
|
-
from adam.utils import log2
|
|
9
|
+
from adam.utils import log2, wait_log
|
|
10
10
|
|
|
11
11
|
# utility collection on secrets; methods are all static
|
|
12
12
|
class Secrets:
|
|
13
13
|
@functools.lru_cache()
|
|
14
14
|
def list_secrets(namespace: str = None, name_pattern: str = None):
|
|
15
|
-
|
|
15
|
+
wait_log('Inspecting Cassandra Instances...')
|
|
16
16
|
|
|
17
17
|
secrets_names = []
|
|
18
18
|
|
|
@@ -39,14 +39,14 @@ class Secrets:
|
|
|
39
39
|
|
|
40
40
|
return secrets_names
|
|
41
41
|
|
|
42
|
-
def get_user_pass(
|
|
42
|
+
def get_user_pass(sts_or_pod_name: str, namespace: str, secret_path: str = 'cql.secret'):
|
|
43
43
|
# cs-d0767a536f-cs-d0767a536f-default-sts ->
|
|
44
44
|
# cs-d0767a536f-superuser
|
|
45
45
|
# cs-d0767a536f-reaper-ui
|
|
46
46
|
user = 'superuser'
|
|
47
47
|
if secret_path == 'reaper.secret':
|
|
48
48
|
user = 'reaper-ui'
|
|
49
|
-
groups = re.match(Config().get(f'{secret_path}.cluster-regex', r'(.*?-.*?)-.*'),
|
|
49
|
+
groups = re.match(Config().get(f'{secret_path}.cluster-regex', r'(.*?-.*?)-.*'), sts_or_pod_name)
|
|
50
50
|
secret_name = Config().get(f'{secret_path}.name', '{cluster}-' + user).replace('{cluster}', groups[1], 1)
|
|
51
51
|
|
|
52
52
|
secret = Secrets.get_data(namespace, secret_name)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from kubernetes import client, config
|
|
2
2
|
|
|
3
3
|
from adam.config import Config
|
|
4
|
+
from adam.utils import debug
|
|
4
5
|
|
|
5
6
|
# utility collection on service accounts; methods are all static
|
|
6
7
|
class ServiceAccounts:
|
|
@@ -37,7 +38,7 @@ class ServiceAccounts:
|
|
|
37
38
|
namespace=namespace,
|
|
38
39
|
body=service_account
|
|
39
40
|
)
|
|
40
|
-
|
|
41
|
+
debug(f"Service Account '{api_response.metadata.name}' created in namespace '{namespace}'.")
|
|
41
42
|
|
|
42
43
|
def delete_service_account(namespace: str, label_selector: str) -> list:
|
|
43
44
|
refs = []
|
|
@@ -45,7 +46,7 @@ class ServiceAccounts:
|
|
|
45
46
|
v1 = client.CoreV1Api()
|
|
46
47
|
sas = v1.list_namespaced_service_account(namespace=namespace, label_selector=label_selector).items
|
|
47
48
|
for sa in sas:
|
|
48
|
-
|
|
49
|
+
debug(f'delete {sa.metadata.name}')
|
|
49
50
|
v1.delete_namespaced_service_account(name=sa.metadata.name, namespace=namespace)
|
|
50
51
|
refs.append(sa)
|
|
51
52
|
|
|
@@ -102,7 +103,7 @@ class ServiceAccounts:
|
|
|
102
103
|
v1_rbac = client.RbacAuthorizationV1Api()
|
|
103
104
|
cluster_role_bindings = v1_rbac.list_namespaced_role_binding(namespace=namespace, label_selector=label_selector).items
|
|
104
105
|
for binding in cluster_role_bindings:
|
|
105
|
-
|
|
106
|
+
debug(f'delete {binding.metadata.name}')
|
|
106
107
|
v1_rbac.delete_namespaced_role_binding(name=binding.metadata.name, namespace=namespace)
|
|
107
108
|
refs.append(binding)
|
|
108
109
|
|
|
@@ -162,7 +163,7 @@ class ServiceAccounts:
|
|
|
162
163
|
v1_rbac = client.RbacAuthorizationV1Api()
|
|
163
164
|
cluster_role_bindings = v1_rbac.list_cluster_role_binding(label_selector=label_selector).items
|
|
164
165
|
for binding in cluster_role_bindings:
|
|
165
|
-
|
|
166
|
+
debug(f'delete {binding.metadata.name}')
|
|
166
167
|
v1_rbac.delete_cluster_role_binding(binding.metadata.name)
|
|
167
168
|
refs.append(binding)
|
|
168
169
|
|
adam/utils_k8s/services.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import List
|
|
|
2
2
|
from kubernetes import client
|
|
3
3
|
|
|
4
4
|
from adam.config import Config
|
|
5
|
-
from adam.utils import log2
|
|
5
|
+
from adam.utils import debug, log2
|
|
6
6
|
|
|
7
7
|
from .kube_context import KubeContext
|
|
8
8
|
|
|
@@ -71,7 +71,7 @@ class Services:
|
|
|
71
71
|
namespace=namespace,
|
|
72
72
|
body=delete_options
|
|
73
73
|
)
|
|
74
|
-
|
|
74
|
+
debug(f"200 Service '{name}' in namespace '{namespace}' deleted successfully.")
|
|
75
75
|
except client.ApiException as e:
|
|
76
76
|
log2(f"Error deleting Service '{name}': {e}")
|
|
77
77
|
except Exception as e:
|
adam/utils_k8s/statefulsets.py
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
2
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
3
1
|
from datetime import datetime
|
|
4
2
|
import functools
|
|
5
3
|
import re
|
|
6
4
|
from typing import List, TypeVar, cast
|
|
7
5
|
from kubernetes import client
|
|
8
6
|
|
|
9
|
-
from .pods import Pods
|
|
10
7
|
from .kube_context import KubeContext
|
|
11
8
|
from adam.utils import log2
|
|
12
9
|
|
|
@@ -58,15 +55,6 @@ class StatefulSets:
|
|
|
58
55
|
|
|
59
56
|
return statefulset_pods
|
|
60
57
|
|
|
61
|
-
def on_cluster(statefulset: str,
|
|
62
|
-
namespace: str,
|
|
63
|
-
body: Callable[[ThreadPoolExecutor, str, str, bool], T],
|
|
64
|
-
post: Callable[[T], T] = None,
|
|
65
|
-
action: str = 'action', max_workers=0, show_out=True, on_any = False, background = False) -> list[T]:
|
|
66
|
-
pods = StatefulSets.pod_names(statefulset, namespace)
|
|
67
|
-
|
|
68
|
-
return Pods.on_pods(pods, namespace, body, post=post, action=action, max_workers=max_workers, show_out=show_out, on_any=on_any, background=background)
|
|
69
|
-
|
|
70
58
|
@functools.lru_cache()
|
|
71
59
|
def pod_names(ss: str, ns: str):
|
|
72
60
|
return [pod.metadata.name for pod in StatefulSets.pods(ss, ns)]
|
|
@@ -92,6 +80,7 @@ class StatefulSets:
|
|
|
92
80
|
|
|
93
81
|
return restarted, False
|
|
94
82
|
|
|
83
|
+
@functools.lru_cache()
|
|
95
84
|
def get_datacenter(sts: str, ns: str) -> str:
|
|
96
85
|
v1 = client.AppsV1Api()
|
|
97
86
|
namespace = ns
|
adam/utils_repl/state_machine.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from typing import Generic, TypeVar
|
|
3
3
|
|
|
4
|
+
from adam.utils import log_exc
|
|
5
|
+
|
|
4
6
|
__all__ = [
|
|
5
7
|
'State',
|
|
6
8
|
'StateMachine',
|
|
@@ -160,14 +162,12 @@ class StateMachine(Generic[T]):
|
|
|
160
162
|
last_name = token
|
|
161
163
|
token = 'word'
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
with log_exc():
|
|
164
166
|
context = state.context
|
|
165
167
|
state = self.states[f'{state.state} > {token}']
|
|
166
168
|
state.context = context
|
|
167
169
|
|
|
168
170
|
if last_name:
|
|
169
171
|
state.context['last_name'] = last_name
|
|
170
|
-
except:
|
|
171
|
-
pass
|
|
172
172
|
|
|
173
173
|
return state
|
adam/utils_sqlite.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
1
3
|
import functools
|
|
2
4
|
import glob
|
|
3
5
|
import os
|
|
@@ -5,41 +7,63 @@ import sqlite3
|
|
|
5
7
|
import pandas
|
|
6
8
|
|
|
7
9
|
from adam.config import Config
|
|
8
|
-
from adam.utils import lines_to_tabular, log
|
|
10
|
+
from adam.utils import lines_to_tabular, log, wait_log
|
|
11
|
+
|
|
12
|
+
class CursorHandler:
|
|
13
|
+
def __init__(self, conn: sqlite3.Connection):
|
|
14
|
+
self.conn = conn
|
|
15
|
+
self.cursor = None
|
|
16
|
+
|
|
17
|
+
def __enter__(self):
|
|
18
|
+
self.cursor = self.conn.cursor()
|
|
19
|
+
|
|
20
|
+
return self.cursor
|
|
21
|
+
|
|
22
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
23
|
+
if self.cursor:
|
|
24
|
+
self.cursor.close()
|
|
25
|
+
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
class SQLiteConnectionHandler:
|
|
29
|
+
def __init__(self, database: str, keyspace = None):
|
|
30
|
+
self.database = database
|
|
31
|
+
self.keyspace = keyspace
|
|
32
|
+
self.conn = None
|
|
33
|
+
|
|
34
|
+
def __enter__(self) -> sqlite3.Connection:
|
|
35
|
+
if self.keyspace:
|
|
36
|
+
self.conn = sqlite3.connect(f'{SQLite.local_db_dir()}/{self.database}_{self.keyspace}.db')
|
|
37
|
+
else:
|
|
38
|
+
self.conn = SQLite.connect(self.database)
|
|
39
|
+
|
|
40
|
+
self.conn.__enter__()
|
|
41
|
+
|
|
42
|
+
return self.conn
|
|
43
|
+
|
|
44
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
45
|
+
if self.conn:
|
|
46
|
+
self.conn.__exit__(exc_type, exc_val, exc_tb)
|
|
47
|
+
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
def sqlite(database: str, keyspace = None):
|
|
51
|
+
return SQLiteConnectionHandler(database, keyspace=keyspace)
|
|
9
52
|
|
|
10
53
|
# no state utility class
|
|
11
54
|
class SQLite:
|
|
55
|
+
def cursor(conn: sqlite3.Connection):
|
|
56
|
+
return CursorHandler(conn)
|
|
57
|
+
|
|
12
58
|
def local_db_dir():
|
|
13
59
|
return Config().get('export.sqlite.local-db-dir', '/tmp/qing-db')
|
|
14
60
|
|
|
15
61
|
def keyspace(database: str):
|
|
16
62
|
return '_'.join(database.replace(".db", "").split('_')[1:])
|
|
17
63
|
|
|
18
|
-
def connect(session: str):
|
|
19
|
-
os.makedirs(SQLite.local_db_dir(), exist_ok=True)
|
|
20
|
-
|
|
21
|
-
conn = None
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
conn = sqlite3.connect(f'{SQLite.local_db_dir()}/{session}_root.db')
|
|
25
|
-
cursor = None
|
|
26
|
-
try:
|
|
27
|
-
cursor = conn.cursor()
|
|
28
|
-
for d in SQLite.database_names(session):
|
|
29
|
-
if d != f'{session}_root.db':
|
|
30
|
-
q = f"ATTACH DATABASE '{SQLite.local_db_dir()}/{d}' AS {SQLite.keyspace(d)};"
|
|
31
|
-
cursor.execute(q)
|
|
32
|
-
finally:
|
|
33
|
-
if cursor:
|
|
34
|
-
cursor.close()
|
|
35
|
-
finally:
|
|
36
|
-
pass
|
|
37
|
-
|
|
38
|
-
return conn
|
|
39
|
-
|
|
40
64
|
@functools.lru_cache()
|
|
41
65
|
def database_names(prefix: str = None):
|
|
42
|
-
|
|
66
|
+
wait_log('Inspecting export databases...')
|
|
43
67
|
|
|
44
68
|
pattern = f'{SQLite.local_db_dir()}/s*.db'
|
|
45
69
|
if prefix:
|
|
@@ -60,15 +84,10 @@ class SQLite:
|
|
|
60
84
|
tables = []
|
|
61
85
|
try:
|
|
62
86
|
conn = sqlite3.connect(f'{SQLite.local_db_dir()}/{ts_prefix}_{keyspace}.db')
|
|
63
|
-
cursor
|
|
64
|
-
try:
|
|
65
|
-
cursor = conn.cursor()
|
|
87
|
+
with SQLite.cursor(conn) as cursor:
|
|
66
88
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
|
|
67
89
|
|
|
68
90
|
tables = [row[0] for row in cursor.fetchall() if row[0] != "sqlite_sequence"]
|
|
69
|
-
finally:
|
|
70
|
-
if cursor:
|
|
71
|
-
cursor.close()
|
|
72
91
|
|
|
73
92
|
return tables
|
|
74
93
|
except sqlite3.Error as e:
|
|
@@ -78,24 +97,41 @@ class SQLite:
|
|
|
78
97
|
if conn:
|
|
79
98
|
conn.close()
|
|
80
99
|
|
|
100
|
+
def connect(database: str):
|
|
101
|
+
os.makedirs(SQLite.local_db_dir(), exist_ok=True)
|
|
102
|
+
|
|
103
|
+
conn = sqlite3.connect(f'{SQLite.local_db_dir()}/{database}.db')
|
|
104
|
+
with SQLite.cursor(conn) as cursor:
|
|
105
|
+
for d in SQLite.database_names(database):
|
|
106
|
+
if d != f'{database}.db':
|
|
107
|
+
q = f"ATTACH DATABASE '{SQLite.local_db_dir()}/{d}' AS {SQLite.keyspace(d)}"
|
|
108
|
+
cursor.execute(q)
|
|
109
|
+
|
|
110
|
+
return conn
|
|
111
|
+
|
|
81
112
|
@functools.lru_cache()
|
|
82
113
|
def column_names(tables: list[str] = [], database: str = None, function: str = 'audit', partition_cols_only = False):
|
|
83
114
|
pass
|
|
84
115
|
|
|
85
|
-
def run_query(query: str, database: str = None,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
def run_query(query: str, database: str = None, output: Callable[[str], str] = None) -> tuple[int, str]:
|
|
117
|
+
with sqlite(database) as conn:
|
|
118
|
+
return SQLite.run_query_with_conn(conn, query, output=output)
|
|
119
|
+
|
|
120
|
+
def run_query_with_conn(conn, query: str, output: Callable[[str], str] = None) -> tuple[int, str]:
|
|
121
|
+
log_file = None
|
|
90
122
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
123
|
+
df = SQLite.query(conn, query)
|
|
124
|
+
lines = ['\t'.join(map(str, line)) for line in df.values.tolist()]
|
|
125
|
+
out = lines_to_tabular(lines, header='\t'.join(df.columns.tolist()), separator='\t')
|
|
126
|
+
if output:
|
|
127
|
+
log_file = output(out)
|
|
128
|
+
else:
|
|
129
|
+
log(out)
|
|
94
130
|
|
|
95
|
-
|
|
96
|
-
finally:
|
|
97
|
-
if conn:
|
|
98
|
-
conn.close()
|
|
131
|
+
return len(lines), log_file
|
|
99
132
|
|
|
100
133
|
def query(conn, sql: str) -> tuple[str, str, list]:
|
|
101
134
|
return pandas.read_sql_query(sql, conn)
|
|
135
|
+
|
|
136
|
+
def log_prefix():
|
|
137
|
+
return Config().get('export.log-prefix', '/tmp/qing')
|
adam/version.py
CHANGED