Switched to new daemon communication

This commit is contained in:
Marcel Baumgartner
2023-06-07 02:46:26 +02:00
parent 732fbdd46a
commit 0f8946fe27
20 changed files with 197 additions and 167 deletions

View File

@@ -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<T> Get<T>(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);
request.Method = Method.Get;
var response = await Client.GetAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -52,4 +38,69 @@ public class DaemonApiHelper
return JsonConvert.DeserializeObject<T>(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<RestRequest> 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);
}
}

View File

@@ -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; } = "";
}

View File

@@ -0,0 +1,6 @@
namespace Moonlight.App.ApiClients.Daemon.Requests;
public class Unmount
{
public string Path { get; set; } = "";
}

View File

@@ -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; }
}

View File

@@ -1,15 +0,0 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class ContainerStats
{
public List<Container> 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; }
}
}

View File

@@ -0,0 +1,7 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class CpuMetrics
{
public string CpuModel { get; set; } = "";
public double CpuUsage { get; set; }
}

View File

@@ -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; } = "";
}

View File

@@ -0,0 +1,7 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class DiskMetrics
{
public long Used { get; set; }
public long Total { get; set; }
}

View File

@@ -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; }
}

View File

@@ -0,0 +1,6 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class DockerMetrics
{
public Container[] Containers { get; set; } = Array.Empty<Container>();
}

View File

@@ -0,0 +1,7 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class MemoryMetrics
{
public long Used { get; set; }
public long Total { get; set; }
}

View File

@@ -1,15 +0,0 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class MemoryStats
{
public List<MemoryStick> 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; } = "";
}
}

View File

@@ -0,0 +1,7 @@
namespace Moonlight.App.ApiClients.Daemon.Resources;
public class SystemMetrics
{
public string OsName { get; set; } = "";
public long Uptime { get; set; }
}

View File

@@ -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; }
}

View File

@@ -81,12 +81,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 || (memoryMetrics.Total - memoryMetrics.Used) < minMemory)
{
var containerStats = await nodeService.GetContainerStats(node);
var dockerMetrics = await nodeService.GetDockerMetrics(node);
var serverRepository = scope.ServiceProvider.GetRequiredService<ServerRepository>();
var imageRepository = scope.ServiceProvider.GetRequiredService<ImageRepository>();
@@ -101,9 +101,9 @@ public class CleanupService
)
.ToArray();
var containerMappedToServers = new Dictionary<ContainerStats.Container, Server>();
var containerMappedToServers = new Dictionary<Container, Server>();
foreach (var container in containerStats.Containers)
foreach (var container in dockerMetrics.Containers)
{
if (Guid.TryParse(container.Name, out Guid uuid))
{

View File

@@ -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,34 +25,55 @@ public class NodeService
return await WingsApiHelper.Get<SystemStatus>(node, "api/system");
}
public async Task<CpuStats> GetCpuStats(Node node)
public async Task<CpuMetrics> GetCpuMetrics(Node node)
{
return await DaemonApiHelper.Get<CpuStats>(node, "stats/cpu");
return await DaemonApiHelper.Get<CpuMetrics>(node, "metrics/cpu");
}
public async Task<MemoryStats> GetMemoryStats(Node node)
public async Task<MemoryMetrics> GetMemoryMetrics(Node node)
{
return await DaemonApiHelper.Get<MemoryStats>(node, "stats/memory");
return await DaemonApiHelper.Get<MemoryMetrics>(node, "metrics/memory");
}
public async Task<DiskStats> GetDiskStats(Node node)
public async Task<DiskMetrics> GetDiskMetrics(Node node)
{
return await DaemonApiHelper.Get<DiskStats>(node, "stats/disk");
return await DaemonApiHelper.Get<DiskMetrics>(node, "metrics/disk");
}
public async Task<ContainerStats> GetContainerStats(Node node)
public async Task<SystemMetrics> GetSystemMetrics(Node node)
{
return await DaemonApiHelper.Get<ContainerStats>(node, "stats/container");
return await DaemonApiHelper.Get<SystemMetrics>(node, "metrics/system");
}
public async Task<DockerMetrics> GetDockerMetrics(Node node)
{
return await DaemonApiHelper.Get<DockerMetrics>(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<bool> IsHostUp(Node node)
{
try
{
//TODO: Implement status caching
var data = await GetStatus(node);
await GetSystemMetrics(node);
if (data != null)
return true;
}
catch (Exception)

View File

@@ -59,17 +59,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;

View File

@@ -72,7 +72,6 @@
<ItemGroup>
<Folder Include="App\ApiClients\CloudPanel\Resources\" />
<Folder Include="App\ApiClients\Daemon\Requests\" />
<Folder Include="App\Http\Middleware" />
<Folder Include="storage\backups\" />
</ItemGroup>

View File

@@ -41,7 +41,7 @@ else
</div>
<div class="m-0">
<span class="fw-bolder d-block fs-2qx lh-1 ls-n1 mb-1">
@if (CpuStats == null)
@if (CpuMetrics == null)
{
<span class="text-muted">
<TL>Loading</TL>
@@ -50,12 +50,12 @@ else
else
{
<span>
@(CpuStats.Usage)% <TL>of</TL> @(CpuStats.Cores) <TL>Cores used</TL>
@(CpuMetrics.CpuUsage)% <TL>of CPU used</TL>
</span>
}
</span>
<span class="fw-semibold fs-6">
@if (CpuStats == null)
@if (CpuMetrics == null)
{
<span class="text-muted">
<TL>Loading</TL>
@@ -63,7 +63,7 @@ else
}
else
{
<span>@(CpuStats.Model)</span>
<span>@(CpuMetrics.CpuModel)</span>
}
</span>
</div>
@@ -78,7 +78,7 @@ else
</div>
<div class="m-0">
<span class="fw-bolder d-block fs-2qx lh-1 ls-n1 mb-1">
@if (MemoryStats == null)
@if (MemoryMetrics == null)
{
<span class="text-muted">
<TL>Loading</TL>
@@ -87,32 +87,12 @@ else
else
{
<span>
@(Formatter.FormatSize(MemoryStats.Used * 1024D * 1024D)) <TL>of</TL> @(Formatter.FormatSize(MemoryStats.Total * 1024D * 1024D)) <TL>used</TL>
@(Formatter.FormatSize(MemoryMetrics.Used * 1024D * 1024D)) <TL>of</TL> @(Formatter.FormatSize(MemoryMetrics.Total * 1024D * 1024D)) <TL>used</TL>
</span>
}
</span>
<span class="fw-semibold fs-6">
@if (MemoryStats == null)
{
<span class="text-muted">
<TL>Loading</TL>
</span>
}
else
{
if (MemoryStats.Sticks.Any())
{
foreach (var stick in SortMemorySticks(MemoryStats.Sticks))
{
<span>@(stick)</span>
<br/>
}
}
else
{
<span>No memory sticks detected</span>
}
}
@*IDK what to put here*@
</span>
</div>
</div>
@@ -126,7 +106,7 @@ else
</div>
<div class="m-0">
<span class="fw-bolder d-block fs-2qx lh-1 ls-n1 mb-1">
@if (DiskStats == null)
@if (DiskMetrics == null)
{
<span class="text-muted">
<TL>Loading</TL>
@@ -135,21 +115,12 @@ else
else
{
<span>
@(Formatter.FormatSize(DiskStats.TotalSize - DiskStats.FreeBytes)) <TL>of</TL> @(Formatter.FormatSize(DiskStats.TotalSize)) <TL>used</TL>
@(Formatter.FormatSize(DiskMetrics.Used)) <TL>of</TL> @(Formatter.FormatSize(DiskMetrics.Total)) <TL>used</TL>
</span>
}
</span>
<span class="fw-semibold fs-6">
@if (DiskStats == null)
{
<span class="text-muted">
<TL>Loading</TL>
</span>
}
else
{
<span>@(DiskStats.Name) - @(DiskStats.DriveFormat)</span>
}
@*IDK what to put here*@
</span>
</div>
</div>
@@ -239,7 +210,7 @@ else
</div>
<div class="m-0">
<span class="fw-bolder d-block fs-2qx lh-1 ls-n1 mb-1">
@if (ContainerStats == null)
@if (DockerMetrics == null)
{
<span class="text-muted">
<TL>Loading</TL>
@@ -248,12 +219,12 @@ else
else
{
<span>
<TL>@(ContainerStats.Containers.Count)</TL>
<TL>@(DockerMetrics.Containers.Length)</TL>
</span>
}
</span>
<span class="fw-semibold fs-6">
@if (ContainerStats == null)
@if (DockerMetrics == null)
{
<span class="text-muted">
<TL>Loading</TL>
@@ -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<string> SortMemorySticks(List<MemoryStats.MemoryStick> 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<string> sortedList = sortedMemory.Select(memory =>
{
string sizeString = $"{memory.Size}GB";
return $"{memory.Count}x {memory.Type} {sizeString}";
}).ToList();
return sortedList;
}
}

View File

@@ -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))
{