added docker support, various little changes
This commit is contained in:
parent
1ca4517984
commit
f903be2342
64
Salmon.Model/Monitor/Container.cs
Normal file
64
Salmon.Model/Monitor/Container.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using Salmon.Core;
|
||||
using Salmon.Core.Cliff;
|
||||
|
||||
namespace Salmon.Model.Monitor;
|
||||
|
||||
public class Container
|
||||
: Element
|
||||
{
|
||||
[PersistentField]
|
||||
public bool? Joinable { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public bool? Running { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public string? ContainerId { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public string? Image { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public string? ImageId { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public string? ContainerStatus { get; set; }
|
||||
|
||||
public bool Ok {
|
||||
get
|
||||
{
|
||||
return Joinable == true && Running == true;
|
||||
}
|
||||
}
|
||||
|
||||
protected Container()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Container(string id)
|
||||
: base(id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyValuePair<string, object>> ImportantProperties()
|
||||
{
|
||||
foreach (var i in base.ImportantProperties())
|
||||
yield return new KeyValuePair<string, object>(i.Key, i.Value);
|
||||
|
||||
yield return new KeyValuePair<string, object>(nameof(Ok), Ok);
|
||||
|
||||
if (ContainerId is not null)
|
||||
yield return new KeyValuePair<string, object>(nameof(ContainerId), ContainerId);
|
||||
|
||||
if (Image is not null)
|
||||
yield return new KeyValuePair<string, object>(nameof(Image), Image);
|
||||
|
||||
if (ContainerStatus is not null)
|
||||
yield return new KeyValuePair<string, object>(nameof(ContainerStatus), ContainerStatus);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
using Salmon.Core.Cliff;
|
||||
using Salmon.Core.Cliff;
|
||||
|
||||
namespace Salmon.Model.Monitor;
|
||||
|
||||
|
||||
92
Salmon.Model/Monitor/WebEndpoint.cs
Normal file
92
Salmon.Model/Monitor/WebEndpoint.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using Salmon.Core;
|
||||
using Salmon.Core.Cliff;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Salmon.Model.Monitor;
|
||||
|
||||
public class WebEndpoint
|
||||
: Element
|
||||
{
|
||||
[PersistentField]
|
||||
public bool Joinable { get; set; } = true;
|
||||
|
||||
[PersistentField]
|
||||
public string? Uri { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public string? Method { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public int? Code { get; set; }
|
||||
|
||||
[PersistentField]
|
||||
public string? Content { get; set; }
|
||||
|
||||
public bool Ok
|
||||
{
|
||||
get
|
||||
{
|
||||
return Joinable && (Code != null && Code >= 200 && Code < 300);
|
||||
}
|
||||
}
|
||||
|
||||
protected WebEndpoint()
|
||||
{
|
||||
}
|
||||
|
||||
public WebEndpoint(string identifier)
|
||||
: base(identifier)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override IEnumerable<KeyValuePair<string, object>> ImportantProperties()
|
||||
{
|
||||
foreach (var kv in base.ImportantProperties())
|
||||
yield return kv;
|
||||
|
||||
if (Uri is not null)
|
||||
yield return new KeyValuePair<string, object>(nameof(Uri), Uri);
|
||||
|
||||
if (Method is not null)
|
||||
yield return new KeyValuePair<string, object>(nameof(Method), Method);
|
||||
|
||||
yield return new KeyValuePair<string, object>(nameof(Ok), Ok);
|
||||
|
||||
if (Code is not null)
|
||||
yield return new KeyValuePair<string, object>(nameof(Code), Code);
|
||||
}
|
||||
|
||||
public static async Task<WebEndpoint> From(string uri, string method = "GET", string? id = null)
|
||||
{
|
||||
if(id is null)
|
||||
id = Helper.CreateIdFrom("webendpoint", uri, method);
|
||||
|
||||
using HttpClient client = new();
|
||||
HttpMethod httpmethod = new(method);
|
||||
HttpRequestMessage request = new(httpmethod, uri);
|
||||
|
||||
HttpResponseMessage response;
|
||||
var ret = new WebEndpoint(id)
|
||||
{
|
||||
Uri = uri,
|
||||
Method = method
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
response = await client.SendAsync(request);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
ret.Joinable = false;
|
||||
ret.Content = e.ToString();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.Code = (int)response.StatusCode;
|
||||
ret.Content = response.Content.ToString();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,4 @@
|
||||
using Salmon.Core;
|
||||
using Salmon.Model.Monitor;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Salmon.Service;
|
||||
namespace Salmon.Service;
|
||||
|
||||
public class Configuration
|
||||
{
|
||||
@ -11,37 +7,5 @@ public class Configuration
|
||||
public bool? MonitorHardware { get; set; } = true;
|
||||
public bool? MonitorLocalSoftware { get; set; } = true;
|
||||
|
||||
public List<WatcherConfiguration> Watch { get; set; } = new ();
|
||||
}
|
||||
|
||||
public abstract class WatcherConfiguration
|
||||
{
|
||||
public abstract Task<IEnumerable<Element>> ForgeElements();
|
||||
}
|
||||
|
||||
public class ExecutableInstanceWatcherConfiguration
|
||||
: WatcherConfiguration
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public override async Task<IEnumerable<Element>> ForgeElements()
|
||||
{
|
||||
List<Element> ret = new();
|
||||
|
||||
foreach (var el in Software.FromPath(Path))
|
||||
ret.Add(el);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
public class HttpPageWatcherConfiguration
|
||||
: WatcherConfiguration
|
||||
{
|
||||
public string Method { get; set; } = "GET";
|
||||
public string Uri { get; set; }
|
||||
|
||||
public override async Task<IEnumerable<Element>> ForgeElements()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public List<Watchers.Base> Watch { get; set; } = new ();
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
namespace Salmon.Service;
|
||||
|
||||
|
||||
public class Monitor
|
||||
{
|
||||
private void FlushStatus()
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,8 @@ int? period = null;
|
||||
Uri? uri = null;
|
||||
bool? monitor_hardware = null;
|
||||
bool? monitor_localsoftware = null;
|
||||
List<Salmon.Service.Watchers.Base> watchers = new();
|
||||
bool? watch_config = true;
|
||||
|
||||
// 1: use environment variable
|
||||
var period_env = Environment.GetEnvironmentVariable(ENV_PERIOD);
|
||||
@ -34,39 +36,70 @@ if(uri_env != null)
|
||||
|
||||
// 2: override with configuration file
|
||||
string configpath = "config.json";
|
||||
if (File.Exists(configpath))
|
||||
Func<bool, Task<int>> reloadConfigFile = new(async (bool configFileMustExists ) =>
|
||||
{
|
||||
await Task.Delay(500);
|
||||
|
||||
if (!File.Exists(configpath))
|
||||
{
|
||||
if (!configFileMustExists)
|
||||
return 0;
|
||||
|
||||
Console.WriteLine($"Cannot find config file\"{configpath}\".");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
string content;
|
||||
try
|
||||
{
|
||||
content = File.ReadAllText(configpath);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Cannot open config file\"{configpath}\": {ex}.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Console.WriteLine("Found a configuration file, parsing...");
|
||||
string content = File.ReadAllText(configpath);
|
||||
|
||||
|
||||
Configuration? conf = null;
|
||||
try
|
||||
{
|
||||
conf = JsonSerializer.Deserialize<Configuration>(content);
|
||||
}
|
||||
catch(Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Cannot parse config file \"{configpath}\", {e}.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(conf == null)
|
||||
if (conf == null)
|
||||
{
|
||||
Console.WriteLine($"Cannot parse config file \"{configpath}\".");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(conf.Url != null)
|
||||
if (conf.Url != null)
|
||||
uri = conf.Url;
|
||||
|
||||
if(conf.Period != null)
|
||||
if (conf.Period != null)
|
||||
period = conf.Period;
|
||||
|
||||
if(conf.MonitorHardware != null)
|
||||
monitor_hardware = conf.MonitorHardware;
|
||||
|
||||
if(conf.MonitorLocalSoftware != null)
|
||||
if (conf.MonitorHardware != null)
|
||||
monitor_hardware = conf.MonitorHardware;
|
||||
|
||||
if (conf.MonitorLocalSoftware != null)
|
||||
monitor_localsoftware = conf.MonitorLocalSoftware;
|
||||
}
|
||||
|
||||
if (conf.Watch != null)
|
||||
watchers = conf.Watch;
|
||||
|
||||
return 0;
|
||||
});
|
||||
if (await reloadConfigFile(false) != 0)
|
||||
return 0;
|
||||
|
||||
//todo: override with parameter
|
||||
|
||||
@ -84,17 +117,32 @@ if(uri == null)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Initialisation
|
||||
Transmitter transmitter = new();
|
||||
|
||||
FileSystemWatcher configWatcher;
|
||||
Console.WriteLine($"Salmon.Service started at {DateTime.Now}.");
|
||||
if(configpath != null && watch_config == true)
|
||||
{
|
||||
FileInfo fi = new (configpath);
|
||||
|
||||
//watch configuration if needed
|
||||
configWatcher = new FileSystemWatcher(fi.DirectoryName);
|
||||
configWatcher.Filter = fi.Name;
|
||||
configWatcher.NotifyFilter = NotifyFilters.CreationTime
|
||||
| NotifyFilters.LastWrite;
|
||||
configWatcher.Changed += async (object sender, FileSystemEventArgs e) =>
|
||||
{
|
||||
if (await reloadConfigFile(false) != 0)
|
||||
Console.WriteLine("Cannot use modified config file, using last valid file.");
|
||||
else
|
||||
Console.WriteLine("Config file reloaded.");
|
||||
};
|
||||
configWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
|
||||
while ( true )
|
||||
{
|
||||
|
||||
var software = Salmon.Model.Monitor.Software.FromLocal();
|
||||
|
||||
List<Element> tosend = new();
|
||||
@ -105,6 +153,18 @@ while ( true )
|
||||
var hardwares = Salmon.Model.Monitor.Hardware.FromAllHardware();
|
||||
tosend.AddRange(hardwares);
|
||||
}
|
||||
|
||||
foreach (var w in watchers)
|
||||
try
|
||||
{
|
||||
foreach (var el in await w.ForgeElements())
|
||||
tosend.Add(el);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Console.WriteLine($"Error while forging element: {e}.");
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
@ -120,4 +180,4 @@ while ( true )
|
||||
}
|
||||
|
||||
await Task.Delay(period.Value);
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,41 @@
|
||||
{
|
||||
"Url":"http://localhost:5009/api/Push"
|
||||
}
|
||||
"Url":"http://localhost:5009/api/Push",
|
||||
"Period":15000,
|
||||
"Watch":[
|
||||
{
|
||||
"Type":"web",
|
||||
"ShortName": "Voie93quarts",
|
||||
"Uri":"https://voie93quarts.fr"
|
||||
},
|
||||
{
|
||||
"Type":"web",
|
||||
"ShortName": "Voie93quarts fallback",
|
||||
"Uri":"https://sdgsdgsdg.voie93quarts.fr"
|
||||
},
|
||||
{
|
||||
"Type":"web",
|
||||
"ShortName": "Home assistant",
|
||||
"Uri":"https://ha.voie93quarts.fr"
|
||||
},
|
||||
{
|
||||
"Type":"web",
|
||||
"ShortName": "Gitea",
|
||||
"Uri":"https://git.voie93quarts.fr"
|
||||
},
|
||||
{
|
||||
"Type":"docker",
|
||||
"Uri":"http://192.168.1.156:4243",
|
||||
"Containers":[
|
||||
{
|
||||
"ShortName": "Jellyfin container",
|
||||
"ContainerName": "/jellyfin"
|
||||
},
|
||||
{
|
||||
"ShortName": "Gitea container",
|
||||
"ContainerName": "/gitea"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
1
Salmon.Service/RunEnv/start-service.bat
Normal file
1
Salmon.Service/RunEnv/start-service.bat
Normal file
@ -0,0 +1 @@
|
||||
dotnet run --project ../Salmon.Service.csproj
|
||||
2
Salmon.Service/RunEnv/start-web.bat
Normal file
2
Salmon.Service/RunEnv/start-web.bat
Normal file
@ -0,0 +1,2 @@
|
||||
cd ../../Salmon.Web
|
||||
dotnet run http
|
||||
@ -1,2 +0,0 @@
|
||||
cd ..
|
||||
dotnet run
|
||||
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Docker.DotNet" Version="3.125.15" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
37
Salmon.Service/Watchers/Base.cs
Normal file
37
Salmon.Service/Watchers/Base.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Salmon.Core;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Salmon.Service.Watchers;
|
||||
|
||||
[JsonPolymorphic(
|
||||
TypeDiscriminatorPropertyName = "Type"
|
||||
, UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization)
|
||||
]
|
||||
[JsonDerivedType(typeof(Executable), typeDiscriminator: "executable")]
|
||||
[JsonDerivedType(typeof(WebStatus), typeDiscriminator: "web")]
|
||||
[JsonDerivedType(typeof(Docker), typeDiscriminator: "docker")]
|
||||
public abstract class Base
|
||||
{
|
||||
public string? UniqueId { get; set; }
|
||||
public string? ShortName { get; set; }
|
||||
public string? LongName { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? ParentId { get; set; } = null;
|
||||
|
||||
public abstract Task<IEnumerable<Element>> ForgeElements();
|
||||
|
||||
public void ApplyField(Element e)
|
||||
{
|
||||
if(ShortName is not null)
|
||||
e.ShortName = ShortName;
|
||||
|
||||
if(LongName is not null)
|
||||
e.LongName = LongName;
|
||||
|
||||
if(Description is not null)
|
||||
e.Description = Description;
|
||||
|
||||
if(ParentId is not null)
|
||||
e.ParentId = ParentId;
|
||||
}
|
||||
}
|
||||
68
Salmon.Service/Watchers/Docker.cs
Normal file
68
Salmon.Service/Watchers/Docker.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using Docker.DotNet;
|
||||
using Docker.DotNet.Models;
|
||||
using Salmon.Core;
|
||||
using Salmon.Model.Monitor;
|
||||
|
||||
namespace Salmon.Service.Watchers;
|
||||
|
||||
public class Docker
|
||||
: Base
|
||||
{
|
||||
public string Uri { get; set; }
|
||||
public string Name { get; set; }
|
||||
public List<DockerContainer> Containers { get; set; } = new List<DockerContainer>();
|
||||
|
||||
public override async Task<IEnumerable<Element>> ForgeElements()
|
||||
{
|
||||
List<Element> elements = new();
|
||||
|
||||
DockerClient client = new DockerClientConfiguration(
|
||||
new Uri(Uri))
|
||||
.CreateClient();
|
||||
|
||||
var listedContainers = await client.Containers.ListContainersAsync(new global::Docker.DotNet.Models.ContainersListParameters
|
||||
{
|
||||
All = true
|
||||
});
|
||||
foreach (var i in Containers)
|
||||
elements.Add(i.FromResponses(Uri, listedContainers));
|
||||
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
|
||||
public class DockerContainer
|
||||
: Base
|
||||
{
|
||||
public string ContainerName { get; set; }
|
||||
|
||||
public override Task<IEnumerable<Element>> ForgeElements()
|
||||
{
|
||||
// Implement later with Single... Not very efficient.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Element FromResponses(string uri, IList<ContainerListResponse> responses)
|
||||
{
|
||||
Container ret = new(UniqueId ?? Helper.CreateIdFrom(uri, ContainerName))
|
||||
{
|
||||
Joinable = false
|
||||
};
|
||||
|
||||
foreach (var r in responses)
|
||||
if(r.Names.Contains(ContainerName))
|
||||
{
|
||||
ret.Joinable = true;
|
||||
ret.Running = r.State == "running";
|
||||
ret.ContainerId = r.ID;
|
||||
ret.ContainerStatus = r.Status;
|
||||
ret.Image = r.Image;
|
||||
ret.ImageId = r.ImageID;
|
||||
break;
|
||||
}
|
||||
|
||||
ApplyField(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
21
Salmon.Service/Watchers/Executable.cs
Normal file
21
Salmon.Service/Watchers/Executable.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using Salmon.Core;
|
||||
using Salmon.Model.Monitor;
|
||||
|
||||
|
||||
namespace Salmon.Service.Watchers;
|
||||
|
||||
public class Executable
|
||||
: Base
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public override async Task<IEnumerable<Element>> ForgeElements()
|
||||
{
|
||||
List<Element> ret = new();
|
||||
|
||||
foreach (var el in Software.FromPath(Path))
|
||||
ret.Add(el);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
19
Salmon.Service/Watchers/WebStatus.cs
Normal file
19
Salmon.Service/Watchers/WebStatus.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using Salmon.Core;
|
||||
|
||||
namespace Salmon.Service.Watchers;
|
||||
|
||||
public class WebStatus
|
||||
: Base
|
||||
{
|
||||
public string Method { get; set; } = "GET";
|
||||
public string Uri { get; set; }
|
||||
|
||||
public override async Task<IEnumerable<Element>> ForgeElements()
|
||||
{
|
||||
List<Element> elements = new();
|
||||
var ws = await Salmon.Model.Monitor.WebEndpoint.From(Uri, Method);
|
||||
ApplyField(ws);
|
||||
elements.Add(ws);
|
||||
return elements;
|
||||
}
|
||||
}
|
||||
@ -2,9 +2,12 @@
|
||||
|
||||
@inject Salmon.Core.Instance Salmon;
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text" id="basic-addon3">UniqueId</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" aria-describedby="basic-addon3" readonly value="@Id">
|
||||
</div>
|
||||
|
||||
@if (Error is not null)
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user