kaqing 2.0.171__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.
Files changed (194) hide show
  1. adam/app_session.py +5 -10
  2. adam/apps.py +18 -4
  3. adam/batch.py +7 -7
  4. adam/checks/check_utils.py +3 -1
  5. adam/checks/disk.py +2 -3
  6. adam/columns/memory.py +3 -4
  7. adam/commands/__init__.py +15 -6
  8. adam/commands/alter_tables.py +26 -41
  9. adam/commands/app/__init__.py +0 -0
  10. adam/commands/{app_cmd.py → app/app.py} +2 -2
  11. adam/commands/{show → app}/show_app_actions.py +7 -15
  12. adam/commands/{show → app}/show_app_queues.py +1 -4
  13. adam/{utils_app.py → commands/app/utils_app.py} +9 -1
  14. adam/commands/audit/audit.py +9 -26
  15. adam/commands/audit/audit_repair_tables.py +5 -7
  16. adam/commands/audit/audit_run.py +1 -1
  17. adam/commands/audit/completions_l.py +15 -0
  18. adam/commands/audit/show_last10.py +2 -14
  19. adam/commands/audit/show_slow10.py +2 -13
  20. adam/commands/audit/show_top10.py +2 -11
  21. adam/commands/audit/utils_show_top10.py +15 -3
  22. adam/commands/bash/bash.py +1 -1
  23. adam/commands/bash/utils_bash.py +1 -1
  24. adam/commands/cassandra/__init__.py +0 -0
  25. adam/commands/cassandra/download_cassandra_log.py +45 -0
  26. adam/commands/cassandra/nodetool.py +64 -0
  27. adam/commands/cassandra/nodetool_commands.py +120 -0
  28. adam/commands/cassandra/restart_cluster.py +47 -0
  29. adam/commands/cassandra/restart_node.py +51 -0
  30. adam/commands/cassandra/restart_nodes.py +47 -0
  31. adam/commands/cassandra/rollout.py +88 -0
  32. adam/commands/cat.py +5 -19
  33. adam/commands/cd.py +7 -9
  34. adam/commands/check.py +10 -18
  35. adam/commands/cli_commands.py +6 -1
  36. adam/commands/{cp.py → clipboard_copy.py} +34 -36
  37. adam/commands/code.py +2 -2
  38. adam/commands/command.py +139 -22
  39. adam/commands/commands_utils.py +14 -12
  40. adam/commands/cql/alter_tables.py +66 -0
  41. adam/commands/cql/completions_c.py +29 -0
  42. adam/commands/cql/cqlsh.py +3 -7
  43. adam/commands/cql/utils_cql.py +23 -61
  44. adam/commands/debug/__init__.py +0 -0
  45. adam/commands/debug/debug.py +22 -0
  46. adam/commands/debug/debug_completes.py +35 -0
  47. adam/commands/debug/debug_timings.py +35 -0
  48. adam/commands/deploy/deploy_pg_agent.py +2 -2
  49. adam/commands/deploy/deploy_pod.py +2 -4
  50. adam/commands/deploy/undeploy_pg_agent.py +2 -2
  51. adam/commands/devices/device.py +40 -9
  52. adam/commands/devices/device_app.py +19 -29
  53. adam/commands/devices/device_auit_log.py +3 -3
  54. adam/commands/devices/device_cass.py +17 -23
  55. adam/commands/devices/device_export.py +12 -11
  56. adam/commands/devices/device_postgres.py +79 -63
  57. adam/commands/devices/devices.py +1 -1
  58. adam/commands/download_cassandra_log.py +45 -0
  59. adam/commands/download_file.py +47 -0
  60. adam/commands/export/clean_up_all_export_sessions.py +3 -3
  61. adam/commands/export/clean_up_export_sessions.py +7 -19
  62. adam/commands/export/completions_x.py +11 -0
  63. adam/commands/export/download_export_session.py +40 -0
  64. adam/commands/export/drop_export_database.py +6 -22
  65. adam/commands/export/drop_export_databases.py +3 -9
  66. adam/commands/export/export.py +1 -17
  67. adam/commands/export/export_databases.py +109 -32
  68. adam/commands/export/export_select.py +8 -55
  69. adam/commands/export/export_sessions.py +211 -0
  70. adam/commands/export/export_use.py +13 -16
  71. adam/commands/export/export_x_select.py +48 -0
  72. adam/commands/export/exporter.py +176 -167
  73. adam/commands/export/import_files.py +44 -0
  74. adam/commands/export/import_session.py +10 -6
  75. adam/commands/export/importer.py +24 -9
  76. adam/commands/export/importer_athena.py +114 -44
  77. adam/commands/export/importer_sqlite.py +45 -23
  78. adam/commands/export/show_column_counts.py +11 -20
  79. adam/commands/export/show_export_databases.py +5 -2
  80. adam/commands/export/show_export_session.py +6 -15
  81. adam/commands/export/show_export_sessions.py +4 -11
  82. adam/commands/export/utils_export.py +79 -27
  83. adam/commands/find_files.py +51 -0
  84. adam/commands/find_processes.py +76 -0
  85. adam/commands/generate_report.py +52 -0
  86. adam/commands/head.py +36 -0
  87. adam/commands/help.py +2 -2
  88. adam/commands/intermediate_command.py +6 -3
  89. adam/commands/login.py +3 -6
  90. adam/commands/ls.py +2 -2
  91. adam/commands/medusa/medusa_backup.py +13 -16
  92. adam/commands/medusa/medusa_restore.py +26 -37
  93. adam/commands/medusa/medusa_show_backupjobs.py +7 -7
  94. adam/commands/medusa/medusa_show_restorejobs.py +6 -6
  95. adam/commands/medusa/utils_medusa.py +15 -0
  96. adam/commands/nodetool.py +3 -8
  97. adam/commands/os/__init__.py +0 -0
  98. adam/commands/os/cat.py +36 -0
  99. adam/commands/os/download_file.py +47 -0
  100. adam/commands/os/find_files.py +51 -0
  101. adam/commands/os/find_processes.py +76 -0
  102. adam/commands/os/head.py +36 -0
  103. adam/commands/os/shell.py +41 -0
  104. adam/commands/param_get.py +10 -12
  105. adam/commands/param_set.py +7 -10
  106. adam/commands/postgres/completions_p.py +22 -0
  107. adam/commands/postgres/postgres.py +25 -40
  108. adam/commands/postgres/postgres_databases.py +269 -0
  109. adam/commands/postgres/utils_postgres.py +33 -20
  110. adam/commands/preview_table.py +4 -2
  111. adam/commands/pwd.py +4 -6
  112. adam/commands/reaper/reaper_forward.py +2 -2
  113. adam/commands/reaper/reaper_run_abort.py +4 -10
  114. adam/commands/reaper/reaper_runs.py +3 -3
  115. adam/commands/reaper/reaper_schedule_activate.py +12 -12
  116. adam/commands/reaper/reaper_schedule_start.py +7 -12
  117. adam/commands/reaper/reaper_schedule_stop.py +7 -12
  118. adam/commands/reaper/utils_reaper.py +13 -6
  119. adam/commands/repair/repair_log.py +1 -4
  120. adam/commands/repair/repair_run.py +3 -8
  121. adam/commands/repair/repair_scan.py +1 -6
  122. adam/commands/repair/repair_stop.py +1 -5
  123. adam/commands/restart_cluster.py +47 -0
  124. adam/commands/restart_node.py +51 -0
  125. adam/commands/restart_nodes.py +47 -0
  126. adam/commands/shell.py +9 -2
  127. adam/commands/show/show.py +4 -4
  128. adam/commands/show/show_adam.py +3 -3
  129. adam/commands/show/show_cassandra_repairs.py +5 -6
  130. adam/commands/show/show_cassandra_status.py +29 -29
  131. adam/commands/show/show_cassandra_version.py +1 -4
  132. adam/commands/show/{show_commands.py → show_cli_commands.py} +3 -6
  133. adam/commands/show/show_login.py +3 -9
  134. adam/commands/show/show_params.py +2 -5
  135. adam/commands/show/show_processes.py +15 -16
  136. adam/commands/show/show_storage.py +9 -8
  137. adam/config.py +4 -5
  138. adam/embedded_params.py +1 -1
  139. adam/log.py +4 -4
  140. adam/repl.py +26 -18
  141. adam/repl_commands.py +32 -20
  142. adam/repl_session.py +9 -1
  143. adam/repl_state.py +39 -10
  144. adam/sql/async_executor.py +44 -0
  145. adam/sql/lark_completer.py +286 -0
  146. adam/sql/lark_parser.py +604 -0
  147. adam/sql/qingl.lark +1076 -0
  148. adam/sql/sql_completer.py +4 -6
  149. adam/sql/sql_state_machine.py +25 -13
  150. adam/sso/authn_ad.py +2 -5
  151. adam/sso/authn_okta.py +2 -4
  152. adam/sso/cred_cache.py +2 -5
  153. adam/sso/idp.py +8 -11
  154. adam/utils.py +299 -105
  155. adam/utils_athena.py +18 -18
  156. adam/utils_audits.py +3 -7
  157. adam/utils_issues.py +2 -2
  158. adam/utils_k8s/app_clusters.py +4 -4
  159. adam/utils_k8s/app_pods.py +8 -6
  160. adam/utils_k8s/cassandra_clusters.py +16 -5
  161. adam/utils_k8s/cassandra_nodes.py +9 -6
  162. adam/utils_k8s/custom_resources.py +11 -17
  163. adam/utils_k8s/jobs.py +7 -11
  164. adam/utils_k8s/k8s.py +14 -5
  165. adam/utils_k8s/kube_context.py +3 -6
  166. adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +4 -4
  167. adam/utils_k8s/pods.py +85 -23
  168. adam/utils_k8s/statefulsets.py +5 -2
  169. adam/utils_local.py +42 -0
  170. adam/utils_repl/appendable_completer.py +6 -0
  171. adam/utils_repl/repl_completer.py +45 -2
  172. adam/utils_repl/state_machine.py +3 -3
  173. adam/utils_sqlite.py +58 -30
  174. adam/version.py +1 -1
  175. {kaqing-2.0.171.dist-info → kaqing-2.0.203.dist-info}/METADATA +1 -1
  176. kaqing-2.0.203.dist-info/RECORD +277 -0
  177. kaqing-2.0.203.dist-info/top_level.txt +2 -0
  178. teddy/__init__.py +0 -0
  179. teddy/lark_parser.py +436 -0
  180. teddy/lark_parser2.py +618 -0
  181. adam/commands/cql/cql_completions.py +0 -33
  182. adam/commands/export/export_handlers.py +0 -71
  183. adam/commands/export/export_select_x.py +0 -54
  184. adam/commands/logs.py +0 -37
  185. adam/commands/postgres/postgres_context.py +0 -274
  186. adam/commands/postgres/psql_completions.py +0 -10
  187. adam/commands/report.py +0 -61
  188. adam/commands/restart.py +0 -60
  189. kaqing-2.0.171.dist-info/RECORD +0 -236
  190. kaqing-2.0.171.dist-info/top_level.txt +0 -1
  191. /adam/commands/{app_ping.py → app/app_ping.py} +0 -0
  192. /adam/commands/{show → app}/show_app_id.py +0 -0
  193. {kaqing-2.0.171.dist-info → kaqing-2.0.203.dist-info}/WHEEL +0 -0
  194. {kaqing-2.0.171.dist-info → kaqing-2.0.203.dist-info}/entry_points.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,22 +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
- PostgresContext.apply(state.namespace, state.pg_path).run_sql(' '.join(args), background=background)
61
+ query = args
62
+ if isinstance(args, list):
63
+ query = ' '.join(args)
64
+
65
+ PostgresDatabases.run_sql(state, query, backgrounded=backgrounded)
53
66
 
54
67
  class PostgresExecHandler:
55
- def __init__(self, state: ReplState, background=False):
68
+ def __init__(self, state: ReplState, backgrounded=False):
56
69
  self.state = state
57
- self.background = background
70
+ self.backgrounded = backgrounded
58
71
 
59
72
  def __enter__(self):
60
73
  return PostgresPodService(self)
@@ -62,5 +75,5 @@ class PostgresExecHandler:
62
75
  def __exit__(self, exc_type, exc_val, exc_tb):
63
76
  return False
64
77
 
65
- def postgres(state: ReplState, background=False):
66
- 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
@@ -18,14 +19,15 @@ class PreviewTable(Command):
18
19
  return PreviewTable.COMMAND
19
20
 
20
21
  def required(self):
21
- return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L]
22
+ return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L, RequiredState.EXPORT_DB]
22
23
 
23
24
  def run(self, cmd: str, state: ReplState):
24
25
  if not(args := self.args(cmd)):
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.of(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
5
+ from adam.utils import tabulize, log, log_exc
6
6
 
7
7
  class Pwd(Command):
8
8
  COMMAND = 'pwd'
@@ -25,17 +25,15 @@ class Pwd(Command):
25
25
 
26
26
  with self.validate(args, state) as (_, state):
27
27
  host = "unknown"
28
- try:
28
+ with log_exc():
29
29
  app_session: AppSession = AppSession.create('c3', 'c3')
30
30
  host = app_session.host
31
- except:
32
- pass
33
31
 
34
- log(lines_to_tabular([device.pwd(state) for device in Devices.all()] + [
32
+ tabulize([device.pwd(state) for device in Devices.all()] + [
35
33
  f'',
36
34
  f'HOST\t{host}',
37
35
  f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
38
- ], 'DEVICE\tLOCATION', separator='\t'))
36
+ ], header='DEVICE\tLOCATION', separator='\t')
39
37
  log()
40
38
 
41
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}')
@@ -1,3 +1,4 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
3
  from adam.commands.reaper.utils_reaper import reaper
3
4
  from adam.repl_state import ReplState, RequiredState
@@ -26,19 +27,12 @@ class ReaperRunAbort(Command):
26
27
  return super().run(cmd, state)
27
28
 
28
29
  with self.validate(args, state) as (args, state):
29
- if not args:
30
- if state.in_repl:
31
- log2('Specify run id to abort.')
32
- else:
33
- Command.display_help()
30
+ with validate_args(args, state, name='run id') as run_id:
31
+ with reaper(state) as http:
32
+ http.put(f'repair_run/{run_id}/state/ABORTED')
34
33
 
35
34
  return state
36
35
 
37
- with reaper(state) as http:
38
- http.put(f'repair_run/{args[0]}/state/ABORTED')
39
-
40
- return state
41
-
42
36
  def completion(self, state: ReplState):
43
37
  return super().completion(state)
44
38
 
@@ -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
 
@@ -1,7 +1,12 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
3
  from adam.commands.reaper.utils_reaper import Reapers, reaper
3
4
  from adam.repl_state import ReplState, RequiredState
4
- from adam.utils import log2
5
+
6
+ import nest_asyncio
7
+ nest_asyncio.apply()
8
+
9
+ import asyncio
5
10
 
6
11
  class ReaperScheduleActivate(Command):
7
12
  COMMAND = 'reaper activate schedule'
@@ -26,20 +31,15 @@ class ReaperScheduleActivate(Command):
26
31
  return super().run(cmd, state)
27
32
 
28
33
  with self.validate(args, state) as (args, state):
29
- if not args:
30
- log2('Specify schedule to activate.')
31
-
32
- return state
33
-
34
- schedule_id = args[0]
35
- with reaper(state) as http:
36
- http.put(f'repair_schedule/{schedule_id}?state=ACTIVE')
37
- Reapers.show_schedule(state, schedule_id)
34
+ with validate_args(args, state, name='schedule') as schedule_id:
35
+ with reaper(state) as http:
36
+ http.put(f'repair_schedule/{schedule_id}?state=ACTIVE')
37
+ Reapers.show_schedule(state, schedule_id)
38
38
 
39
- return schedule_id
39
+ return schedule_id
40
40
 
41
41
  def completion(self, state: ReplState):
42
- 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')
43
43
 
44
44
  def help(self, _: ReplState):
45
45
  return f'{ReaperScheduleActivate.COMMAND} <schedule-id>\t resume reaper schedule'
@@ -1,7 +1,7 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
3
  from adam.commands.reaper.utils_reaper import Reapers, reaper
3
4
  from adam.repl_state import ReplState, RequiredState
4
- from adam.utils import log2
5
5
 
6
6
  class ReaperScheduleStart(Command):
7
7
  COMMAND = 'reaper start schedule'
@@ -26,20 +26,15 @@ class ReaperScheduleStart(Command):
26
26
  return super().run(cmd, state)
27
27
 
28
28
  with self.validate(args, state) as (args, state):
29
- if not args:
30
- log2('Specify schedule to activate.')
29
+ with validate_args(args, state, name='schedule') as schedule_id:
30
+ with reaper(state) as http:
31
+ http.post(f'repair_schedule/start/{schedule_id}')
32
+ Reapers.show_schedule(state, schedule_id)
31
33
 
32
- return state
33
-
34
- schedule_id = args[0]
35
- with reaper(state) as http:
36
- http.post(f'repair_schedule/start/{schedule_id}')
37
- Reapers.show_schedule(state, schedule_id)
38
-
39
- return schedule_id
34
+ return schedule_id
40
35
 
41
36
  def completion(self, state: ReplState):
42
- 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')
43
38
 
44
39
  def help(self, _: ReplState):
45
40
  return f'{ReaperScheduleStart.COMMAND} <schedule-id>\t start reaper runs for schedule'
@@ -1,7 +1,7 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
3
  from adam.commands.reaper.utils_reaper import Reapers, reaper
3
4
  from adam.repl_state import ReplState, RequiredState
4
- from adam.utils import log2
5
5
 
6
6
  class ReaperScheduleStop(Command):
7
7
  COMMAND = 'reaper stop schedule'
@@ -26,20 +26,15 @@ class ReaperScheduleStop(Command):
26
26
  return super().run(cmd, state)
27
27
 
28
28
  with self.validate(args, state) as (args, state):
29
- if not args:
30
- log2('Specify run schedule to stop.')
29
+ with validate_args(args, state, name='schedule') as schedule_id:
30
+ with reaper(state) as http:
31
+ http.put(f'repair_schedule/{schedule_id}?state=PAUSED')
32
+ Reapers.show_schedule(state, schedule_id)
31
33
 
32
- return state
33
-
34
- with reaper(state) as http:
35
- schedule_id = args[0]
36
- http.put(f'repair_schedule/{schedule_id}?state=PAUSED')
37
- Reapers.show_schedule(state, schedule_id)
38
-
39
- return schedule_id
34
+ return schedule_id
40
35
 
41
36
  def completion(self, state: ReplState):
42
- 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')
43
38
 
44
39
  def help(self, _: ReplState):
45
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')
@@ -31,10 +31,7 @@ class RepairLog(Command):
31
31
  return state
32
32
 
33
33
  def completion(self, state: ReplState):
34
- if state.sts:
35
- return super().completion(state)
36
-
37
- return {}
34
+ return super().completion(state)
38
35
 
39
36
  def help(self, _: ReplState):
40
37
  return f'{RepairLog.COMMAND}\t get repair job logs'
@@ -6,7 +6,7 @@ from adam.repl_state import ReplState, RequiredState
6
6
  from adam.config import Config
7
7
  from adam.commands.reaper.reaper_runs_abort import ReaperRunsAbort
8
8
  from adam.commands.reaper.reaper_schedule_stop import ReaperScheduleStop
9
- from adam.utils import log2
9
+ from adam.utils import log2, log_exc
10
10
 
11
11
  class RepairRun(Command):
12
12
  COMMAND = 'repair run'
@@ -35,14 +35,12 @@ class RepairRun(Command):
35
35
  if len(args) == 1:
36
36
  replace = args[0] == 'replace'
37
37
 
38
- try:
38
+ with log_exc():
39
39
  log2("Stopping all reaper schedules...")
40
40
  for schedule_id in Reapers.cached_schedule_ids(state):
41
41
  ReaperScheduleStop().run(f'reaper stop schedule {schedule_id}', state)
42
42
  log2("Aborting all reaper runs...")
43
43
  state = ReaperRunsAbort().run('reaper abort runs', state)
44
- except:
45
- pass
46
44
 
47
45
  image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
48
46
  secret = Config().get('repair.secret', 'ciregistryc3iotio')
@@ -61,10 +59,7 @@ class RepairRun(Command):
61
59
  return state
62
60
 
63
61
  def completion(self, state: ReplState):
64
- if state.sts:
65
- return super().completion(state)
66
-
67
- return {}
62
+ return super().completion(state)
68
63
 
69
64
  def help(self, _: ReplState):
70
65
  return f'{RepairRun.COMMAND} [replace]\t start a repair job, default not replacing'
@@ -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
@@ -62,10 +60,7 @@ class RepairScan(Command):
62
60
  return state
63
61
 
64
62
  def completion(self, state: ReplState):
65
- if state.sts:
66
- return super().completion(state)
67
-
68
- return {}
63
+ return super().completion(state)
69
64
 
70
65
  def help(self, _: ReplState):
71
66
  return f'{RepairScan.COMMAND} [n]\t scan last n days repair log, default 7 days'
@@ -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'
@@ -32,10 +31,7 @@ class RepairStop(Command):
32
31
  return state
33
32
 
34
33
  def completion(self, state: ReplState):
35
- if state.sts:
36
- return super().completion(state)
37
-
38
- return {}
34
+ return super().completion(state)
39
35
 
40
36
  def help(self, _: ReplState):
41
37
  return f'{RepairStop.COMMAND}\t delete a repair job'
@@ -0,0 +1,47 @@
1
+ from adam.commands import extract_options
2
+ from adam.commands.command import Command
3
+ from adam.utils_k8s.pods import Pods
4
+ from adam.utils_k8s.statefulsets import StatefulSets
5
+ from adam.repl_state import ReplState, RequiredState
6
+ from adam.utils import log2
7
+
8
+ class RestartCluster(Command):
9
+ COMMAND = 'restart cluster'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(RestartCluster, cls).__new__(cls)
14
+
15
+ return cls.instance
16
+
17
+ def __init__(self, successor: Command=None):
18
+ super().__init__(successor)
19
+
20
+ def command(self):
21
+ return RestartCluster.COMMAND
22
+
23
+ def required(self):
24
+ return RequiredState.CLUSTER
25
+
26
+ def run(self, cmd: str, state: ReplState):
27
+ if not(args := self.args(cmd)):
28
+ return super().run(cmd, state)
29
+
30
+ with self.validate(args, state) as (args, state):
31
+ with extract_options(args, '--force') as (args, forced):
32
+ if not forced:
33
+ log2('Please add --force for restarting all nodes in a cluster.')
34
+
35
+ return 'force-needed'
36
+
37
+ log2(f'Restarting all pods from {state.sts}...')
38
+ for pod_name in StatefulSets.pod_names(state.sts, state.namespace):
39
+ Pods.delete(pod_name, state.namespace)
40
+
41
+ return state
42
+
43
+ def completion(self, state: ReplState):
44
+ return super().completion(state, {'--force': None})
45
+
46
+ def help(self, _: ReplState):
47
+ return f"{RestartCluster.COMMAND} --force\t restart all the nodes in the cluster"
@@ -0,0 +1,51 @@
1
+ from adam.commands import extract_options
2
+ from adam.commands.command import Command
3
+ from adam.commands.devices.devices import Devices
4
+ from adam.utils_k8s.pods import Pods
5
+ from adam.repl_state import ReplState, RequiredState
6
+ from adam.utils import log2
7
+
8
+ class RestartNode(Command):
9
+ COMMAND = 'restart node'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(RestartNode, cls).__new__(cls)
14
+
15
+ return cls.instance
16
+
17
+ def __init__(self, successor: Command=None):
18
+ super().__init__(successor)
19
+
20
+ def command(self):
21
+ return RestartNode.COMMAND
22
+
23
+ def required(self):
24
+ return RequiredState.POD
25
+
26
+ def run(self, cmd: str, state: ReplState):
27
+ if not(args := self.args(cmd)):
28
+ return super().run(cmd, state)
29
+
30
+ with self.validate(args, state) as (args, state):
31
+ if not state.pod:
32
+ log2("'pod' is required")
33
+
34
+ return 'pod-needed'
35
+
36
+ with extract_options(args, '--force') as (args, forced):
37
+ if not forced:
38
+ log2('Please add --force for restarting pod.')
39
+
40
+ return 'force-needed'
41
+
42
+ log2(f'Restarting {state.pod}...')
43
+ Pods.delete(state.pod, state.namespace)
44
+
45
+ return state
46
+
47
+ def completion(self, state: ReplState):
48
+ return super().completion(state, {'--force': None}, pods=Devices.of(state).pods(state, '-'))
49
+
50
+ def help(self, _: ReplState):
51
+ return f"{RestartNode.COMMAND} --force\t restart the node"
@@ -0,0 +1,47 @@
1
+ from adam.commands import extract_options, validate_args
2
+ from adam.commands.command import Command
3
+ from adam.utils_k8s.pods import Pods
4
+ from adam.utils_k8s.statefulsets import StatefulSets
5
+ from adam.repl_state import ReplState, RequiredState
6
+ from adam.utils import log2
7
+
8
+ class RestartNodes(Command):
9
+ COMMAND = 'restart nodes'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(RestartNodes, cls).__new__(cls)
14
+
15
+ return cls.instance
16
+
17
+ def __init__(self, successor: Command=None):
18
+ super().__init__(successor)
19
+
20
+ def command(self):
21
+ return RestartNodes.COMMAND
22
+
23
+ def required(self):
24
+ return RequiredState.CLUSTER
25
+
26
+ def run(self, cmd: str, state: ReplState):
27
+ if not(args := self.args(cmd)):
28
+ return super().run(cmd, state)
29
+
30
+ with self.validate(args, state, apply=False) as (args, state):
31
+ with extract_options(args, '--force') as (args, forced):
32
+ with validate_args(args, state, name='pod name'):
33
+ if not forced:
34
+ log2('Please add --force for restarting nodes.')
35
+
36
+ return 'force-needed'
37
+
38
+ for arg in args:
39
+ Pods.delete(arg, state.namespace)
40
+
41
+ return state
42
+
43
+ def completion(self, state: ReplState):
44
+ return super().completion(state, lambda: {p: {'--force': None} for p in StatefulSets.pod_names(state.sts, state.namespace)})
45
+
46
+ def help(self, _: ReplState):
47
+ return f"{RestartNodes.COMMAND} <pod-name>... --force\t restart Cassandra nodes"
adam/commands/shell.py CHANGED
@@ -1,7 +1,9 @@
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
6
+ from adam.utils import log2
5
7
 
6
8
  class Shell(Command):
7
9
  COMMAND = ':sh'
@@ -22,8 +24,13 @@ class Shell(Command):
22
24
  if not(args := self.args(cmd)):
23
25
  return super().run(cmd, state)
24
26
 
25
- with self.validate(args, state):
26
- os.system('QING_DROPPED=true bash')
27
+ with self.validate(args, state) as (args, _):
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')
27
34
 
28
35
  return state
29
36