From 7860d7e0f71e61fd8d0efadd158e8f9fca877e1c Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 16:37:00 +0200 Subject: [PATCH 1/4] Db Update, Endpoint eingerichtet, Ui angepasst --- Watcher/Controllers/MonitoringController.cs | 53 ++- ...20251003135555_ContainerUpdate.Designer.cs | 355 ++++++++++++++++++ .../20251003135555_ContainerUpdate.cs | 113 ++++++ .../Migrations/AppDbContextModelSnapshot.cs | 28 +- Watcher/Models/Container.cs | 19 +- Watcher/Views/Container/Overview.cshtml | 15 +- Watcher/Views/Home/_DashboardStats.cshtml | 8 +- 7 files changed, 533 insertions(+), 58 deletions(-) create mode 100644 Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs create mode 100644 Watcher/Migrations/20251003135555_ContainerUpdate.cs diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 5ab60ff..02d4b4a 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Watcher.Data; @@ -77,6 +78,13 @@ public class MetricDto public double NET_Out { get; set; } // Bytes/s } +public class ServiceDto +{ + public int ServerId { get; set; } // Vom Watcher-Server zugewiesene ID des Hosts + public String? ContainerId { get; set; } + public String? Image { get; set; } + public String? Name { get; set; } +} [ApiController] [Route("[controller]")] @@ -128,10 +136,8 @@ public class MonitoringController : Controller _logger.LogInformation("Agent für '{server}' erfolgreich registriert.", server.Name); return Ok(); } - _logger.LogError("Kein Server für Registrierung gefunden"); return NotFound("No Matching Server found."); - } [HttpGet("server-id-by-ip")] @@ -215,6 +221,47 @@ public class MonitoringController : Controller } + // Endpoint, an dem Agents Ihre laufenden Services registrieren + public async Task ServiceDetection([FromBody] ServiceDto dto) + { + // Gültigkeit des Payloads prüfen + if (!ModelState.IsValid) + { + var errors = ModelState.Values + .SelectMany(v => v.Errors) + .Select(e => e.ErrorMessage) + .ToList(); + + _logger.LogError("Ungültiger ServiceDetection-Payload."); + return BadRequest(new { error = "Ungültiger Payload", details = errors }); + } + + // Metrics für Container, die zu ServerId gehören, löschen + + // Container, die zu ServerId gehören löschen + + // Container neu in Datenbank einlesen + try + { + Container container = new Container + { + ServerId = dto.ServerId, + ContainerId = dto.ContainerId, + Image = dto.Name, + Name = dto.Name + }; + + _context.Containers.Add(container); + await _context.SaveChangesAsync(); + } + catch (SqliteException e) + { + _logger.LogError("ServiceDiscovery failed: " + e.Message); + } + + return Ok(); + + } // Durchschnittliche Werte Berechnen [HttpGet("median")] @@ -317,6 +364,4 @@ public class MonitoringController : Controller return metric_input; } - - } \ No newline at end of file diff --git a/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs b/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs new file mode 100644 index 0000000..052eaa2 --- /dev/null +++ b/Watcher/Migrations/20251003135555_ContainerUpdate.Designer.cs @@ -0,0 +1,355 @@ +// +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("20251003135555_ContainerUpdate")] + partial class ContainerUpdate + { + /// + 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("ContainerId") + .HasColumnType("TEXT"); + + b.Property("Image") + .HasColumnType("TEXT"); + + b.Property("ImageId") + .HasColumnType("INTEGER"); + + b.Property("IsRunning") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("ServerId") + .HasColumnType("INTEGER"); + + b.Property("TagId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + 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("CPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("CPU_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("CpuCores") + .HasColumnType("INTEGER"); + + b.Property("CpuType") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DISK_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("DISK_Temp_Warning") + .HasColumnType("REAL"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("DiskSpace") + .HasColumnType("TEXT"); + + b.Property("Disk_Usage_Critical") + .HasColumnType("REAL"); + + b.Property("Disk_Usage_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Load_Warning") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Critical") + .HasColumnType("REAL"); + + b.Property("GPU_Temp_Warning") + .HasColumnType("REAL"); + + 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("RAM_Load_Critical") + .HasColumnType("REAL"); + + b.Property("RAM_Load_Warning") + .HasColumnType("REAL"); + + 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.Image", null) + .WithMany("Containers") + .HasForeignKey("ImageId"); + + b.HasOne("Watcher.Models.Tag", null) + .WithMany("Containers") + .HasForeignKey("TagId"); + }); + + 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/20251003135555_ContainerUpdate.cs b/Watcher/Migrations/20251003135555_ContainerUpdate.cs new file mode 100644 index 0000000..8074ae7 --- /dev/null +++ b/Watcher/Migrations/20251003135555_ContainerUpdate.cs @@ -0,0 +1,113 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Watcher.Migrations +{ + /// + public partial class ContainerUpdate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Containers_Servers_HostServerId", + table: "Containers"); + + migrationBuilder.DropIndex( + name: "IX_Containers_HostServerId", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "ExposedPort", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "Health", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "HostServerId", + table: "Containers"); + + migrationBuilder.DropColumn( + name: "Status", + table: "Containers"); + + migrationBuilder.RenameColumn( + name: "InternalPort", + table: "Containers", + newName: "ServerId"); + + migrationBuilder.AddColumn( + name: "ContainerId", + table: "Containers", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ContainerId", + table: "Containers"); + + migrationBuilder.RenameColumn( + name: "ServerId", + table: "Containers", + newName: "InternalPort"); + + migrationBuilder.AddColumn( + name: "CreatedAt", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AddColumn( + name: "ExposedPort", + table: "Containers", + type: "INTEGER", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Health", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: ""); + + migrationBuilder.AddColumn( + name: "HostServerId", + table: "Containers", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "Status", + table: "Containers", + type: "TEXT", + nullable: false, + defaultValue: ""); + + 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"); + } + } +} diff --git a/Watcher/Migrations/AppDbContextModelSnapshot.cs b/Watcher/Migrations/AppDbContextModelSnapshot.cs index 7fee30d..b642f34 100644 --- a/Watcher/Migrations/AppDbContextModelSnapshot.cs +++ b/Watcher/Migrations/AppDbContextModelSnapshot.cs @@ -23,45 +23,29 @@ namespace Watcher.Migrations .ValueGeneratedOnAdd() .HasColumnType("INTEGER"); - b.Property("CreatedAt") + b.Property("ContainerId") .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("ServerId") + .HasColumnType("INTEGER"); b.Property("TagId") .HasColumnType("INTEGER"); b.HasKey("Id"); - b.HasIndex("HostServerId"); - b.HasIndex("ImageId"); b.HasIndex("TagId"); @@ -320,10 +304,6 @@ namespace Watcher.Migrations 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"); @@ -331,8 +311,6 @@ namespace Watcher.Migrations b.HasOne("Watcher.Models.Tag", null) .WithMany("Containers") .HasForeignKey("TagId"); - - b.Navigation("HostServer"); }); modelBuilder.Entity("Watcher.Models.LogEvent", b => diff --git a/Watcher/Models/Container.cs b/Watcher/Models/Container.cs index 0101b4f..2807d80 100644 --- a/Watcher/Models/Container.cs +++ b/Watcher/Models/Container.cs @@ -4,22 +4,11 @@ public class Container { public int Id { get; set; } - // Container Details - public string? Name { get; set; } + public int ServerId { get; set; } - public int ExposedPort { get; set; } - - public int InternalPort { get; set; } - - public string Status { get; set; } = string.Empty; - - public string Health { get; set; } = string.Empty; - - public string? Image { get; set; } - - public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public String? ContainerId { get; set; } + public String? Image { get; set; } + public String? Name { get; set; } public Boolean IsRunning { get; set; } = true; - - public Server? HostServer { get; set; } } diff --git a/Watcher/Views/Container/Overview.cshtml b/Watcher/Views/Container/Overview.cshtml index 609b675..07cccb7 100644 --- a/Watcher/Views/Container/Overview.cshtml +++ b/Watcher/Views/Container/Overview.cshtml @@ -10,26 +10,21 @@
- - - - + - @for (int i = 0; i < 3; i++) + @foreach (Container container in Model.Containers) { - - - - - + + + } diff --git a/Watcher/Views/Home/_DashboardStats.cshtml b/Watcher/Views/Home/_DashboardStats.cshtml index dc201ff..dc011e4 100644 --- a/Watcher/Views/Home/_DashboardStats.cshtml +++ b/Watcher/Views/Home/_DashboardStats.cshtml @@ -139,12 +139,12 @@

Services

    - @foreach (var service in Model.Containers) + @foreach (var container in Model.Containers) {
  • - @service.Name - - @service.Status + @container.Name + + @container.Name
  • } From 9a59e10b0c7a988f3ccb6ac513260882ab6e31c5 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 16:45:06 +0200 Subject: [PATCH 2/4] Endpoint Definition --- Watcher/Controllers/MonitoringController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 02d4b4a..2aa68a8 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -222,6 +222,7 @@ public class MonitoringController : Controller } // Endpoint, an dem Agents Ihre laufenden Services registrieren + [HttpPost("service-discovery")] public async Task ServiceDetection([FromBody] ServiceDto dto) { // Gültigkeit des Payloads prüfen From 27792ff7f426cd31c5e787a520da89b74574cea2 Mon Sep 17 00:00:00 2001 From: daniel-hbn Date: Fri, 3 Oct 2025 17:17:28 +0200 Subject: [PATCH 3/4] Test und Log --- Tests/servicediscovery.py | 28 +++++++++++++++++++++ Watcher/Controllers/MonitoringController.cs | 2 ++ 2 files changed, 30 insertions(+) create mode 100644 Tests/servicediscovery.py diff --git a/Tests/servicediscovery.py b/Tests/servicediscovery.py new file mode 100644 index 0000000..d73ad5f --- /dev/null +++ b/Tests/servicediscovery.py @@ -0,0 +1,28 @@ +import json +import urllib.request + +url = "http://localhost:5000/monitoring/service-discovery" +payload = { +"ServerId": 1, +"ContainerId": "aaaaaaaaaaaa", +"Name": "test-Name", +"Image": "test-Image" +} + +data = json.dumps(payload).encode("utf-8") +req = urllib.request.Request( +url, +data=data, +headers={"Content-Type": "application/json"}, +method="POST" +) + +try: + with urllib.request.urlopen(req) as response: + resp_data = response.read().decode("utf-8") + print("Status Code:", response.status) + print("Response:", resp_data) +except Exception as e: + print("Fehler beim Senden der Request:", e) + + diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index 2aa68a8..dc89756 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -252,6 +252,8 @@ public class MonitoringController : Controller Name = dto.Name }; + _logger.LogInformation(container.Name + " added for Host " + container.ServerId); + _context.Containers.Add(container); await _context.SaveChangesAsync(); } From 2169b3d45fc3c9bdfc32d9809dd8c43af1e8dd67 Mon Sep 17 00:00:00 2001 From: triggermeelmo Date: Fri, 3 Oct 2025 23:54:35 +0200 Subject: [PATCH 4/4] Changed Endpoint Names --- Watcher/Controllers/MonitoringController.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Watcher/Controllers/MonitoringController.cs b/Watcher/Controllers/MonitoringController.cs index dc89756..9a61003 100644 --- a/Watcher/Controllers/MonitoringController.cs +++ b/Watcher/Controllers/MonitoringController.cs @@ -12,7 +12,7 @@ using Watcher.ViewModels; namespace Watcher.Controllers; -public class RegistrationDto +public class HardwareDto { // Server Identity [Required] @@ -101,9 +101,9 @@ public class MonitoringController : Controller } - // Endpoint, an dem sich neue Agents registrieren - [HttpPost("register-agent-by-id")] - public async Task Register([FromBody] RegistrationDto dto) + // Endpoint, an den der Agent seine Hardwareinformationen schickt + [HttpPost("hardware-info")] + public async Task Register([FromBody] HardwareDto dto) { // Gültigkeit des Payloads prüfen if (!ModelState.IsValid) @@ -140,7 +140,8 @@ public class MonitoringController : Controller return NotFound("No Matching Server found."); } - [HttpGet("server-id-by-ip")] + // Endpoint, an dem sich ein Agent initial registriert + [HttpGet("register")] public async Task GetServerIdByIp([FromQuery] string IpAddress) { var server = await _context.Servers
Service HostServer StatusSSL/TLSProxy Deployment
testtestOnlinetruetrue@container.Name@container.ServerIdnicht automatisiert Docker