kaqing 2.0.171__py3-none-any.whl → 2.0.204__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.
- adam/app_session.py +5 -10
- adam/apps.py +18 -4
- adam/batch.py +7 -7
- adam/checks/check_utils.py +3 -1
- adam/checks/disk.py +2 -3
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +15 -6
- adam/commands/alter_tables.py +26 -41
- adam/commands/app/__init__.py +0 -0
- adam/commands/{app_cmd.py → app/app.py} +2 -2
- adam/commands/{show → app}/show_app_actions.py +7 -15
- adam/commands/{show → app}/show_app_queues.py +1 -4
- adam/{utils_app.py → commands/app/utils_app.py} +9 -1
- adam/commands/audit/audit.py +9 -26
- adam/commands/audit/audit_repair_tables.py +5 -7
- adam/commands/audit/audit_run.py +1 -1
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +2 -14
- adam/commands/audit/show_slow10.py +2 -13
- adam/commands/audit/show_top10.py +2 -11
- adam/commands/audit/utils_show_top10.py +15 -3
- adam/commands/bash/bash.py +1 -1
- adam/commands/bash/utils_bash.py +1 -1
- adam/commands/cassandra/__init__.py +0 -0
- adam/commands/cassandra/download_cassandra_log.py +45 -0
- adam/commands/cassandra/nodetool.py +64 -0
- adam/commands/cassandra/nodetool_commands.py +120 -0
- adam/commands/cassandra/restart_cluster.py +47 -0
- adam/commands/cassandra/restart_node.py +51 -0
- adam/commands/cassandra/restart_nodes.py +47 -0
- adam/commands/cassandra/rollout.py +88 -0
- adam/commands/cat.py +5 -19
- adam/commands/cd.py +7 -9
- adam/commands/check.py +10 -18
- adam/commands/cli_commands.py +6 -1
- adam/commands/{cp.py → clipboard_copy.py} +34 -36
- adam/commands/code.py +2 -2
- adam/commands/command.py +139 -22
- adam/commands/commands_utils.py +14 -12
- adam/commands/cql/alter_tables.py +66 -0
- adam/commands/cql/completions_c.py +29 -0
- adam/commands/cql/cqlsh.py +3 -7
- adam/commands/cql/utils_cql.py +23 -61
- adam/commands/debug/__init__.py +0 -0
- adam/commands/debug/debug.py +22 -0
- adam/commands/debug/debug_completes.py +35 -0
- adam/commands/debug/debug_timings.py +35 -0
- adam/commands/deploy/deploy_pg_agent.py +2 -2
- adam/commands/deploy/deploy_pod.py +2 -4
- adam/commands/deploy/undeploy_pg_agent.py +2 -2
- adam/commands/devices/device.py +40 -9
- adam/commands/devices/device_app.py +19 -29
- adam/commands/devices/device_auit_log.py +3 -3
- adam/commands/devices/device_cass.py +17 -23
- adam/commands/devices/device_export.py +12 -11
- adam/commands/devices/device_postgres.py +79 -63
- adam/commands/devices/devices.py +1 -1
- adam/commands/download_cassandra_log.py +45 -0
- adam/commands/download_file.py +47 -0
- adam/commands/export/clean_up_all_export_sessions.py +3 -3
- adam/commands/export/clean_up_export_sessions.py +7 -19
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +6 -22
- adam/commands/export/drop_export_databases.py +3 -9
- adam/commands/export/export.py +1 -17
- adam/commands/export/export_databases.py +109 -32
- adam/commands/export/export_select.py +8 -55
- adam/commands/export/export_sessions.py +211 -0
- adam/commands/export/export_use.py +13 -16
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +176 -167
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +10 -6
- adam/commands/export/importer.py +24 -9
- adam/commands/export/importer_athena.py +114 -44
- adam/commands/export/importer_sqlite.py +45 -23
- adam/commands/export/show_column_counts.py +11 -20
- adam/commands/export/show_export_databases.py +5 -2
- adam/commands/export/show_export_session.py +6 -15
- adam/commands/export/show_export_sessions.py +4 -11
- adam/commands/export/utils_export.py +79 -27
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/generate_report.py +52 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +2 -2
- adam/commands/intermediate_command.py +6 -3
- adam/commands/login.py +3 -6
- adam/commands/ls.py +2 -2
- adam/commands/medusa/medusa_backup.py +13 -16
- adam/commands/medusa/medusa_restore.py +26 -37
- adam/commands/medusa/medusa_show_backupjobs.py +7 -7
- adam/commands/medusa/medusa_show_restorejobs.py +6 -6
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +3 -8
- adam/commands/os/__init__.py +0 -0
- adam/commands/os/cat.py +36 -0
- adam/commands/os/download_file.py +47 -0
- adam/commands/os/find_files.py +51 -0
- adam/commands/os/find_processes.py +76 -0
- adam/commands/os/head.py +36 -0
- adam/commands/os/shell.py +41 -0
- adam/commands/param_get.py +10 -12
- adam/commands/param_set.py +7 -10
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +25 -40
- adam/commands/postgres/postgres_databases.py +269 -0
- adam/commands/postgres/utils_postgres.py +33 -20
- adam/commands/preview_table.py +4 -2
- adam/commands/pwd.py +4 -6
- adam/commands/reaper/reaper_forward.py +2 -2
- adam/commands/reaper/reaper_run_abort.py +4 -10
- adam/commands/reaper/reaper_runs.py +3 -3
- adam/commands/reaper/reaper_schedule_activate.py +12 -12
- adam/commands/reaper/reaper_schedule_start.py +7 -12
- adam/commands/reaper/reaper_schedule_stop.py +7 -12
- adam/commands/reaper/utils_reaper.py +13 -6
- adam/commands/repair/repair_log.py +1 -4
- adam/commands/repair/repair_run.py +3 -8
- adam/commands/repair/repair_scan.py +1 -6
- adam/commands/repair/repair_stop.py +1 -5
- adam/commands/restart_cluster.py +47 -0
- adam/commands/restart_node.py +51 -0
- adam/commands/restart_nodes.py +47 -0
- adam/commands/shell.py +9 -2
- adam/commands/show/show.py +4 -4
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +5 -6
- adam/commands/show/show_cassandra_status.py +29 -29
- adam/commands/show/show_cassandra_version.py +1 -4
- adam/commands/show/{show_commands.py → show_cli_commands.py} +3 -6
- adam/commands/show/show_login.py +3 -9
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +15 -16
- adam/commands/show/show_storage.py +9 -8
- adam/config.py +4 -5
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/repl.py +26 -18
- adam/repl_commands.py +32 -20
- adam/repl_session.py +9 -1
- adam/repl_state.py +39 -10
- adam/sql/async_executor.py +44 -0
- adam/sql/lark_completer.py +286 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/qingl.lark +1076 -0
- adam/sql/sql_completer.py +4 -6
- adam/sql/sql_state_machine.py +25 -13
- adam/sso/authn_ad.py +2 -5
- adam/sso/authn_okta.py +2 -4
- adam/sso/cred_cache.py +2 -5
- adam/sso/idp.py +8 -11
- adam/utils.py +299 -105
- adam/utils_athena.py +18 -18
- adam/utils_audits.py +3 -7
- adam/utils_issues.py +2 -2
- adam/utils_k8s/app_clusters.py +4 -4
- adam/utils_k8s/app_pods.py +8 -6
- adam/utils_k8s/cassandra_clusters.py +16 -5
- adam/utils_k8s/cassandra_nodes.py +7 -6
- adam/utils_k8s/custom_resources.py +11 -17
- adam/utils_k8s/jobs.py +7 -11
- adam/utils_k8s/k8s.py +14 -5
- adam/utils_k8s/kube_context.py +3 -6
- adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +4 -4
- adam/utils_k8s/pods.py +98 -36
- adam/utils_k8s/statefulsets.py +5 -2
- adam/utils_local.py +42 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/repl_completer.py +45 -2
- adam/utils_repl/state_machine.py +3 -3
- adam/utils_sqlite.py +58 -30
- adam/version.py +1 -1
- {kaqing-2.0.171.dist-info → kaqing-2.0.204.dist-info}/METADATA +1 -1
- kaqing-2.0.204.dist-info/RECORD +277 -0
- kaqing-2.0.204.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/cql/cql_completions.py +0 -33
- adam/commands/export/export_handlers.py +0 -71
- adam/commands/export/export_select_x.py +0 -54
- adam/commands/logs.py +0 -37
- adam/commands/postgres/postgres_context.py +0 -274
- adam/commands/postgres/psql_completions.py +0 -10
- adam/commands/report.py +0 -61
- adam/commands/restart.py +0 -60
- kaqing-2.0.171.dist-info/RECORD +0 -236
- kaqing-2.0.171.dist-info/top_level.txt +0 -1
- /adam/commands/{app_ping.py → app/app_ping.py} +0 -0
- /adam/commands/{show → app}/show_app_id.py +0 -0
- {kaqing-2.0.171.dist-info → kaqing-2.0.204.dist-info}/WHEEL +0 -0
- {kaqing-2.0.171.dist-info → kaqing-2.0.204.dist-info}/entry_points.txt +0 -0
adam/utils_athena.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
1
2
|
import functools
|
|
2
3
|
import time
|
|
3
4
|
import boto3
|
|
4
5
|
import botocore
|
|
5
6
|
|
|
6
7
|
from adam.config import Config
|
|
7
|
-
from adam.utils import
|
|
8
|
+
from adam.utils import tabulize, log, log2, log_exc, wait_log
|
|
8
9
|
|
|
9
10
|
# no state utility class
|
|
10
11
|
class Athena:
|
|
@@ -17,7 +18,7 @@ class Athena:
|
|
|
17
18
|
if like:
|
|
18
19
|
query = f"{query} AND schema_name like '{like}'"
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
with log_exc():
|
|
21
22
|
state, reason, rs = Athena.query(query)
|
|
22
23
|
if rs:
|
|
23
24
|
names = []
|
|
@@ -26,8 +27,6 @@ class Athena:
|
|
|
26
27
|
names.append(row_data[0])
|
|
27
28
|
|
|
28
29
|
return names
|
|
29
|
-
except:
|
|
30
|
-
pass
|
|
31
30
|
|
|
32
31
|
return []
|
|
33
32
|
|
|
@@ -64,7 +63,7 @@ class Athena:
|
|
|
64
63
|
|
|
65
64
|
@functools.lru_cache()
|
|
66
65
|
def column_names(tables: list[str] = [], database: str = None, function: str = 'audit', partition_cols_only = False):
|
|
67
|
-
|
|
66
|
+
with log_exc():
|
|
68
67
|
if not database:
|
|
69
68
|
database = Config().get(f'{function}.athena.database', 'audit')
|
|
70
69
|
|
|
@@ -80,32 +79,33 @@ class Athena:
|
|
|
80
79
|
_, _, rs = Athena.query(query)
|
|
81
80
|
if rs:
|
|
82
81
|
return [row['Data'][0].get('VarCharValue') for row in rs[1:]]
|
|
83
|
-
except:
|
|
84
|
-
# aws credentials not found
|
|
85
|
-
pass
|
|
86
82
|
|
|
87
83
|
return []
|
|
88
84
|
|
|
89
|
-
def run_query(sql: str, database: str = None):
|
|
85
|
+
def run_query(sql: str, database: str = None, output: Callable[[str], str] = None):
|
|
90
86
|
state, reason, rs = Athena.query(sql, database)
|
|
91
87
|
|
|
88
|
+
log_file = None
|
|
92
89
|
if state == 'SUCCEEDED':
|
|
93
90
|
if rs:
|
|
94
91
|
column_info = rs[0]['Data']
|
|
95
92
|
columns = [col.get('VarCharValue') for col in column_info]
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
out = tabulize(rs[1:],
|
|
94
|
+
lambda r: '\t'.join(col.get('VarCharValue') if col else '' for col in r['Data']),
|
|
95
|
+
header='\t'.join(columns),
|
|
96
|
+
separator='\t',
|
|
97
|
+
to=0)
|
|
98
|
+
if output:
|
|
99
|
+
log_file = output(out)
|
|
100
|
+
else:
|
|
101
|
+
log(out)
|
|
102
|
+
|
|
103
|
+
return len(rs)-1, log_file
|
|
104
104
|
else:
|
|
105
105
|
log2(f"Query failed or was cancelled. State: {state}")
|
|
106
106
|
log2(f"Reason: {reason}")
|
|
107
107
|
|
|
108
|
-
return 0
|
|
108
|
+
return 0, log_file
|
|
109
109
|
|
|
110
110
|
def query(sql: str, database: str = None, function: str = 'audit') -> tuple[str, str, list]:
|
|
111
111
|
region_name = Config().get(f'{function}.athena.region', 'us-west-2')
|
adam/utils_audits.py
CHANGED
|
@@ -4,7 +4,7 @@ import time
|
|
|
4
4
|
import requests
|
|
5
5
|
|
|
6
6
|
from adam.config import Config
|
|
7
|
-
from adam.utils import OffloadHandler, debug, log2, offload
|
|
7
|
+
from adam.utils import OffloadHandler, debug, log2, log_exc, offload
|
|
8
8
|
from adam.utils_athena import Athena
|
|
9
9
|
from adam.utils_net import get_my_host
|
|
10
10
|
|
|
@@ -46,12 +46,10 @@ class Audits:
|
|
|
46
46
|
state, _, rs = Athena.query(f'select partitions_last_checked, clusters_last_checked from meta')
|
|
47
47
|
if state == 'SUCCEEDED':
|
|
48
48
|
if len(rs) > 1:
|
|
49
|
-
|
|
49
|
+
with log_exc():
|
|
50
50
|
row = rs[1]['Data']
|
|
51
51
|
checked_in = float(row[0]['VarCharValue'])
|
|
52
52
|
cluster_last_checked = float(row[1]['VarCharValue'])
|
|
53
|
-
except:
|
|
54
|
-
pass
|
|
55
53
|
|
|
56
54
|
return AuditMeta(checked_in, cluster_last_checked)
|
|
57
55
|
|
|
@@ -88,10 +86,8 @@ class Audits:
|
|
|
88
86
|
state, _, rs = Athena.query(query)
|
|
89
87
|
if state == 'SUCCEEDED':
|
|
90
88
|
if len(rs) > 1:
|
|
91
|
-
|
|
89
|
+
with log_exc():
|
|
92
90
|
return [r['Data'][0]['VarCharValue'] for r in rs[1:]]
|
|
93
|
-
except:
|
|
94
|
-
pass
|
|
95
91
|
|
|
96
92
|
return []
|
|
97
93
|
|
adam/utils_issues.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from adam.checks.check_result import CheckResult
|
|
2
2
|
from adam.checks.issue import Issue
|
|
3
3
|
from adam.repl_session import ReplSession
|
|
4
|
-
from adam.utils import
|
|
4
|
+
from adam.utils import tabulize, log2
|
|
5
5
|
|
|
6
6
|
class IssuesUtils:
|
|
7
7
|
def show(check_results: list[CheckResult], in_repl = False):
|
|
@@ -26,7 +26,7 @@ class IssuesUtils:
|
|
|
26
26
|
if in_repl:
|
|
27
27
|
ReplSession().prompt_session.history.append_string(issue.suggestion)
|
|
28
28
|
suggested += 1
|
|
29
|
-
|
|
29
|
+
tabulize(lines, separator='||', to=2)
|
|
30
30
|
if suggested:
|
|
31
31
|
log2()
|
|
32
32
|
log2(f'* {suggested} suggested commands are added to history. Press <Up> arrow to access them.')
|
adam/utils_k8s/app_clusters.py
CHANGED
|
@@ -2,7 +2,7 @@ import sys
|
|
|
2
2
|
from typing import TypeVar
|
|
3
3
|
|
|
4
4
|
from adam.utils_k8s.app_pods import AppPods
|
|
5
|
-
from adam.pod_exec_result import PodExecResult
|
|
5
|
+
from adam.utils_k8s.pod_exec_result import PodExecResult
|
|
6
6
|
from adam.utils import log, log2
|
|
7
7
|
from adam.utils_k8s.pods import Pods
|
|
8
8
|
from .kube_context import KubeContext
|
|
@@ -12,17 +12,17 @@ T = TypeVar('T')
|
|
|
12
12
|
# utility collection on app clusters; methods are all static
|
|
13
13
|
class AppClusters:
|
|
14
14
|
def exec(pods: list[str], namespace: str, command: str, action: str = 'action',
|
|
15
|
-
max_workers=0, show_out=True, on_any = False, shell = '/bin/sh',
|
|
15
|
+
max_workers=0, show_out=True, on_any = False, shell = '/bin/sh', backgrounded = False) -> list[PodExecResult]:
|
|
16
16
|
samples = 1 if on_any else sys.maxsize
|
|
17
17
|
msg = 'd`Running|Ran ' + action + ' command onto {size} pods'
|
|
18
18
|
with Pods.parallelize(pods, max_workers, samples, msg, action=action) as exec:
|
|
19
|
-
results: list[PodExecResult] = exec.map(lambda pod: AppPods.exec(pod, namespace, command, False, False, shell,
|
|
19
|
+
results: list[PodExecResult] = exec.map(lambda pod: AppPods.exec(pod, namespace, command, False, False, shell, backgrounded))
|
|
20
20
|
for result in results:
|
|
21
21
|
if KubeContext.show_out(show_out):
|
|
22
22
|
log(result.command)
|
|
23
23
|
if result.stdout:
|
|
24
24
|
log(result.stdout)
|
|
25
25
|
if result.stderr:
|
|
26
|
-
log2(result.stderr
|
|
26
|
+
log2(result.stderr)
|
|
27
27
|
|
|
28
28
|
return results
|
adam/utils_k8s/app_pods.py
CHANGED
|
@@ -4,13 +4,16 @@ from kubernetes import client
|
|
|
4
4
|
|
|
5
5
|
from adam.config import Config
|
|
6
6
|
from adam.utils_k8s.pods import Pods
|
|
7
|
-
from adam.pod_exec_result import PodExecResult
|
|
7
|
+
from adam.utils_k8s.pod_exec_result import PodExecResult
|
|
8
8
|
from adam.repl_session import ReplSession
|
|
9
9
|
|
|
10
10
|
# utility collection on app pods; methods are all static
|
|
11
11
|
class AppPods:
|
|
12
12
|
@functools.lru_cache()
|
|
13
13
|
def pod_names(namespace: str, env: str, app: str):
|
|
14
|
+
if not env or not app:
|
|
15
|
+
return []
|
|
16
|
+
|
|
14
17
|
return [pod.metadata.name for pod in AppPods.app_pods(namespace, env, app)]
|
|
15
18
|
|
|
16
19
|
def app_pods(namespace: str, env: str, app: str) -> List[client.V1Pod]:
|
|
@@ -22,12 +25,11 @@ class AppPods:
|
|
|
22
25
|
|
|
23
26
|
return v1.list_namespaced_pod(namespace, label_selector=label_selector).items
|
|
24
27
|
|
|
25
|
-
def exec(pod_name: str, namespace: str, command: str, show_out = True, throw_err = False, shell = '/bin/sh',
|
|
28
|
+
def exec(pod_name: str, namespace: str, command: str, show_out = True, throw_err = False, shell = '/bin/sh', backgrounded = False) -> PodExecResult:
|
|
26
29
|
container = Config().get('app.container-name', 'c3-server')
|
|
27
|
-
r = Pods.exec(pod_name, container, namespace, command, show_out = show_out, throw_err = throw_err, shell = shell,
|
|
30
|
+
r = Pods.exec(pod_name, container, namespace, command, show_out = show_out, throw_err = throw_err, shell = shell, backgrounded = backgrounded)
|
|
28
31
|
|
|
29
|
-
if r and
|
|
30
|
-
|
|
31
|
-
ReplSession().prompt_session.history.append_string(f'@{r.pod} cat {r.log_file}')
|
|
32
|
+
if r and r.log_file:
|
|
33
|
+
ReplSession().append_history(f'@{r.pod} cat {r.log_file}')
|
|
32
34
|
|
|
33
35
|
return r
|
|
@@ -3,7 +3,7 @@ from typing import TypeVar
|
|
|
3
3
|
|
|
4
4
|
from adam.config import Config
|
|
5
5
|
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
6
|
-
from adam.pod_exec_result import PodExecResult
|
|
6
|
+
from adam.utils_k8s.pod_exec_result import PodExecResult
|
|
7
7
|
from adam.utils import log, log2
|
|
8
8
|
from adam.utils_k8s.pods import Pods
|
|
9
9
|
from adam.utils_k8s.statefulsets import StatefulSets
|
|
@@ -12,21 +12,32 @@ T = TypeVar('T')
|
|
|
12
12
|
|
|
13
13
|
# utility collection on cassandra clusters; methods are all static
|
|
14
14
|
class CassandraClusters:
|
|
15
|
-
def exec(sts: str,
|
|
16
|
-
|
|
15
|
+
def exec(sts: str,
|
|
16
|
+
namespace: str,
|
|
17
|
+
command: str,
|
|
18
|
+
action: str = 'action',
|
|
19
|
+
max_workers=0,
|
|
20
|
+
show_out=True,
|
|
21
|
+
on_any = False,
|
|
22
|
+
shell = '/bin/sh',
|
|
23
|
+
backgrounded = False,
|
|
24
|
+
log_file = None) -> list[PodExecResult]:
|
|
17
25
|
|
|
18
26
|
pods = StatefulSets.pod_names(sts, namespace)
|
|
19
27
|
samples = 1 if on_any else sys.maxsize
|
|
28
|
+
if (backgrounded or command.endswith(' &')) and not log_file:
|
|
29
|
+
log_file = Pods.log_file(command)
|
|
30
|
+
|
|
20
31
|
msg = 'd`Running|Ran ' + action + ' command onto {size} pods'
|
|
21
32
|
with Pods.parallelize(pods, max_workers, samples, msg, action=action) as exec:
|
|
22
|
-
results: list[PodExecResult] = exec.map(lambda pod: CassandraNodes.exec(pod, namespace, command, False, False, shell,
|
|
33
|
+
results: list[PodExecResult] = exec.map(lambda pod: CassandraNodes.exec(pod, namespace, command, False, False, shell, backgrounded, log_file))
|
|
23
34
|
for result in results:
|
|
24
35
|
if show_out and not Config().is_debug():
|
|
25
36
|
log(result.command)
|
|
26
37
|
if result.stdout:
|
|
27
38
|
log(result.stdout)
|
|
28
39
|
if result.stderr:
|
|
29
|
-
log2(result.stderr
|
|
40
|
+
log2(result.stderr)
|
|
30
41
|
|
|
31
42
|
return results
|
|
32
43
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
from adam.config import Config
|
|
2
2
|
from adam.utils_k8s.pods import Pods
|
|
3
3
|
from adam.utils_k8s.secrets import Secrets
|
|
4
|
-
from adam.pod_exec_result import PodExecResult
|
|
4
|
+
from adam.utils_k8s.pod_exec_result import PodExecResult
|
|
5
5
|
from adam.repl_session import ReplSession
|
|
6
6
|
|
|
7
7
|
# utility collection on cassandra nodes; methods are all static
|
|
8
8
|
class CassandraNodes:
|
|
9
|
-
def exec(pod_name: str, namespace: str, command: str, show_out = True, throw_err = False, shell = '/bin/sh',
|
|
10
|
-
r = Pods.exec(pod_name, "cassandra", namespace, command, show_out = show_out, throw_err = throw_err, shell = shell,
|
|
9
|
+
def exec(pod_name: str, namespace: str, command: str, show_out = True, throw_err = False, shell = '/bin/sh', backgrounded = False, log_file = None, no_history = False) -> PodExecResult:
|
|
10
|
+
r = Pods.exec(pod_name, "cassandra", namespace, command, show_out = show_out, throw_err = throw_err, shell = shell, backgrounded = backgrounded, log_file=log_file)
|
|
11
11
|
|
|
12
|
-
if r and
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if not no_history and r and r.log_file:
|
|
13
|
+
entry = f':sh cat {r.log_file}'
|
|
14
|
+
|
|
15
|
+
ReplSession().append_history(entry)
|
|
15
16
|
|
|
16
17
|
return r
|
|
17
18
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import re
|
|
3
|
-
import time
|
|
4
3
|
from kubernetes import client
|
|
5
4
|
|
|
6
5
|
from adam.config import Config
|
|
7
6
|
from .kube_context import KubeContext
|
|
8
|
-
from adam.utils import
|
|
7
|
+
from adam.utils import log2, log_exc
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
# utility collection; methods are all static
|
|
@@ -20,14 +19,12 @@ class CustomResources:
|
|
|
20
19
|
strip = Config().get('app.strip', '0')
|
|
21
20
|
|
|
22
21
|
v1 = client.CustomObjectsApi()
|
|
23
|
-
|
|
22
|
+
with log_exc():
|
|
24
23
|
c3cassandras = v1.list_cluster_custom_object(group=group, version=v, plural=plural)
|
|
25
24
|
for c in c3cassandras.items():
|
|
26
25
|
if c[0] == 'items':
|
|
27
26
|
for item in c[1]:
|
|
28
27
|
app_ids_by_ss[f"{item['metadata']['name']}@{item['metadata']['namespace']}"] = item['metadata']['labels'][label].strip(strip)
|
|
29
|
-
except Exception:
|
|
30
|
-
pass
|
|
31
28
|
|
|
32
29
|
return app_ids_by_ss
|
|
33
30
|
|
|
@@ -122,11 +119,10 @@ class CustomResources:
|
|
|
122
119
|
body = bkspecs
|
|
123
120
|
pretty = 'true'
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
with log_exc(lambda e: "Exception when calling create_medusa_backupjob.create_namespaced_custom_object: %s\n" % e):
|
|
126
123
|
api_instance.create_namespaced_custom_object(group, version, namespace, plural, body, pretty=pretty)
|
|
127
124
|
log2(f"create_medusa_backupjob: created Full Backup {bkname}: {api_instance}")
|
|
128
|
-
|
|
129
|
-
log2("Exception when calling create_medusa_backupjob.create_namespaced_custom_object: %s\n" % e)
|
|
125
|
+
|
|
130
126
|
return None
|
|
131
127
|
|
|
132
128
|
def create_medusa_restorejob(restorejobname: str, bkname: str, dc: str, ns: str):
|
|
@@ -155,11 +151,10 @@ class CustomResources:
|
|
|
155
151
|
body = rtspecs
|
|
156
152
|
pretty = 'true'
|
|
157
153
|
|
|
158
|
-
|
|
154
|
+
with log_exc(lambda e: "Exception when calling create_medusa_restorejob.create_namespaced_custom_object: %s\n" % e):
|
|
159
155
|
api_instance.create_namespaced_custom_object(group, version, namespace, plural, body, pretty=pretty)
|
|
160
156
|
log2(f"create_medusa_restorejob: created Restore Job {restorejobname}: {api_instance}")
|
|
161
|
-
|
|
162
|
-
log2("Exception when calling create_medusa_restorejob.create_namespaced_custom_object: %s\n" % e)
|
|
157
|
+
|
|
163
158
|
return None
|
|
164
159
|
|
|
165
160
|
def medusa_show_backup_names(dc: str, ns: str) -> list[dict]:
|
|
@@ -185,11 +180,10 @@ class CustomResources:
|
|
|
185
180
|
pretty = 'true'
|
|
186
181
|
label_selector = 'cassandra.datastax.com/datacenter=' + dc
|
|
187
182
|
|
|
188
|
-
|
|
183
|
+
with log_exc(lambda e: "Exception when calling medusa_show_backupjobs.list_namespaced_custom_object: %s\n" % e):
|
|
189
184
|
api_response = api_instance.list_namespaced_custom_object(group, version, namespace, plural, pretty=pretty, label_selector=label_selector)
|
|
190
185
|
return api_response['items']
|
|
191
|
-
|
|
192
|
-
log2("Exception when calling medusa_show_backupjobs.list_namespaced_custom_object: %s\n" % e)
|
|
186
|
+
|
|
193
187
|
return None
|
|
194
188
|
|
|
195
189
|
def medusa_show_restorejobs(dc: str, ns: str):
|
|
@@ -201,11 +195,11 @@ class CustomResources:
|
|
|
201
195
|
pretty = 'true'
|
|
202
196
|
label_selector = 'cassandra.datastax.com/datacenter=' + dc
|
|
203
197
|
rtlist = []
|
|
204
|
-
|
|
198
|
+
|
|
199
|
+
with log_exc(lambda e: "Exception when calling medusa_show_restorejobs.list_namespaced_custom_object: %s\n" % e):
|
|
205
200
|
api_response = api_instance.list_namespaced_custom_object(group, version, namespace, plural, pretty=pretty, label_selector=label_selector)
|
|
206
201
|
for x in api_response['items']:
|
|
207
202
|
rtlist.append(f"{x['metadata']['name']}\t{x['metadata']['creationTimestamp']}\t{x['status'].get('finishTime', '')}")
|
|
208
203
|
return rtlist
|
|
209
|
-
|
|
210
|
-
log2("Exception when calling medusa_show_restorejobs.list_namespaced_custom_object: %s\n" % e)
|
|
204
|
+
|
|
211
205
|
return None
|
adam/utils_k8s/jobs.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from kubernetes import client
|
|
2
2
|
from time import sleep
|
|
3
3
|
from .pods import Pods
|
|
4
|
-
from adam.utils import log2
|
|
4
|
+
from adam.utils import log2, log_exc
|
|
5
5
|
|
|
6
6
|
# utility collection on jobs; methods are all static
|
|
7
7
|
class Jobs:
|
|
@@ -20,11 +20,10 @@ class Jobs:
|
|
|
20
20
|
metadata=client.V1ObjectMeta(name=job_name),
|
|
21
21
|
spec=spec)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
with log_exc(lambda e: "Exception when calling BatchV1Apii->create_namespaced_job: %s\n" % e):
|
|
24
24
|
client.BatchV1Api().create_namespaced_job(body=job, namespace=namespace)
|
|
25
25
|
log2(f"Job {job_name} created in {namespace}")
|
|
26
|
-
|
|
27
|
-
log2("Exception when calling BatchV1Apii->create_namespaced_job: %s\n" % e)
|
|
26
|
+
|
|
28
27
|
return
|
|
29
28
|
|
|
30
29
|
def get_job_pods(job_name: str, namespace: str):
|
|
@@ -32,7 +31,7 @@ class Jobs:
|
|
|
32
31
|
return pods
|
|
33
32
|
|
|
34
33
|
def delete(job_name: str, namespace: str, wait=True):
|
|
35
|
-
|
|
34
|
+
with log_exc(lambda e: "Exception when calling BatchV1Apii->delete_namespaced_job: %s\n" % e):
|
|
36
35
|
client.BatchV1Api().delete_namespaced_job(name=job_name, namespace=namespace, propagation_policy='Background')
|
|
37
36
|
if wait:
|
|
38
37
|
while True:
|
|
@@ -41,14 +40,11 @@ class Jobs:
|
|
|
41
40
|
return
|
|
42
41
|
sleep(5)
|
|
43
42
|
log2(f"Job {job_name} in {namespace} deleted.")
|
|
44
|
-
|
|
45
|
-
log2("Exception when calling BatchV1Apii->delete_namespaced_job: %s\n" % e)
|
|
43
|
+
|
|
46
44
|
return
|
|
47
45
|
|
|
48
46
|
def get_logs(job_name: str, namespace: str):
|
|
49
47
|
v1 = client.CoreV1Api()
|
|
50
|
-
|
|
48
|
+
with log_exc(lambda e: "Exception when calling CorV1Apii->list_namespaced_pod, cannot find job pod: %s\n" % e):
|
|
51
49
|
pod_name = Jobs.get_job_pods(job_name, namespace).items[0].metadata.name
|
|
52
|
-
log2(v1.read_namespaced_pod_log(name=pod_name, namespace=namespace))
|
|
53
|
-
except Exception as e:
|
|
54
|
-
log2("Exception when calling CorV1Apii->list_namespaced_pod, cannot find job pod: %s\n" % e)
|
|
50
|
+
log2(v1.read_namespaced_pod_log(name=pod_name, namespace=namespace))
|
adam/utils_k8s/k8s.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
|
+
import inspect
|
|
2
3
|
import re
|
|
3
4
|
import portforward
|
|
4
5
|
|
|
5
|
-
from adam.commands.command import
|
|
6
|
+
from adam.commands.command import InvalidStateException
|
|
6
7
|
from adam.repl_state import ReplState
|
|
7
8
|
from adam.utils import log2
|
|
8
9
|
from adam.utils_k8s.kube_context import KubeContext
|
|
@@ -24,14 +25,14 @@ class PortForwardHandler:
|
|
|
24
25
|
if not self.svc_or_pod:
|
|
25
26
|
log2('No service or pod found.')
|
|
26
27
|
|
|
27
|
-
raise
|
|
28
|
+
raise InvalidStateException(state)
|
|
28
29
|
|
|
29
30
|
if KubeContext.in_cluster():
|
|
30
31
|
svc_name = self.svc_or_pod(True)
|
|
31
32
|
if not svc_name:
|
|
32
33
|
log2('No service found.')
|
|
33
34
|
|
|
34
|
-
raise
|
|
35
|
+
raise InvalidStateException(state)
|
|
35
36
|
|
|
36
37
|
# cs-a526330d23-cs-a526330d23-default-sts-0 ->
|
|
37
38
|
# curl http://cs-a526330d23-cs-a526330d23-reaper-service.stgawsscpsr.svc.cluster.local:8080
|
|
@@ -40,15 +41,23 @@ class PortForwardHandler:
|
|
|
40
41
|
svc = f'{groups[1]}{svc_name}.{state.namespace}.svc.cluster.local:{self.target_port}'
|
|
41
42
|
return (svc, svc)
|
|
42
43
|
else:
|
|
43
|
-
raise
|
|
44
|
+
raise InvalidStateException(state)
|
|
44
45
|
else:
|
|
45
46
|
pod = self.svc_or_pod(False)
|
|
46
47
|
if not pod:
|
|
47
48
|
log2('No pod found.')
|
|
48
49
|
|
|
49
|
-
raise
|
|
50
|
+
raise InvalidStateException(state)
|
|
50
51
|
|
|
51
52
|
self.pod = pod
|
|
53
|
+
|
|
54
|
+
# pf = portforward.forward(state.namespace, pod, self.local_port + 1, self.target_port, log_level=portforward.LogLevel.DEBUG)
|
|
55
|
+
# print(inspect.getsource(pf.__enter__))
|
|
56
|
+
# print('test portforward START', state.namespace, pod, self.local_port + 1, self.target_port, pf.__enter__)
|
|
57
|
+
# with pf:
|
|
58
|
+
# print('test portforward BODY')
|
|
59
|
+
# print('test portforward OK')
|
|
60
|
+
|
|
52
61
|
self.forward_connection = portforward.forward(state.namespace, pod, self.local_port, self.target_port)
|
|
53
62
|
if self.inc_connection_cnt() == 1:
|
|
54
63
|
self.forward_connection.__enter__()
|
adam/utils_k8s/kube_context.py
CHANGED
|
@@ -3,7 +3,7 @@ import re
|
|
|
3
3
|
from kubernetes import config as kconfig
|
|
4
4
|
|
|
5
5
|
from adam.config import Config
|
|
6
|
-
from adam.utils import idp_token_from_env,
|
|
6
|
+
from adam.utils import idp_token_from_env, log2, tabulize
|
|
7
7
|
|
|
8
8
|
class KubeContext:
|
|
9
9
|
_in_cluster = False
|
|
@@ -56,7 +56,7 @@ class KubeContext:
|
|
|
56
56
|
log2('Use -v <key>=<value> format.')
|
|
57
57
|
log2()
|
|
58
58
|
lines = [f'{key}\t{Config().get(key, None)}' for key in Config().keys()]
|
|
59
|
-
|
|
59
|
+
tabulize(lines, separator='\t', to=2)
|
|
60
60
|
|
|
61
61
|
for p in param_ovrs:
|
|
62
62
|
tokens = p.split('=')
|
|
@@ -102,7 +102,4 @@ class KubeContext:
|
|
|
102
102
|
return name if re.match(r"^(?!pg-).*-k8spg-.*$", name) else None
|
|
103
103
|
|
|
104
104
|
def show_out(s: bool):
|
|
105
|
-
return s or Config().is_debug()
|
|
106
|
-
|
|
107
|
-
def show_parallelism():
|
|
108
|
-
return Config().get('debugs.show-parallelism', False)
|
|
105
|
+
return s or Config().is_debug()
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import yaml
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
from adam.utils import ExecResult, log_exc
|
|
4
|
+
|
|
5
|
+
class PodExecResult(ExecResult):
|
|
4
6
|
# {
|
|
5
7
|
# 'metadata': {},
|
|
6
8
|
# 'status': 'Failure',
|
|
@@ -27,10 +29,8 @@ class PodExecResult:
|
|
|
27
29
|
def exit_code(self) -> int:
|
|
28
30
|
code = 0
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
with log_exc(False):
|
|
31
33
|
code = self.error['details']['causes'][0]['message']
|
|
32
|
-
except:
|
|
33
|
-
pass
|
|
34
34
|
|
|
35
35
|
return code
|
|
36
36
|
|