diff --git a/Moonlight/App/ApiClients/Daemon/DaemonApiHelper.cs b/Moonlight/App/ApiClients/Daemon/DaemonApiHelper.cs index 7cdf11ae..c9d14065 100644 --- a/Moonlight/App/ApiClients/Daemon/DaemonApiHelper.cs +++ b/Moonlight/App/ApiClients/Daemon/DaemonApiHelper.cs @@ -1,5 +1,4 @@ using Moonlight.App.Database.Entities; -using Moonlight.App.Exceptions; using Newtonsoft.Json; using RestSharp; @@ -14,26 +13,13 @@ public class DaemonApiHelper Client = new(); } - private string GetApiUrl(Node node) - { - /* SSL not implemented in moonlight daemon - if(node.Ssl) - return $"https://{node.Fqdn}:{node.MoonlightDaemonPort}/"; - else - return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/";*/ - - return $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; - } - public async Task Get(Node node, string resource) { - RestRequest request = new(GetApiUrl(node) + resource); + var request = await CreateRequest(node, resource); - request.AddHeader("Content-Type", "application/json"); - request.AddHeader("Accept", "application/json"); - request.AddHeader("Authorization", node.Token); - - var response = await Client.GetAsync(request); + request.Method = Method.Get; + + var response = await Client.ExecuteAsync(request); if (!response.IsSuccessful) { @@ -52,4 +38,69 @@ public class DaemonApiHelper return JsonConvert.DeserializeObject(response.Content!)!; } + + public async Task Post(Node node, string resource, object body) + { + var request = await CreateRequest(node, resource); + + request.Method = Method.Post; + + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new DaemonException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + public async Task Delete(Node node, string resource, object body) + { + var request = await CreateRequest(node, resource); + + request.Method = Method.Delete; + + request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody); + + var response = await Client.ExecuteAsync(request); + + if (!response.IsSuccessful) + { + if (response.StatusCode != 0) + { + throw new DaemonException( + $"An error occured: ({response.StatusCode}) {response.Content}", + (int)response.StatusCode + ); + } + else + { + throw new Exception($"An internal error occured: {response.ErrorMessage}"); + } + } + } + + private Task CreateRequest(Node node, string resource) + { + var url = $"http://{node.Fqdn}:{node.MoonlightDaemonPort}/"; + + RestRequest request = new(url + resource); + + request.AddHeader("Content-Type", "application/json"); + request.AddHeader("Accept", "application/json"); + request.AddHeader("Authorization", node.Token); + + return Task.FromResult(request); + } } \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Requests/Mount.cs b/Moonlight/App/ApiClients/Daemon/Requests/Mount.cs new file mode 100644 index 00000000..fb69f557 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Requests/Mount.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.ApiClients.Daemon.Requests; + +public class Mount +{ + public string Server { get; set; } = ""; + public string ServerPath { get; set; } = ""; + public string Path { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Requests/Unmount.cs b/Moonlight/App/ApiClients/Daemon/Requests/Unmount.cs new file mode 100644 index 00000000..c18fe8e1 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Requests/Unmount.cs @@ -0,0 +1,6 @@ +namespace Moonlight.App.ApiClients.Daemon.Requests; + +public class Unmount +{ + public string Path { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/Container.cs b/Moonlight/App/ApiClients/Daemon/Resources/Container.cs new file mode 100644 index 00000000..25741299 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Resources/Container.cs @@ -0,0 +1,10 @@ +namespace Moonlight.App.ApiClients.Daemon.Resources; + +public class Container +{ + public string Name { get; set; } = ""; + public long Memory { get; set; } + public double Cpu { get; set; } + public long NetworkIn { get; set; } + public long NetworkOut { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/ContainerStats.cs b/Moonlight/App/ApiClients/Daemon/Resources/ContainerStats.cs deleted file mode 100644 index b8b30c25..00000000 --- a/Moonlight/App/ApiClients/Daemon/Resources/ContainerStats.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Moonlight.App.ApiClients.Daemon.Resources; - -public class ContainerStats -{ - public List Containers { get; set; } = new(); - - public class Container - { - public string Name { get; set; } - public long Memory { get; set; } - public double Cpu { get; set; } - public long NetworkIn { get; set; } - public long NetworkOut { get; set; } - } -} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/CpuMetrics.cs b/Moonlight/App/ApiClients/Daemon/Resources/CpuMetrics.cs new file mode 100644 index 00000000..d954b945 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Resources/CpuMetrics.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.ApiClients.Daemon.Resources; + +public class CpuMetrics +{ + public string CpuModel { get; set; } = ""; + public double CpuUsage { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/CpuStats.cs b/Moonlight/App/ApiClients/Daemon/Resources/CpuStats.cs deleted file mode 100644 index ff72425f..00000000 --- a/Moonlight/App/ApiClients/Daemon/Resources/CpuStats.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Moonlight.App.ApiClients.Daemon.Resources; - -public class CpuStats -{ - public double Usage { get; set; } - public int Cores { get; set; } - public string Model { get; set; } = ""; -} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/DiskMetrics.cs b/Moonlight/App/ApiClients/Daemon/Resources/DiskMetrics.cs new file mode 100644 index 00000000..b301f20f --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Resources/DiskMetrics.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.ApiClients.Daemon.Resources; + +public class DiskMetrics +{ + public long Used { get; set; } + public long Total { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/DiskStats.cs b/Moonlight/App/ApiClients/Daemon/Resources/DiskStats.cs deleted file mode 100644 index 4bcd8467..00000000 --- a/Moonlight/App/ApiClients/Daemon/Resources/DiskStats.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Moonlight.App.ApiClients.Daemon.Resources; - -public class DiskStats -{ - public long FreeBytes { get; set; } - public string DriveFormat { get; set; } - public string Name { get; set; } - public long TotalSize { get; set; } -} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/DockerMetrics.cs b/Moonlight/App/ApiClients/Daemon/Resources/DockerMetrics.cs new file mode 100644 index 00000000..80d44ed2 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Resources/DockerMetrics.cs @@ -0,0 +1,6 @@ +namespace Moonlight.App.ApiClients.Daemon.Resources; + +public class DockerMetrics +{ + public Container[] Containers { get; set; } = Array.Empty(); +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/MemoryMetrics.cs b/Moonlight/App/ApiClients/Daemon/Resources/MemoryMetrics.cs new file mode 100644 index 00000000..cb3fd8d4 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Resources/MemoryMetrics.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.ApiClients.Daemon.Resources; + +public class MemoryMetrics +{ + public long Used { get; set; } + public long Total { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/MemoryStats.cs b/Moonlight/App/ApiClients/Daemon/Resources/MemoryStats.cs deleted file mode 100644 index 6e3a20bf..00000000 --- a/Moonlight/App/ApiClients/Daemon/Resources/MemoryStats.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Moonlight.App.ApiClients.Daemon.Resources; - -public class MemoryStats -{ - public List Sticks { get; set; } = new(); - public double Free { get; set; } - public double Used { get; set; } - public double Total { get; set; } - - public class MemoryStick - { - public int Size { get; set; } - public string Type { get; set; } = ""; - } -} \ No newline at end of file diff --git a/Moonlight/App/ApiClients/Daemon/Resources/SystemMetrics.cs b/Moonlight/App/ApiClients/Daemon/Resources/SystemMetrics.cs new file mode 100644 index 00000000..28c6bb72 --- /dev/null +++ b/Moonlight/App/ApiClients/Daemon/Resources/SystemMetrics.cs @@ -0,0 +1,7 @@ +namespace Moonlight.App.ApiClients.Daemon.Resources; + +public class SystemMetrics +{ + public string OsName { get; set; } = ""; + public long Uptime { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/Formatter.cs b/Moonlight/App/Helpers/Formatter.cs index 99a8ab62..929a7bb9 100644 --- a/Moonlight/App/Helpers/Formatter.cs +++ b/Moonlight/App/Helpers/Formatter.cs @@ -116,4 +116,12 @@ public static class Formatter return (i / (1024D * 1024D)).Round(2) + " GB"; } } + + public static double BytesToGb(long bytes) + { + const double gbMultiplier = 1024 * 1024 * 1024; // 1 GB = 1024 MB * 1024 KB * 1024 B + + double gigabytes = (double)bytes / gbMultiplier; + return gigabytes; + } } \ No newline at end of file diff --git a/Moonlight/App/Models/Misc/RunningServer.cs b/Moonlight/App/Models/Misc/RunningServer.cs index 3b00c257..6bf9a14e 100644 --- a/Moonlight/App/Models/Misc/RunningServer.cs +++ b/Moonlight/App/Models/Misc/RunningServer.cs @@ -6,5 +6,5 @@ namespace Moonlight.App.Models.Misc; public class RunningServer { public Server Server { get; set; } - public ContainerStats.Container Container { get; set; } + public Container Container { get; set; } } \ No newline at end of file diff --git a/Moonlight/App/Services/Background/CleanupService.cs b/Moonlight/App/Services/Background/CleanupService.cs index 6491db28..0a99bea6 100644 --- a/Moonlight/App/Services/Background/CleanupService.cs +++ b/Moonlight/App/Services/Background/CleanupService.cs @@ -5,6 +5,7 @@ using Moonlight.App.ApiClients.Daemon.Resources; using Moonlight.App.ApiClients.Wings; using Moonlight.App.Database.Entities; using Moonlight.App.Events; +using Moonlight.App.Helpers; using Moonlight.App.Repositories; using Moonlight.App.Repositories.Servers; using Newtonsoft.Json; @@ -81,12 +82,12 @@ public class CleanupService { try { - var cpuStats = await nodeService.GetCpuStats(node); - var memoryStats = await nodeService.GetMemoryStats(node); + var cpuMetrics = await nodeService.GetCpuMetrics(node); + var memoryMetrics = await nodeService.GetMemoryMetrics(node); - if (cpuStats.Usage > maxCpu || memoryStats.Free < minMemory) + if (cpuMetrics.CpuUsage > maxCpu || (Formatter.BytesToGb(memoryMetrics.Total) - (Formatter.BytesToGb(memoryMetrics.Used))) < minMemory) { - var containerStats = await nodeService.GetContainerStats(node); + var dockerMetrics = await nodeService.GetDockerMetrics(node); var serverRepository = scope.ServiceProvider.GetRequiredService(); var imageRepository = scope.ServiceProvider.GetRequiredService(); @@ -101,9 +102,9 @@ public class CleanupService ) .ToArray(); - var containerMappedToServers = new Dictionary(); + var containerMappedToServers = new Dictionary(); - foreach (var container in containerStats.Containers) + foreach (var container in dockerMetrics.Containers) { if (Guid.TryParse(container.Name, out Guid uuid)) { diff --git a/Moonlight/App/Services/NodeService.cs b/Moonlight/App/Services/NodeService.cs index a2607086..1a4d0288 100644 --- a/Moonlight/App/Services/NodeService.cs +++ b/Moonlight/App/Services/NodeService.cs @@ -1,4 +1,5 @@ using Moonlight.App.ApiClients.Daemon; +using Moonlight.App.ApiClients.Daemon.Requests; using Moonlight.App.ApiClients.Daemon.Resources; using Moonlight.App.ApiClients.Wings; using Moonlight.App.ApiClients.Wings.Resources; @@ -24,35 +25,56 @@ public class NodeService return await WingsApiHelper.Get(node, "api/system"); } - public async Task GetCpuStats(Node node) + public async Task GetCpuMetrics(Node node) { - return await DaemonApiHelper.Get(node, "stats/cpu"); + return await DaemonApiHelper.Get(node, "metrics/cpu"); } - public async Task GetMemoryStats(Node node) + public async Task GetMemoryMetrics(Node node) { - return await DaemonApiHelper.Get(node, "stats/memory"); + return await DaemonApiHelper.Get(node, "metrics/memory"); } - public async Task GetDiskStats(Node node) + public async Task GetDiskMetrics(Node node) { - return await DaemonApiHelper.Get(node, "stats/disk"); + return await DaemonApiHelper.Get(node, "metrics/disk"); } - public async Task GetContainerStats(Node node) + public async Task GetSystemMetrics(Node node) { - return await DaemonApiHelper.Get(node, "stats/container"); + return await DaemonApiHelper.Get(node, "metrics/system"); + } + + public async Task GetDockerMetrics(Node node) + { + return await DaemonApiHelper.Get(node, "metrics/docker"); + } + + public async Task Mount(Node node, string server, string serverPath, string path) + { + await DaemonApiHelper.Post(node, "mount", new Mount() + { + Server = server, + ServerPath = serverPath, + Path = path + }); + } + + public async Task Unmount(Node node, string path) + { + await DaemonApiHelper.Delete(node, "mount", new Unmount() + { + Path = path + }); } public async Task IsHostUp(Node node) { try { - //TODO: Implement status caching - var data = await GetStatus(node); + await GetSystemMetrics(node); - if (data != null) - return true; + return true; } catch (Exception) { diff --git a/Moonlight/App/Services/SmartDeployService.cs b/Moonlight/App/Services/SmartDeployService.cs index 88f6ff84..3aaaf4c1 100644 --- a/Moonlight/App/Services/SmartDeployService.cs +++ b/Moonlight/App/Services/SmartDeployService.cs @@ -74,17 +74,17 @@ public class SmartDeployService try { - var cpuStats = await NodeService.GetCpuStats(node); - var memoryStats = await NodeService.GetMemoryStats(node); - var diskStats = await NodeService.GetDiskStats(node); + var cpuMetrics = await NodeService.GetCpuMetrics(node); + var memoryMetrics = await NodeService.GetMemoryMetrics(node); + var diskMetrics = await NodeService.GetDiskMetrics(node); var cpuWeight = 0.5; // Weight of CPU usage in the final score var memoryWeight = 0.3; // Weight of memory usage in the final score var diskSpaceWeight = 0.2; // Weight of free disk space in the final score - var cpuScore = (1 - cpuStats.Usage) * cpuWeight; // CPU score is based on the inverse of CPU usage - var memoryScore = (1 - (memoryStats.Used / 1024)) * memoryWeight; // Memory score is based on the percentage of free memory - var diskSpaceScore = (double) diskStats.FreeBytes / 1000000000 * diskSpaceWeight; // Disk space score is based on the amount of free disk space in GB + var cpuScore = (1 - cpuMetrics.CpuUsage) * cpuWeight; // CPU score is based on the inverse of CPU usage + var memoryScore = (1 - (memoryMetrics.Used / 1024)) * memoryWeight; // Memory score is based on the percentage of free memory + var diskSpaceScore = (double) (diskMetrics.Total - diskMetrics.Used) / 1000000000 * diskSpaceWeight; // Disk space score is based on the amount of free disk space in GB var finalScore = cpuScore + memoryScore + diskSpaceScore; diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index d519c643..7531db6e 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -71,7 +71,6 @@ - diff --git a/Moonlight/Shared/Views/Admin/Nodes/View.razor b/Moonlight/Shared/Views/Admin/Nodes/View.razor index de6f61ab..d4c59945 100644 --- a/Moonlight/Shared/Views/Admin/Nodes/View.razor +++ b/Moonlight/Shared/Views/Admin/Nodes/View.razor @@ -41,7 +41,7 @@ else
- @if (CpuStats == null) + @if (CpuMetrics == null) { Loading @@ -50,12 +50,12 @@ else else { - @(CpuStats.Usage)% of @(CpuStats.Cores) Cores used + @(CpuMetrics.CpuUsage)% of CPU used } - @if (CpuStats == null) + @if (CpuMetrics == null) { Loading @@ -63,7 +63,7 @@ else } else { - @(CpuStats.Model) + @(CpuMetrics.CpuModel) }
@@ -78,7 +78,7 @@ else
- @if (MemoryStats == null) + @if (MemoryMetrics == null) { Loading @@ -87,32 +87,12 @@ else else { - @(Formatter.FormatSize(MemoryStats.Used * 1024D * 1024D)) of @(Formatter.FormatSize(MemoryStats.Total * 1024D * 1024D)) used + @(Formatter.FormatSize(MemoryMetrics.Used)) of @(Formatter.FormatSize(MemoryMetrics.Total)) memory used } - @if (MemoryStats == null) - { - - Loading - - } - else - { - if (MemoryStats.Sticks.Any()) - { - foreach (var stick in SortMemorySticks(MemoryStats.Sticks)) - { - @(stick) -
- } - } - else - { - No memory sticks detected - } - } + @*IDK what to put here*@
@@ -126,7 +106,7 @@ else
- @if (DiskStats == null) + @if (DiskMetrics == null) { Loading @@ -135,21 +115,12 @@ else else { - @(Formatter.FormatSize(DiskStats.TotalSize - DiskStats.FreeBytes)) of @(Formatter.FormatSize(DiskStats.TotalSize)) used + @(Formatter.FormatSize(DiskMetrics.Used)) of @(Formatter.FormatSize(DiskMetrics.Total)) used } - @if (DiskStats == null) - { - - Loading - - } - else - { - @(DiskStats.Name) - @(DiskStats.DriveFormat) - } + @*IDK what to put here*@
@@ -239,7 +210,7 @@ else
- @if (ContainerStats == null) + @if (DockerMetrics == null) { Loading @@ -248,12 +219,12 @@ else else { - @(ContainerStats.Containers.Count) + @(DockerMetrics.Containers.Length) } - @if (ContainerStats == null) + @if (DockerMetrics == null) { Loading @@ -290,11 +261,11 @@ else private Node? Node; - private CpuStats CpuStats; - private MemoryStats MemoryStats; - private DiskStats DiskStats; + private CpuMetrics CpuMetrics; + private MemoryMetrics MemoryMetrics; + private DiskMetrics DiskMetrics; + private DockerMetrics DockerMetrics; private SystemStatus SystemStatus; - private ContainerStats ContainerStats; private async Task Load(LazyLoader arg) { @@ -311,16 +282,16 @@ else SystemStatus = await NodeService.GetStatus(Node); await InvokeAsync(StateHasChanged); - CpuStats = await NodeService.GetCpuStats(Node); + CpuMetrics = await NodeService.GetCpuMetrics(Node); await InvokeAsync(StateHasChanged); - MemoryStats = await NodeService.GetMemoryStats(Node); + MemoryMetrics = await NodeService.GetMemoryMetrics(Node); await InvokeAsync(StateHasChanged); - DiskStats = await NodeService.GetDiskStats(Node); + DiskMetrics = await NodeService.GetDiskMetrics(Node); await InvokeAsync(StateHasChanged); - ContainerStats = await NodeService.GetContainerStats(Node); + DockerMetrics = await NodeService.GetDockerMetrics(Node); await InvokeAsync(StateHasChanged); } catch (Exception e) @@ -330,28 +301,4 @@ else }); } } - - private List SortMemorySticks(List sticks) - { - // Thank you ChatGPT <3 - - var groupedMemory = sticks.GroupBy(memory => new { memory.Type, memory.Size }) - .Select(group => new - { - Type = group.Key.Type, - Size = group.Key.Size, - Count = group.Count() - }); - - var sortedMemory = groupedMemory.OrderBy(memory => memory.Type) - .ThenBy(memory => memory.Size); - - List sortedList = sortedMemory.Select(memory => - { - string sizeString = $"{memory.Size}GB"; - return $"{memory.Count}x {memory.Type} {sizeString}"; - }).ToList(); - - return sortedList; - } } \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Servers/Manager.razor b/Moonlight/Shared/Views/Admin/Servers/Manager.razor index c311343a..c30d7142 100644 --- a/Moonlight/Shared/Views/Admin/Servers/Manager.razor +++ b/Moonlight/Shared/Views/Admin/Servers/Manager.razor @@ -132,9 +132,9 @@ try { - var containerStats = await NodeService.GetContainerStats(node); + var dockerMetrics = await NodeService.GetDockerMetrics(node); - foreach (var container in containerStats.Containers) + foreach (var container in dockerMetrics.Containers) { if (Guid.TryParse(container.Name, out Guid uuid)) {