From 0816759bd23826bc661e1dcbfe9e6a5c969d2289 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 11 Jul 2025 08:52:03 +0200 Subject: [PATCH] Monitoring Logik + Container View Fix --- Watcher/Controllers/ContainerController.cs | 2 + Watcher/Controllers/MonitoringController.cs | 94 ++++- Watcher/Controllers/ServerController.cs | 2 +- ...0710090920_container-attribute.Designer.cs | 332 ++++++++++++++++++ .../20250710090920_container-attribute.cs | 119 +++++++ .../Migrations/AppDbContextModelSnapshot.cs | 29 +- .../ViewModels/ContainerOverviewViewModel.cs | 8 +- Watcher/Views/Container/Overview.cshtml | 25 +- Watcher/Views/Server/_ServerCard.cshtml | 5 +- 9 files changed, 588 insertions(+), 28 deletions(-) create mode 100644 Watcher/Migrations/20250710090920_container-attribute.Designer.cs create mode 100644 Watcher/Migrations/20250710090920_container-attribute.cs diff --git a/Watcher/Controllers/ContainerController.cs b/Watcher/Controllers/ContainerController.cs index acba5cf..b18e4dd 100644 --- a/Watcher/Controllers/ContainerController.cs +++ b/Watcher/Controllers/ContainerController.cs @@ -20,9 +20,11 @@ public class ContainerController : Controller public async Task Overview() { var containers = await _context.Containers.ToListAsync(); + var servers = await _context.Servers.ToListAsync(); var viewModel = new ContainerOverviewViewModel { + Servers = servers, Containers = containers }; diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 1cd2061..f85e8de 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -39,16 +39,42 @@ public class MetricDto { // Server Identity [Required] - public int Id { get; set; } + public int ServerId { get; set; } [Required] public string? IpAddress { get; set; } - // Metrics - // CPU Auslastung, CPU Temperatur - // GPU Auslastung, GPU Temperatur - // RAM Auslastung - // Disks? + // Hardware Metrics + // CPU + public double CPU_Load { get; set; } // % + + public double CPU_Temp { get; set; } // deg C + + // GPU + public double GPU_Load { get; set; } // % + + public double GPU_Temp { get; set; } // deg C + + public double GPU_Vram_Size { get; set; } // GB + + public double GPU_Vram_Usage { get; set; } // % + + // RAM + public double RAM_Size { get; set; } // GB + + public double RAM_Load { get; set; } // % + + // Disks + public double DISK_Size { get; set; } // GB + + public double DISK_Usage { get; set; } // % + + public double DISK_Temp { get; set; } // deg C + + // Network + public double NET_In { get; set; } // Bit + + public double NET_Out { get; set; } // Bit } @@ -128,18 +154,64 @@ public class MonitoringController : Controller // Server in Datenbank finden var server = await _context.Servers .FirstOrDefaultAsync(s => s.IPAddress == dto.IpAddress); - + if (server != null) { - // TODO: Serverdaten in Datenbank eintragen + var NewMetric = new Metric + { + Timestamp = DateTime.UtcNow, + ServerId = dto.ServerId, + CPU_Load = dto.CPU_Load, + CPU_Temp = dto.CPU_Temp, + GPU_Load = dto.GPU_Load, + GPU_Temp = dto.GPU_Temp, + GPU_Vram_Size = dto.GPU_Vram_Size, + GPU_Vram_Usage = dto.GPU_Vram_Usage, + RAM_Load = dto.RAM_Load, + RAM_Size = dto.RAM_Size, + DISK_Size = dto.RAM_Size, + DISK_Usage = dto.DISK_Usage, + DISK_Temp = dto.DISK_Temp, + NET_In = dto.NET_In, + NET_Out = dto.NET_Out + }; + try + { + _context.Metrics.Add(NewMetric); + await _context.SaveChangesAsync(); - // Success - _logger.LogInformation("Monitoring-Daten für '{server}' empfangen", server.Name); - return Ok(); + _logger.LogInformation("Monitoring-Daten für '{server}' empfangen", server.Name); + return Ok(); + } + catch + { + // Alert triggern + + _logger.LogError("Metric für {server} konnte nicht in Datenbank geschrieben werden.", server.Name); + return BadRequest(); + } } _logger.LogError("Kein Server für eingegangenen Monitoring-Payload gefunden"); return NotFound("No Matching Server found."); } + + + // Durchschnittliche Werte Berechnen + [HttpGet] + public async Task CalculateMedian(string Metric, int HoursToMonitor, int ServerId) + { + // Aktuelle Zeit - X Stunden = letzter Wert, der berücksichtigt werden soll + DateTime TimeToMonitor = DateTime.Now.AddHours(-HoursToMonitor); + + // Alle Metrics von Server "ServerId" finden, die in der festgelegten Zeitspanne liegen + var MetricsInTimeFrame = await _context.Metrics + .Where(e => e.Timestamp >= TimeToMonitor && e.ServerId == ServerId) + .ToListAsync(); + + // return Action (MetricsInTimeFrame) + + return NotFound(); + } } \ No newline at end of file diff --git a/Watcher/Controllers/ServerController.cs b/Watcher/Controllers/ServerController.cs index 932d36d..8efc86c 100644 --- a/Watcher/Controllers/ServerController.cs +++ b/Watcher/Controllers/ServerController.cs @@ -153,7 +153,7 @@ public class ServerController : Controller await _context.SaveChangesAsync(); } - return PartialView("_ServerCard", servers); // korrekt + return PartialView("_ServerCard", servers); } diff --git a/Watcher/Migrations/20250710090920_container-attribute.Designer.cs b/Watcher/Migrations/20250710090920_container-attribute.Designer.cs new file mode 100644 index 0000000..deb4c2c --- /dev/null +++ b/Watcher/Migrations/20250710090920_container-attribute.Designer.cs @@ -0,0 +1,332 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Watcher.Data; + +#nullable disable + +namespace Watcher.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20250710090920_container-attribute")] + partial class containerattribute + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.6"); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ExposedPort") + .HasColumnType("INTEGER"); + + b.Property("Health") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("HostServerId") + .HasColumnType("INTEGER"); + + b.Property("Image") + .HasColumnType("TEXT"); + + b.Property("ImageId") + .HasColumnType("INTEGER"); + + b.Property("InternalPort") + .HasColumnType("INTEGER"); + + b.Property("IsRunning") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Status") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("HostServerId"); + + b.HasIndex("ImageId"); + + b.HasIndex("TagId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Tag") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ContainerId") + .HasColumnType("INTEGER"); + + b.Property("Level") + .HasColumnType("TEXT"); + + b.Property("Message") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("ServerId"); + + b.ToTable("LogEvents"); + }); + + modelBuilder.Entity("Watcher.Models.Metric", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CPU_Load") + .HasColumnType("REAL"); + + b.Property("CPU_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Size") + .HasColumnType("REAL"); + + b.Property("DISK_Temp") + .HasColumnType("REAL"); + + b.Property("DISK_Usage") + .HasColumnType("REAL"); + + b.Property("GPU_Load") + .HasColumnType("REAL"); + + b.Property("GPU_Temp") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Size") + .HasColumnType("REAL"); + + b.Property("GPU_Vram_Usage") + .HasColumnType("REAL"); + + b.Property("NET_In") + .HasColumnType("REAL"); + + b.Property("NET_Out") + .HasColumnType("REAL"); + + b.Property("RAM_Load") + .HasColumnType("REAL"); + + b.Property("RAM_Size") + .HasColumnType("REAL"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Metrics"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("GpuType") + .HasColumnType("TEXT"); + + b.Property("IPAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsOnline") + .HasColumnType("INTEGER"); + + b.Property("IsVerified") + .HasColumnType("INTEGER"); + + b.Property("LastSeen") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("RamSize") + .HasColumnType("REAL"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TagId"); + + b.ToTable("Servers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Tags"); + }); + + modelBuilder.Entity("Watcher.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("IdentityProvider") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastLogin") + .HasColumnType("TEXT"); + + b.Property("OIDC_Id") + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Watcher.Models.Container", b => + { + b.HasOne("Watcher.Models.Server", "HostServer") + .WithMany() + .HasForeignKey("HostServerId"); + + b.HasOne("Watcher.Models.Image", null) + .WithMany("Containers") + .HasForeignKey("ImageId"); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + + b.Navigation("HostServer"); + }); + + modelBuilder.Entity("Watcher.Models.LogEvent", b => + { + b.HasOne("Watcher.Models.Container", "Container") + .WithMany() + .HasForeignKey("ContainerId"); + + b.HasOne("Watcher.Models.Server", "Server") + .WithMany() + .HasForeignKey("ServerId"); + + b.Navigation("Container"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Watcher.Models.Server", b => + { + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Servers") + .HasForeignKey("TagId"); + }); + + modelBuilder.Entity("Watcher.Models.Image", b => + { + b.Navigation("Containers"); + }); + + modelBuilder.Entity("Watcher.Models.Tag", b => + { + b.Navigation("Containers"); + + b.Navigation("Servers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Watcher/Migrations/20250710090920_container-attribute.cs b/Watcher/Migrations/20250710090920_container-attribute.cs new file mode 100644 index 0000000..7af4972 --- /dev/null +++ b/Watcher/Migrations/20250710090920_container-attribute.cs @@ -0,0 +1,119 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class containerattribute : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Hostname", + table: "Containers"); + + migrationBuilder.RenameColumn( + name: "Type", + table: "Containers", + newName: "Health"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Containers", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AddColumn( + name: "ExposedPort", + table: "Containers", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "HostServerId", + table: "Containers", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "Image", + table: "Containers", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "InternalPort", + table: "Containers", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_Containers_HostServerId", + table: "Containers", + column: "HostServerId"); + + migrationBuilder.AddForeignKey( + name: "FK_Containers_Servers_HostServerId", + table: "Containers", + column: "HostServerId", + principalTable: "Servers", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Containers_Servers_HostServerId", + table: "Containers"); + + migrationBuilder.DropIndex( + name: "IX_Containers_HostServerId", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "ExposedPort", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "HostServerId", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "Image", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "InternalPort", + table: "Containers"); + + migrationBuilder.RenameColumn( + name: "Health", + table: "Containers", + newName: "Type"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "Hostname", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: ""); + } + } +} diff --git a/Watcher/Migrations/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index 11d908f..cbd7e77 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -26,18 +26,29 @@ namespace Watcher.Migrations b.Property("CreatedAt") .HasColumnType("TEXT"); - b.Property("Hostname") + b.Property("ExposedPort") + .HasColumnType("INTEGER"); + + b.Property("Health") .IsRequired() .HasColumnType("TEXT"); + b.Property("HostServerId") + .HasColumnType("INTEGER"); + + b.Property("Image") + .HasColumnType("TEXT"); + b.Property("ImageId") .HasColumnType("INTEGER"); + b.Property("InternalPort") + .HasColumnType("INTEGER"); + b.Property("IsRunning") .HasColumnType("INTEGER"); b.Property("Name") - .IsRequired() .HasColumnType("TEXT"); b.Property("Status") @@ -47,12 +58,10 @@ namespace Watcher.Migrations b.Property("TagId") .HasColumnType("INTEGER"); - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT"); - b.HasKey("Id"); + b.HasIndex("HostServerId"); + b.HasIndex("ImageId"); b.HasIndex("TagId"); @@ -266,7 +275,11 @@ namespace Watcher.Migrations modelBuilder.Entity("Watcher.Models.Container", b => { - b.HasOne("Watcher.Models.Image", "Image") + b.HasOne("Watcher.Models.Server", "HostServer") + .WithMany() + .HasForeignKey("HostServerId"); + + b.HasOne("Watcher.Models.Image", null) .WithMany("Containers") .HasForeignKey("ImageId"); @@ -274,7 +287,7 @@ namespace Watcher.Migrations .WithMany("Containers") .HasForeignKey("TagId"); - b.Navigation("Image"); + b.Navigation("HostServer"); }); modelBuilder.Entity("Watcher.Models.LogEvent", b => diff --git a/Watcher/ViewModels/ContainerOverviewViewModel.cs b/Watcher/ViewModels/ContainerOverviewViewModel.cs index e075100..dbb4422 100644 --- a/Watcher/ViewModels/ContainerOverviewViewModel.cs +++ b/Watcher/ViewModels/ContainerOverviewViewModel.cs @@ -3,8 +3,10 @@ using Watcher.Models; namespace Watcher.ViewModels { public class ContainerOverviewViewModel -{ - public List Containers { get; set; } = new(); -} + { + public List Containers { get; set; } = new(); + + public List Servers { get; set; } = new(); + } } diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index 9ec20d4..c3b1005 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -4,13 +4,30 @@ }
- @foreach (var container in Model.Containers) + @foreach (var server in Model.Servers) {
-

@container.Name

-

Image: @container.Image

+

@server.Name

+
+ +
+ @if (Model.Containers.Count > 0) + { + + @foreach (var container in Model.Containers) + { + test + if (container.HostServer.Equals(server.Name)) + { + + } + } +
+ } else + { +

keine Container gefunden

+ } -

Status: @(container.IsRunning ? "online" : "offline")

}
diff --git a/Watcher/Views/Server/_ServerCard.cshtml b/Watcher/Views/Server/_ServerCard.cshtml index bedd9fa..ec6cebf 100644 --- a/Watcher/Views/Server/_ServerCard.cshtml +++ b/Watcher/Views/Server/_ServerCard.cshtml @@ -42,12 +42,15 @@ Metrics + + Container + +
-