kaqing 2.0.174__py3-none-any.whl → 2.0.186__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.

Files changed (134) hide show
  1. adam/app_session.py +2 -2
  2. adam/apps.py +18 -4
  3. adam/batch.py +1 -1
  4. adam/checks/check_utils.py +3 -1
  5. adam/commands/__init__.py +4 -2
  6. adam/commands/app/__init__.py +0 -0
  7. adam/commands/app/app.py +38 -0
  8. adam/commands/app/app_ping.py +38 -0
  9. adam/commands/app/show_app_actions.py +49 -0
  10. adam/commands/app/show_app_id.py +44 -0
  11. adam/commands/app/show_app_queues.py +38 -0
  12. adam/commands/app/utils_app.py +106 -0
  13. adam/commands/audit/audit.py +9 -27
  14. adam/commands/audit/audit_repair_tables.py +5 -7
  15. adam/commands/audit/audit_run.py +1 -1
  16. adam/commands/audit/completions_l.py +15 -0
  17. adam/commands/audit/show_last10.py +2 -14
  18. adam/commands/audit/show_slow10.py +2 -13
  19. adam/commands/audit/show_top10.py +2 -11
  20. adam/commands/audit/utils_show_top10.py +14 -1
  21. adam/commands/bash/bash.py +1 -1
  22. adam/commands/cat.py +3 -7
  23. adam/commands/check.py +2 -2
  24. adam/commands/cli_commands.py +6 -1
  25. adam/commands/{cp.py → clipboard_copy.py} +18 -12
  26. adam/commands/code.py +2 -2
  27. adam/commands/command.py +61 -11
  28. adam/commands/commands_utils.py +19 -12
  29. adam/commands/cql/completions_c.py +28 -0
  30. adam/commands/cql/cqlsh.py +3 -7
  31. adam/commands/cql/utils_cql.py +22 -58
  32. adam/commands/deploy/deploy_pg_agent.py +2 -2
  33. adam/commands/deploy/undeploy_pg_agent.py +2 -2
  34. adam/commands/devices/device.py +39 -8
  35. adam/commands/devices/device_app.py +18 -28
  36. adam/commands/devices/device_auit_log.py +3 -3
  37. adam/commands/devices/device_cass.py +16 -22
  38. adam/commands/devices/device_export.py +6 -3
  39. adam/commands/devices/device_postgres.py +79 -63
  40. adam/commands/download_file.py +47 -0
  41. adam/commands/export/clean_up_all_export_sessions.py +3 -3
  42. adam/commands/export/clean_up_export_sessions.py +5 -10
  43. adam/commands/export/completions_x.py +11 -0
  44. adam/commands/export/download_export_session.py +40 -0
  45. adam/commands/export/export.py +0 -16
  46. adam/commands/export/export_databases.py +26 -9
  47. adam/commands/export/export_select.py +9 -58
  48. adam/commands/export/export_sessions.py +90 -5
  49. adam/commands/export/export_use.py +13 -10
  50. adam/commands/export/export_x_select.py +48 -0
  51. adam/commands/export/exporter.py +60 -22
  52. adam/commands/export/import_files.py +44 -0
  53. adam/commands/export/import_session.py +8 -4
  54. adam/commands/export/importer.py +7 -0
  55. adam/commands/export/importer_athena.py +101 -34
  56. adam/commands/export/importer_sqlite.py +30 -5
  57. adam/commands/export/show_column_counts.py +11 -11
  58. adam/commands/export/show_export_databases.py +5 -3
  59. adam/commands/export/show_export_session.py +5 -6
  60. adam/commands/export/show_export_sessions.py +4 -11
  61. adam/commands/export/utils_export.py +42 -14
  62. adam/commands/find_files.py +51 -0
  63. adam/commands/find_processes.py +76 -0
  64. adam/commands/head.py +36 -0
  65. adam/commands/help.py +2 -2
  66. adam/commands/intermediate_command.py +6 -3
  67. adam/commands/ls.py +1 -1
  68. adam/commands/medusa/medusa_backup.py +12 -14
  69. adam/commands/medusa/medusa_restore.py +20 -15
  70. adam/commands/medusa/medusa_show_backupjobs.py +6 -4
  71. adam/commands/medusa/medusa_show_restorejobs.py +5 -3
  72. adam/commands/medusa/utils_medusa.py +15 -0
  73. adam/commands/nodetool.py +3 -8
  74. adam/commands/param_get.py +2 -3
  75. adam/commands/param_set.py +1 -1
  76. adam/commands/postgres/completions_p.py +22 -0
  77. adam/commands/postgres/postgres.py +14 -21
  78. adam/commands/postgres/postgres_databases.py +270 -0
  79. adam/commands/postgres/utils_postgres.py +29 -20
  80. adam/commands/preview_table.py +3 -1
  81. adam/commands/pwd.py +3 -3
  82. adam/commands/reaper/reaper_forward.py +2 -2
  83. adam/commands/reaper/reaper_runs.py +3 -3
  84. adam/commands/reaper/reaper_schedule_activate.py +6 -2
  85. adam/commands/reaper/reaper_schedule_start.py +1 -2
  86. adam/commands/reaper/reaper_schedule_stop.py +1 -2
  87. adam/commands/reaper/utils_reaper.py +13 -6
  88. adam/commands/repair/repair_scan.py +0 -2
  89. adam/commands/repair/repair_stop.py +0 -1
  90. adam/commands/shell.py +7 -5
  91. adam/commands/show/show.py +1 -1
  92. adam/commands/show/show_adam.py +3 -3
  93. adam/commands/show/show_cassandra_repairs.py +5 -3
  94. adam/commands/show/show_cassandra_status.py +27 -20
  95. adam/commands/show/{show_commands.py → show_cli_commands.py} +2 -2
  96. adam/commands/show/show_login.py +2 -2
  97. adam/commands/show/show_params.py +2 -5
  98. adam/commands/show/show_processes.py +15 -14
  99. adam/commands/show/show_storage.py +9 -8
  100. adam/config.py +1 -0
  101. adam/embedded_params.py +1 -1
  102. adam/repl.py +16 -9
  103. adam/repl_commands.py +16 -9
  104. adam/repl_session.py +8 -1
  105. adam/repl_state.py +33 -10
  106. adam/sql/lark_completer.py +280 -0
  107. adam/sql/lark_parser.py +604 -0
  108. adam/sql/sql_state_machine.py +8 -2
  109. adam/utils.py +116 -29
  110. adam/utils_athena.py +7 -8
  111. adam/utils_issues.py +2 -2
  112. adam/utils_k8s/app_clusters.py +2 -2
  113. adam/utils_k8s/app_pods.py +5 -2
  114. adam/utils_k8s/cassandra_clusters.py +11 -3
  115. adam/utils_k8s/cassandra_nodes.py +2 -2
  116. adam/utils_k8s/k8s.py +9 -0
  117. adam/utils_k8s/kube_context.py +2 -2
  118. adam/utils_k8s/pods.py +23 -5
  119. adam/utils_k8s/statefulsets.py +5 -2
  120. adam/utils_local.py +4 -0
  121. adam/utils_repl/appendable_completer.py +6 -0
  122. adam/utils_repl/repl_completer.py +128 -2
  123. adam/utils_sqlite.py +2 -2
  124. adam/version.py +1 -1
  125. {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/METADATA +1 -1
  126. kaqing-2.0.186.dist-info/RECORD +250 -0
  127. adam/commands/cql/cql_completions.py +0 -32
  128. adam/commands/export/export_select_x.py +0 -54
  129. adam/commands/postgres/postgres_context.py +0 -272
  130. adam/commands/postgres/psql_completions.py +0 -10
  131. kaqing-2.0.174.dist-info/RECORD +0 -230
  132. {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/WHEEL +0 -0
  133. {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/entry_points.txt +0 -0
  134. {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/top_level.txt +0 -0
@@ -1,36 +1,45 @@
1
1
  import functools
2
2
 
3
- from adam.commands.postgres.postgres_context import PostgresContext
3
+ from adam.commands.postgres.postgres_databases import PostgresDatabases, pg_path
4
4
  from adam.repl_state import ReplState
5
5
  from adam.utils import log2, wait_log
6
6
  from adam.utils_k8s.pods import Pods
7
7
 
8
8
  TestPG = [False]
9
9
 
10
+ def direct_dirs(state: ReplState) -> list[str]:
11
+ with pg_path(state) as (host, database):
12
+ if database:
13
+ return ['..']
14
+ elif host:
15
+ return ['..'] + pg_database_names(state)
16
+ else:
17
+ return PostgresDatabases.host_names(state.namespace)
18
+
19
+ def pg_database_names(state: ReplState):
20
+ # cache on pg_path
21
+ return _pg_database_names(state, state.pg_path)
22
+
10
23
  @functools.lru_cache()
11
- def pg_database_names(ns: str, pg_path: str):
24
+ def _pg_database_names(state: ReplState, pg_path: str):
12
25
  if TestPG[0]:
13
26
  return ['azops88_c3ai_c3']
14
27
 
15
28
  wait_log('Inspecting Postgres Databases...')
16
29
 
17
- pg = PostgresContext.apply(ns, pg_path)
18
- return [db['name'] for db in pg.databases() if db['owner'] == PostgresContext.default_owner()]
30
+ return [db['name'] for db in PostgresDatabases.databases(state, default_owner=True)]
31
+
32
+ def pg_table_names(state: ReplState):
33
+ # cache on pg_path
34
+ return _pg_table_names(state, state.pg_path)
19
35
 
20
36
  @functools.lru_cache()
21
- def pg_table_names(ns: str, pg_path: str):
37
+ def _pg_table_names(state: ReplState, pg_path: str):
22
38
  if TestPG[0]:
23
39
  return ['C3_2_XYZ1']
24
40
 
25
41
  wait_log('Inspecting Postgres Database...')
26
- return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresContext.default_schema()]
27
-
28
- def pg_tables(ns: str, pg_path: str):
29
- pg = PostgresContext.apply(ns, pg_path)
30
- if pg.db:
31
- return pg.tables()
32
-
33
- return []
42
+ return [table['name'] for table in PostgresDatabases.tables(state, default_schema=True)]
34
43
 
35
44
  class PostgresPodService:
36
45
  def __init__(self, handler: 'PostgresExecHandler'):
@@ -39,26 +48,26 @@ class PostgresPodService:
39
48
  def exec(self, command: str, show_out=True):
40
49
  state = self.handler.state
41
50
 
42
- pod, container = PostgresContext.pod_and_container(state.namespace)
51
+ pod, container = PostgresDatabases.pod_and_container(state.namespace)
43
52
  if not pod:
44
53
  log2('Cannot locate postgres agent or ops pod.')
45
54
  return state
46
55
 
47
56
  return Pods.exec(pod, container, state.namespace, command, show_out=show_out)
48
57
 
49
- def sql(self, args: list[str], background=False):
58
+ def sql(self, args: list[str], backgrounded=False):
50
59
  state = self.handler.state
51
60
 
52
61
  query = args
53
62
  if isinstance(args, list):
54
63
  query = ' '.join(args)
55
64
 
56
- PostgresContext.apply(state.namespace, state.pg_path).run_sql(query, background=background)
65
+ PostgresDatabases.run_sql(state, query, backgrounded=backgrounded)
57
66
 
58
67
  class PostgresExecHandler:
59
- def __init__(self, state: ReplState, background=False):
68
+ def __init__(self, state: ReplState, backgrounded=False):
60
69
  self.state = state
61
- self.background = background
70
+ self.backgrounded = backgrounded
62
71
 
63
72
  def __enter__(self):
64
73
  return PostgresPodService(self)
@@ -66,5 +75,5 @@ class PostgresExecHandler:
66
75
  def __exit__(self, exc_type, exc_val, exc_tb):
67
76
  return False
68
77
 
69
- def postgres(state: ReplState, background=False):
70
- return PostgresExecHandler(state, background=background)
78
+ def postgres(state: ReplState, backgrounded=False):
79
+ return PostgresExecHandler(state, backgrounded=backgrounded)
@@ -1,3 +1,4 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
3
  from adam.commands.devices.devices import Devices
3
4
  from adam.repl_state import ReplState, RequiredState
@@ -25,7 +26,8 @@ class PreviewTable(Command):
25
26
  return super().run(cmd, state)
26
27
 
27
28
  with self.validate(args, state) as (args, state):
28
- Devices.device(state).preview(args[0] if args else None, state)
29
+ with validate_args(args, state, at_least=1) as table:
30
+ Devices.device(state).preview(table, state)
29
31
 
30
32
  return state
31
33
 
adam/commands/pwd.py CHANGED
@@ -2,7 +2,7 @@ from adam.app_session import AppSession
2
2
  from adam.commands.command import Command
3
3
  from adam.commands.devices.devices import Devices
4
4
  from adam.repl_state import ReplState
5
- from adam.utils import lines_to_tabular, log, log_exc
5
+ from adam.utils import tabulize, log, log_exc
6
6
 
7
7
  class Pwd(Command):
8
8
  COMMAND = 'pwd'
@@ -29,11 +29,11 @@ class Pwd(Command):
29
29
  app_session: AppSession = AppSession.create('c3', 'c3')
30
30
  host = app_session.host
31
31
 
32
- log(lines_to_tabular([device.pwd(state) for device in Devices.all()] + [
32
+ tabulize([device.pwd(state) for device in Devices.all()] + [
33
33
  f'',
34
34
  f'HOST\t{host}',
35
35
  f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
36
- ], 'DEVICE\tLOCATION', separator='\t'))
36
+ ], header='DEVICE\tLOCATION', separator='\t')
37
37
  log()
38
38
 
39
39
  return state
@@ -8,7 +8,7 @@ from adam.commands.reaper.utils_reaper import Reapers, port_forwarding
8
8
  from adam.config import Config
9
9
  from adam.repl_session import ReplSession
10
10
  from adam.repl_state import ReplState, RequiredState
11
- from adam.utils import lines_to_tabular, log2
11
+ from adam.utils import tabulize, log2
12
12
 
13
13
  class ReaperForward(Command):
14
14
  COMMAND = 'reaper forward'
@@ -56,7 +56,7 @@ class ReaperForward(Command):
56
56
  'reaper-password': spec["password"]
57
57
  }
58
58
  log2()
59
- log2(lines_to_tabular([f'{k},{v}' for k, v in d.items()], separator=','))
59
+ tabulize(d.items(), lambda a: f'{a[0]},{a[1]}', separator=',')
60
60
 
61
61
  for k, v in d.items():
62
62
  ReplSession().prompt_session.history.append_string(f'cp {k}')
@@ -2,7 +2,7 @@ from adam.commands.command import Command
2
2
  from adam.commands.reaper.utils_reaper import reaper
3
3
  from adam.config import Config
4
4
  from adam.repl_state import ReplState, RequiredState
5
- from adam.utils import convert_seconds, epoch, lines_to_tabular, log, log2
5
+ from adam.utils import convert_seconds, epoch, tabulize, log, log2
6
6
 
7
7
  class ReaperRuns(Command):
8
8
  COMMAND = 'reaper show runs'
@@ -54,7 +54,7 @@ class ReaperRuns(Command):
54
54
 
55
55
  runs = response.json()
56
56
  if runs:
57
- log(lines_to_tabular(sorted([line(run) for run in runs], reverse=True), header, separator=","))
57
+ tabulize(sorted([line(run) for run in runs], reverse=True), header=header, separator=",")
58
58
  else:
59
59
  log2('No running runs found.')
60
60
  log2()
@@ -66,7 +66,7 @@ class ReaperRuns(Command):
66
66
 
67
67
  runs = response.json()
68
68
  if runs:
69
- log(lines_to_tabular(sorted([line(run) for run in runs], reverse=True), header, separator=","))
69
+ tabulize(sorted([line(run) for run in runs], reverse=True), header=header, separator=",")
70
70
  else:
71
71
  log2('No runs found.')
72
72
 
@@ -2,7 +2,11 @@ from adam.commands import validate_args
2
2
  from adam.commands.command import Command
3
3
  from adam.commands.reaper.utils_reaper import Reapers, reaper
4
4
  from adam.repl_state import ReplState, RequiredState
5
- from adam.utils import log2
5
+
6
+ import nest_asyncio
7
+ nest_asyncio.apply()
8
+
9
+ import asyncio
6
10
 
7
11
  class ReaperScheduleActivate(Command):
8
12
  COMMAND = 'reaper activate schedule'
@@ -35,7 +39,7 @@ class ReaperScheduleActivate(Command):
35
39
  return schedule_id
36
40
 
37
41
  def completion(self, state: ReplState):
38
- return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)})
42
+ return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)}, auto_key='reaper.schedules')
39
43
 
40
44
  def help(self, _: ReplState):
41
45
  return f'{ReaperScheduleActivate.COMMAND} <schedule-id>\t resume reaper schedule'
@@ -2,7 +2,6 @@ from adam.commands import validate_args
2
2
  from adam.commands.command import Command
3
3
  from adam.commands.reaper.utils_reaper import Reapers, reaper
4
4
  from adam.repl_state import ReplState, RequiredState
5
- from adam.utils import log2
6
5
 
7
6
  class ReaperScheduleStart(Command):
8
7
  COMMAND = 'reaper start schedule'
@@ -35,7 +34,7 @@ class ReaperScheduleStart(Command):
35
34
  return schedule_id
36
35
 
37
36
  def completion(self, state: ReplState):
38
- return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)})
37
+ return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)}, auto_key='reaper.schedules')
39
38
 
40
39
  def help(self, _: ReplState):
41
40
  return f'{ReaperScheduleStart.COMMAND} <schedule-id>\t start reaper runs for schedule'
@@ -2,7 +2,6 @@ from adam.commands import validate_args
2
2
  from adam.commands.command import Command
3
3
  from adam.commands.reaper.utils_reaper import Reapers, reaper
4
4
  from adam.repl_state import ReplState, RequiredState
5
- from adam.utils import log2
6
5
 
7
6
  class ReaperScheduleStop(Command):
8
7
  COMMAND = 'reaper stop schedule'
@@ -35,7 +34,7 @@ class ReaperScheduleStop(Command):
35
34
  return schedule_id
36
35
 
37
36
  def completion(self, state: ReplState):
38
- return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)})
37
+ return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)}, auto_key='reaper.schedules')
39
38
 
40
39
  def help(self, _: ReplState):
41
40
  return f'{ReaperScheduleStop.COMMAND} <schedule-id>\t pause reaper schedule'
@@ -7,7 +7,7 @@ import re
7
7
  import requests
8
8
  from adam.config import Config
9
9
  from adam.repl_state import ReplState
10
- from adam.utils import lines_to_tabular, log2, wait_log
10
+ from adam.utils import tabulize, log2, wait_log
11
11
  from adam.utils_k8s.k8s import port_forwarding
12
12
 
13
13
  class ReaperService:
@@ -100,7 +100,8 @@ class Reapers:
100
100
  # forced refresh of schedule list
101
101
  if not filter:
102
102
  Reapers.schedules_ids_by_cluster[state.sts] = [schedule['id'] for schedule in schedules]
103
- Reapers.show_schedules_tabular(schedules)
103
+
104
+ tabulize(schedules, lambda s: f"{s['id']} {s['state']} {s['cluster_name']} {s['keyspace_name']}", header='ID STATE CLUSTER KEYSPACE', to=2)
104
105
 
105
106
  def schedule_ids(state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None):
106
107
  schedules = Reapers.list_schedules(state, show_output=show_output, filter=filter)
@@ -117,9 +118,6 @@ class Reapers:
117
118
 
118
119
  return res
119
120
 
120
- def show_schedules_tabular(schedules: list[dict]):
121
- log2(lines_to_tabular([f"{schedule['id']} {schedule['state']} {schedule['cluster_name']} {schedule['keyspace_name']}" for schedule in schedules], 'ID STATE CLUSTER KEYSPACE'))
122
-
123
121
  def list_reaper_pods(sts_name: str, namespace: str) -> List[client.V1Pod]:
124
122
  v1 = client.CoreV1Api()
125
123
 
@@ -193,4 +191,13 @@ class Reapers:
193
191
  if is_service:
194
192
  return Reapers.svc_name()
195
193
  else:
196
- return Reapers.pod_name(state)
194
+ return Reapers.pod_name(state)
195
+
196
+ def schedules_auto_completion(ids: callable):
197
+ auto = Config().get('reaper.schedules-auto-complete', 'off')
198
+
199
+ leaf = None
200
+ if auto == 'on':
201
+ leaf = {id: None for id in ids()}
202
+
203
+ return (leaf, auto == 'lazy')
@@ -1,5 +1,3 @@
1
- import time
2
-
3
1
  from adam.commands.command import Command
4
2
  from adam.utils_k8s.pods import Pods
5
3
  from adam.repl_state import ReplState, RequiredState
@@ -1,7 +1,6 @@
1
1
  from adam.commands.command import Command
2
2
  from adam.utils_k8s.jobs import Jobs
3
3
  from adam.repl_state import ReplState, RequiredState
4
- from adam.config import Config
5
4
 
6
5
  class RepairStop(Command):
7
6
  COMMAND = 'repair stop'
adam/commands/shell.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import os
2
2
 
3
+ from adam.commands import validate_args
3
4
  from adam.commands.command import Command
4
5
  from adam.repl_state import ReplState
5
6
  from adam.utils import log2
@@ -24,11 +25,12 @@ class Shell(Command):
24
25
  return super().run(cmd, state)
25
26
 
26
27
  with self.validate(args, state) as (args, _):
27
- if args:
28
- os.system(' '.join(args))
29
- log2()
30
- else:
31
- os.system('QING_DROPPED=true bash')
28
+ with validate_args(args, state, at_least=0) as args_str:
29
+ if args_str:
30
+ os.system(args_str)
31
+ log2()
32
+ else:
33
+ os.system('QING_DROPPED=true bash')
32
34
 
33
35
  return state
34
36
 
@@ -11,7 +11,7 @@ from adam.commands.show.show_login import ShowLogin
11
11
  from .show_params import ShowParams
12
12
  from .show_cassandra_status import ShowCassandraStatus
13
13
  from .show_cassandra_version import ShowCassandraVersion
14
- from .show_commands import ShowKubectlCommands
14
+ from .show_cli_commands import ShowKubectlCommands
15
15
  from .show_processes import ShowProcesses
16
16
  from .show_cassandra_repairs import ShowCassandraRepairs
17
17
  from .show_storage import ShowStorage
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  import os
3
3
 
4
- from adam.utils import lines_to_tabular, log2
4
+ from adam.utils import tabulize, log2
5
5
 
6
6
  current_dir = os.path.dirname(os.path.abspath(__file__))
7
7
 
@@ -34,10 +34,10 @@ class ShowAdam(Command):
34
34
 
35
35
  package = os.path.dirname(os.path.abspath(__file__))
36
36
  package = package.split('/adam/')[0] + '/adam'
37
- log2(lines_to_tabular([
37
+ tabulize([
38
38
  f'version\t{__version__}',
39
39
  f'source\t{package}'
40
- ], separator='\t'))
40
+ ], separator='\t', to=2)
41
41
 
42
42
  return state
43
43
 
@@ -1,3 +1,4 @@
1
+ from adam.commands import extract_trailing_options
1
2
  from adam.commands.command import Command
2
3
  from adam.commands.cql.utils_cql import cassandra
3
4
  from adam.repl_state import ReplState, RequiredState
@@ -25,11 +26,12 @@ class ShowCassandraRepairs(Command):
25
26
  return super().run(cmd, state)
26
27
 
27
28
  with self.validate(args, state) as (args, state):
28
- with cassandra(state) as pods:
29
- return pods.nodetool('repair_admin list')
29
+ with extract_trailing_options(args, '&') as (args, backgrounded):
30
+ with cassandra(state) as pods:
31
+ return pods.nodetool('repair_admin list', backgrounded=backgrounded)
30
32
 
31
33
  def completion(self, state: ReplState):
32
- return super().completion(state)
34
+ return super().completion(state, {'&': None})
33
35
 
34
36
  def help(self, _: ReplState):
35
37
  return f'{ShowCassandraRepairs.COMMAND}\t show Cassandra repairs'
@@ -1,3 +1,4 @@
1
+ from datetime import datetime
1
2
  import sys
2
3
 
3
4
  from adam.checks.check_result import CheckResult
@@ -5,14 +6,15 @@ from adam.checks.check_utils import run_checks
5
6
  from adam.checks.compactionstats import CompactionStats
6
7
  from adam.checks.gossip import Gossip
7
8
  from adam.columns.columns import Columns
8
- from adam.commands import extract_options
9
+ from adam.commands import extract_options, extract_trailing_options
9
10
  from adam.commands.command import Command
10
11
  from adam.commands.cql.utils_cql import cassandra
11
12
  from adam.config import Config
13
+ from adam.repl_session import ReplSession
12
14
  from adam.utils_issues import IssuesUtils
13
15
  from adam.utils_k8s.statefulsets import StatefulSets
14
16
  from adam.repl_state import ReplState, RequiredState
15
- from adam.utils import lines_to_tabular, log, log2, log_exc
17
+ from adam.utils import SORT, tabulize, log2, log_exc
16
18
  from adam.checks.status import parse_nodetool_status
17
19
 
18
20
  class ShowCassandraStatus(Command):
@@ -38,23 +40,24 @@ class ShowCassandraStatus(Command):
38
40
  return super().run(cmd, state)
39
41
 
40
42
  with self.validate(args, state) as (args, state):
41
- with extract_options(args, ['-s', '--show']) as (args, show_out):
42
- if state.namespace and state.pod:
43
- self.show_single_pod(state, show_out=show_out)
44
- elif state.namespace and state.sts:
45
- self.merge(state, Config().get('nodetool.samples', sys.maxsize), show_output=show_out)
43
+ with extract_trailing_options(args, '&') as (args, backgrounded):
44
+ with extract_options(args, ['-s', '--show']) as (args, show_out):
45
+ if state.namespace and state.pod:
46
+ self.show_single_pod(state, show_out=show_out, backgrounded=backgrounded)
47
+ elif state.namespace and state.sts:
48
+ self.merge(state, Config().get('nodetool.samples', sys.maxsize), show_output=show_out, backgrounded=backgrounded)
46
49
 
47
- return state
50
+ return state
48
51
 
49
- def show_single_pod(self, state: ReplState, show_out = False):
52
+ def show_single_pod(self, state: ReplState, show_out = False, backgrounded = False):
50
53
  with log_exc(True):
51
54
  with cassandra(state) as pods:
52
55
  result = pods.nodetool('status', show_out=False)
53
56
  status = parse_nodetool_status(result.stdout)
54
57
  check_results = run_checks(cluster=state.sts, namespace=state.namespace, checks=[CompactionStats(), Gossip()], show_out=show_out)
55
- self.show_table(status, check_results)
58
+ self.show_table(status, check_results, backgrounded=backgrounded)
56
59
 
57
- def merge(self, state: ReplState, samples: int, show_output=False):
60
+ def merge(self, state: ReplState, samples: int, show_output=False, backgrounded = False):
58
61
  statuses: list[list[dict]] = []
59
62
 
60
63
  pod_names = StatefulSets.pod_names(state.sts, state.namespace)
@@ -73,7 +76,7 @@ class ShowCassandraStatus(Command):
73
76
  combined_status = self.merge_status(statuses)
74
77
  log2(f'Showing merged status from {len(statuses)}/{len(pod_names)} nodes...')
75
78
  check_results = run_checks(cluster=state.sts, namespace=state.namespace, checks=[CompactionStats(), Gossip()], show_out=show_output)
76
- self.show_table(combined_status, check_results)
79
+ self.show_table(combined_status, check_results, backgrounded=backgrounded)
77
80
 
78
81
  return combined_status
79
82
 
@@ -94,24 +97,28 @@ class ShowCassandraStatus(Command):
94
97
 
95
98
  return combined
96
99
 
97
- def show_table(self, status: list[dict[str, any]], check_results: list[CheckResult]):
100
+ def show_table(self, status: list[dict[str, any]], check_results: list[CheckResult], backgrounded=False):
98
101
  cols = Config().get('status.columns', 'status,address,load,tokens,owns,host_id,gossip,compactions')
99
102
  header = Config().get('status.header', '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS')
100
103
  columns = Columns.create_columns(cols)
101
104
 
102
- def line(status: dict):
103
- cells = [c.host_value(check_results, status) for c in columns]
104
- return ','.join(cells)
105
+ r = tabulize(status, lambda s: ','.join([c.host_value(check_results, s) for c in columns]), header=header, separator=',', sorted=SORT, to = 0 if backgrounded else 1)
105
106
 
106
- lines = [line(d) for d in status]
107
- lines.sort()
107
+ if backgrounded:
108
+ log_prefix = Config().get('log-prefix', '/tmp/qing')
109
+ log_file = f'{log_prefix}-{datetime.now().strftime("%d%H%M%S")}.log'
108
110
 
109
- log(lines_to_tabular(lines, header, separator=','))
111
+ with open(log_file, 'w') as f:
112
+ f.write(r)
113
+
114
+ ReplSession().append_history(f':sh cat {log_file}')
115
+
116
+ r = log_file
110
117
 
111
118
  IssuesUtils.show(check_results)
112
119
 
113
120
  def completion(self, state: ReplState):
114
- return super().completion(state, {'-s': None})
121
+ return super().completion(state, {'-s': {'&': None}, '&': None})
115
122
 
116
123
  def help(self, _: ReplState):
117
124
  return f'{ShowCassandraStatus.COMMAND} [-s]\t show merged nodetool status -s show commands on nodes'
@@ -1,7 +1,7 @@
1
1
  from adam.commands.command import Command
2
2
  from adam.commands.cli_commands import CliCommands
3
3
  from adam.repl_state import ReplState, RequiredState
4
- from adam.utils import lines_to_tabular, log
4
+ from adam.utils import tabulize, log
5
5
 
6
6
  class ShowKubectlCommands(Command):
7
7
  COMMAND = 'show cli-commands'
@@ -45,7 +45,7 @@ class ShowKubectlCommands(Command):
45
45
 
46
46
  cmds += [f'{k},{v0}' for k, v0 in v.items() if k.startswith('pg-')]
47
47
 
48
- log(lines_to_tabular(cmds, separator=','))
48
+ tabulize(cmds, separator=',')
49
49
 
50
50
  return cmds
51
51
 
@@ -5,7 +5,7 @@ from adam.sso.idp import Idp
5
5
  from adam.sso.idp_login import IdpLogin
6
6
  from adam.commands.command import Command
7
7
  from adam.repl_state import ReplState
8
- from adam.utils import duration, lines_to_tabular, log, log2, log_exc
8
+ from adam.utils import duration, tabulize, log, log2, log_exc
9
9
 
10
10
  class ShowLogin(Command):
11
11
  COMMAND = 'show login'
@@ -45,7 +45,7 @@ class ShowLogin(Command):
45
45
  f'IDP expires in\t{duration(time.time(), it.exp)}',
46
46
  f'IDP Groups\t{",".join(it.groups)}'
47
47
  ]
48
- log(lines_to_tabular(lines, separator='\t'))
48
+ tabulize(lines, separator='\t')
49
49
 
50
50
  return state
51
51
 
@@ -1,7 +1,7 @@
1
1
  from adam.commands.command import Command
2
2
  from adam.config import Config
3
3
  from adam.repl_state import ReplState
4
- from adam.utils import lines_to_tabular, log
4
+ from adam.utils import tabulize
5
5
 
6
6
  class ShowParams(Command):
7
7
  COMMAND = 'show params'
@@ -22,10 +22,7 @@ class ShowParams(Command):
22
22
  if not self.args(cmd):
23
23
  return super().run(cmd, state)
24
24
 
25
- lines = [f'{key}\t{Config().get(key, None)}' for key in Config().keys()]
26
- log(lines_to_tabular(lines, separator='\t'))
27
-
28
- return lines
25
+ return tabulize(Config().keys(), lambda k: f'{k}\t{Config().get(k, None)}', separator='\t')
29
26
 
30
27
  def completion(self, state: ReplState):
31
28
  return super().completion(state)
@@ -1,9 +1,7 @@
1
- from adam.commands import extract_options, extract_sequence
1
+ from adam.commands import extract_options, extract_sequence, extract_trailing_options
2
2
  from adam.commands.command import Command
3
- from adam.commands.commands_utils import show_table
4
3
  from adam.commands.cql.utils_cql import cassandra
5
4
  from adam.config import Config
6
- from adam.utils_k8s.statefulsets import StatefulSets
7
5
  from adam.repl_state import ReplState, RequiredState
8
6
 
9
7
  class ShowProcesses(Command):
@@ -29,21 +27,24 @@ class ShowProcesses(Command):
29
27
  return super().run(cmd, state)
30
28
 
31
29
  with self.validate(args, state) as (args, state):
32
- with extract_options(args, ['-s', '--show']) as (args, show_out):
33
- with extract_sequence(args, ['with', 'recipe', '=', 'qing']) as (_, recipe_qing):
34
- cols = Config().get('processes.columns', 'pod,cpu-metrics,mem')
35
- header = Config().get('processes.header', 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT')
36
- if recipe_qing:
37
- cols = Config().get('processes-qing.columns', 'pod,cpu,mem')
38
- header = Config().get('processes-qing.header', 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT')
30
+ with extract_trailing_options(args, '&') as (args, backgrounded):
31
+ with extract_options(args, ['-s', '--show']) as (args, show_out):
32
+ with extract_sequence(args, ['with', 'recipe', '=', 'qing']) as (_, recipe_qing):
33
+ cols = Config().get('processes.columns', 'pod,cpu-metrics,mem')
34
+ header = Config().get('processes.header', 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT')
35
+ if recipe_qing:
36
+ cols = Config().get('processes-qing.columns', 'pod,cpu,mem')
37
+ header = Config().get('processes-qing.header', 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT')
39
38
 
40
- with cassandra(state) as pods:
41
- pods.display_table(cols, header, show_out=show_out)
39
+ with cassandra(state) as pods:
40
+ pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded)
42
41
 
43
- return state
42
+ return state
44
43
 
45
44
  def completion(self, state: ReplState):
46
- return super().completion(state, {'with': {'recipe': {'=': {'metrics': {'-s': None}, 'qing': {'-s': None}}}}, '-s': None})
45
+ recipes = ['metrics', 'qing']
46
+ return super().completion(state, {'with': {'recipe': {'=': {r: {'-s': {'&': None}, '&': None} for r in recipes}}}, '-s': {'&': None}, '&': None})
47
+ # return super().completion(state, {'with': {'recipe': {'=': {'metrics': {'-s': {'&': None}, '&': None}, 'qing': {'-s': {'&': None}}}}}, '-s': {'&': None}, '&': None})
47
48
 
48
49
  def help(self, _: ReplState):
49
50
  return f'{ShowProcesses.COMMAND} [with recipe qing|metrics] [-s]\t show process overview -s show commands on nodes'
@@ -1,4 +1,4 @@
1
- from adam.commands import extract_options
1
+ from adam.commands import extract_options, extract_trailing_options
2
2
  from adam.commands.command import Command
3
3
  from adam.commands.cql.utils_cql import cassandra
4
4
  from adam.config import Config
@@ -27,16 +27,17 @@ class ShowStorage(Command):
27
27
  return super().run(cmd, state)
28
28
 
29
29
  with self.validate(args, state) as (args, state):
30
- with extract_options(args, ['-s', '--show']) as (args, show_out):
31
- cols = Config().get('storage.columns', 'pod,volume_root,volume_cassandra,snapshots,data,compactions')
32
- header = Config().get('storage.header', 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS')
33
- with cassandra(state) as pods:
34
- pods.display_table(cols, header, show_out=show_out)
30
+ with extract_trailing_options(args, '&') as (args, backgrounded):
31
+ with extract_options(args, ['-s', '--show']) as (args, show_out):
32
+ cols = Config().get('storage.columns', 'pod,volume_root,volume_cassandra,snapshots,data,compactions')
33
+ header = Config().get('storage.header', 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS')
34
+ with cassandra(state) as pods:
35
+ pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded)
35
36
 
36
- return state
37
+ return state
37
38
 
38
39
  def completion(self, state: ReplState):
39
- return super().completion(state, {'-s': None})
40
+ return super().completion(state, {'-s': {'&': None}, '&': None})
40
41
 
41
42
  def help(self, _: ReplState):
42
43
  return f'{ShowStorage.COMMAND} [-s]\t show storage overview -s show commands on nodes'
adam/config.py CHANGED
@@ -11,6 +11,7 @@ class Config:
11
11
  EMBEDDED_PARAMS = {}
12
12
 
13
13
  LogConfig.is_debug = lambda: Config().is_debug()
14
+ LogConfig.is_debug_complete = lambda: Config().get('debugs.complete', False)
14
15
  LogConfig.is_debug_timing = lambda: Config().get('debugs.timings', False)
15
16
 
16
17
  # the singleton pattern