Automata-based approach (draft)
This commit is contained in:
parent
9921116d68
commit
3073d6e9f0
289
kls
289
kls
|
@ -1,11 +1,17 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import curses
|
import curses
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
stdscr = None
|
stdscr = None
|
||||||
|
|
||||||
def filter_words(words, start):
|
|
||||||
return list(filter(lambda s: s.startswith(start), words))
|
class MenuState(Enum):
|
||||||
|
SELECTED_WITHOUT_SEARCH = 1
|
||||||
|
SELECTED_WITH_SEARCH = 2
|
||||||
|
NOT_SELECTED_WITHOUT_SEARCH = 3
|
||||||
|
NOT_SELECTED_WITH_SEARCH = 4
|
||||||
|
|
||||||
|
|
||||||
# я не знаю, что делается в этой функции.
|
# я не знаю, что делается в этой функции.
|
||||||
def init_screen():
|
def init_screen():
|
||||||
|
@ -20,7 +26,8 @@ def init_screen():
|
||||||
init_screen()
|
init_screen()
|
||||||
|
|
||||||
class Menu:
|
class Menu:
|
||||||
def __init__(self, name, rows, begin_x):
|
def __init__(self, name, rows, begin_x, state):
|
||||||
|
self.state = state
|
||||||
self.name = name # заголовок окна
|
self.name = name # заголовок окна
|
||||||
self.rows = rows # строки окна
|
self.rows = rows # строки окна
|
||||||
self.begin_x = begin_x # где начинается окно по х?
|
self.begin_x = begin_x # где начинается окно по х?
|
||||||
|
@ -30,6 +37,7 @@ class Menu:
|
||||||
for index, row in enumerate(self.rows): # рисуем строки
|
for index, row in enumerate(self.rows): # рисуем строки
|
||||||
self.win.addstr(index + 3, 2, row) # + 3 потому что я хочу чтобы строки оборажались ниже заголовка на три строки
|
self.win.addstr(index + 3, 2, row) # + 3 потому что я хочу чтобы строки оборажались ниже заголовка на три строки
|
||||||
self.row = 0 # выбранная строка
|
self.row = 0 # выбранная строка
|
||||||
|
self.search_string = ""
|
||||||
|
|
||||||
|
|
||||||
# рисуем первое меню
|
# рисуем первое меню
|
||||||
|
@ -37,27 +45,27 @@ class Menu:
|
||||||
bytes_list = subprocess.check_output("kubectl get ns --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split()
|
bytes_list = subprocess.check_output("kubectl get ns --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split()
|
||||||
namespaces = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
|
namespaces = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
|
||||||
## отрисовываем меню
|
## отрисовываем меню
|
||||||
menu1 = Menu("Namespaces", namespaces, 0)
|
menu1 = Menu("Namespaces", namespaces, 0, MenuState.SELECTED_WITHOUT_SEARCH)
|
||||||
|
|
||||||
# рисуем второе меню
|
# рисуем второе меню
|
||||||
## готовим контент
|
## готовим контент
|
||||||
api_resources = ["pods", "services", "deployments", "ingresses"]
|
api_resources = ["pods", "services", "deployments", "ingresses"]
|
||||||
## отрисовываем меню
|
## отрисовываем меню
|
||||||
menu2 = Menu("API resources", api_resources, 0 + curses.COLS // 3)
|
menu2 = Menu("API resources", api_resources, 0 + curses.COLS // 3, MenuState.NOT_SELECTED_WITHOUT_SEARCH)
|
||||||
|
|
||||||
# рисуем третье меню
|
# рисуем третье меню
|
||||||
## готовим контент
|
## готовим контент
|
||||||
bytes_list = subprocess.check_output("kubectl get pods -n kube-system --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split()
|
bytes_list = subprocess.check_output("kubectl get pods -n kube-system --no-headers -o template='{{range .items}}{{.metadata.name}} {{end}}'", shell=True).split()
|
||||||
pods = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
|
pods = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
|
||||||
## отрисовываем меню
|
## отрисовываем меню
|
||||||
menu3 = Menu("Resources", pods, 0 + curses.COLS // 3 * 2)
|
menu3 = Menu("Resources", pods, 0 + curses.COLS // 3 * 2, MenuState.NOT_SELECTED_WITHOUT_SEARCH)
|
||||||
|
|
||||||
menus = [menu1, menu2, menu3]
|
menus = [menu1, menu2, menu3]
|
||||||
|
|
||||||
def run_command(command, current_menu, rows=None):
|
def run_command(command, current_menu, rows=None):
|
||||||
namespace = menus[0].rows[menus[0].row]
|
namespace = menu1.rows[menu1.row]
|
||||||
api_resource = menus[1].rows[menus[1].row]
|
api_resource = menu2.rows[menu2.row]
|
||||||
resource = menus[2].rows[menus[2].row]
|
resource = menu3.rows[menu3.row]
|
||||||
subprocess.call(eval(command), shell=True)
|
subprocess.call(eval(command), shell=True)
|
||||||
init_screen()
|
init_screen()
|
||||||
for menu in menus:
|
for menu in menus:
|
||||||
|
@ -102,102 +110,197 @@ def navigate_vertically(direction, current_menu):
|
||||||
run_command(command, current_menu, rows=resources)
|
run_command(command, current_menu, rows=resources)
|
||||||
|
|
||||||
|
|
||||||
|
def filter_menu1():
|
||||||
|
global menu1
|
||||||
|
menu1.win.clear()
|
||||||
|
menu1.win.addstr(curses.LINES - 2, 2, f"/{menu1.search_string}") # рисуем её
|
||||||
|
menu1.win.clrtoeol() # очищаем остальную часть строки
|
||||||
|
menu1.win.addstr(1, 2, menu1.name, curses.A_REVERSE | curses.A_ITALIC) # рисуем заголовок
|
||||||
|
menu1.rows = list(filter(lambda x: (x.startswith(menu1.search_string)), namespaces)) # фильтруем нэймспейсы
|
||||||
|
for index, row in enumerate(menu1.rows): # рисуем то, что отфильтровали
|
||||||
|
menu1.win.addstr(index + 3, 2, row)
|
||||||
|
if menu1.rows:
|
||||||
|
menu1.win.addstr(3, 2, menu1.rows[menu1.row], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
|
||||||
|
menu1.win.box()
|
||||||
|
menu1.win.refresh()
|
||||||
|
|
||||||
|
|
||||||
def main(stdscr):
|
def main(stdscr):
|
||||||
search_mode = False
|
|
||||||
search_string = ""
|
search_string = ""
|
||||||
stdscr.refresh()
|
stdscr.refresh()
|
||||||
running = True
|
running = True
|
||||||
current_menu = 0
|
menu1.win.addstr(1, 2, menu1.name, curses.A_REVERSE | curses.A_ITALIC)
|
||||||
menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC)
|
|
||||||
for menu in menus:
|
for menu in menus:
|
||||||
menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC)
|
menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC)
|
||||||
if menu.name == "Namespaces":
|
|
||||||
menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
|
menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
|
||||||
|
menu.win.refresh()
|
||||||
while running:
|
while running:
|
||||||
[menu.win.refresh() for menu in menus] # refresh all menus
|
|
||||||
key_pressed = stdscr.getkey()
|
key_pressed = stdscr.getkey()
|
||||||
|
match menu1.state:
|
||||||
if current_menu == 0:
|
case MenuState.SELECTED_WITHOUT_SEARCH:
|
||||||
if key_pressed == "/":
|
|
||||||
search_mode = True
|
|
||||||
if search_mode:
|
|
||||||
match key_pressed:
|
match key_pressed:
|
||||||
case '\t' | "KEY_RIGHT":
|
case "/":
|
||||||
current_menu = navigate_horizontally("right", current_menu)
|
menu1.win.addstr(curses.LINES - 2, 2, "/") # рисуем слэш
|
||||||
case "KEY_BTAB" | "KEY_LEFT":
|
menu1.win.clrtoeol() # очищаем остальную часть строки
|
||||||
current_menu = navigate_horizontally("left", current_menu)
|
menu1.win.box() # рисуем рамку
|
||||||
case "KEY_DOWN":
|
menu1.win.refresh()
|
||||||
navigate_vertically("down", current_menu)
|
menu1.state = MenuState.SELECTED_WITH_SEARCH
|
||||||
case "KEY_UP":
|
|
||||||
navigate_vertically("up", current_menu)
|
|
||||||
case "KEY_BACKSPACE":
|
|
||||||
if search_string:
|
|
||||||
search_string = search_string[:-1]
|
|
||||||
else:
|
|
||||||
search_mode = False
|
|
||||||
if key_pressed.isalpha() or key_pressed == "-":
|
|
||||||
search_string += key_pressed
|
|
||||||
elif key_pressed == "/" and search_string == "":
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
continue
|
continue
|
||||||
init_screen()
|
case "q":
|
||||||
for menu in menus:
|
|
||||||
menu.win.clear() # очищаем окно
|
|
||||||
menu.win.box()
|
|
||||||
menu.win.addstr(1, 2, menu.name) # добавляем заголовок окна
|
|
||||||
if menu.name == "Namespaces":
|
|
||||||
menu.rows = list(filter(lambda x: (x.startswith(search_string)), namespaces)) # меняем строки у окна Namespaces
|
|
||||||
menu.row = 0
|
|
||||||
if search_mode:
|
|
||||||
menu.win.addstr(curses.LINES - 2, 2, f"/{search_string}")
|
|
||||||
else:
|
|
||||||
menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
|
|
||||||
elif menu.name == "Resources":
|
|
||||||
if menus[0].rows:
|
|
||||||
namespace = menus[0].rows[menus[0].row]
|
|
||||||
api_resource = menus[1].rows[menus[1].row]
|
|
||||||
command = "f'kubectl get {api_resource} -n {namespace} --no-headers -o template=\"{{{{range .items}}}}{{{{.metadata.name}}}} {{{{end}}}}\"'"
|
|
||||||
bytes_list = subprocess.check_output(eval(command), shell=True).split()
|
|
||||||
resources = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
|
|
||||||
if not resources:
|
|
||||||
resources = [f"No resources found in {namespace} namespace.",]
|
|
||||||
else:
|
|
||||||
resources = ["No namespace selected",]
|
|
||||||
menu.rows = resources
|
|
||||||
|
|
||||||
for index, row in enumerate(menu.rows):
|
|
||||||
menu.win.addstr(index + 3, 2, row)
|
|
||||||
if menu.rows:
|
|
||||||
menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
|
|
||||||
menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC) # помечаем выбранное меню
|
|
||||||
continue
|
|
||||||
|
|
||||||
match key_pressed:
|
|
||||||
case 'q':
|
|
||||||
running = False
|
running = False
|
||||||
case '/':
|
continue
|
||||||
search_mode = True
|
match menu2.state:
|
||||||
case 'g':
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
|
match menu3.state:
|
||||||
run_command("f'kubectl -n {namespace} get {api_resource} {resource} -o yaml | batcat -l yaml --paging always --style numbers'", current_menu)
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
case 'd':
|
pass
|
||||||
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
run_command("f'kubectl -n {namespace} describe {api_resource} {resource} | batcat -l yaml --paging always --style numbers'", current_menu)
|
pass
|
||||||
case 'l':
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources") and menus[1].rows[menus[1].row] == "pods":
|
pass
|
||||||
run_command("f'kubectl -n {namespace} logs {resource} | batcat -l log --paging always --style numbers'", current_menu)
|
case MenuState.SELECTED_WITH_SEARCH:
|
||||||
case 'e':
|
match key_pressed:
|
||||||
if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
|
case "KEY_BACKSPACE":
|
||||||
run_command("f'kubectl edit {api_resource} -n {namespace} {resource}'", current_menu)
|
if menu1.search_string:
|
||||||
case '\t' | "KEY_RIGHT":
|
menu1.search_string = menu1.search_string[:-1] # удаляем символ из строки поиска
|
||||||
current_menu = navigate_horizontally("right", current_menu)
|
filter_menu1()
|
||||||
case "KEY_BTAB" | "KEY_LEFT":
|
else:
|
||||||
current_menu = navigate_horizontally("left", current_menu)
|
menu1.win.addstr(curses.LINES - 2, 2, "Press / for search")
|
||||||
case "KEY_DOWN":
|
menu1.win.box()
|
||||||
navigate_vertically("down", current_menu)
|
menu1.win.refresh()
|
||||||
case "KEY_UP":
|
menu1.state = MenuState.SELECTED_WITHOUT_SEARCH
|
||||||
navigate_vertically("up", current_menu)
|
continue
|
||||||
|
case _:
|
||||||
|
if key_pressed.isalpha() or key_pressed == "-": # namespace не может иметь иных символов кроме a-z и -
|
||||||
|
menu1.search_string += key_pressed
|
||||||
|
filter_menu1()
|
||||||
|
match menu2.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
match menu3.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
match menu2.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
match menu3.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.SELECTED_WITHOUT_SEARCH:
|
||||||
|
match menu3.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
match menu2.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
match menu3.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.SELECTED_WITHOUT_SEARCH:
|
||||||
|
match menu3.state:
|
||||||
|
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.NOT_SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
case MenuState.SELECTED_WITH_SEARCH:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# if current_menu == 0:
|
||||||
|
# if key_pressed == "/":
|
||||||
|
# search_mode = True
|
||||||
|
# if search_mode:
|
||||||
|
# match key_pressed:
|
||||||
|
# case '\t' | "KEY_RIGHT":
|
||||||
|
# current_menu = navigate_horizontally("right", current_menu)
|
||||||
|
# case "KEY_BTAB" | "KEY_LEFT":
|
||||||
|
# current_menu = navigate_horizontally("left", current_menu)
|
||||||
|
# case "KEY_DOWN":
|
||||||
|
# navigate_vertically("down", current_menu)
|
||||||
|
# case "KEY_UP":
|
||||||
|
# navigate_vertically("up", current_menu)
|
||||||
|
# case "KEY_BACKSPACE":
|
||||||
|
# if search_string:
|
||||||
|
# search_string = search_string[:-1]
|
||||||
|
# else:
|
||||||
|
# search_mode = False
|
||||||
|
# if key_pressed.isalpha() or key_pressed == "-":
|
||||||
|
# search_string += key_pressed
|
||||||
|
# elif key_pressed == "/" and search_string == "":
|
||||||
|
# pass
|
||||||
|
# else:
|
||||||
|
# continue
|
||||||
|
# init_screen()
|
||||||
|
# for menu in menus:
|
||||||
|
# menu.win.clear() # очищаем окно
|
||||||
|
# menu.win.box()
|
||||||
|
# menu.win.addstr(1, 2, menu.name) # добавляем заголовок окна
|
||||||
|
# if menu.name == "Namespaces":
|
||||||
|
# menu.rows = list(filter(lambda x: (x.startswith(search_string)), namespaces)) # меняем строки у окна Namespaces
|
||||||
|
# menu.row = 0
|
||||||
|
# if search_mode:
|
||||||
|
# menu.win.addstr(curses.LINES - 2, 2, f"/{search_string}")
|
||||||
|
# else:
|
||||||
|
# menu.win.addstr(curses.LINES - 2, 2, "Press / for search")
|
||||||
|
# elif menu.name == "Resources":
|
||||||
|
# if menus[0].rows:
|
||||||
|
# namespace = menus[0].rows[menus[0].row]
|
||||||
|
# api_resource = menus[1].rows[menus[1].row]
|
||||||
|
# command = "f'kubectl get {api_resource} -n {namespace} --no-headers -o template=\"{{{{range .items}}}}{{{{.metadata.name}}}} {{{{end}}}}\"'"
|
||||||
|
# bytes_list = subprocess.check_output(eval(command), shell=True).split()
|
||||||
|
# resources = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
|
||||||
|
# if not resources:
|
||||||
|
# resources = [f"No resources found in {namespace} namespace.",]
|
||||||
|
# else:
|
||||||
|
# resources = ["No namespace selected",]
|
||||||
|
# menu.rows = resources
|
||||||
|
#
|
||||||
|
# for index, row in enumerate(menu.rows):
|
||||||
|
# menu.win.addstr(index + 3, 2, row)
|
||||||
|
# if menu.rows:
|
||||||
|
# menu.win.addstr(3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
|
||||||
|
# menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC) # помечаем выбранное меню
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# match key_pressed:
|
||||||
|
# case 'q':
|
||||||
|
# running = False
|
||||||
|
# case '/':
|
||||||
|
# search_mode = True
|
||||||
|
# case 'g':
|
||||||
|
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
|
||||||
|
# run_command("f'kubectl -n {namespace} get {api_resource} {resource} -o yaml | batcat -l yaml --paging always --style numbers'", current_menu)
|
||||||
|
# case 'd':
|
||||||
|
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
|
||||||
|
# run_command("f'kubectl -n {namespace} describe {api_resource} {resource} | batcat -l yaml --paging always --style numbers'", current_menu)
|
||||||
|
# case 'l':
|
||||||
|
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources") and menus[1].rows[menus[1].row] == "pods":
|
||||||
|
# run_command("f'kubectl -n {namespace} logs {resource} | batcat -l log --paging always --style numbers'", current_menu)
|
||||||
|
# case 'e':
|
||||||
|
# if current_menu == 2 and not menus[2].rows[menus[2].row].startswith("No resources"):
|
||||||
|
# run_command("f'kubectl edit {api_resource} -n {namespace} {resource}'", current_menu)
|
||||||
|
# case '\t' | "KEY_RIGHT":
|
||||||
|
# current_menu = navigate_horizontally("right", current_menu)
|
||||||
|
# case "KEY_BTAB" | "KEY_LEFT":
|
||||||
|
# current_menu = navigate_horizontally("left", current_menu)
|
||||||
|
# case "KEY_DOWN":
|
||||||
|
# navigate_vertically("down", current_menu)
|
||||||
|
# case "KEY_UP":
|
||||||
|
# navigate_vertically("up", current_menu)
|
||||||
main(stdscr)
|
main(stdscr)
|
||||||
|
|
||||||
curses.nocbreak()
|
curses.nocbreak()
|
||||||
|
|
Loading…
Reference in New Issue