kaqing 2.0.98__py3-none-any.whl → 2.0.203__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/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +18 -4
- adam/batch.py +11 -25
- adam/checks/check_utils.py +16 -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 +24 -0
- adam/commands/alter_tables.py +37 -63
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +8 -14
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +8 -11
- adam/commands/{show → app}/show_app_queues.py +8 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/audit.py +31 -35
- adam/commands/audit/audit_repair_tables.py +26 -48
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +36 -0
- adam/commands/audit/show_slow10.py +36 -0
- adam/commands/audit/show_top10.py +36 -0
- adam/commands/audit/utils_show_top10.py +71 -0
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +36 -0
- adam/commands/bash/bash_completer.py +93 -0
- adam/commands/bash/utils_bash.py +16 -0
- 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/{restart.py → cassandra/restart_cluster.py} +12 -26
- 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 +36 -0
- adam/commands/cd.py +14 -92
- adam/commands/check.py +18 -21
- adam/commands/cli_commands.py +8 -4
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +57 -0
- adam/commands/command.py +212 -39
- adam/commands/commands_utils.py +20 -28
- adam/commands/cql/alter_tables.py +66 -0
- adam/commands/cql/completions_c.py +29 -0
- adam/commands/cql/cqlsh.py +10 -29
- adam/commands/cql/utils_cql.py +305 -0
- 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/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 -21
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +3 -6
- adam/commands/deploy/deploy_pod.py +65 -73
- adam/commands/deploy/deploy_utils.py +14 -24
- adam/commands/deploy/undeploy.py +4 -21
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +6 -8
- adam/commands/deploy/undeploy_pod.py +11 -12
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +149 -0
- adam/commands/devices/device_app.py +163 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +179 -0
- adam/commands/devices/device_export.py +87 -0
- adam/commands/devices/device_postgres.py +160 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/download_cassandra_log.py +45 -0
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/__init__.py +0 -0
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +39 -0
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +39 -0
- adam/commands/export/drop_export_databases.py +37 -0
- adam/commands/export/export.py +37 -0
- adam/commands/export/export_databases.py +247 -0
- adam/commands/export/export_select.py +34 -0
- adam/commands/export/export_sessions.py +211 -0
- adam/commands/export/export_use.py +49 -0
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +361 -0
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +44 -0
- adam/commands/export/importer.py +82 -0
- adam/commands/export/importer_athena.py +150 -0
- adam/commands/export/importer_sqlite.py +69 -0
- adam/commands/export/show_column_counts.py +45 -0
- adam/commands/export/show_export_databases.py +39 -0
- adam/commands/export/show_export_session.py +39 -0
- adam/commands/export/show_export_sessions.py +37 -0
- adam/commands/export/utils_export.py +366 -0
- 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 +12 -8
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +14 -40
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +26 -25
- adam/commands/ls.py +11 -116
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -27
- adam/commands/medusa/medusa_restore.py +35 -48
- adam/commands/medusa/medusa_show_backupjobs.py +17 -18
- adam/commands/medusa/medusa_show_restorejobs.py +13 -18
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +8 -19
- 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 +11 -14
- adam/commands/param_set.py +8 -12
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +47 -55
- adam/commands/postgres/postgres_databases.py +269 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +10 -61
- adam/commands/pwd.py +14 -46
- adam/commands/reaper/reaper.py +4 -24
- adam/commands/reaper/reaper_forward.py +49 -56
- 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 +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -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 -40
- adam/commands/repair/repair_stop.py +5 -12
- adam/commands/restart_cluster.py +47 -0
- adam/commands/restart_node.py +51 -0
- adam/commands/restart_nodes.py +47 -0
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +10 -23
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +47 -51
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +23 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +26 -29
- adam/config.py +5 -15
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/repl.py +105 -133
- adam/repl_commands.py +68 -28
- adam/repl_session.py +9 -1
- adam/repl_state.py +300 -62
- 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 +104 -64
- adam/sql/sql_state_machine.py +630 -0
- adam/sql/term_completer.py +3 -0
- 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 +640 -10
- adam/utils_athena.py +140 -87
- adam/utils_audits.py +102 -0
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +28 -0
- adam/utils_k8s/app_pods.py +35 -0
- adam/utils_k8s/cassandra_clusters.py +34 -21
- adam/utils_k8s/cassandra_nodes.py +9 -6
- 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 +96 -0
- adam/utils_k8s/kube_context.py +3 -6
- adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +11 -5
- adam/utils_k8s/pods.py +146 -75
- 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 +6 -14
- adam/utils_local.py +42 -0
- adam/utils_net.py +4 -4
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +89 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +137 -0
- adam/version.py +1 -1
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/METADATA +1 -1
- kaqing-2.0.203.dist-info/RECORD +277 -0
- kaqing-2.0.203.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/app.py +0 -67
- adam/commands/bash.py +0 -92
- adam/commands/cp.py +0 -95
- adam/commands/cql/cql_completions.py +0 -11
- adam/commands/cql/cql_table_completer.py +0 -8
- adam/commands/cql/cql_utils.py +0 -115
- adam/commands/describe/describe.py +0 -47
- adam/commands/describe/describe_keyspace.py +0 -60
- adam/commands/describe/describe_keyspaces.py +0 -49
- adam/commands/describe/describe_schema.py +0 -49
- adam/commands/describe/describe_table.py +0 -60
- adam/commands/describe/describe_tables.py +0 -49
- adam/commands/devices.py +0 -118
- adam/commands/logs.py +0 -39
- adam/commands/postgres/postgres_session.py +0 -240
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/postgres/psql_completions.py +0 -10
- adam/commands/postgres/psql_table_completer.py +0 -11
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/report.py +0 -57
- adam/commands/show/show_app_actions.py +0 -53
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- adam/sql/state_machine.py +0 -460
- kaqing-2.0.98.dist-info/RECORD +0 -191
- kaqing-2.0.98.dist-info/top_level.txt +0 -1
- /adam/commands/{describe → app}/__init__.py +0 -0
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/WHEEL +0 -0
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/entry_points.txt +0 -0
adam/commands/devices.py
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
from adam.commands.command import Command
|
|
2
|
-
from adam.repl_state import ReplState
|
|
3
|
-
|
|
4
|
-
class DeviceAuditLog(Command):
|
|
5
|
-
COMMAND = f'{ReplState.L}:'
|
|
6
|
-
|
|
7
|
-
# the singleton pattern
|
|
8
|
-
def __new__(cls, *args, **kwargs):
|
|
9
|
-
if not hasattr(cls, 'instance'): cls.instance = super(DeviceAuditLog, cls).__new__(cls)
|
|
10
|
-
|
|
11
|
-
return cls.instance
|
|
12
|
-
|
|
13
|
-
def __init__(self, successor: Command=None):
|
|
14
|
-
super().__init__(successor)
|
|
15
|
-
|
|
16
|
-
def command(self):
|
|
17
|
-
return DeviceAuditLog.COMMAND
|
|
18
|
-
|
|
19
|
-
def run(self, cmd: str, state: ReplState):
|
|
20
|
-
if not self.args(cmd):
|
|
21
|
-
return super().run(cmd, state)
|
|
22
|
-
|
|
23
|
-
state.device = ReplState.L
|
|
24
|
-
|
|
25
|
-
return state
|
|
26
|
-
|
|
27
|
-
def completion(self, state: ReplState):
|
|
28
|
-
return super().completion(state)
|
|
29
|
-
|
|
30
|
-
def help(self, _: ReplState):
|
|
31
|
-
return f'{DeviceAuditLog.COMMAND}\t move to Audit Log Operations device'
|
|
32
|
-
|
|
33
|
-
class DeviceCass(Command):
|
|
34
|
-
COMMAND = f'{ReplState.C}:'
|
|
35
|
-
|
|
36
|
-
# the singleton pattern
|
|
37
|
-
def __new__(cls, *args, **kwargs):
|
|
38
|
-
if not hasattr(cls, 'instance'): cls.instance = super(DeviceCass, cls).__new__(cls)
|
|
39
|
-
|
|
40
|
-
return cls.instance
|
|
41
|
-
|
|
42
|
-
def __init__(self, successor: Command=None):
|
|
43
|
-
super().__init__(successor)
|
|
44
|
-
|
|
45
|
-
def command(self):
|
|
46
|
-
return DeviceCass.COMMAND
|
|
47
|
-
|
|
48
|
-
def run(self, cmd: str, state: ReplState):
|
|
49
|
-
if not self.args(cmd):
|
|
50
|
-
return super().run(cmd, state)
|
|
51
|
-
|
|
52
|
-
state.device = ReplState.C
|
|
53
|
-
|
|
54
|
-
return state
|
|
55
|
-
|
|
56
|
-
def completion(self, state: ReplState):
|
|
57
|
-
return super().completion(state)
|
|
58
|
-
|
|
59
|
-
def help(self, _: ReplState):
|
|
60
|
-
return f'{DeviceCass.COMMAND}\t move to Cassandra Operations device'
|
|
61
|
-
|
|
62
|
-
class DevicePostgres(Command):
|
|
63
|
-
COMMAND = f'{ReplState.P}:'
|
|
64
|
-
|
|
65
|
-
# the singleton pattern
|
|
66
|
-
def __new__(cls, *args, **kwargs):
|
|
67
|
-
if not hasattr(cls, 'instance'): cls.instance = super(DevicePostgres, cls).__new__(cls)
|
|
68
|
-
|
|
69
|
-
return cls.instance
|
|
70
|
-
|
|
71
|
-
def __init__(self, successor: Command=None):
|
|
72
|
-
super().__init__(successor)
|
|
73
|
-
|
|
74
|
-
def command(self):
|
|
75
|
-
return DevicePostgres.COMMAND
|
|
76
|
-
|
|
77
|
-
def run(self, cmd: str, state: ReplState):
|
|
78
|
-
if not self.args(cmd):
|
|
79
|
-
return super().run(cmd, state)
|
|
80
|
-
|
|
81
|
-
state.device = ReplState.P
|
|
82
|
-
|
|
83
|
-
return state
|
|
84
|
-
|
|
85
|
-
def completion(self, state: ReplState):
|
|
86
|
-
return super().completion(state)
|
|
87
|
-
|
|
88
|
-
def help(self, _: ReplState):
|
|
89
|
-
return f'{DevicePostgres.COMMAND}\t move to Postgres Operations device'
|
|
90
|
-
|
|
91
|
-
class DeviceApp(Command):
|
|
92
|
-
COMMAND = f'{ReplState.A}:'
|
|
93
|
-
|
|
94
|
-
# the singleton pattern
|
|
95
|
-
def __new__(cls, *args, **kwargs):
|
|
96
|
-
if not hasattr(cls, 'instance'): cls.instance = super(DeviceApp, cls).__new__(cls)
|
|
97
|
-
|
|
98
|
-
return cls.instance
|
|
99
|
-
|
|
100
|
-
def __init__(self, successor: Command=None):
|
|
101
|
-
super().__init__(successor)
|
|
102
|
-
|
|
103
|
-
def command(self):
|
|
104
|
-
return DeviceApp.COMMAND
|
|
105
|
-
|
|
106
|
-
def run(self, cmd: str, state: ReplState):
|
|
107
|
-
if not self.args(cmd):
|
|
108
|
-
return super().run(cmd, state)
|
|
109
|
-
|
|
110
|
-
state.device = ReplState.A
|
|
111
|
-
|
|
112
|
-
return state
|
|
113
|
-
|
|
114
|
-
def completion(self, state: ReplState):
|
|
115
|
-
return super().completion(state)
|
|
116
|
-
|
|
117
|
-
def help(self, _: ReplState):
|
|
118
|
-
return f'{DeviceApp.COMMAND}\t move to App Operations device'
|
adam/commands/logs.py
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
from adam.commands.command import Command
|
|
2
|
-
from adam.config import Config
|
|
3
|
-
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
4
|
-
from adam.repl_state import ReplState, RequiredState
|
|
5
|
-
|
|
6
|
-
class Logs(Command):
|
|
7
|
-
COMMAND = 'logs'
|
|
8
|
-
|
|
9
|
-
# the singleton pattern
|
|
10
|
-
def __new__(cls, *args, **kwargs):
|
|
11
|
-
if not hasattr(cls, 'instance'): cls.instance = super(Logs, cls).__new__(cls)
|
|
12
|
-
|
|
13
|
-
return cls.instance
|
|
14
|
-
|
|
15
|
-
def __init__(self, successor: Command=None):
|
|
16
|
-
super().__init__(successor)
|
|
17
|
-
|
|
18
|
-
def command(self):
|
|
19
|
-
return Logs.COMMAND
|
|
20
|
-
|
|
21
|
-
def required(self):
|
|
22
|
-
return RequiredState.POD
|
|
23
|
-
|
|
24
|
-
def run(self, cmd: str, state: ReplState):
|
|
25
|
-
if not(args := self.args(cmd)):
|
|
26
|
-
return super().run(cmd, state)
|
|
27
|
-
|
|
28
|
-
state, args = self.apply_state(args, state)
|
|
29
|
-
if not self.validate_state(state):
|
|
30
|
-
return state
|
|
31
|
-
|
|
32
|
-
path = Config().get('logs.path', '/c3/cassandra/logs/system.log')
|
|
33
|
-
return CassandraNodes.exec(state.pod, state.namespace, f'cat {path}')
|
|
34
|
-
|
|
35
|
-
def completion(self, _: ReplState):
|
|
36
|
-
return {}
|
|
37
|
-
|
|
38
|
-
def help(self, _: ReplState):
|
|
39
|
-
return f'{Logs.COMMAND}\t show cassandra system log'
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import functools
|
|
2
|
-
import re
|
|
3
|
-
import subprocess
|
|
4
|
-
|
|
5
|
-
from adam.config import Config
|
|
6
|
-
from adam.utils_k8s.kube_context import KubeContext
|
|
7
|
-
from adam.utils_k8s.pods import Pods
|
|
8
|
-
from adam.utils_k8s.secrets import Secrets
|
|
9
|
-
from adam.utils import log2
|
|
10
|
-
|
|
11
|
-
class PostgresSession:
|
|
12
|
-
def __init__(self, ns: str, path: str):
|
|
13
|
-
self.namespace = ns
|
|
14
|
-
self.conn_details = None
|
|
15
|
-
self.host = None
|
|
16
|
-
self.db = None
|
|
17
|
-
|
|
18
|
-
if path:
|
|
19
|
-
tks = path.split('/')
|
|
20
|
-
hn = tks[0].split('@')
|
|
21
|
-
self.host = hn[0]
|
|
22
|
-
if len(hn) > 1 and not ns:
|
|
23
|
-
self.namespace = hn[1]
|
|
24
|
-
|
|
25
|
-
if len(tks) > 1:
|
|
26
|
-
self.db = tks[1]
|
|
27
|
-
|
|
28
|
-
def find_namespace(self, arg: str):
|
|
29
|
-
if arg:
|
|
30
|
-
tks = arg.split('@')
|
|
31
|
-
if len(tks) > 1:
|
|
32
|
-
return tks[1]
|
|
33
|
-
|
|
34
|
-
return None
|
|
35
|
-
|
|
36
|
-
def directory(self, arg: str = None):
|
|
37
|
-
if arg:
|
|
38
|
-
if arg == '..':
|
|
39
|
-
if self.db:
|
|
40
|
-
self.db = None
|
|
41
|
-
else:
|
|
42
|
-
self.host = None
|
|
43
|
-
else:
|
|
44
|
-
tks = arg.split('@')
|
|
45
|
-
arg = tks[0]
|
|
46
|
-
if not self.host:
|
|
47
|
-
self.host = arg
|
|
48
|
-
else:
|
|
49
|
-
self.db = arg
|
|
50
|
-
|
|
51
|
-
if not self.host:
|
|
52
|
-
return None
|
|
53
|
-
|
|
54
|
-
d = self.host
|
|
55
|
-
if not self.db:
|
|
56
|
-
return d
|
|
57
|
-
|
|
58
|
-
return f'{self.host}/{self.db}'
|
|
59
|
-
|
|
60
|
-
def hosts(ns: str):
|
|
61
|
-
return PostgresSession.hosts_for_namespace(ns)
|
|
62
|
-
|
|
63
|
-
@functools.lru_cache()
|
|
64
|
-
def hosts_for_namespace(ns: str):
|
|
65
|
-
ss = Secrets.list_secrets(ns, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
|
|
66
|
-
|
|
67
|
-
def excludes(name: str):
|
|
68
|
-
exs = Config().get('pg.excludes', '.helm., -admin-secret')
|
|
69
|
-
if exs:
|
|
70
|
-
for ex in exs.split(','):
|
|
71
|
-
if ex.strip(' ') in name:
|
|
72
|
-
return True
|
|
73
|
-
|
|
74
|
-
return False
|
|
75
|
-
|
|
76
|
-
return [s for s in ss if not excludes(s)]
|
|
77
|
-
|
|
78
|
-
def databases(self):
|
|
79
|
-
dbs = []
|
|
80
|
-
# List of databases
|
|
81
|
-
# Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
|
|
82
|
-
# ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
|
|
83
|
-
# postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
|
|
84
|
-
# stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
|
|
85
|
-
# template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
|
|
86
|
-
# | | | | | | | postgres=CTc/postgres
|
|
87
|
-
# (48 rows)
|
|
88
|
-
if r := self.run_sql('\l', show_out=False):
|
|
89
|
-
s = 0
|
|
90
|
-
for line in r.stdout.split('\n'):
|
|
91
|
-
line: str = line.strip(' \r')
|
|
92
|
-
if s == 0:
|
|
93
|
-
if 'List of databases' in line:
|
|
94
|
-
s = 1
|
|
95
|
-
elif s == 1:
|
|
96
|
-
if 'Name' in line and 'Owner' in line and 'Encoding' in line:
|
|
97
|
-
s = 2
|
|
98
|
-
elif s == 2:
|
|
99
|
-
if line.startswith('---------'):
|
|
100
|
-
s = 3
|
|
101
|
-
elif s == 3:
|
|
102
|
-
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
103
|
-
if groups and groups[1] != '|':
|
|
104
|
-
dbs.append({'name': groups[1], 'owner': groups[2]})
|
|
105
|
-
|
|
106
|
-
return dbs
|
|
107
|
-
|
|
108
|
-
def tables(self):
|
|
109
|
-
dbs = []
|
|
110
|
-
# List of relations
|
|
111
|
-
# Schema | Name | Type | Owner
|
|
112
|
-
# ----------+------------------------------------------------------------+-------+---------------
|
|
113
|
-
# postgres | c3_2_admin_aclpriv | table | postgres
|
|
114
|
-
# postgres | c3_2_admin_aclpriv_a | table | postgres
|
|
115
|
-
if r := self.run_sql('\dt', show_out=False):
|
|
116
|
-
s = 0
|
|
117
|
-
for line in r.stdout.split('\n'):
|
|
118
|
-
line: str = line.strip(' \r')
|
|
119
|
-
if s == 0:
|
|
120
|
-
if 'List of relations' in line:
|
|
121
|
-
s = 1
|
|
122
|
-
elif s == 1:
|
|
123
|
-
if 'Schema' in line and 'Name' in line and 'Type' in line:
|
|
124
|
-
s = 2
|
|
125
|
-
elif s == 2:
|
|
126
|
-
if line.startswith('---------'):
|
|
127
|
-
s = 3
|
|
128
|
-
elif s == 3:
|
|
129
|
-
groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
|
|
130
|
-
if groups and groups[1] != '|':
|
|
131
|
-
dbs.append({'schema': groups[1], 'name': groups[2]})
|
|
132
|
-
|
|
133
|
-
return dbs
|
|
134
|
-
|
|
135
|
-
def run_sql(self, sql: str, show_out = True):
|
|
136
|
-
db = self.db if self.db else PostgresSession.default_db()
|
|
137
|
-
|
|
138
|
-
if KubeContext.in_cluster():
|
|
139
|
-
cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
|
|
140
|
-
log2(f'{cmd1} "{sql}"')
|
|
141
|
-
# remove double quotes from the sql argument
|
|
142
|
-
cmd = cmd1.split(' ') + [sql]
|
|
143
|
-
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
144
|
-
if show_out:
|
|
145
|
-
log2(r.stdout)
|
|
146
|
-
log2(r.stderr)
|
|
147
|
-
|
|
148
|
-
return r
|
|
149
|
-
else:
|
|
150
|
-
ns = self.namespace
|
|
151
|
-
pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
|
|
152
|
-
|
|
153
|
-
if Config().get('pg.agent.just-in-time', False):
|
|
154
|
-
if not PostgresSession.deploy_pg_agent(pod_name, ns):
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
real_pod_name = pod_name
|
|
158
|
-
try:
|
|
159
|
-
# try with dedicated pg agent pod name configured
|
|
160
|
-
Pods.get(ns, pod_name)
|
|
161
|
-
except:
|
|
162
|
-
try:
|
|
163
|
-
# try with the ops pod
|
|
164
|
-
pod_name = Config().get('pod.name', 'ops')
|
|
165
|
-
real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
|
|
166
|
-
except:
|
|
167
|
-
log2(f"Could not locate {pod_name} pod.")
|
|
168
|
-
return None
|
|
169
|
-
|
|
170
|
-
cmd = f'PGPASSWORD="{self.password()}" psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
|
|
171
|
-
|
|
172
|
-
return Pods.exec(real_pod_name, pod_name, ns, cmd, show_out=show_out)
|
|
173
|
-
|
|
174
|
-
def deploy_pg_agent(pod_name: str, ns: str) -> str:
|
|
175
|
-
image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
|
|
176
|
-
timeout = Config().get('pg.agent.timeout', 3600)
|
|
177
|
-
try:
|
|
178
|
-
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
179
|
-
except Exception as e:
|
|
180
|
-
if e.status == 409:
|
|
181
|
-
if Pods.completed(ns, pod_name):
|
|
182
|
-
try:
|
|
183
|
-
Pods.delete(pod_name, ns)
|
|
184
|
-
Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
|
|
185
|
-
except Exception as e2:
|
|
186
|
-
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
|
|
187
|
-
|
|
188
|
-
return
|
|
189
|
-
else:
|
|
190
|
-
log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
|
|
191
|
-
|
|
192
|
-
return
|
|
193
|
-
|
|
194
|
-
Pods.wait_for_running(ns, pod_name)
|
|
195
|
-
|
|
196
|
-
return pod_name
|
|
197
|
-
|
|
198
|
-
def undeploy_pg_agent(pod_name: str, ns: str):
|
|
199
|
-
Pods.delete(pod_name, ns, grace_period_seconds=0)
|
|
200
|
-
|
|
201
|
-
def endpoint(self):
|
|
202
|
-
if not self.conn_details:
|
|
203
|
-
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
204
|
-
|
|
205
|
-
endpoint_key = Config().get('pg.secret.endpoint-key', 'postgres-db-endpoint')
|
|
206
|
-
|
|
207
|
-
return self.conn_details[endpoint_key] if endpoint_key in self.conn_details else ''
|
|
208
|
-
|
|
209
|
-
def port(self):
|
|
210
|
-
if not self.conn_details:
|
|
211
|
-
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
212
|
-
|
|
213
|
-
port_key = Config().get('pg.secret.port-key', 'postgres-db-port')
|
|
214
|
-
|
|
215
|
-
return self.conn_details[port_key] if port_key in self.conn_details else ''
|
|
216
|
-
|
|
217
|
-
def username(self):
|
|
218
|
-
if not self.conn_details:
|
|
219
|
-
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
220
|
-
|
|
221
|
-
username_key = Config().get('pg.secret.username-key', 'postgres-admin-username')
|
|
222
|
-
|
|
223
|
-
return self.conn_details[username_key] if username_key in self.conn_details else ''
|
|
224
|
-
|
|
225
|
-
def password(self):
|
|
226
|
-
if not self.conn_details:
|
|
227
|
-
self.conn_details = Secrets.get_data(self.namespace, self.host)
|
|
228
|
-
|
|
229
|
-
password_key = Config().get('pg.secret.password-key', 'postgres-admin-password')
|
|
230
|
-
|
|
231
|
-
return self.conn_details[password_key] if password_key in self.conn_details else ''
|
|
232
|
-
|
|
233
|
-
def default_db():
|
|
234
|
-
return Config().get('pg.default-db', 'postgres')
|
|
235
|
-
|
|
236
|
-
def default_owner():
|
|
237
|
-
return Config().get('pg.default-owner', 'postgres')
|
|
238
|
-
|
|
239
|
-
def default_schema():
|
|
240
|
-
return Config().get('pg.default-schema', 'postgres')
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import functools
|
|
2
|
-
|
|
3
|
-
from adam.commands.postgres.postgres_session import PostgresSession
|
|
4
|
-
from adam.config import Config
|
|
5
|
-
|
|
6
|
-
TestPG = [False]
|
|
7
|
-
|
|
8
|
-
@functools.lru_cache()
|
|
9
|
-
def pg_database_names(ns: str, pg_path: str):
|
|
10
|
-
if TestPG[0]:
|
|
11
|
-
return ['azops88_c3ai_c3']
|
|
12
|
-
|
|
13
|
-
Config().wait_log('Inspecting Postgres Databases...')
|
|
14
|
-
|
|
15
|
-
pg = PostgresSession(ns, pg_path)
|
|
16
|
-
return [db['name'] for db in pg.databases() if db['owner'] == PostgresSession.default_owner()]
|
|
17
|
-
|
|
18
|
-
@functools.lru_cache()
|
|
19
|
-
def pg_table_names(ns: str, pg_path: str):
|
|
20
|
-
if TestPG[0]:
|
|
21
|
-
return ['C3_2_XYZ1']
|
|
22
|
-
|
|
23
|
-
Config().wait_log('Inspecting Postgres Database...')
|
|
24
|
-
return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresSession.default_schema()]
|
|
25
|
-
|
|
26
|
-
def pg_tables(ns: str, pg_path: str):
|
|
27
|
-
pg = PostgresSession(ns, pg_path)
|
|
28
|
-
if pg.db:
|
|
29
|
-
return pg.tables()
|
|
30
|
-
|
|
31
|
-
return []
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
from adam.commands.postgres.postgres_utils import pg_table_names
|
|
2
|
-
from adam.sql.sql_completer import SqlCompleter
|
|
3
|
-
|
|
4
|
-
def psql_completions(ns: str, pg_path: str):
|
|
5
|
-
return {
|
|
6
|
-
'\h': None,
|
|
7
|
-
'\d': None,
|
|
8
|
-
'\dt': None,
|
|
9
|
-
'\du': None
|
|
10
|
-
} | SqlCompleter.completions(lambda: pg_table_names(ns, pg_path))
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from adam.commands.postgres.postgres_utils import pg_table_names
|
|
2
|
-
from adam.sql.term_completer import TermCompleter
|
|
3
|
-
|
|
4
|
-
class PsqlTableNameCompleter(TermCompleter):
|
|
5
|
-
def __init__(self, namespace: str, pg_path: str, ignore_case: bool = True):
|
|
6
|
-
super().__init__(pg_table_names(namespace, pg_path), ignore_case=ignore_case)
|
|
7
|
-
self.namespace = namespace
|
|
8
|
-
self.pg_path = pg_path
|
|
9
|
-
|
|
10
|
-
def __repr__(self) -> str:
|
|
11
|
-
return "PsqlTableCompleter(%r, pg_path=%r)" % (self.namespace, self.pg_path)
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
2
|
-
import threading
|
|
3
|
-
from kubernetes import client
|
|
4
|
-
import portforward
|
|
5
|
-
import re
|
|
6
|
-
import requests
|
|
7
|
-
from typing import List, cast
|
|
8
|
-
|
|
9
|
-
from adam.config import Config
|
|
10
|
-
from adam.utils_k8s.kube_context import KubeContext
|
|
11
|
-
from adam.repl_state import ReplState
|
|
12
|
-
from adam.utils import lines_to_tabular, log2
|
|
13
|
-
|
|
14
|
-
class ReaperSession:
|
|
15
|
-
is_forwarding = False
|
|
16
|
-
stopping = threading.Event()
|
|
17
|
-
schedules_ids_by_cluster: dict[str, list[str]] = {}
|
|
18
|
-
|
|
19
|
-
def __init__(self, pod: str, headers: dict[str, str] = None):
|
|
20
|
-
self.pod = pod
|
|
21
|
-
self.headers = headers
|
|
22
|
-
|
|
23
|
-
def login(self, state: ReplState, local_addr: str, remote_addr: str, show_output = True) -> str :
|
|
24
|
-
user, pw = state.user_pass(secret_path='reaper.secret')
|
|
25
|
-
|
|
26
|
-
response = requests.post(f'http://{local_addr}/login', headers={
|
|
27
|
-
'Accept': '*'
|
|
28
|
-
},data={
|
|
29
|
-
'username':user,
|
|
30
|
-
'password':pw})
|
|
31
|
-
if show_output:
|
|
32
|
-
log2(f'POST {remote_addr}/login')
|
|
33
|
-
log2(f' username={user}&password={pw}')
|
|
34
|
-
|
|
35
|
-
if int(response.status_code / 100) != 2:
|
|
36
|
-
if show_output:
|
|
37
|
-
log2("login failed")
|
|
38
|
-
return None
|
|
39
|
-
|
|
40
|
-
return response.headers['Set-Cookie']
|
|
41
|
-
|
|
42
|
-
def port_forwarded(self, state: ReplState, path: str, body: Callable[[str, dict[str, str]], requests.Response], method: str = None, show_output = True):
|
|
43
|
-
local_port = Config().get('reaper.port-forward.local-port', 9001)
|
|
44
|
-
target_port = 8080
|
|
45
|
-
|
|
46
|
-
def f(local_addr: str, remote_addr: str):
|
|
47
|
-
if not self.headers:
|
|
48
|
-
self.headers = self.cookie_header(state, local_addr, remote_addr, show_output=show_output)
|
|
49
|
-
|
|
50
|
-
if show_output and method:
|
|
51
|
-
log2(f'{method} {remote_addr}/{path}')
|
|
52
|
-
response = body(f'http://{local_addr}/{path}', self.headers)
|
|
53
|
-
|
|
54
|
-
if response:
|
|
55
|
-
if int(response.status_code / 100) != 2:
|
|
56
|
-
if show_output:
|
|
57
|
-
log2(response.status_code)
|
|
58
|
-
return response
|
|
59
|
-
|
|
60
|
-
if show_output:
|
|
61
|
-
log2()
|
|
62
|
-
|
|
63
|
-
return response if response else 'no-response'
|
|
64
|
-
|
|
65
|
-
if KubeContext.in_cluster():
|
|
66
|
-
# cs-a526330d23-cs-a526330d23-default-sts-0 ->
|
|
67
|
-
# curl http://cs-a526330d23-cs-a526330d23-reaper-service.stgawsscpsr.svc.cluster.local:8080
|
|
68
|
-
groups = re.match(r'^(.*?-.*?-.*?-.*?-).*', state.sts)
|
|
69
|
-
if groups:
|
|
70
|
-
svc_name = Config().get('reaper.service-name', 'reaper-service')
|
|
71
|
-
svc = f'{groups[1]}{svc_name}.{state.namespace}.svc.cluster.local:{target_port}'
|
|
72
|
-
return f(local_addr=svc, remote_addr=svc)
|
|
73
|
-
else:
|
|
74
|
-
return None
|
|
75
|
-
else:
|
|
76
|
-
with portforward.forward(state.namespace, self.pod, local_port, target_port):
|
|
77
|
-
return f(local_addr=f'localhost:{local_port}', remote_addr=f'{self.pod}:{target_port}')
|
|
78
|
-
|
|
79
|
-
def cookie_header(self, state: ReplState, local_addr, remote_addr, show_output = True):
|
|
80
|
-
return {'Cookie': self.login(state, local_addr, remote_addr, show_output=show_output)}
|
|
81
|
-
|
|
82
|
-
def create(state: ReplState) -> 'ReaperSession':
|
|
83
|
-
pods = ReaperSession.list_reaper_pods(state.sts if state.sts else state.pod, state.namespace)
|
|
84
|
-
if pods:
|
|
85
|
-
return ReaperSession(pods[0].metadata.name)
|
|
86
|
-
else:
|
|
87
|
-
log2('No reaper found.')
|
|
88
|
-
|
|
89
|
-
return None
|
|
90
|
-
|
|
91
|
-
def list_reaper_pods(sts_name: str, namespace: str) -> List[client.V1Pod]:
|
|
92
|
-
v1 = client.CoreV1Api()
|
|
93
|
-
|
|
94
|
-
# k8ssandra.io/reaper: cs-d0767a536f-cs-d0767a536f-reaper
|
|
95
|
-
groups = re.match(Config().get('reaper.pod.cluster-regex', r'(.*?-.*?-.*?-.*?)-.*'), sts_name)
|
|
96
|
-
label_selector = Config().get('reaper.pod.label-selector', 'k8ssandra.io/reaper={cluster}-reaper').replace('{cluster}', groups[1])
|
|
97
|
-
|
|
98
|
-
return cast(List[client.V1Pod], v1.list_namespaced_pod(namespace, label_selector=label_selector).items)
|
|
99
|
-
|
|
100
|
-
def show_schedules(self, state: ReplState, filter: Callable[[list[dict]], dict] = None):
|
|
101
|
-
schedules = self.list_schedules(state, filter=filter)
|
|
102
|
-
# forced refresh of schedule list
|
|
103
|
-
if not filter:
|
|
104
|
-
self.schedules_ids_by_cluster[state.sts] = [schedule['id'] for schedule in schedules]
|
|
105
|
-
self.show_schedules_tabular(schedules)
|
|
106
|
-
|
|
107
|
-
def schedule_ids(self, state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None):
|
|
108
|
-
schedules = self.list_schedules(state, show_output=show_output, filter=filter)
|
|
109
|
-
return [schedule['id'] for schedule in schedules]
|
|
110
|
-
|
|
111
|
-
def list_schedules(self, state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None) -> list[dict]:
|
|
112
|
-
def body(uri: str, headers: dict[str, str]):
|
|
113
|
-
return requests.get(uri, headers=headers)
|
|
114
|
-
|
|
115
|
-
response = self.port_forwarded(state, 'repair_schedule', body, method='GET', show_output=show_output)
|
|
116
|
-
if not response:
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
res = response.json()
|
|
120
|
-
if filter:
|
|
121
|
-
res = filter(res)
|
|
122
|
-
|
|
123
|
-
return res
|
|
124
|
-
|
|
125
|
-
def show_schedules_tabular(self, schedules: list[dict]):
|
|
126
|
-
log2(lines_to_tabular([f"{schedule['id']} {schedule['state']} {schedule['cluster_name']} {schedule['keyspace_name']}" for schedule in schedules], 'ID STATE CLUSTER KEYSPACE'))
|
|
127
|
-
|
|
128
|
-
def show_schedule(self, state: ReplState, schedule_id: str):
|
|
129
|
-
def filter(schedules: list[dict]):
|
|
130
|
-
return [schedule for schedule in schedules if schedule['id'] == schedule_id]
|
|
131
|
-
|
|
132
|
-
self.show_schedules(state, filter)
|
|
133
|
-
|
|
134
|
-
def reaper_spec(self, state: ReplState) -> dict[str, any]:
|
|
135
|
-
user, pw = state.user_pass(secret_path='reaper.secret')
|
|
136
|
-
local_port = Config().get('reaper.port-forward.local-port', 9001)
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
'pod': self.pod,
|
|
140
|
-
'exec': f'kubectl exec -it {self.pod} -n {state.namespace} -- bash',
|
|
141
|
-
'forward': f'kubectl port-forward pods/{self.pod} -n {state.namespace} {local_port}:8080',
|
|
142
|
-
'web-uri': f'http://localhost:{local_port}/webui',
|
|
143
|
-
'username': user,
|
|
144
|
-
'password': pw
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
def cached_schedule_ids(state: ReplState) -> list[str]:
|
|
148
|
-
if state.sts in ReaperSession.schedules_ids_by_cluster:
|
|
149
|
-
return ReaperSession.schedules_ids_by_cluster[state.sts]
|
|
150
|
-
|
|
151
|
-
if reaper := ReaperSession.create(state):
|
|
152
|
-
Config().wait_log('Inspecting Cassandra Reaper...')
|
|
153
|
-
|
|
154
|
-
schedules = reaper.schedule_ids(state, show_output = False)
|
|
155
|
-
ReaperSession.schedules_ids_by_cluster[state.sts] = schedules
|
|
156
|
-
|
|
157
|
-
return schedules
|
|
158
|
-
|
|
159
|
-
return []
|
adam/commands/report.py
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
from adam.checks.check_result import CheckResult
|
|
5
|
-
from adam.checks.check_utils import run_checks
|
|
6
|
-
from adam.commands.command import Command
|
|
7
|
-
from adam.repl_state import ReplState
|
|
8
|
-
from adam.utils import log2
|
|
9
|
-
|
|
10
|
-
class Report(Command):
|
|
11
|
-
COMMAND = 'report'
|
|
12
|
-
|
|
13
|
-
# the singleton pattern
|
|
14
|
-
def __new__(cls, *args, **kwargs):
|
|
15
|
-
if not hasattr(cls, 'instance'): cls.instance = super(Report, cls).__new__(cls)
|
|
16
|
-
|
|
17
|
-
return cls.instance
|
|
18
|
-
|
|
19
|
-
def __init__(self, successor: Command=None):
|
|
20
|
-
super().__init__(successor)
|
|
21
|
-
|
|
22
|
-
def command(self):
|
|
23
|
-
return Report.COMMAND
|
|
24
|
-
|
|
25
|
-
def run(self, cmd: str, state: ReplState):
|
|
26
|
-
if not(args := self.args(cmd)):
|
|
27
|
-
return super().run(cmd, state)
|
|
28
|
-
|
|
29
|
-
output: dict[str, any] = {}
|
|
30
|
-
state, args = self.apply_state(args, state)
|
|
31
|
-
if state.in_repl:
|
|
32
|
-
args, show = Command.extract_options(args, ['-s', '--show'])
|
|
33
|
-
|
|
34
|
-
args, redirect = Command.extract_options(args, ['>'])
|
|
35
|
-
if not redirect or not args:
|
|
36
|
-
log2('Please specify file name: e.g. report > /tmp/report.log')
|
|
37
|
-
return 'no-report-destination'
|
|
38
|
-
|
|
39
|
-
results = run_checks(state.sts, state.namespace, state.pod, show_output=show)
|
|
40
|
-
output = CheckResult.report(results)
|
|
41
|
-
with open(args[0], "w") as json_file:
|
|
42
|
-
json.dump(output, json_file, indent=2)
|
|
43
|
-
log2(f'Report stored in {args[0]}.')
|
|
44
|
-
else:
|
|
45
|
-
args, show = Command.extract_options(args, ['-s', '--show'])
|
|
46
|
-
|
|
47
|
-
results = run_checks(state.sts, state.namespace, state.pod, show_output=show)
|
|
48
|
-
output = CheckResult.report(results)
|
|
49
|
-
click.echo(json.dumps(output, indent=2))
|
|
50
|
-
|
|
51
|
-
return output
|
|
52
|
-
|
|
53
|
-
def completion(self, _: ReplState):
|
|
54
|
-
return {Report.COMMAND: {">": None}}
|
|
55
|
-
|
|
56
|
-
def help(self, _: ReplState):
|
|
57
|
-
return f"{Report.COMMAND} > <file-name>\t generate report"
|