moved cpu usage to a grpc call that the gui will listen to, fixed grpc stream typing to be grpc.Future, fixed pygui issue for start callback when a start fails, but there are no exceptions

This commit is contained in:
Blake Harnden 2020-07-23 09:41:39 -07:00
parent 3544d00431
commit fff4bd7963
7 changed files with 85 additions and 47 deletions

View file

@ -467,7 +467,7 @@ class CoreGrpcClient:
session_id: int, session_id: int,
handler: Callable[[core_pb2.Event], None], handler: Callable[[core_pb2.Event], None],
events: List[core_pb2.Event] = None, events: List[core_pb2.Event] = None,
) -> grpc.Channel: ) -> grpc.Future:
""" """
Listen for session events. Listen for session events.
@ -484,7 +484,7 @@ class CoreGrpcClient:
def throughputs( def throughputs(
self, session_id: int, handler: Callable[[core_pb2.ThroughputsEvent], None] self, session_id: int, handler: Callable[[core_pb2.ThroughputsEvent], None]
) -> grpc.Channel: ) -> grpc.Future:
""" """
Listen for throughput events with information for interfaces and bridges. Listen for throughput events with information for interfaces and bridges.
@ -498,6 +498,21 @@ class CoreGrpcClient:
start_streamer(stream, handler) start_streamer(stream, handler)
return stream return stream
def cpu_usage(
self, delay: int, handler: Callable[[core_pb2.CpuUsageEvent], None]
) -> grpc.Future:
"""
Listen for cpu usage events with the given repeat delay.
:param delay: delay between receiving events
:param handler: handler for every event
:return: stream processing events, can be used to cancel stream
"""
request = core_pb2.CpuUsageRequest(delay=delay)
stream = self.stub.CpuUsage(request)
start_streamer(stream, handler)
return stream
def add_node( def add_node(
self, session_id: int, node: core_pb2.Node, source: str = None self, session_id: int, node: core_pb2.Node, source: str = None
) -> core_pb2.AddNodeResponse: ) -> core_pb2.AddNodeResponse:

View file

@ -1,5 +1,6 @@
import logging import logging
import time import time
from pathlib import Path
from typing import Any, Dict, List, Tuple, Type, Union from typing import Any, Dict, List, Tuple, Type, Union
import grpc import grpc
@ -20,6 +21,25 @@ from core.services.coreservices import CoreService
WORKERS = 10 WORKERS = 10
class CpuUsage:
def __init__(self) -> None:
self.stat_file: Path = Path("/proc/stat")
self.prev_idle: int = 0
self.prev_total: int = 0
def run(self) -> float:
lines = self.stat_file.read_text().splitlines()[0]
values = [int(x) for x in lines.split()[1:]]
idle = sum(values[3:5])
non_idle = sum(values[:3] + values[5:8])
total = idle + non_idle
total_diff = total - self.prev_total
idle_diff = idle - self.prev_idle
self.prev_idle = idle
self.prev_total = total
return (total_diff - idle_diff) / total_diff
def add_node_data(node_proto: core_pb2.Node) -> Tuple[NodeTypes, int, NodeOptions]: def add_node_data(node_proto: core_pb2.Node) -> Tuple[NodeTypes, int, NodeOptions]:
""" """
Convert node protobuf message to data for creating a node. Convert node protobuf message to data for creating a node.

View file

@ -681,6 +681,15 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer):
last_stats = stats last_stats = stats
time.sleep(delay) time.sleep(delay)
def CpuUsage(
self, request: core_pb2.CpuUsageRequest, context: ServicerContext
) -> None:
cpu_usage = grpcutils.CpuUsage()
while self._is_running(context):
usage = cpu_usage.run()
yield core_pb2.CpuUsageEvent(usage=usage)
time.sleep(request.delay)
def AddNode( def AddNode(
self, request: core_pb2.AddNodeRequest, context: ServicerContext self, request: core_pb2.AddNodeRequest, context: ServicerContext
) -> core_pb2.AddNodeResponse: ) -> core_pb2.AddNodeResponse:

View file

@ -16,6 +16,7 @@ from core.api.grpc import client
from core.api.grpc.common_pb2 import ConfigOption from core.api.grpc.common_pb2 import ConfigOption
from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig from core.api.grpc.configservices_pb2 import ConfigService, ConfigServiceConfig
from core.api.grpc.core_pb2 import ( from core.api.grpc.core_pb2 import (
CpuUsageEvent,
Event, Event,
ExceptionEvent, ExceptionEvent,
Hook, Hook,
@ -55,6 +56,7 @@ if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
GUI_SOURCE = "gui" GUI_SOURCE = "gui"
CPU_USAGE_DELAY = 3
class CoreClient: class CoreClient:
@ -92,8 +94,9 @@ class CoreClient:
self.hooks: Dict[str, Hook] = {} self.hooks: Dict[str, Hook] = {}
self.emane_config: Dict[str, ConfigOption] = {} self.emane_config: Dict[str, ConfigOption] = {}
self.mobility_players: Dict[int, MobilityPlayer] = {} self.mobility_players: Dict[int, MobilityPlayer] = {}
self.handling_throughputs: Optional[grpc.Channel] = None self.handling_throughputs: Optional[grpc.Future] = None
self.handling_events: Optional[grpc.Channel] = None self.handling_cpu_usage: Optional[grpc.Future] = None
self.handling_events: Optional[grpc.Future] = None
self.xml_dir: Optional[str] = None self.xml_dir: Optional[str] = None
self.xml_file: Optional[str] = None self.xml_file: Optional[str] = None
@ -111,6 +114,7 @@ class CoreClient:
) )
if throughputs_enabled: if throughputs_enabled:
self.enable_throughputs() self.enable_throughputs()
self.setup_cpu_usage()
return self._client return self._client
def reset(self) -> None: def reset(self) -> None:
@ -258,6 +262,20 @@ class CoreClient:
self.handling_events.cancel() self.handling_events.cancel()
self.handling_events = None self.handling_events = None
def cancel_cpu_usage(self) -> None:
if self.handling_cpu_usage:
self.handling_cpu_usage.cancel()
self.handling_cpu_usage = None
def setup_cpu_usage(self) -> None:
if self.handling_cpu_usage and self.handling_cpu_usage.running():
return
if self.handling_cpu_usage:
self.handling_cpu_usage.cancel()
self.handling_cpu_usage = self._client.cpu_usage(
CPU_USAGE_DELAY, self.handle_cpu_event
)
def handle_throughputs(self, event: ThroughputsEvent) -> None: def handle_throughputs(self, event: ThroughputsEvent) -> None:
if event.session_id != self.session_id: if event.session_id != self.session_id:
logging.warning( logging.warning(
@ -269,6 +287,9 @@ class CoreClient:
logging.debug("handling throughputs event: %s", event) logging.debug("handling throughputs event: %s", event)
self.app.after(0, self.app.canvas.set_throughputs, event) self.app.after(0, self.app.canvas.set_throughputs, event)
def handle_cpu_event(self, event: CpuUsageEvent) -> None:
self.app.after(0, self.app.statusbar.set_cpu, event.usage)
def handle_exception_event(self, event: ExceptionEvent) -> None: def handle_exception_event(self, event: ExceptionEvent) -> None:
logging.info("exception event: %s", event) logging.info("exception event: %s", event)
self.app.statusbar.add_alert(event) self.app.statusbar.add_alert(event)
@ -479,6 +500,8 @@ class CoreClient:
""" """
try: try:
self.client.connect() self.client.connect()
self.setup_cpu_usage()
# get service information # get service information
response = self.client.get_services() response = self.client.get_services()
for service in response.services: for service in response.services:

View file

@ -1,10 +1,7 @@
""" """
status bar status bar
""" """
import sched
import tkinter as tk import tkinter as tk
from pathlib import Path
from threading import Thread
from tkinter import ttk from tkinter import ttk
from typing import TYPE_CHECKING, List, Optional from typing import TYPE_CHECKING, List, Optional
@ -16,41 +13,6 @@ if TYPE_CHECKING:
from core.gui.app import Application from core.gui.app import Application
class CpuUsage:
def __init__(self, statusbar: "StatusBar") -> None:
self.scheduler: sched.scheduler = sched.scheduler()
self.running: bool = False
self.thread: Optional[Thread] = None
self.prev_idle: int = 0
self.prev_total: int = 0
self.stat_file: Path = Path("/proc/stat")
self.statusbar: "StatusBar" = statusbar
def start(self) -> None:
self.running = True
self.thread = Thread(target=self._start, daemon=True)
self.thread.start()
def _start(self):
self.scheduler.enter(0, 0, self.run)
self.scheduler.run()
def run(self) -> None:
lines = self.stat_file.read_text().splitlines()[0]
values = [int(x) for x in lines.split()[1:]]
idle = sum(values[3:5])
non_idle = sum(values[:3] + values[5:8])
total = idle + non_idle
total_diff = total - self.prev_total
idle_diff = idle - self.prev_idle
cpu_percent = (total_diff - idle_diff) / total_diff
self.statusbar.after(0, self.statusbar.set_cpu, cpu_percent)
self.prev_idle = idle
self.prev_total = total
if self.running:
self.scheduler.enter(3, 0, self.run)
class StatusBar(ttk.Frame): class StatusBar(ttk.Frame):
def __init__(self, master: tk.Widget, app: "Application") -> None: def __init__(self, master: tk.Widget, app: "Application") -> None:
super().__init__(master) super().__init__(master)
@ -64,8 +26,6 @@ class StatusBar(ttk.Frame):
self.running: bool = False self.running: bool = False
self.core_alarms: List[ExceptionEvent] = [] self.core_alarms: List[ExceptionEvent] = []
self.draw() self.draw()
self.cpu_usage: CpuUsage = CpuUsage(self)
self.cpu_usage.start()
def draw(self) -> None: def draw(self) -> None:
self.columnconfigure(0, weight=7) self.columnconfigure(0, weight=7)

View file

@ -305,10 +305,11 @@ class Toolbar(ttk.Frame):
self.set_runtime() self.set_runtime()
self.app.core.set_metadata() self.app.core.set_metadata()
self.app.core.show_mobility_players() self.app.core.show_mobility_players()
elif response.exceptions: else:
enable_buttons(self.design_frame, enabled=True) enable_buttons(self.design_frame, enabled=True)
message = "\n".join(response.exceptions) if response.exceptions:
self.app.show_error("Start Session Error", message) message = "\n".join(response.exceptions)
self.app.show_error("Start Session Error", message)
def set_runtime(self) -> None: def set_runtime(self) -> None:
enable_buttons(self.runtime_frame, enabled=True) enable_buttons(self.runtime_frame, enabled=True)

View file

@ -51,6 +51,8 @@ service CoreApi {
} }
rpc Throughputs (ThroughputsRequest) returns (stream ThroughputsEvent) { rpc Throughputs (ThroughputsRequest) returns (stream ThroughputsEvent) {
} }
rpc CpuUsage (CpuUsageRequest) returns (stream CpuUsageEvent) {
}
// node rpc // node rpc
rpc AddNode (AddNodeRequest) returns (AddNodeResponse) { rpc AddNode (AddNodeRequest) returns (AddNodeResponse) {
@ -347,6 +349,14 @@ message ThroughputsEvent {
repeated InterfaceThroughput iface_throughputs = 3; repeated InterfaceThroughput iface_throughputs = 3;
} }
message CpuUsageRequest {
int32 delay = 1;
}
message CpuUsageEvent {
double usage = 1;
}
message InterfaceThroughput { message InterfaceThroughput {
int32 node_id = 1; int32 node_id = 1;
int32 iface_id = 2; int32 iface_id = 2;