-
Notifications
You must be signed in to change notification settings - Fork 0
/
nomad.py
150 lines (119 loc) · 4.03 KB
/
nomad.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/env python
import argparse
import logging
import time
from rich.text import Text
from rich.tree import Tree
from textual.app import App, ComposeResult
from textual.containers import Container
from textual.css.query import NoMatches
from textual.screen import Screen
from textual.widget import Widget
from textual.widgets import DataTable, Footer, Header, Static
from backends import NomadCluster
logger = logging.getLogger()
class NomadJobsWidget(DataTable):
def on_mount(self) -> None:
self.add_column("Name")
self.add_column("Status")
self.add_column("Type")
self.add_column("Last deployment")
self.add_column("Subtasks")
self.update_jobs()
self.set_interval(1, self.update_jobs)
def update_jobs(self) -> None:
self.clear()
started = time.monotonic()
self.app.cluster.refresh_jobs()
elapsed = time.monotonic() - started
try:
self.screen.query_one("#stats").update(f"Last refresh took: {elapsed:2.3}s")
except NoMatches:
pass
for name, job in self.app.cluster.jobs.items():
tasks_height = len(job.tasks)
tasks = Tree("Tasks", hide_root=True)
for task_name, task in job.tasks.items():
if job.deployment == "successful":
deployment = Text(job.deployment, style="green")
elif job.deployment == "failed":
deployment = Text(job.deployment, style="red")
else:
deployment = Text(job.deployment, style="dark_orange3")
if job.type == "system":
tasks.add(Text(f"{task_name} ({task.running})"))
else:
style = "green"
if task.running != task.expected:
style = "red"
tasks.add(
Text(
f"{task_name} ({task.running} / {task.expected})",
style=style,
)
)
self.add_row(
name,
job.status,
job.type,
deployment,
tasks,
height=tasks_height,
)
class Status(Widget):
def compose(self) -> None:
yield Static("Cluster URL:", id="cluster")
yield Static("Last refresh took", id="stats")
class Filter(Screen):
"""Display filters"""
DEFAULT_CSS = """
Screen {
margin: 4 8;
layout: grid;
content-align: center middle;
grid-size: 2 2;
}
"""
BINDINGS = [("escape", "app.pop_screen", "close")]
def compose(self) -> ComposeResult:
yield Static("running")
yield Static("dead")
yield Footer()
class NomadMonitor(Screen):
BINDINGS = [("f", "app.push_screen('filter')", "Filters")]
def __init__(self):
super().__init__()
# Force focus on job list
def on_mount(self) -> None:
self.set_focus(self.query_one("#jobs"))
self.query_one("#cluster").update(f"Nomad URL: {self.app.cluster.url}")
def compose(self) -> ComposeResult:
yield Header()
yield Container(
NomadJobsWidget(id="jobs"),
Status(),
)
yield Footer()
class App(App):
BINDINGS = [("q", "quit", "Quit")]
CSS_PATH = "nomad.css"
SCREENS = {
"main": NomadMonitor,
"filter": Filter,
}
def on_mount(self) -> None:
self.capture_mouse(None)
self.cluster: NomadCluster = NomadCluster.from_environ()
self.push_screen("main")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--debug", action="store_true", default=False)
parser.add_argument("--job-type")
# logging.basicConfig(level=logging.DEBUG)
logging.basicConfig()
args = parser.parse_args()
if args.debug:
logger.setLevel(logging.DEBUG)
logger.debug("debug level test message")
app = App()
app.run()