Логирование, багфиксы, добавил мониторин /proc/mdstat. Юнит для systemd
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||||
|
<PackageReference Include="NLog" Version="4.7.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace Barsik
|
||||||
|
{
|
||||||
|
public static class Logging
|
||||||
|
{
|
||||||
|
public static void Setup()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
var logConsole = new NLog.Targets.ConsoleTarget("logconsole");
|
||||||
|
config.AddRuleForAllLevels(logConsole);
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using IniParser.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Barsik
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Monitors configured RAID array status via /proc/mdstat
|
||||||
|
/// Will fill registers info starting from baseAddress with
|
||||||
|
/// disk status (0 - fail, 1 - up) in following order:
|
||||||
|
/// raid #0 disk #0
|
||||||
|
/// raid #0 disk #1
|
||||||
|
/// ...
|
||||||
|
/// raid #0 disk #N
|
||||||
|
/// raid #1 disk #0
|
||||||
|
/// ...
|
||||||
|
/// etc
|
||||||
|
/// </summary>
|
||||||
|
class MDStatMonitoringUnit : BaseMonitoringUnit
|
||||||
|
{
|
||||||
|
public MDStatMonitoringUnit(SectionData config, RegistersContainer registers) : base(config, registers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int currentAddress = baseAddress;
|
||||||
|
// the file is not so big, so read it completely
|
||||||
|
var lines = File.ReadAllLines("/proc/mdstat");
|
||||||
|
for (int i = 0; i < lines.Length;)
|
||||||
|
{
|
||||||
|
if (lines[i].StartsWith("md") && i < lines.Length - 1)
|
||||||
|
{
|
||||||
|
// we found device description
|
||||||
|
// second line of it is as follows:
|
||||||
|
// 2930264896 blocks super 1.0 [2/2] [UU]
|
||||||
|
// we need last characters in brackets []
|
||||||
|
currentAddress += FillDiskStatus(currentAddress, lines[i + 1]);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else i++;
|
||||||
|
}
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int FillDiskStatus(int address, string v)
|
||||||
|
{
|
||||||
|
int last_pos = v.LastIndexOf(']');
|
||||||
|
int pos;
|
||||||
|
for (pos = last_pos; pos > 0 && v[pos] != '['; pos--) ;
|
||||||
|
if (v[pos] != '[') return 0;
|
||||||
|
for (int i = pos + 1; i < last_pos; i++)
|
||||||
|
{
|
||||||
|
if (v[i] == 'U') registers[address + i - (pos + 1)] = 1;
|
||||||
|
else registers[address + i - (pos + 1)] = 1;
|
||||||
|
}
|
||||||
|
return last_pos - pos - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-2
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using NLog;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -20,11 +21,14 @@ namespace Barsik
|
|||||||
|
|
||||||
internal void Run()
|
internal void Run()
|
||||||
{
|
{
|
||||||
|
var logger = LogManager.GetCurrentClassLogger();
|
||||||
|
logger.Info("Accepting connections on port " + Port);
|
||||||
var listener = new TcpListener(IPAddress.Any, Port);
|
var listener = new TcpListener(IPAddress.Any, Port);
|
||||||
listener.Start();
|
listener.Start();
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var client = listener.AcceptTcpClient();
|
var client = listener.AcceptTcpClient();
|
||||||
|
logger.Info("Accepted connection from " + client.Client.RemoteEndPoint);
|
||||||
var worker = new ModbusServerWorker(registers, client);
|
var worker = new ModbusServerWorker(registers, client);
|
||||||
Thread clientThread = new Thread(new ThreadStart(worker.Run));
|
Thread clientThread = new Thread(new ThreadStart(worker.Run));
|
||||||
clientThread.Start();
|
clientThread.Start();
|
||||||
@@ -38,6 +42,7 @@ namespace Barsik
|
|||||||
|
|
||||||
private RegistersContainer registers;
|
private RegistersContainer registers;
|
||||||
private TcpClient client;
|
private TcpClient client;
|
||||||
|
private static Logger logger = LogManager.GetCurrentClassLogger();
|
||||||
public ModbusServerWorker(RegistersContainer registers, TcpClient client)
|
public ModbusServerWorker(RegistersContainer registers, TcpClient client)
|
||||||
{
|
{
|
||||||
this.registers = registers;
|
this.registers = registers;
|
||||||
@@ -52,6 +57,7 @@ namespace Barsik
|
|||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
|
logger.Debug("Working with client");
|
||||||
received = new List<byte>();
|
received = new List<byte>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -63,11 +69,13 @@ namespace Barsik
|
|||||||
readCompleted.Reset();
|
readCompleted.Reset();
|
||||||
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.OnReadCompleted), this);
|
stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(this.OnReadCompleted), this);
|
||||||
readCompleted.WaitOne();
|
readCompleted.WaitOne();
|
||||||
if (currentReceived < 0)
|
if (currentReceived <= 0)
|
||||||
{
|
{
|
||||||
|
logger.Debug("Received no data, closing ");
|
||||||
client.Close();
|
client.Close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
logger.Debug("ProcessReceivedData of " + received.Count);
|
||||||
ProcessReceivedData();
|
ProcessReceivedData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +83,11 @@ namespace Barsik
|
|||||||
{
|
{
|
||||||
client.Close();
|
client.Close();
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (stream != null) stream.Dispose();
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessReceivedData()
|
private void ProcessReceivedData()
|
||||||
@@ -122,6 +135,8 @@ namespace Barsik
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void OnReadCompleted(IAsyncResult ar)
|
private void OnReadCompleted(IAsyncResult ar)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
currentReceived = stream.EndRead(ar);
|
currentReceived = stream.EndRead(ar);
|
||||||
lock (received)
|
lock (received)
|
||||||
@@ -130,5 +145,11 @@ namespace Barsik
|
|||||||
}
|
}
|
||||||
readCompleted.Set();
|
readCompleted.Set();
|
||||||
}
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.Info("Error receiving " + e);
|
||||||
|
currentReceived = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-58
@@ -1,8 +1,5 @@
|
|||||||
using IniParser.Model;
|
using IniParser.Model;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Barsik
|
namespace Barsik
|
||||||
{
|
{
|
||||||
@@ -30,6 +27,10 @@ namespace Barsik
|
|||||||
if (type == "Sensors")
|
if (type == "Sensors")
|
||||||
{
|
{
|
||||||
return new SensorsMonitoringUnit(config, registers);
|
return new SensorsMonitoringUnit(config, registers);
|
||||||
|
}
|
||||||
|
else if (type == "MDStat")
|
||||||
|
{
|
||||||
|
return new MDStatMonitoringUnit(config, registers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -38,59 +39,4 @@ namespace Barsik
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SensorsMonitoringUnit : BaseMonitoringUnit
|
|
||||||
{
|
|
||||||
public SensorsMonitoringUnit(SectionData config, RegistersContainer registers) : base(config, registers)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run()
|
|
||||||
{
|
|
||||||
var stringToFind = config.Keys["String"];
|
|
||||||
var multiplier = float.Parse(config.Keys["Multiplier"]);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors");
|
|
||||||
procStartInfo.RedirectStandardOutput = true;
|
|
||||||
procStartInfo.UseShellExecute = false;
|
|
||||||
procStartInfo.CreateNoWindow = true;
|
|
||||||
System.Diagnostics.Process proc = new System.Diagnostics.Process();
|
|
||||||
proc.StartInfo = procStartInfo;
|
|
||||||
proc.Start();
|
|
||||||
while (!proc.HasExited)
|
|
||||||
{
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
string result = proc.StandardOutput.ReadToEnd();
|
|
||||||
var lines = result.Split('\n');
|
|
||||||
for (int i = 0; i < lines.Length; i++)
|
|
||||||
{
|
|
||||||
if (lines[i].Contains(stringToFind))
|
|
||||||
{
|
|
||||||
var value = ExtractValue(lines[i]);
|
|
||||||
registers[baseAddress] = (int)(value * multiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Good(char c)
|
|
||||||
{
|
|
||||||
if (c == '-' || c == '+' || c == '.') return true;
|
|
||||||
if (c >= '0' && c <= '9') return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float ExtractValue(string v)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
while (pos < v.Length && !Good(v[pos])) pos++;
|
|
||||||
int pos2 = pos;
|
|
||||||
while (pos2 < v.Length && Good(v[pos2])) pos2++;
|
|
||||||
float res = 0;
|
|
||||||
float.TryParse(v.Substring(pos, pos2 - pos), out res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-6
@@ -1,6 +1,8 @@
|
|||||||
using IniParser;
|
using IniParser;
|
||||||
using System;
|
using NLog;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Barsik
|
namespace Barsik
|
||||||
@@ -10,14 +12,20 @@ namespace Barsik
|
|||||||
private static RegistersContainer registers;
|
private static RegistersContainer registers;
|
||||||
private static List<BaseMonitoringUnit> units;
|
private static List<BaseMonitoringUnit> units;
|
||||||
|
|
||||||
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
Logging.Setup();
|
||||||
|
logger.Info("Barsik monitoring service starting");
|
||||||
|
|
||||||
registers = new RegistersContainer();
|
registers = new RegistersContainer();
|
||||||
var server = new ModbusServer(registers);
|
var server = new ModbusServer(registers);
|
||||||
units = new List<BaseMonitoringUnit>();
|
units = new List<BaseMonitoringUnit>();
|
||||||
|
|
||||||
var configParser = new FileIniDataParser();
|
var configParser = new FileIniDataParser();
|
||||||
var data = configParser.ReadFile("barsik.ini");
|
string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
|
var data = configParser.ReadFile(Path.Combine(assemblyFolder, "barsik.ini"));
|
||||||
foreach (var section in data.Sections)
|
foreach (var section in data.Sections)
|
||||||
{
|
{
|
||||||
if (section.SectionName.StartsWith("Unit"))
|
if (section.SectionName.StartsWith("Unit"))
|
||||||
@@ -31,12 +39,10 @@ namespace Barsik
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.Info("Configuration loaded, we have " + units.Count + " units");
|
||||||
server.Port = int.Parse(data["Server"]["Port"]);
|
server.Port = int.Parse(data["Server"]["Port"]);
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
server.Run();
|
server.Run();
|
||||||
Thread.Sleep(1000);
|
logger.Info("Exiting");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<PublishDir>bin\Debug\netcoreapp2.1\publish\</PublishDir>
|
<PublishDir>bin\Debug\netcoreapp2.1\publish\</PublishDir>
|
||||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||||
<SelfContained>true</SelfContained>
|
<SelfContained>false</SelfContained>
|
||||||
<_IsPortable>false</_IsPortable>
|
<_IsPortable>true</_IsPortable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
using IniParser.Model;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Barsik
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parses output of "sensors" command and puts CPU core temperatures into
|
||||||
|
/// registers
|
||||||
|
/// </summary>
|
||||||
|
class SensorsMonitoringUnit : BaseMonitoringUnit
|
||||||
|
{
|
||||||
|
const string CORE_STR = "Core{0} Temp:";
|
||||||
|
|
||||||
|
public SensorsMonitoringUnit(SectionData config, RegistersContainer registers) : base(config, registers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
int cores = int.Parse(config.Keys["Cores"]);
|
||||||
|
var multiplier = float.Parse(config.Keys["Multiplier"]);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("sensors");
|
||||||
|
procStartInfo.RedirectStandardOutput = true;
|
||||||
|
procStartInfo.UseShellExecute = false;
|
||||||
|
procStartInfo.CreateNoWindow = true;
|
||||||
|
using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
|
||||||
|
{
|
||||||
|
proc.StartInfo = procStartInfo;
|
||||||
|
proc.Start();
|
||||||
|
while (!proc.HasExited)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
string result = proc.StandardOutput.ReadToEnd();
|
||||||
|
var lines = result.Split('\n');
|
||||||
|
// lines in output will be of format:
|
||||||
|
// Core0 Temp: +29.0°C
|
||||||
|
// and there could be several lines for one core
|
||||||
|
// so let's take all of them and use maximal value as core temp
|
||||||
|
float[] t = new float[cores];
|
||||||
|
for (int i = 0; i < cores; i++)
|
||||||
|
{
|
||||||
|
t[i] = 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < cores; j++)
|
||||||
|
{
|
||||||
|
string toFind = string.Format(CORE_STR, j);
|
||||||
|
if (lines[i].Contains(toFind))
|
||||||
|
{
|
||||||
|
var value = ExtractValue(lines[i].Substring(toFind.Length));
|
||||||
|
t[j] = Math.Max(t[j], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fill register data starting from baseAddress
|
||||||
|
for (int i = 0; i < cores; i++)
|
||||||
|
{
|
||||||
|
registers[baseAddress + i] = (int)(t[i] * multiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Good(char c)
|
||||||
|
{
|
||||||
|
if (c == '-' || c == '+' || c == '.') return true;
|
||||||
|
if (c >= '0' && c <= '9') return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float ExtractValue(string v)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < v.Length && !Good(v[pos])) pos++;
|
||||||
|
int pos2 = pos;
|
||||||
|
while (pos2 < v.Length && Good(v[pos2])) pos2++;
|
||||||
|
float res;
|
||||||
|
float.TryParse(v.Substring(pos, pos2 - pos), out res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-1
@@ -4,5 +4,9 @@ Port = 4500
|
|||||||
[Unit_CPU]
|
[Unit_CPU]
|
||||||
Type = Sensors
|
Type = Sensors
|
||||||
BaseAddress = 1
|
BaseAddress = 1
|
||||||
String = CPU Temperature
|
|
||||||
Multiplier = 100
|
Multiplier = 100
|
||||||
|
Cores = 2
|
||||||
|
|
||||||
|
[Unit_RAID]
|
||||||
|
Type = MDStat
|
||||||
|
BaseAddress = 3
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Barsik monitoring service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=dotnet /root/barsik/Barsik.dll
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
RuntimeDirectory=/root/barsik
|
||||||
|
RuntimeDirectoryMode=0755
|
||||||
|
ProtectHome=false
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user