kaqing 1.98.15__py3-none-any.whl → 2.0.145__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 (180) hide show
  1. adam/app_session.py +1 -1
  2. adam/apps.py +2 -2
  3. adam/batch.py +30 -31
  4. adam/checks/check_utils.py +4 -4
  5. adam/checks/compactionstats.py +1 -1
  6. adam/checks/cpu.py +2 -2
  7. adam/checks/disk.py +1 -1
  8. adam/checks/gossip.py +1 -1
  9. adam/checks/memory.py +3 -3
  10. adam/checks/status.py +1 -1
  11. adam/commands/alter_tables.py +81 -0
  12. adam/commands/app.py +3 -3
  13. adam/commands/app_ping.py +2 -2
  14. adam/commands/audit/audit.py +86 -0
  15. adam/commands/audit/audit_repair_tables.py +77 -0
  16. adam/commands/audit/audit_run.py +58 -0
  17. adam/commands/audit/show_last10.py +51 -0
  18. adam/commands/audit/show_slow10.py +50 -0
  19. adam/commands/audit/show_top10.py +48 -0
  20. adam/commands/audit/utils_show_top10.py +59 -0
  21. adam/commands/bash/bash.py +133 -0
  22. adam/commands/bash/bash_completer.py +93 -0
  23. adam/commands/cat.py +56 -0
  24. adam/commands/cd.py +12 -82
  25. adam/commands/check.py +6 -0
  26. adam/commands/cli_commands.py +3 -3
  27. adam/commands/code.py +60 -0
  28. adam/commands/command.py +48 -12
  29. adam/commands/commands_utils.py +4 -5
  30. adam/commands/cql/cql_completions.py +28 -0
  31. adam/commands/cql/cql_utils.py +209 -0
  32. adam/commands/{cqlsh.py → cql/cqlsh.py} +15 -10
  33. adam/commands/deploy/__init__.py +0 -0
  34. adam/commands/{frontend → deploy}/code_start.py +1 -1
  35. adam/commands/{frontend → deploy}/code_stop.py +1 -1
  36. adam/commands/{frontend → deploy}/code_utils.py +2 -2
  37. adam/commands/deploy/deploy.py +48 -0
  38. adam/commands/deploy/deploy_frontend.py +52 -0
  39. adam/commands/deploy/deploy_pg_agent.py +38 -0
  40. adam/commands/deploy/deploy_pod.py +110 -0
  41. adam/commands/deploy/deploy_utils.py +29 -0
  42. adam/commands/deploy/undeploy.py +48 -0
  43. adam/commands/deploy/undeploy_frontend.py +41 -0
  44. adam/commands/deploy/undeploy_pg_agent.py +42 -0
  45. adam/commands/deploy/undeploy_pod.py +51 -0
  46. adam/commands/devices/__init__.py +0 -0
  47. adam/commands/devices/device.py +27 -0
  48. adam/commands/devices/device_app.py +146 -0
  49. adam/commands/devices/device_auit_log.py +43 -0
  50. adam/commands/devices/device_cass.py +145 -0
  51. adam/commands/devices/device_export.py +86 -0
  52. adam/commands/devices/device_postgres.py +109 -0
  53. adam/commands/devices/devices.py +25 -0
  54. adam/commands/export/__init__.py +0 -0
  55. adam/commands/export/clean_up_export_session.py +53 -0
  56. adam/commands/{frontend/teardown_frontend.py → export/clean_up_export_sessions.py} +9 -11
  57. adam/commands/export/drop_export_database.py +58 -0
  58. adam/commands/export/drop_export_databases.py +46 -0
  59. adam/commands/export/export.py +83 -0
  60. adam/commands/export/export_databases.py +170 -0
  61. adam/commands/export/export_select.py +85 -0
  62. adam/commands/export/export_select_x.py +54 -0
  63. adam/commands/export/export_use.py +55 -0
  64. adam/commands/export/exporter.py +364 -0
  65. adam/commands/export/import_session.py +68 -0
  66. adam/commands/export/importer.py +67 -0
  67. adam/commands/export/importer_athena.py +80 -0
  68. adam/commands/export/importer_sqlite.py +47 -0
  69. adam/commands/export/show_column_counts.py +63 -0
  70. adam/commands/export/show_export_databases.py +39 -0
  71. adam/commands/export/show_export_session.py +51 -0
  72. adam/commands/export/show_export_sessions.py +47 -0
  73. adam/commands/export/utils_export.py +291 -0
  74. adam/commands/help.py +12 -7
  75. adam/commands/issues.py +6 -0
  76. adam/commands/kubectl.py +41 -0
  77. adam/commands/login.py +9 -5
  78. adam/commands/logs.py +2 -1
  79. adam/commands/ls.py +4 -107
  80. adam/commands/medusa/medusa.py +2 -26
  81. adam/commands/medusa/medusa_backup.py +2 -2
  82. adam/commands/medusa/medusa_restore.py +3 -4
  83. adam/commands/medusa/medusa_show_backupjobs.py +4 -3
  84. adam/commands/medusa/medusa_show_restorejobs.py +3 -3
  85. adam/commands/nodetool.py +9 -4
  86. adam/commands/param_set.py +1 -1
  87. adam/commands/postgres/postgres.py +42 -43
  88. adam/commands/postgres/postgres_context.py +248 -0
  89. adam/commands/postgres/postgres_preview.py +0 -1
  90. adam/commands/postgres/postgres_utils.py +31 -0
  91. adam/commands/postgres/psql_completions.py +10 -0
  92. adam/commands/preview_table.py +18 -40
  93. adam/commands/pwd.py +2 -28
  94. adam/commands/reaper/reaper.py +4 -24
  95. adam/commands/reaper/reaper_restart.py +1 -1
  96. adam/commands/reaper/reaper_session.py +2 -2
  97. adam/commands/repair/repair.py +3 -27
  98. adam/commands/repair/repair_log.py +1 -1
  99. adam/commands/repair/repair_run.py +2 -2
  100. adam/commands/repair/repair_scan.py +2 -7
  101. adam/commands/repair/repair_stop.py +1 -1
  102. adam/commands/report.py +6 -0
  103. adam/commands/restart.py +2 -2
  104. adam/commands/rollout.py +1 -1
  105. adam/commands/shell.py +33 -0
  106. adam/commands/show/show.py +11 -26
  107. adam/commands/show/show_app_actions.py +3 -0
  108. adam/commands/show/show_app_id.py +1 -1
  109. adam/commands/show/show_app_queues.py +3 -2
  110. adam/commands/show/show_cassandra_status.py +3 -3
  111. adam/commands/show/show_cassandra_version.py +3 -3
  112. adam/commands/show/show_commands.py +4 -1
  113. adam/commands/show/show_host.py +33 -0
  114. adam/commands/show/show_login.py +3 -0
  115. adam/commands/show/show_processes.py +1 -1
  116. adam/commands/show/show_repairs.py +2 -2
  117. adam/commands/show/show_storage.py +1 -1
  118. adam/commands/watch.py +1 -1
  119. adam/config.py +16 -3
  120. adam/embedded_params.py +1 -1
  121. adam/pod_exec_result.py +10 -2
  122. adam/repl.py +132 -117
  123. adam/repl_commands.py +62 -18
  124. adam/repl_state.py +276 -55
  125. adam/sql/__init__.py +0 -0
  126. adam/sql/sql_completer.py +120 -0
  127. adam/sql/sql_state_machine.py +617 -0
  128. adam/sql/term_completer.py +76 -0
  129. adam/sso/authenticator.py +1 -1
  130. adam/sso/authn_ad.py +36 -56
  131. adam/sso/authn_okta.py +6 -32
  132. adam/sso/cred_cache.py +1 -1
  133. adam/sso/idp.py +74 -9
  134. adam/sso/idp_login.py +2 -2
  135. adam/sso/idp_session.py +10 -7
  136. adam/utils.py +85 -4
  137. adam/utils_athena.py +145 -0
  138. adam/utils_audits.py +102 -0
  139. adam/utils_k8s/__init__.py +0 -0
  140. adam/utils_k8s/app_clusters.py +33 -0
  141. adam/utils_k8s/app_pods.py +31 -0
  142. adam/{k8s_utils → utils_k8s}/cassandra_clusters.py +6 -21
  143. adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
  144. adam/utils_k8s/config_maps.py +34 -0
  145. adam/utils_k8s/deployment.py +56 -0
  146. adam/{k8s_utils → utils_k8s}/jobs.py +1 -1
  147. adam/{k8s_utils → utils_k8s}/kube_context.py +1 -1
  148. adam/utils_k8s/pods.py +342 -0
  149. adam/{k8s_utils → utils_k8s}/secrets.py +4 -0
  150. adam/utils_k8s/service_accounts.py +169 -0
  151. adam/{k8s_utils → utils_k8s}/statefulsets.py +5 -4
  152. adam/{k8s_utils → utils_k8s}/volumes.py +9 -0
  153. adam/utils_net.py +24 -0
  154. adam/utils_repl/__init__.py +0 -0
  155. adam/utils_repl/automata_completer.py +48 -0
  156. adam/utils_repl/repl_completer.py +46 -0
  157. adam/utils_repl/state_machine.py +173 -0
  158. adam/utils_sqlite.py +101 -0
  159. adam/version.py +1 -1
  160. {kaqing-1.98.15.dist-info → kaqing-2.0.145.dist-info}/METADATA +1 -1
  161. kaqing-2.0.145.dist-info/RECORD +227 -0
  162. adam/commands/bash.py +0 -87
  163. adam/commands/cql_utils.py +0 -53
  164. adam/commands/devices.py +0 -89
  165. adam/commands/frontend/setup.py +0 -60
  166. adam/commands/frontend/setup_frontend.py +0 -58
  167. adam/commands/frontend/teardown.py +0 -61
  168. adam/commands/postgres/postgres_session.py +0 -225
  169. adam/commands/user_entry.py +0 -77
  170. adam/k8s_utils/pods.py +0 -211
  171. kaqing-1.98.15.dist-info/RECORD +0 -160
  172. /adam/commands/{frontend → audit}/__init__.py +0 -0
  173. /adam/{k8s_utils → commands/bash}/__init__.py +0 -0
  174. /adam/{medusa_show_restorejobs.py → commands/cql/__init__.py} +0 -0
  175. /adam/{k8s_utils → utils_k8s}/custom_resources.py +0 -0
  176. /adam/{k8s_utils → utils_k8s}/ingresses.py +0 -0
  177. /adam/{k8s_utils → utils_k8s}/services.py +0 -0
  178. {kaqing-1.98.15.dist-info → kaqing-2.0.145.dist-info}/WHEEL +0 -0
  179. {kaqing-1.98.15.dist-info → kaqing-2.0.145.dist-info}/entry_points.txt +0 -0
  180. {kaqing-1.98.15.dist-info → kaqing-2.0.145.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,110 @@
1
+ from kubernetes import client
2
+
3
+ from adam.commands.command import Command
4
+ from adam.commands.deploy.deploy_utils import deploy_frontend, gen_labels
5
+ from adam.commands.deploy.undeploy_pod import UndeployPod
6
+ from adam.config import Config
7
+ from adam.utils_k8s.config_maps import ConfigMaps
8
+ from adam.utils_k8s.deployment import Deployments
9
+ from adam.utils_k8s.kube_context import KubeContext
10
+ from adam.utils_k8s.pods import Pods
11
+ from adam.utils_k8s.service_accounts import ServiceAccounts
12
+ from adam.utils_k8s.volumes import ConfigMapMount
13
+ from adam.repl_state import ReplState, RequiredState
14
+ from adam.utils import ing, log2
15
+
16
+ class DeployPod(Command):
17
+ COMMAND = 'deploy pod'
18
+
19
+ # the singleton pattern
20
+ def __new__(cls, *args, **kwargs):
21
+ if not hasattr(cls, 'instance'): cls.instance = super(DeployPod, cls).__new__(cls)
22
+
23
+ return cls.instance
24
+
25
+ def __init__(self, successor: Command=None):
26
+ super().__init__(successor)
27
+
28
+ def command(self):
29
+ return DeployPod.COMMAND
30
+
31
+ def required(self):
32
+ return RequiredState.NAMESPACE
33
+
34
+ def run(self, cmd: str, state: ReplState):
35
+ if not(args := self.args(cmd)):
36
+ return super().run(cmd, state)
37
+
38
+ state, args = self.apply_state(args, state)
39
+ if not self.validate_state(state):
40
+ return state
41
+
42
+ args, forced = Command.extract_options(args, '--force')
43
+ if forced:
44
+ UndeployPod().run(UndeployPod.COMMAND, state)
45
+
46
+ if KubeContext.in_cluster():
47
+ log2('This is doable only from outside of the Kubernetes cluster.')
48
+ return state
49
+
50
+ sa_name = Config().get('pod.sa.name', 'ops')
51
+ sa_proto = Config().get('pod.sa.proto', 'c3')
52
+ additional_cluster_roles = Config().get('pod.sa.additional-cluster-roles', 'c3aiops-k8ssandra-operator').split(',')
53
+ label_selector = Config().get('pod.label-selector', 'run=ops')
54
+ labels = gen_labels(label_selector)
55
+
56
+ with ing('Creating service account'):
57
+ ServiceAccounts.replicate(sa_name, state.namespace, sa_proto, labels=labels, add_cluster_roles=additional_cluster_roles)
58
+
59
+ settings_filename = 'settings.yaml'
60
+ settings_path = f'/kaqing/{settings_filename}'
61
+ settings_data = None
62
+ try:
63
+ with open(settings_filename, 'r') as file:
64
+ settings_data = file.read()
65
+ except:
66
+ try:
67
+ with open(settings_path, 'r') as file:
68
+ settings_data = file.read()
69
+ except:
70
+ pass
71
+
72
+ if not settings_data:
73
+ log2(f'{settings_filename} not found.')
74
+ return state
75
+
76
+ cm_name = Config().get('pod.cm.name', 'ops')
77
+ map_data = {
78
+ settings_filename : settings_data
79
+ }
80
+ with ing('Creating config map'):
81
+ ConfigMaps.create(cm_name, state.namespace, map_data, labels=labels)
82
+
83
+ pod_name = Config().get('pod.name', 'ops')
84
+ image = Config().get('pod.image', 'seanahnsf/kaqing')
85
+ security_context = client.V1SecurityContext(
86
+ capabilities=client.V1Capabilities(
87
+ add=["SYS_PTRACE"]
88
+ )
89
+ )
90
+ with ing('Creating deployment'):
91
+ Deployments.create(state.namespace,
92
+ pod_name,
93
+ image,
94
+ env={'NAMESPACE': state.namespace},
95
+ container_security_context=security_context,
96
+ labels=labels,
97
+ sa_name=sa_name,
98
+ config_map_mount=ConfigMapMount(cm_name, settings_filename, settings_path))
99
+
100
+ uri = deploy_frontend(pod_name, state.namespace, label_selector)
101
+
102
+ Pods.wait_for_running(state.namespace, pod_name, msg=f'In moments, ops pod will be available at {uri}.', label_selector=label_selector)
103
+
104
+ return state
105
+
106
+ def completion(self, state: ReplState):
107
+ return super().completion(state, {'--force': None})
108
+
109
+ def help(self, _: ReplState):
110
+ return f'{DeployPod.COMMAND} [--force]\t deploy Ops pod, --force to undeploy first'
@@ -0,0 +1,29 @@
1
+ from adam.app_session import AppSession
2
+ from adam.utils import ing
3
+ from adam.utils_k8s.ingresses import Ingresses
4
+ from adam.utils_k8s.services import Services
5
+
6
+ def deploy_frontend(name: str, namespace: str, label_selector: str):
7
+ app_session: AppSession = AppSession.create('c3', 'c3', namespace)
8
+ port = 7678
9
+ labels = gen_labels(label_selector)
10
+ with ing('Creating service'):
11
+ Services.create_service(name, namespace, port, labels, labels=labels)
12
+ with ing('Creating ingress'):
13
+ Ingresses.create_ingress(name, namespace, app_session.host, '/c3/c3/ops($|/)', port, annotations={
14
+ 'kubernetes.io/ingress.class': 'nginx',
15
+ 'nginx.ingress.kubernetes.io/use-regex': 'true',
16
+ 'nginx.ingress.kubernetes.io/rewrite-target': '/'
17
+ }, labels=labels)
18
+
19
+ return f'https://{app_session.host}/c3/c3/ops'
20
+
21
+ def undeploy_frontend(namespace: str, label_selector: str):
22
+ with ing('Deleting ingress'):
23
+ Ingresses.delete_ingresses(namespace, label_selector=label_selector)
24
+ with ing('Deleting service'):
25
+ Services.delete_services(namespace, label_selector=label_selector)
26
+
27
+ def gen_labels(label_selector: str):
28
+ kv = label_selector.split('=')
29
+ return {kv[0]: kv[1]}
@@ -0,0 +1,48 @@
1
+ import click
2
+
3
+ from adam.commands.command import Command
4
+ from adam.commands.deploy.undeploy_frontend import UndeployFrontend
5
+ from adam.commands.deploy.undeploy_pg_agent import UndeployPgAgent
6
+ from adam.commands.deploy.undeploy_pod import UndeployPod
7
+ from adam.repl_state import ReplState, RequiredState
8
+
9
+ class Undeploy(Command):
10
+ COMMAND = 'undeploy'
11
+ reaper_login = None
12
+
13
+ # the singleton pattern
14
+ def __new__(cls, *args, **kwargs):
15
+ if not hasattr(cls, 'instance'): cls.instance = super(Undeploy, 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 Undeploy.COMMAND
24
+
25
+ def required(self):
26
+ return RequiredState.NAMESPACE
27
+
28
+ def run(self, cmd: str, state: ReplState):
29
+ if not(args := self.args(cmd)):
30
+ return super().run(cmd, state)
31
+
32
+ if not self.validate_state(state):
33
+ return state
34
+
35
+ return super().intermediate_run(cmd, state, args, Undeploy.cmd_list())
36
+
37
+ def cmd_list():
38
+ return [UndeployFrontend(), UndeployPod(), UndeployPgAgent()]
39
+
40
+ def completion(self, state: ReplState):
41
+ if state.sts:
42
+ return super().completion(state)
43
+
44
+ return {}
45
+
46
+ class UndeployCommandHelper(click.Command):
47
+ def get_help(self, ctx: click.Context):
48
+ Command.intermediate_help(super().get_help(ctx), Undeploy.COMMAND, Undeploy.cmd_list())
@@ -0,0 +1,41 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.deploy.deploy_utils import undeploy_frontend
3
+ from adam.config import Config
4
+ from adam.repl_state import ReplState, RequiredState
5
+
6
+ class UndeployFrontend(Command):
7
+ COMMAND = 'undeploy frontend'
8
+
9
+ # the singleton pattern
10
+ def __new__(cls, *args, **kwargs):
11
+ if not hasattr(cls, 'instance'): cls.instance = super(UndeployFrontend, 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 UndeployFrontend.COMMAND
20
+
21
+ def required(self):
22
+ return RequiredState.NAMESPACE
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
+ label_selector = Config().get('pod.label-selector', 'run=ops')
33
+ undeploy_frontend(state.namespace, label_selector)
34
+
35
+ return state
36
+
37
+ def completion(self, state: ReplState):
38
+ return super().completion(state)
39
+
40
+ def help(self, _: ReplState):
41
+ return f'{UndeployFrontend.COMMAND}\t undeploy Web frontend'
@@ -0,0 +1,42 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.postgres.postgres_context import PostgresContext
3
+ from adam.config import Config
4
+ from adam.repl_state import ReplState, RequiredState
5
+ from adam.utils import ing
6
+
7
+ class UndeployPgAgent(Command):
8
+ COMMAND = 'undeploy pg-agent'
9
+
10
+ # the singleton pattern
11
+ def __new__(cls, *args, **kwargs):
12
+ if not hasattr(cls, 'instance'): cls.instance = super(UndeployPgAgent, cls).__new__(cls)
13
+
14
+ return cls.instance
15
+
16
+ def __init__(self, successor: Command=None):
17
+ super().__init__(successor)
18
+
19
+ def command(self):
20
+ return UndeployPgAgent.COMMAND
21
+
22
+ def required(self):
23
+ return RequiredState.NAMESPACE
24
+
25
+ def run(self, cmd: str, state: ReplState):
26
+ if not(args := self.args(cmd)):
27
+ return super().run(cmd, state)
28
+
29
+ state, args = self.apply_state(args, state)
30
+ if not self.validate_state(state):
31
+ return state
32
+
33
+ with ing('Deleting pod'):
34
+ PostgresContext.undeploy_pg_agent(Config().get('pg.agent.name', 'ops-pg-agent'), state.namespace)
35
+
36
+ return state
37
+
38
+ def completion(self, state: ReplState):
39
+ return super().completion(state)
40
+
41
+ def help(self, _: ReplState):
42
+ return f'{UndeployPgAgent.COMMAND}\t undeploy Postgres agent'
@@ -0,0 +1,51 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.deploy.deploy_utils import undeploy_frontend
3
+ from adam.config import Config
4
+ from adam.utils import ing
5
+ from adam.utils_k8s.config_maps import ConfigMaps
6
+ from adam.utils_k8s.deployment import Deployments
7
+ from adam.utils_k8s.pods import Pods
8
+ from adam.utils_k8s.service_accounts import ServiceAccounts
9
+ from adam.repl_state import ReplState, RequiredState
10
+
11
+ class UndeployPod(Command):
12
+ COMMAND = 'undeploy pod'
13
+
14
+ # the singleton pattern
15
+ def __new__(cls, *args, **kwargs):
16
+ if not hasattr(cls, 'instance'): cls.instance = super(UndeployPod, cls).__new__(cls)
17
+
18
+ return cls.instance
19
+
20
+ def __init__(self, successor: Command=None):
21
+ super().__init__(successor)
22
+
23
+ def command(self):
24
+ return UndeployPod.COMMAND
25
+
26
+ def required(self):
27
+ return RequiredState.NAMESPACE
28
+
29
+ def run(self, cmd: str, state: ReplState):
30
+ if not(args := self.args(cmd)):
31
+ return super().run(cmd, state)
32
+
33
+ state, args = self.apply_state(args, state)
34
+ if not self.validate_state(state):
35
+ return state
36
+
37
+ label_selector = Config().get('pod.label-selector', 'run=ops')
38
+ with ing('Deleting service account'):
39
+ ServiceAccounts.delete(state.namespace, label_selector=label_selector)
40
+ with ing('Deleting config map'): ConfigMaps.delete_with_selector(state.namespace, label_selector)
41
+ with ing('Deleting deployment'): Deployments.delete_with_selector(state.namespace, label_selector, grace_period_seconds=0)
42
+ with ing('Deleting pod'): Pods.delete_with_selector(state.namespace, label_selector, grace_period_seconds=0)
43
+ undeploy_frontend(state.namespace, label_selector)
44
+
45
+ return state
46
+
47
+ def completion(self, state: ReplState):
48
+ return super().completion(state)
49
+
50
+ def help(self, _: ReplState):
51
+ return f'{UndeployPod.COMMAND}\t undeploy Ops pod'
File without changes
@@ -0,0 +1,27 @@
1
+ from abc import abstractmethod
2
+ from adam.commands.command import Command
3
+ from adam.repl_state import ReplState
4
+
5
+ class Device:
6
+ @abstractmethod
7
+ def ls(self, cmd: str, state: ReplState):
8
+ pass
9
+
10
+ def ls_completion(self, cmd: str, state: ReplState, default: dict = {}):
11
+ return default
12
+
13
+ def cd(self, dir: str, state: ReplState):
14
+ pass
15
+
16
+ def cd_completion(self, cmd: str, state: ReplState, default: dict = {}):
17
+ return default
18
+
19
+ @abstractmethod
20
+ def pwd(self, state: ReplState):
21
+ pass
22
+
23
+ def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
24
+ return False, None
25
+
26
+ def enter(state: ReplState):
27
+ pass
@@ -0,0 +1,146 @@
1
+ from adam.apps import Apps
2
+ from adam.commands.bash.bash import Bash
3
+ from adam.commands.command import Command
4
+ from adam.commands.devices.device import Device
5
+ from adam.config import Config
6
+ from adam.repl_state import ReplState
7
+ from adam.utils import lines_to_tabular, log
8
+ from adam.utils_k8s.app_pods import AppPods
9
+ from adam.utils_k8s.ingresses import Ingresses
10
+
11
+ class DeviceApp(Command, Device):
12
+ COMMAND = f'{ReplState.A}:'
13
+
14
+ # the singleton pattern
15
+ def __new__(cls, *args, **kwargs):
16
+ if not hasattr(cls, 'instance'): cls.instance = super(DeviceApp, cls).__new__(cls)
17
+
18
+ return cls.instance
19
+
20
+ def __init__(self, successor: Command=None):
21
+ super().__init__(successor)
22
+
23
+ def command(self):
24
+ return DeviceApp.COMMAND
25
+
26
+ def run(self, cmd: str, state: ReplState):
27
+ if not self.args(cmd):
28
+ return super().run(cmd, state)
29
+
30
+ state.device = ReplState.A
31
+
32
+ return state
33
+
34
+ def completion(self, state: ReplState):
35
+ return super().completion(state)
36
+
37
+ def help(self, _: ReplState):
38
+ return f'{DeviceApp.COMMAND}\t move to App Operations device'
39
+
40
+ def ls(self, cmd: str, state: ReplState):
41
+ if state.app_pod:
42
+ return Bash().run('bash ' + cmd, state)
43
+ elif state.app_app:
44
+ pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
45
+
46
+ log(lines_to_tabular(pods, 'POD_NAME'))
47
+ elif state.app_env:
48
+ def line(n: str, ns: str):
49
+ host = Ingresses.get_host(Config().get('app.login.ingress', '{app_id}-k8singr-appleader-001').replace('{app_id}', f'{ns}-{n}'), ns)
50
+ if not host:
51
+ return None
52
+
53
+ endpoint = Config().get('app.login.url', 'https://{host}/{env}/{app}').replace('{host}', host).replace('{env}', state.app_env).replace('{app}', 'c3')
54
+ if not endpoint:
55
+ return None
56
+
57
+ return f"{n.split('-')[1]},{Ingresses.get_host(f'{ns}-{n}-k8singr-appleader-001', ns)},{endpoint}"
58
+
59
+ svcs = [l for l in [line(n, ns) for n, ns in Apps.apps(state.app_env)] if l]
60
+
61
+ log(lines_to_tabular(svcs, 'APP,HOST,ENDPOINT', separator=','))
62
+ else:
63
+ svcs = [n for n, ns in Apps.envs()]
64
+
65
+ log(lines_to_tabular(svcs, 'ENV', separator=','))
66
+
67
+
68
+ def ls_completion(self, cmd, state, default: dict = {}):
69
+ if state.app_app:
70
+ def pod_names():
71
+ return [p for p in AppPods.pod_names(state.namespace, state.app_env, state.app_app)]
72
+
73
+ return super().completion(state) | {f'@{p}': {cmd: None} for p in pod_names()}
74
+
75
+ return default
76
+
77
+ def cd(self, dir: str, state: ReplState):
78
+ if dir == '':
79
+ state.app_env = None
80
+ state.app_app = None
81
+ state.app_pod = None
82
+ elif dir == '..':
83
+ if state.app_pod:
84
+ state.app_pod = None
85
+ elif state.app_app:
86
+ state.app_app = None
87
+ else:
88
+ state.app_env = None
89
+ else:
90
+ if state.app_app:
91
+ state.app_pod = dir
92
+ elif not state.app_env:
93
+ tks = dir.split('@')
94
+ if len(tks) > 1:
95
+ state.namespace = tks[1]
96
+
97
+ state.app_env = dir.split('@')[0]
98
+ else:
99
+ state.app_app = dir
100
+
101
+ def cd_completion(self, cmd: str, state: ReplState, default: dict = {}):
102
+ if state.app_app:
103
+ return {cmd: {'..': None} | {pod: None for pod in AppPods.pod_names(state.namespace, state.app_env, state.app_app)}}
104
+ elif state.app_env:
105
+ return {cmd: {'..': None} | {app[0].split('-')[1]: None for app in Apps.apps(state.app_env)}}
106
+ else:
107
+ return {cmd: {'..': None} | {env[0]: None for env in Apps.envs()}}
108
+
109
+ def pwd(self, state: ReplState):
110
+ words = []
111
+
112
+ if state.app_env:
113
+ words.append(f'env/{state.app_env}')
114
+ if state.app_app:
115
+ words.append(f'app/{state.app_app}')
116
+
117
+ return '\t'.join([f'{ReplState.X}:>'] + (words if words else ['/']))
118
+
119
+ def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
120
+ if state.app_app:
121
+ return True, chain.run(f'app {cmd}', state)
122
+
123
+ return False, None
124
+
125
+ def enter(self, state: ReplState):
126
+ if not state.app_env:
127
+ if auto_enter := Config().get('repl.a.auto-enter', 'c3/c3/*'):
128
+ if auto_enter != 'no':
129
+ ea = auto_enter.split('/')
130
+ state.app_env = ea[0]
131
+ if len(ea) > 2:
132
+ state.app_app = ea[1]
133
+ state.app_pod = ea[2]
134
+ if state.app_pod == '*':
135
+ if (pods := AppPods.pod_names(state.namespace, ea[0], ea[1])):
136
+ state.app_pod = pods[0]
137
+ Config().wait_log(f'Moving to {state.app_env}/{state.app_app}/{state.app_pod}...')
138
+ else:
139
+ Config().wait_log(f'No pods found, moving to {state.app_env}/{state.app_app}...')
140
+ else:
141
+ Config().wait_log(f'Moving to {state.app_env}/{state.app_app}/{state.app_pod}...')
142
+ elif len(ea) > 1:
143
+ state.app_app = ea[1]
144
+ Config().wait_log(f'Moving to {state.app_env}/{state.app_app}...')
145
+ else:
146
+ Config().wait_log(f'Moving to {state.app_env}...')
@@ -0,0 +1,43 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.devices.device import Device
3
+ from adam.repl_state import ReplState
4
+ from adam.utils import lines_to_tabular, log
5
+ from adam.utils_athena import Athena
6
+
7
+ class DeviceAuditLog(Command, Device):
8
+ COMMAND = f'{ReplState.L}:'
9
+
10
+ # the singleton pattern
11
+ def __new__(cls, *args, **kwargs):
12
+ if not hasattr(cls, 'instance'): cls.instance = super(DeviceAuditLog, cls).__new__(cls)
13
+
14
+ return cls.instance
15
+
16
+ def __init__(self, successor: Command=None):
17
+ super().__init__(successor)
18
+
19
+ def command(self):
20
+ return DeviceAuditLog.COMMAND
21
+
22
+ def run(self, cmd: str, state: ReplState):
23
+ if not self.args(cmd):
24
+ return super().run(cmd, state)
25
+
26
+ state.device = ReplState.L
27
+
28
+ return state
29
+
30
+ def completion(self, state: ReplState):
31
+ return super().completion(state)
32
+
33
+ def help(self, _: ReplState):
34
+ return f'{DeviceAuditLog.COMMAND}\t move to Audit Log Operations device'
35
+
36
+ def ls(self, cmd: str, _: ReplState):
37
+ log(lines_to_tabular(Athena.table_names(), 'NAME', separator=','))
38
+
39
+ def pwd(self, state: ReplState):
40
+ return '\t'.join([f'{ReplState.L}:>', '/'])
41
+
42
+ def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
43
+ return True, chain.run(f'audit {cmd}', state)
@@ -0,0 +1,145 @@
1
+ from adam.commands.bash.bash import Bash
2
+ from adam.commands.command import Command
3
+ from adam.commands.commands_utils import show_pods, show_rollout
4
+ from adam.commands.devices.device import Device
5
+ from adam.config import Config
6
+ from adam.repl_state import ReplState
7
+ from adam.utils import lines_to_tabular, log, log2
8
+ from adam.utils_k8s.cassandra_clusters import CassandraClusters
9
+ from adam.utils_k8s.custom_resources import CustomResources
10
+ from adam.utils_k8s.kube_context import KubeContext
11
+ from adam.utils_k8s.statefulsets import StatefulSets
12
+
13
+ class DeviceCass(Command, Device):
14
+ COMMAND = f'{ReplState.C}:'
15
+
16
+ # the singleton pattern
17
+ def __new__(cls, *args, **kwargs):
18
+ if not hasattr(cls, 'instance'): cls.instance = super(DeviceCass, cls).__new__(cls)
19
+
20
+ return cls.instance
21
+
22
+ def __init__(self, successor: Command=None):
23
+ super().__init__(successor)
24
+
25
+ def command(self):
26
+ return DeviceCass.COMMAND
27
+
28
+ def run(self, cmd: str, state: ReplState):
29
+ if not self.args(cmd):
30
+ return super().run(cmd, state)
31
+
32
+ state.device = ReplState.C
33
+
34
+ return state
35
+
36
+ def completion(self, state: ReplState):
37
+ return super().completion(state)
38
+
39
+ def help(self, _: ReplState):
40
+ return f'{DeviceCass.COMMAND}\t move to Cassandra Operations device'
41
+
42
+ def ls(self, cmd: str, state: ReplState):
43
+ if state.pod:
44
+ return Bash().run('bash ' + cmd, state)
45
+ elif state.sts and state.namespace:
46
+ show_pods(StatefulSets.pods(state.sts, state.namespace), state.namespace, show_namespace=not KubeContext.in_cluster_namespace())
47
+ show_rollout(state.sts, state.namespace)
48
+ else:
49
+ self.show_statefulsets()
50
+
51
+ def ls_completion(self, cmd: str, state: ReplState, default: dict = {}):
52
+ def pod_names():
53
+ return [p for p in StatefulSets.pod_names(state.sts, state.namespace)]
54
+
55
+ if state.sts:
56
+ return super().completion(state) | {f'@{p}': {'ls': None} for p in pod_names()}
57
+ else:
58
+ return {cmd: {n: None for n in StatefulSets.list_sts_names()}}
59
+
60
+ def show_statefulsets(self):
61
+ ss = StatefulSets.list_sts_names()
62
+ if len(ss) == 0:
63
+ log2('No Cassandra clusters found.')
64
+ return
65
+
66
+ app_ids = CustomResources.get_app_ids()
67
+ list = []
68
+ for s in ss:
69
+ cr_name = CustomResources.get_cr_name(s)
70
+ app_id = 'Unknown'
71
+ if cr_name in app_ids:
72
+ app_id = app_ids[cr_name]
73
+ list.append(f"{s} {app_id}")
74
+
75
+ header = 'STATEFULSET_NAME@NAMESPACE APP_ID'
76
+ if KubeContext.in_cluster_namespace():
77
+ header = 'STATEFULSET_NAME APP_ID'
78
+ log(lines_to_tabular(list, header))
79
+
80
+ def cd(self, dir: str, state: ReplState):
81
+ if dir == '':
82
+ state.sts = None
83
+ state.pod = None
84
+ elif dir == '..':
85
+ if state.pod:
86
+ state.pod = None
87
+ else:
88
+ state.sts = None
89
+ else:
90
+ if not state.sts:
91
+ ss_and_ns = dir.split('@')
92
+ state.sts = ss_and_ns[0]
93
+ if len(ss_and_ns) > 1:
94
+ state.namespace = ss_and_ns[1]
95
+ elif not state.pod:
96
+ p, _ = KubeContext.is_pod_name(dir)
97
+ if p:
98
+ state.pod = p
99
+ else:
100
+ names = CassandraClusters.pod_names_by_host_id(state.sts, state.namespace);
101
+ if dir in names:
102
+ state.pod = names[dir]
103
+ else:
104
+ log2('Not a valid pod name or host id.')
105
+
106
+ def cd_completion(self, cmd: str, state: ReplState, default: dict = {}):
107
+ if state.pod:
108
+ return {cmd: {'..': None}}
109
+ elif state.sts:
110
+ return {cmd: {'..': None} | {p: None for p in StatefulSets.pod_names(state.sts, state.namespace)}}
111
+ else:
112
+ return {cmd: {p: None for p in StatefulSets.list_sts_names()}}
113
+
114
+ def pwd(self, state: ReplState):
115
+ words = []
116
+
117
+ if state.sts:
118
+ words.append(f'sts/{state.sts}')
119
+ if state.pod:
120
+ words.append(f'pod/{state.pod}')
121
+
122
+ return '\t'.join([f'{ReplState.C}:>'] + (words if words else ['/']))
123
+
124
+ def try_fallback_action(self, chain: Command, state: ReplState, cmd: str):
125
+ if state.sts:
126
+ return True, chain.run(f'cql {cmd}', state)
127
+
128
+ return False, None
129
+
130
+ def enter(self, state: ReplState):
131
+ auto_enter = Config().get('repl.c.auto-enter', 'cluster')
132
+ if auto_enter and auto_enter in ['cluster', 'first-pod']:
133
+ sts = StatefulSets.list_sts_name_and_ns()
134
+ if not sts:
135
+ log2("No Cassandra clusters found.")
136
+ elif not state.sts and len(sts) == 1:
137
+ cluster = sts[0]
138
+ state.sts = cluster[0]
139
+ state.namespace = cluster[1]
140
+ if auto_enter == 'first-pod':
141
+ state.pod = f'{state.sts}-0'
142
+ if KubeContext().in_cluster_namespace:
143
+ Config().wait_log(f'Moving to the only Cassandra cluster: {state.sts}...')
144
+ else:
145
+ Config().wait_log(f'Moving to the only Cassandra cluster: {state.sts}@{state.namespace}...')