kls/kls

413 lines
21 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
import curses
import subprocess
from enum import Enum
stdscr = None
class MenuState(Enum):
SELECTED_WITHOUT_SEARCH = 1
SELECTED_WITH_SEARCH = 2
NOT_SELECTED_WITHOUT_SEARCH = 3
NOT_SELECTED_WITH_SEARCH = 4
# я не знаю, что делается в этой функции.
def init_screen():
global stdscr
stdscr = curses.initscr()
stdscr.refresh()
curses.noecho()
curses.cbreak()
stdscr.keypad(True)
curses.curs_set(0)
init_screen()
class Menu:
def __init__(self, name, rows, begin_x, state):
self.state = state
self.name = name # заголовок окна
self.rows = rows # строки окна
self.filtered_rows = rows # фильтрованные строки окна
self.begin_x = begin_x # где начинается окно по х?
self.win = curses.newwin(curses.LINES, curses.COLS // 3, 0, begin_x) # окно с высотой во весь экран, шириной экран / 3, и началом по х в точке begin_x
self.win.box() # ?
self.win.addstr(1, 2, self.name) # рисуем заголовок
for index, row in enumerate(self.rows): # рисуем строки
self.win.addstr(index + 3, 2, row) # + 3 потому что я хочу чтобы строки оборажались ниже заголовка на три строки
self.row = 0 # выбранная строка
self.search_string = ""
# рисуем первое меню
## готовим контент
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))]
## отрисовываем меню
menu1 = Menu("Namespaces", namespaces, 0, MenuState.SELECTED_WITHOUT_SEARCH)
# рисуем второе меню
## готовим контент
api_resources = ["pods", "services", "deployments", "ingresses"]
## отрисовываем меню
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()
pods = [bytes_list[i].decode('utf-8') for i in range(len(bytes_list))]
## отрисовываем меню
menu3 = Menu("Resources", pods, 0 + curses.COLS // 3 * 2, MenuState.NOT_SELECTED_WITHOUT_SEARCH)
menus = [menu1, menu2, menu3]
#def run_command(command, menu, rows=None):
# namespace = menu1.rows[menu1.row]
# api_resource = menu2.rows[menu2.row]
# resource = menu3.rows[menu3.row]
# subprocess.call(eval(command), shell=True)
# init_screen()
# for menu in menus:
# menu.win.clear()
# menu.win.box()
# menu.win.addstr(1, 2, menu.name)
# menu.win.addstr(curses.LINES - 2, 2, menu.search_string)
# if rows and menu.name == "Resources":
# menu.rows = rows
# menu.row = 0
# for index, row in enumerate(menu.rows):
# menu.win.addstr(index + 3, 2, row)
# menu.win.addstr(menu.row + 3, 2, menu.rows[menu.row], curses.A_REVERSE | curses.A_ITALIC)
# menu.win.addstr(1, 2, menu.name, curses.A_REVERSE | curses.A_ITALIC)
#def navigate_horizontally(direction, current_menu):
# increment = {"right": 1, "left": -1}
# menus[current_menu].win.addstr(1, 2, menus[current_menu].name) # удаляем выделение с текущего меню
# current_menu = (current_menu + increment[direction]) % 3 # переходим к предыдущему/следующему меню
# menus[current_menu].win.addstr(1, 2, menus[current_menu].name, curses.A_REVERSE | curses.A_ITALIC) # и выделяем его
# return current_menu
def update_menu3(menu):
namespace = menu1.filtered_rows[menu1.row]
api_resource = menu2.filtered_rows[menu2.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.",]
menu3.rows = resources
menu3.filtered_rows = resources
menu3.win.clear()
draw_rows(menu3)
# run_command(command, menu, rows=resources)
def navigate_vertically(direction, menu):
increment = {"down": 1, "up": -1}
if not menu.filtered_rows:
return
if menu.filtered_rows[menu.row].startswith("No resources"): # это касается только третьего меню
return
menu.win.addstr(menu.row + 3, 2, menu.filtered_rows[menu.row]) # удаляем выделение с текущей строки
menu.row = (menu.row + increment[direction]) % len(menu.filtered_rows) # переходим к предыдущей/следующей строке
menu.win.addstr(menu.row + 3, 2, menu.filtered_rows[menu.row], curses.A_REVERSE | curses.A_ITALIC) # и выделяем её
def draw_search_box(menu, content):
menu.win.addstr(curses.LINES - 2, 2, content) # рисуем контент
menu.win.clrtoeol() # очищаем остальную часть строки
menu.win.box() # рисуем рамку
menu.win.refresh() # обновляем окно
def draw_header(menu, selected=False):
if selected:
menu.win.addstr(1, 2, menu.name, curses.A_REVERSE | curses.A_ITALIC)
else:
menu.win.addstr(1, 2, menu.name)
menu.win.refresh() # обновляем окно
def draw_rows(menu):
menu.win.addstr(1, 2, menu.name) # рисуем заголовок
menu.filtered_rows = list(filter(lambda x: (x.startswith(menu.search_string)), menu.rows)) # фильтруем строки
for index, row in enumerate(menu.filtered_rows): # рисуем то, что отфильтровали
menu.win.addstr(index + 3, 2, row)
if menu.filtered_rows:
menu.win.addstr(3, 2, menu.filtered_rows[0], curses.A_REVERSE | curses.A_ITALIC) # выделяем первую строку
menu.win.box()
menu.win.refresh()
def main(stdscr):
stdscr.refresh()
running = True
# начальный экран
for menu in menus:
draw_rows(menu) # рисуем строки меню
draw_search_box(menu, "Press / for search") # рисуем строку поиска
while running:
match menu1.state:
# если первое меню выделено и в нём выключен поиск
case MenuState.SELECTED_WITHOUT_SEARCH:
menu = menu1
draw_header(menu, selected=True)
draw_search_box(menu, "Press / for search") # рисуем строку поиска
key_pressed = stdscr.getkey()
match key_pressed:
case "/":
menu.state = MenuState.SELECTED_WITH_SEARCH
case "q":
running = False
continue
case '\t' | "KEY_RIGHT":
menu.state = MenuState.NOT_SELECTED_WITHOUT_SEARCH
menu2.state = MenuState.SELECTED_WITHOUT_SEARCH
case "KEY_BTAB" | "KEY_LEFT":
menu.state = MenuState.NOT_SELECTED_WITHOUT_SEARCH
menu3.state = MenuState.SELECTED_WITHOUT_SEARCH
case "KEY_DOWN":
navigate_vertically("down", menu)
#update_menu3(menu)
case "KEY_UP":
navigate_vertically("up", menu)
#update_menu3(menu)
match menu2.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
menu = menu2
draw_search_box(menu, "Press / for search") # рисуем строку поиска
match menu3.state:
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
menu = menu3
draw_header(menu)
draw_search_box(menu, "Press / for search")
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
# если первое меню выделено и в нём включен поиск
case MenuState.SELECTED_WITH_SEARCH:
menu = menu1
draw_header(menu, selected=True)
draw_search_box(menu, f"/{menu.search_string}") # рисуем строку поиска
key_pressed = stdscr.getkey()
match key_pressed:
case "KEY_BACKSPACE":
if menu.search_string:
menu.search_string = menu.search_string[:-1] # удаляем символ из строки поиска
menu.win.clear()
draw_search_box(menu, f"/{menu1.search_string}")
draw_rows(menu)
else:
menu.state = MenuState.SELECTED_WITHOUT_SEARCH
continue
case "KEY_DOWN":
navigate_vertically("down", menu)
#update_menu3(menu)
case "KEY_UP":
navigate_vertically("up", menu)
#update_menu3(menu)
case _:
if key_pressed.isalpha() or key_pressed == "-": # namespace не может иметь иных символов кроме a-z и -
menu.search_string += key_pressed
menu.win.clear()
draw_search_box(menu, f"/{menu1.search_string}")
draw_rows(menu)
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:
menu = menu1
draw_header(menu)
draw_search_box(menu, "Press / for search") # рисуем строку поиска
match menu2.state:
# если второе меню не выделено и в нём выключен поиск
case MenuState.NOT_SELECTED_WITHOUT_SEARCH:
menu = menu2
draw_header(menu)
draw_search_box(menu, "Press / for search") # рисуем строку поиска
match menu3.state:
# если третье меню выделено и в нём выключен поиск
case MenuState.SELECTED_WITHOUT_SEARCH:
menu = menu3
draw_header(menu, selected=True)
draw_search_box(menu, "Press / for search") # рисуем строку поиска
key_pressed = stdscr.getkey()
match key_pressed:
case "/":
menu.state = MenuState.SELECTED_WITH_SEARCH
continue
case "q":
running = False
continue
case '\t' | "KEY_RIGHT":
menu.state = MenuState.NOT_SELECTED_WITHOUT_SEARCH
draw_header(menu)
menu1.state = MenuState.SELECTED_WITHOUT_SEARCH
continue
case "KEY_BTAB" | "KEY_LEFT":
menu.state = MenuState.NOT_SELECTED_WITHOUT_SEARCH
draw_header(menu)
menu2.state = MenuState.SELECTED_WITHOUT_SEARCH
case "KEY_DOWN":
navigate_vertically("down", menu)
case "KEY_UP":
navigate_vertically("up", menu)
# если второе меню не выделено и в нём включен поиск
case MenuState.NOT_SELECTED_WITH_SEARCH:
pass
# если второе меню выделено и в нём выключен поиск
case MenuState.SELECTED_WITHOUT_SEARCH:
menu = menu2
draw_header(menu, selected=True)
draw_search_box(menu, "Press / for search")
key_pressed = stdscr.getkey()
match key_pressed:
case "/":
menu.state = MenuState.SELECTED_WITH_SEARCH
continue
case "q":
running = False
continue
case '\t' | "KEY_RIGHT":
menu.state = MenuState.NOT_SELECTED_WITHOUT_SEARCH
menu3.state = MenuState.SELECTED_WITHOUT_SEARCH
continue
case "KEY_BTAB" | "KEY_LEFT":
menu.state = MenuState.NOT_SELECTED_WITHOUT_SEARCH
draw_header(menu)
menu1.state = MenuState.SELECTED_WITHOUT_SEARCH
case "KEY_DOWN":
navigate_vertically("down", menu)
case "KEY_UP":
navigate_vertically("up", menu)
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)
curses.nocbreak()
stdscr.keypad(False)
curses.echo()
curses.endwin()
subprocess.call(["clear"])