From cf1acebef7e3e16e1335c53e87a1840bbd2f8629 Mon Sep 17 00:00:00 2001 From: Luca Cuzzocrea Date: Wed, 27 Sep 2023 01:42:49 +0200 Subject: [PATCH] Virtualization info --- build.sh | 3 + code/main.cpp | 332 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 277 insertions(+), 58 deletions(-) diff --git a/build.sh b/build.sh index 010f035..b335ef8 100755 --- a/build.sh +++ b/build.sh @@ -39,6 +39,9 @@ CompilerFlags+=" -lpulse" # NetworkManager CompilerFlags+=" "$(pkg-config --libs --cflags libnm) +# Libvirtd +CompilerFlags+=" -lvirt" + # External libs CompilerFlags+=" -Iexternal" ExternalFiles="external/*.cpp" diff --git a/code/main.cpp b/code/main.cpp index cf0a843..dbcbf65 100644 --- a/code/main.cpp +++ b/code/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include bool process_input(); // Returns true when the program needs to exit @@ -24,6 +25,77 @@ void process_gui(); void app_init(); void app_deinit(); +struct App_Data +{ + f64 glib_iteration_t = 0; + f64 system_sample_t = 0; + f64 network_sample_t = 0; + f64 virt_sample_t = 0; + + f64 glib_iteration_delta = 0.1; + f64 system_sample_delta = 0.2; + f64 network_sample_delta = 0.2; + f64 virt_sample_delta = 0.5; +}; + +struct System_Info +{ + char hostname[128]; + char kernel[128]; + time_t time; + char cached_date_string[128]; + char cached_time_string[128]; + + s32 cpus_total; + s32 cpus_active; + + f32 load[3]; + s64 uptime; + + u64 ram_total; + u64 ram_used; + u64 ram_available; +}; + +struct Network_Device +{ + char name[128]; + NMDeviceType type; + NMDeviceState state; +}; + +struct Network_Info +{ + s32 device_count; + Network_Device devices[32]; +}; + +struct Virt_Domain +{ + char name[128]; + s32 state; + + u32 vcpus; + f32 cpu_usage; + u64 prev_cpuTime; + + u64 ram_total; + u64 ram_used; + u64 ram_available; +}; + +struct Virt_Info +{ + s32 domain_count; + Virt_Domain domains[32]; +}; + +System_Info system_info; +Network_Info network_info; +Virt_Info virt_info; +App_Data app_data; + + u32 seconds_to_duration_text(char *text, f64 seconds, bool show_millis = false) { u32 written = 0; @@ -175,66 +247,27 @@ bool process_input() -NMClient *nmclient; -void app_init() -{ - nmclient = nm_client_new(NULL, NULL); -} - -void app_deinit() -{ -} void system_info_window() { Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,10}*engine.gui_scaling, 3, 6, 0.4*engine.gui_scaling); gui_window_start(Rect{0.1*engine.gui_scaling, 0.1*engine.gui_scaling, layout.window_size.x, layout.window_size.y}, 0xabcdef01); - // Hostname - struct utsname host_info; - uname(&host_info); - - char hostname[128] = "Server manager"; - if(host_info.nodename[0]) - strcpy(hostname, host_info.nodename); - char kernel[256]; - sprintf(kernel, "%s %s", host_info.sysname, host_info.release); - - // Clock - time_t time_now = time(NULL); - struct tm *time_info = localtime(&time_now); - char date_string[128]; - strftime(date_string, 128, "%a %e %b %Y", time_info); - char time_string[128]; - strftime(time_string, 128, "%H:%M:%S %Z", time_info); - - - gui_text_aligned(layout.cell(), hostname, GUI_ALIGN_LEFT); - gui_text_aligned(layout.cell(), time_string, GUI_ALIGN_CENTER); - gui_text_aligned(layout.cell(), date_string, GUI_ALIGN_RIGHT); - + // Host date and time + gui_text_aligned(layout.cell(), system_info.hostname, GUI_ALIGN_LEFT); + gui_text_aligned(layout.cell(), system_info.cached_time_string, GUI_ALIGN_CENTER); + gui_text_aligned(layout.cell(), system_info.cached_date_string, GUI_ALIGN_RIGHT); // Load, Memory, Uptime struct sysinfo sys_info; sysinfo(&sys_info); char uptime[128] = "Uptime: "; seconds_to_duration_text(uptime + strlen("Uptime: "), sys_info.uptime); - f32 load_scale = 1.0f / (1 << SI_LOAD_SHIFT); - f32 loads[3] = { - load_scale * sys_info.loads[1], - load_scale * sys_info.loads[1], - load_scale * sys_info.loads[1] - }; - for(int i = 0; i < 3; i++) - loads[i] = round(load_scale * sys_info.loads[i] * 100) / 100; - char load[128]; sprintf(load, "Load: %.2f %.2f %.2f", loads[0], loads[1], loads[2]); - int n_processors = get_nprocs(); - int n_processors_active = get_nprocs_conf(); - char processors[128]; sprintf(processors, "CPUs: %d/%d", n_processors_active, n_processors); + char load[128]; snprintf(load, 128, "Load: %.2f %.2f %.2f", system_info.load[0], system_info.load[1], system_info.load[2]); - u64 ram_total = sys_info.totalram * sys_info.mem_unit; - u64 ram_used = (sys_info.totalram - sys_info.freeram - sys_info.bufferram) * sys_info.mem_unit; - char ram[128]; sprintf(ram, "RAM: %.2f/%.2f GiB", ram_used / (1024.0*1024.0*1024.0), ram_total / (1024.0*1024.0*1024.0)); + char processors[128]; snprintf(processors, 128, "CPUs: %d/%d", system_info.cpus_active, system_info.cpus_total); + + char ram[128]; snprintf(ram, 128, "RAM: %.2f/%.2f GiB", system_info.ram_used / (1024.0*1024.0*1024.0), system_info.ram_total / (1024.0*1024.0*1024.0)); layout.row(2); gui_text_aligned(layout.cell(), processors, GUI_ALIGN_LEFT); @@ -251,17 +284,17 @@ void network_window() Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,12}*engine.gui_scaling, 3, 7, 0.4*engine.gui_scaling); gui_window_start(Rect{0.1*engine.gui_scaling, 11*engine.gui_scaling, layout.window_size.x, layout.window_size.y}, 0xabcdef02); - const GPtrArray *devices = nm_client_get_devices(nmclient); - for(int i = 0; i < devices->len; i++) + Gui_Context *ctx = &global_gui_state.default_context; + + for(s32 i = 0; i < network_info.device_count; i++) { - NMDevice *device = (NMDevice*)devices->pdata[i]; - const char *device_name = nm_device_get_iface(device); - gui_button(layout.cell(), device_name); + Network_Device *device = &network_info.devices[i]; - Gui_Context *ctx = &global_gui_state.default_context; - gui_id_stack_push(ctx, gui_id_from_pointer(ctx, device_name)); + gui_button(layout.cell(), device->name); - switch(nm_device_get_device_type(device)) + gui_id_stack_push(ctx, gui_id_from_pointer(ctx, device->name)); + + switch(device->type) { case NM_DEVICE_TYPE_ETHERNET: gui_button(layout.cell(), "ETHERNET"); break; case NM_DEVICE_TYPE_WIFI: gui_button(layout.cell(), "WIFI"); break; @@ -272,7 +305,7 @@ void network_window() default: gui_button(layout.cell(), ""); break; } - switch(nm_device_get_state(device)) + switch(device->state) { case NM_DEVICE_STATE_UNKNOWN: gui_button(layout.cell(), "UNKNOWN"); break; case NM_DEVICE_STATE_UNMANAGED: gui_button(layout.cell(), "UNMANAGED"); break; @@ -298,18 +331,201 @@ void network_window() void vm_window() { - Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,8}*engine.gui_scaling, 3, 7, 0.4*engine.gui_scaling); + Gui_Layout_Grid layout = gui_layout_grid_create_by_divisions(v2{0,0}, v2{40,7}*engine.gui_scaling, 6, 4, 0.4*engine.gui_scaling); gui_window_start(Rect{0.1*engine.gui_scaling, 24*engine.gui_scaling, layout.window_size.x, layout.window_size.y}, 0xabcdef03); + Gui_Context *ctx = &global_gui_state.default_context; + Gui_Style old_style = ctx->style; + for(s32 i = 0; i < virt_info.domain_count; i++) + { + Virt_Domain *domain = &virt_info.domains[i]; + + // Name and state + switch(domain->state) + { + case VIR_DOMAIN_NOSTATE: + ctx->style.button_color = old_style.button_color; + break; + case VIR_DOMAIN_RUNNING: + ctx->style.button_color = v4{0,1,0,1}; + break; + case VIR_DOMAIN_BLOCKED: + case VIR_DOMAIN_SHUTOFF: + case VIR_DOMAIN_CRASHED: + ctx->style.button_color = v4{1,0,0,1}; + break; + case VIR_DOMAIN_SHUTDOWN: + ctx->style.button_color = v4{0,.6,0,1}; + break; + case VIR_DOMAIN_PAUSED: + case VIR_DOMAIN_PMSUSPENDED: + ctx->style.button_color = v4{.7,.7,0,1}; + break; + default: + ctx->style.button_color = old_style.button_color; + } + gui_button(layout.cell(), domain->name); + + gui_id_stack_push(ctx, gui_id_from_pointer(ctx, domain->name)); + + ctx->style = old_style; + + // CPU usage + char cpu[128]; snprintf(cpu, 128, "%.2f%%", domain->cpu_usage * 100); + gui_button(layout.cell(), cpu); + + char ram[128]; snprintf(ram, 128, "%.2f GB", domain->ram_total / (1024.0*1024.0*1024.0)); + gui_button(layout.cell(), ram); + + char cpu_count[128]; snprintf(cpu_count, 128, "%hd vCPU", domain->vcpus); + gui_button(layout.cell(), cpu_count); + + gui_id_stack_pop(ctx); + layout.row(); + } + + ctx->style = old_style; + gui_window_end(); } + +NMClient *nmclient; +virConnectPtr virt_connection; + +void collect_static_data() +{ + // Hostname + struct utsname host_info; + uname(&host_info); + + system_info.hostname[0] = 0; + if(host_info.nodename[0]) + strncpy(system_info.hostname, host_info.nodename, 128); + snprintf(system_info.kernel, 128, "%s %s", host_info.sysname, host_info.release); +} + +void collect_new_data_if_needed() +{ + if(engine.time - app_data.glib_iteration_t >= app_data.glib_iteration_delta) + { + g_main_context_iteration(NULL, false); + app_data.glib_iteration_t = engine.time; + } + + + if(engine.time - app_data.system_sample_t >= app_data.system_sample_delta) + { + // Clock + system_info.time = time(NULL); + struct tm *time_info = localtime(&system_info.time); + strftime(system_info.cached_date_string, 128, "%a %e %b %Y", time_info); + strftime(system_info.cached_time_string, 128, "%H:%M:%S %Z", time_info); + + // CPU count + system_info.cpus_total = get_nprocs(); + system_info.cpus_active = get_nprocs_conf(); + + // System info for later + struct sysinfo sys_info; + sysinfo(&sys_info); + // Uptime + system_info.uptime = sys_info.uptime; + // Load + system_info.load; + for(int i = 0; i < 3; i++) + system_info.load[i] = round((f32)sys_info.loads[i] / (1 << SI_LOAD_SHIFT) * 100) / 100; + + // Memory + system_info.ram_total = sys_info.totalram * sys_info.mem_unit; + system_info.ram_available = (sys_info.freeram + sys_info.bufferram) * sys_info.mem_unit; + system_info.ram_used = sys_info.totalram * sys_info.mem_unit - system_info.ram_available; + + + app_data.system_sample_t = engine.time; + } + + + if(engine.time - app_data.network_sample_t >= app_data.network_sample_delta) + { + const GPtrArray *devices = nm_client_get_devices(nmclient); + network_info.device_count = devices->len; + for(int i = 0; i < devices->len; i++) + { + NMDevice *device = (NMDevice*)devices->pdata[i]; + + strncpy(network_info.devices[i].name, nm_device_get_iface(device), 128); + network_info.devices[i].type = nm_device_get_device_type(device); + network_info.devices[i].state = nm_device_get_state(device); + } + + + app_data.network_sample_t = engine.time; + } + + + if(engine.time - app_data.virt_sample_t >= app_data.virt_sample_delta) + { + virDomainPtr *domains; + virt_info.domain_count = virConnectListAllDomains(virt_connection, &domains, 0); + + for(int i = 0; i < virt_info.domain_count; i++) + { + Virt_Domain *dom = &virt_info.domains[i]; + // Name + strncpy(dom->name, virDomainGetName(domains[i]), 128); + + // State + virDomainInfo info; + int res = virDomainGetInfo(domains[i], &info); + dom->state = info.state; + + // CPU + dom->vcpus = info.nrVirtCpu; + + dom->cpu_usage = (info.cpuTime - dom->prev_cpuTime) / ((engine.time - app_data.virt_sample_t) * 1e9 * dom->vcpus); + dom->prev_cpuTime = info.cpuTime; + + dom->ram_total = info.memory * 1024; // mem * 1000 or mem * 1024 ?? + dom->ram_used = dom->ram_total; + dom->ram_available = 0; + } + + p_free(domains); + + + app_data.virt_sample_t = engine.time; + } + + +} + void process_gui() { - g_main_context_iteration(NULL, false); + collect_new_data_if_needed(); + + system_info_window(); network_window(); vm_window(); } + +void app_init() +{ + app_data.system_sample_t = 0; + app_data.network_sample_t = 0; + app_data.virt_sample_t = 0; + app_data.glib_iteration_t = 0; + + nmclient = nm_client_new(NULL, NULL); + virt_connection = virConnectOpenReadOnly("qemu:///system"); + + collect_static_data(); +} + +void app_deinit() +{ + virConnectClose(virt_connection); +}