├── Examples
├── SimplestExample
│ ├── App.config
│ ├── packages.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Program.cs
│ └── SimplestExample.csproj
├── Nabbix.Console
│ ├── packages.config
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── AdvancedCounters.cs
│ ├── App.config
│ ├── SimpleCounters.cs
│ ├── RandomGenerator.cs
│ ├── Program.cs
│ └── Nabbix.ConsoleApp.csproj
├── SimplestExample.NetCore
│ └── SimplestExample.NetCore.csproj
├── Nabbix.ConsoleApp.NetCore
│ └── Nabbix.ConsoleApp.NetCore.csproj
└── Examples.sln
├── Nabbix.Tests
├── Nabbix.Tests.csproj
├── WindowsPerformanceCountersTests.cs
├── NabbixItemAttributeTests.cs
└── NabbixAgentTests.cs
├── Nabbix
├── Items
│ ├── NabbixDiskSpace.cs
│ ├── NabbixFileCountItemAttribute.cs
│ ├── NabbixFileCount.cs
│ ├── PropertyHelper.cs
│ ├── NabbixItemAttribute.cs
│ ├── NabbixDiskSpaceItemAttribute.cs
│ └── BaseTypeHelper.cs
├── Item.cs
├── Nabbix.csproj
├── ItemRegistry.cs
├── WindowsPerformanceCounters.cs
├── QueryHandler.cs
└── NabbixAgent.cs
├── LICENSE.txt
├── Nabbix.sln
├── README.md
└── .gitignore
/Examples/SimplestExample/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/SimplestExample.NetCore/SimplestExample.NetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Examples/SimplestExample/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Nabbix.Tests/Nabbix.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp2.0
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Nabbix/Items/NabbixDiskSpace.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Nabbix.Items
5 | {
6 | public class NabbixDiskSpace
7 | {
8 | private readonly string _drive;
9 |
10 | public NabbixDiskSpace(string drive)
11 | {
12 | if (string.IsNullOrWhiteSpace(drive))
13 | throw new ArgumentException("Argument is null or whitespace", nameof(drive));
14 |
15 | _drive = drive;
16 | }
17 |
18 | internal DriveInfo GetDriveInfo()
19 | {
20 | return new DriveInfo(_drive);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("Nabbix.Console")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("Nabbix.Console")]
9 | [assembly: AssemblyCopyright("Copyright © 2016")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 | [assembly: ComVisible(false)]
13 | [assembly: Guid("04307925-07fa-42d0-a1a4-81f5ab643f64")]
14 | [assembly: AssemblyVersion("1.0.0.0")]
15 | [assembly: AssemblyFileVersion("1.0.0.0")]
16 |
--------------------------------------------------------------------------------
/Examples/SimplestExample/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("SimplestExample")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("SimplestExample")]
9 | [assembly: AssemblyCopyright("Copyright © 2016")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 | [assembly: ComVisible(false)]
13 | [assembly: Guid("f055c4fa-1232-406b-a98e-acf3002e17e2")]
14 | [assembly: AssemblyVersion("1.0.0.0")]
15 | [assembly: AssemblyFileVersion("1.0.0.0")]
16 |
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/AdvancedCounters.cs:
--------------------------------------------------------------------------------
1 | using Nabbix.Items;
2 |
3 | namespace Nabbix.ConsoleApp
4 | {
5 | public class AdvancedCounters
6 | {
7 | [NabbixDiskSpaceItem("c_available_free", "c_total_free", "c_total_size", "c_volume_label")]
8 | public NabbixDiskSpace DiskSpace { get; } = new NabbixDiskSpace(@"C");
9 |
10 | [NabbixFileCountItem("all_files")]
11 | public NabbixFileCount NabbixAllFiles { get; } = new NabbixFileCount(@"C:\git\nabbix");
12 |
13 | [NabbixFileCountItem("csharp_files")]
14 | public NabbixFileCount NabbixFiles { get; } = new NabbixFileCount(@"C:\git\nabbix", "*.cs");
15 | }
16 | }
--------------------------------------------------------------------------------
/Nabbix/Items/NabbixFileCountItemAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Nabbix.Items
5 | {
6 | [AttributeUsage(AttributeTargets.Property)]
7 | public class NabbixFileCountItemAttribute : NabbixItemAttribute
8 | {
9 | public NabbixFileCountItemAttribute(string zabbixItemKey) : base(zabbixItemKey)
10 | {
11 | }
12 |
13 | protected override string GetPropertyValue(string key, object propertyValue)
14 | {
15 | var fileCount = PropertyHelper.GetType(propertyValue);
16 | return fileCount?.GetFileCount().ToString(CultureInfo.InvariantCulture) ?? Item.NotSupported;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Nabbix.Tests/WindowsPerformanceCountersTests.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using Xunit;
3 |
4 | namespace Nabbix.Tests
5 | {
6 | public class WindowsPerformanceCountersTests
7 | {
8 | [Fact]
9 | public void ParseCounter_HappyPath_NonNullReturn()
10 | {
11 | PerformanceCounter counterA = WindowsPerformanceCounters.ParseCounter(
12 | @"perf_counter[""\Processor Information(_Total)\% Processor Time""]");
13 | PerformanceCounter counterB = WindowsPerformanceCounters.ParseCounter(
14 | @"perf_counter[""\Memory\Available Bytes""]");
15 |
16 | Assert.True(counterA != null);
17 | Assert.True(counterB != null);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Examples/Nabbix.ConsoleApp.NetCore/Nabbix.ConsoleApp.NetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Nabbix/Items/NabbixFileCount.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Nabbix.Items
5 | {
6 | public class NabbixFileCount
7 | {
8 | private readonly string _folder;
9 | private readonly string _searchPattern;
10 | private readonly SearchOption _searchOption;
11 |
12 | public NabbixFileCount(string folder, string searchPattern = "*", SearchOption searchOption = SearchOption.AllDirectories)
13 | {
14 | if (string.IsNullOrWhiteSpace(folder))
15 | throw new ArgumentException("Argument is null or whitespace", nameof(folder));
16 |
17 | _folder = folder;
18 | _searchPattern = searchPattern;
19 | _searchOption = searchOption;
20 | }
21 |
22 | internal int GetFileCount() => Directory.GetFiles(_folder, _searchPattern, _searchOption).Length;
23 | }
24 | }
--------------------------------------------------------------------------------
/Nabbix/Item.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using Nabbix.Items;
4 |
5 | namespace Nabbix
6 | {
7 | public class Item
8 | {
9 | public const string NotSupported = "ZBX_NOTSUPPORTED";
10 |
11 | private readonly PropertyInfo _property;
12 | private readonly NabbixItemAttribute _attribute;
13 | private readonly object _instance;
14 |
15 | public Item(PropertyInfo property, NabbixItemAttribute attribute, object instance)
16 | {
17 | if (property == null) throw new ArgumentNullException(nameof(property));
18 | if (attribute == null) throw new ArgumentNullException(nameof(attribute));
19 | if (instance == null) throw new ArgumentNullException(nameof(instance));
20 |
21 | _property = property;
22 | _attribute = attribute;
23 | _instance = instance;
24 | }
25 |
26 | public string GetValue(string key)
27 | {
28 | return _attribute.GetValue(key, _instance, _property);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/Nabbix/Nabbix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net461
4 | $(PackageVersion)
5 | $(PackageVersion)
6 | https://github.com/nolstoy/nabbix/blob/master/LICENSE.txt
7 | https://github.com/nolstoy/nabbix
8 | This package contains a lightweight, embeddable C# Zabbix Agent that responds to Zabbix Passive Checks.
9 | https://github.com/nolstoy/nabbix
10 | $(PackageVersion)
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/Examples/SimplestExample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using Nabbix;
4 | using Nabbix.Items;
5 |
6 | namespace SimplestExample
7 | {
8 | internal class Program
9 | {
10 | private class MyCounter
11 | {
12 | private long _incrementing;
13 | internal void Increment()
14 | {
15 | Interlocked.Increment(ref _incrementing);
16 | }
17 |
18 | [NabbixItem("long_example")]
19 | public long Incrementing => Interlocked.Read(ref _incrementing);
20 | }
21 |
22 | private static void Main()
23 | {
24 | // Create the instance of our counter
25 | var counters = new MyCounter();
26 |
27 | // Start the agent.
28 | var agent = new NabbixAgent(10052, counters);
29 |
30 | // Increment the counter. Normally done on API or method call.
31 | counters.Increment();
32 |
33 | // Shutdown
34 | Console.ReadKey();
35 | agent.Stop();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Nabbix/Items/PropertyHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Common.Logging;
3 |
4 | namespace Nabbix.Items
5 | {
6 | public static class PropertyHelper
7 | {
8 | private static readonly ILog Log = LogManager.GetLogger(typeof(PropertyHelper));
9 |
10 | public static T GetType(object propertyValue) where T : class
11 | {
12 | var propertyType = propertyValue as T;
13 | if (propertyType == null)
14 | {
15 | Log.ErrorFormat(
16 | "propertyValue is of the wrong type. It must be associated with a {0} property, but it's associated with: {1}",
17 | typeof(T).Name,
18 | propertyValue.GetType().Name);
19 | }
20 |
21 | return propertyType;
22 | }
23 |
24 |
25 | internal static string GetZabbixItem(string prefix, string value)
26 | {
27 | if (string.IsNullOrWhiteSpace(prefix))
28 | throw new ArgumentException("Argument is null or whitespace", nameof(prefix));
29 | if (string.IsNullOrWhiteSpace(value))
30 | throw new ArgumentException("Argument is null or whitespace", nameof(value));
31 |
32 | return prefix + "_" + value;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Nabbix.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.0.0
5 | MinimumVisualStudioVersion = 10.0.0.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nabbix", "Nabbix\Nabbix.csproj", "{DA811772-A05C-41A3-81DC-FEEAC4E08DA4}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nabbix.Tests", "Nabbix.Tests\Nabbix.Tests.csproj", "{17F29A0E-352F-4608-A515-7A916E2AE490}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {DA811772-A05C-41A3-81DC-FEEAC4E08DA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {DA811772-A05C-41A3-81DC-FEEAC4E08DA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {DA811772-A05C-41A3-81DC-FEEAC4E08DA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {DA811772-A05C-41A3-81DC-FEEAC4E08DA4}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {17F29A0E-352F-4608-A515-7A916E2AE490}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {17F29A0E-352F-4608-A515-7A916E2AE490}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {17F29A0E-352F-4608-A515-7A916E2AE490}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {17F29A0E-352F-4608-A515-7A916E2AE490}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/Nabbix/Items/NabbixItemAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using Fasterflect;
6 |
7 | namespace Nabbix.Items
8 | {
9 | [AttributeUsage(AttributeTargets.Property)]
10 | public class NabbixItemAttribute : Attribute
11 | {
12 | public NabbixItemAttribute(string zabbixItemKey)
13 | {
14 | if (string.IsNullOrWhiteSpace(zabbixItemKey)) throw new ArgumentNullException(nameof(zabbixItemKey));
15 |
16 | ZabbixItemKeys = new List { zabbixItemKey};
17 | }
18 |
19 | public NabbixItemAttribute(ICollection itemKeys)
20 | {
21 | if (itemKeys == null || itemKeys.Count == 0) throw new ArgumentException("Argument is empty collection", nameof(itemKeys));
22 |
23 | ZabbixItemKeys = itemKeys;
24 | }
25 |
26 | public ICollection ZabbixItemKeys { get; }
27 |
28 | protected virtual string GetPropertyValue(string key, object propertyValue)
29 | {
30 | return BaseTypeHelper.GetPropertyValue(propertyValue);
31 | }
32 |
33 | internal string GetValue(string key, object instance, PropertyInfo propertyInfo)
34 | {
35 | if (ZabbixItemKeys.Count == 1)
36 | {
37 | string first = ZabbixItemKeys.First();
38 | if (key != first)
39 | {
40 | throw new ArgumentException($"key is invalid. {key} != {first}");
41 | }
42 | }
43 |
44 | object propertyValue = propertyInfo.Get(instance);
45 | if (propertyValue == null)
46 | return Item.NotSupported;
47 |
48 | return GetPropertyValue(key, propertyValue);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/SimpleCounters.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using Nabbix.Items;
3 |
4 | namespace Nabbix.ConsoleApp
5 | {
6 | public class SimpleCounters
7 | {
8 | private readonly object _floatLockObj = new object();
9 | private float _float;
10 | private long _incrementing;
11 |
12 | private readonly object _doubleLockObj = new object();
13 | private double _double;
14 |
15 | private readonly object _decimalLockObj = new object();
16 | private decimal _decimal;
17 |
18 | private readonly object _stringLockObj = new object();
19 | private string _string;
20 |
21 | internal void Increment()
22 | {
23 | Interlocked.Increment(ref _incrementing);
24 | }
25 |
26 | [NabbixItem("long_example")]
27 | public long Incrementing => Interlocked.Read(ref _incrementing);
28 |
29 | [NabbixItem("float_example")]
30 | public float FloatExample
31 | {
32 | get { lock (_floatLockObj) return _float; }
33 | set { lock (_floatLockObj) _float = value; }
34 | }
35 |
36 | [NabbixItem("double_example")]
37 | public double DoubleExample
38 | {
39 | get { lock (_doubleLockObj) return _double; }
40 | set { lock (_doubleLockObj) _double = value; }
41 | }
42 |
43 | [NabbixItem("decimal_example")]
44 | public decimal DecimalExample
45 | {
46 | get { lock (_decimalLockObj) return _decimal; }
47 | set { lock (_decimalLockObj) _decimal = value; }
48 | }
49 |
50 | [NabbixItem("string_example")]
51 | public string StringExample
52 | {
53 | get { lock (_stringLockObj) return _string; }
54 | set { lock (_stringLockObj) _string = value; }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.myget.org/feed/nolstoy-packages/package/nuget/Nabbix)[](https://www.nuget.org/packages/Nabbix/) [](https://www.nuget.org/packages/Nabbix/)
2 | # What?
3 |
4 | A lightweight Zabbix Agent that can be embedded in any .Net program ~ ASP.Net, Windows Service or Console Applications.
5 |
6 | # Why?
7 |
8 | Zabbix does have a Windows Agent. It's largely limited to Performance Counters, Log Files and the Windows Event Log. When monitoring a .Net application, it's a non trivial task to install the agent and register custom performance counters.
9 |
10 | With Nabbix, monitoring a .Net program only requires referencing a NuGet package and adding a few lines of code.
11 |
12 | # How?
13 |
14 | ## 1. Add nabbix NuGet Package
15 |
16 | ```
17 | Install-Package Nabbix
18 | ```
19 |
20 | ## 2. Create class(es) with the counter(s)
21 |
22 | ```
23 |
24 | // Class containing a single Zabbix Item 'long_example'
25 | private class MyCounter
26 | {
27 | private long _incrementing;
28 | internal void Increment()
29 | {
30 | Interlocked.Increment(ref _incrementing);
31 | }
32 |
33 | [NabbixItem("long_example")]
34 | public long Incrementing => Interlocked.Read(ref _incrementing);
35 | }
36 | ```
37 |
38 | ## 3. Create a Nabbix Agent and register instances of the counter classes.
39 |
40 | ```
41 | private static void Main()
42 | {
43 | // Create the instance of the counter class with a single Zabbix Item
44 | var counters = new MyCounter();
45 |
46 | // Start the agent.
47 | var agent = new NabbixAgent(10052, counters);
48 |
49 | // Increment the counter. Normally done on API or method call.
50 | counters.Increment();
51 |
52 | // Shutdown
53 | Console.ReadKey();
54 | agent.Stop();
55 | }
56 | ```
57 |
58 | # More information?
59 |
60 | https://github.com/nolstoy/nabbix/wiki
61 |
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/RandomGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace Nabbix.ConsoleApp
5 | {
6 | public class RandomGenerator
7 | {
8 | private readonly Random _rand = new Random();
9 |
10 | public int NextInt(int min, int max)
11 | {
12 | return _rand.Next(min, max);
13 | }
14 |
15 | internal long NextLong()
16 | {
17 | return _rand.Next(1000, 10000);
18 | }
19 |
20 | internal double NextDouble()
21 | {
22 | return _rand.NextDouble();
23 | }
24 |
25 | // http://stackoverflow.com/questions/3365337/best-way-to-generate-a-random-float-in-c-sharp
26 | internal float NextFloat()
27 | {
28 | double range = (double)float.MaxValue - (double)float.MinValue;
29 | double sample = _rand.NextDouble();
30 | double scaled = (sample * range) + float.MinValue;
31 | return (float)scaled;
32 | }
33 |
34 | private int NextInt32()
35 | {
36 | unchecked
37 | {
38 | int firstBits = _rand.Next(0, 1 << 4) << 28;
39 | int lastBits = _rand.Next(0, 1 << 28);
40 | return firstBits | lastBits;
41 | }
42 | }
43 |
44 | // http://stackoverflow.com/questions/609501/generating-a-random-decimal-in-c-sharp
45 | internal decimal NextDecimal()
46 | {
47 | byte scale = (byte) _rand.Next(29);
48 | bool sign = _rand.Next(2) == 1;
49 | return new decimal(NextInt32(),
50 | NextInt32(),
51 | NextInt32(),
52 | sign,
53 | scale);
54 | }
55 |
56 | public string NextString()
57 | {
58 | const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
59 | var random = new Random();
60 | return new string(Enumerable.Repeat(chars, _rand.Next(10, 50))
61 | .Select(s => s[random.Next(s.Length)]).ToArray());
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/Nabbix/Items/NabbixDiskSpaceItemAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using static Nabbix.Items.PropertyHelper;
6 |
7 | namespace Nabbix.Items
8 | {
9 | public class NabbixDiskSpaceItemAttribute : NabbixItemAttribute
10 | {
11 | public NabbixDiskSpaceItemAttribute(string zabbixItemPrefix)
12 | : this(GetZabbixItem(zabbixItemPrefix, "available_freespace"),
13 | GetZabbixItem(zabbixItemPrefix, "total_freespace"),
14 | GetZabbixItem(zabbixItemPrefix, "total_size"),
15 | GetZabbixItem(zabbixItemPrefix, "volume_label"))
16 | {
17 | }
18 |
19 | public NabbixDiskSpaceItemAttribute(string availableFreeSpaceZabbixItem,
20 | string totalFreeSpaceZabbixItem,
21 | string totalSizeZabbixItem,
22 | string volumeLabelZabbixItem)
23 | : base(new[] {availableFreeSpaceZabbixItem, totalFreeSpaceZabbixItem, totalSizeZabbixItem, volumeLabelZabbixItem})
24 | {
25 | _itemMapping = new Dictionary>();
26 | _itemMapping.Add(availableFreeSpaceZabbixItem, d => d.AvailableFreeSpace.ToString(CultureInfo.InvariantCulture));
27 | _itemMapping.Add(totalFreeSpaceZabbixItem, d => d.TotalFreeSpace.ToString(CultureInfo.InvariantCulture));
28 | _itemMapping.Add(totalSizeZabbixItem, d => d.TotalSize.ToString(CultureInfo.InvariantCulture));
29 | _itemMapping.Add(volumeLabelZabbixItem, d => d.VolumeLabel);
30 | }
31 |
32 | private readonly Dictionary> _itemMapping;
33 |
34 | protected override string GetPropertyValue(string key, object propertyValue)
35 | {
36 | NabbixDiskSpace diskSpace = GetType(propertyValue);
37 | DriveInfo info = diskSpace?.GetDriveInfo();
38 | if (info == null)
39 | return Item.NotSupported;
40 |
41 | Func item;
42 | if (_itemMapping.TryGetValue(key, out item))
43 | {
44 | return item(info);
45 | }
46 |
47 | return Item.NotSupported;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/Nabbix/ItemRegistry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Reflection;
4 | using Common.Logging;
5 | using Nabbix.Items;
6 |
7 | namespace Nabbix
8 | {
9 | public class ItemRegistry
10 | {
11 | private static readonly ILog Log = LogManager.GetLogger(typeof (ItemRegistry));
12 |
13 | public ConcurrentDictionary RegisteredProperties { get; }
14 |
15 | public ItemRegistry()
16 | {
17 | RegisteredProperties = new ConcurrentDictionary();
18 | }
19 |
20 | public void RegisterInstance(object instance)
21 | {
22 | foreach (PropertyInfo property in instance.GetType().GetProperties())
23 | {
24 | var attribute = property.GetCustomAttribute();
25 | if (attribute != null)
26 | {
27 | foreach (var key in attribute.ZabbixItemKeys)
28 |
29 | if (key != null)
30 | {
31 | RegisteredProperties.TryAdd(key, new Item(property, attribute, instance));
32 | }
33 | else
34 | {
35 | Log.WarnFormat("NabbixItemAttribute - Missing key for object {0}, {1}",
36 | instance.GetType(), property.Name);
37 | }
38 | }
39 | }
40 | }
41 |
42 | public string GetItemValue(string key)
43 | {
44 | key = key.Trim();
45 |
46 | if (key == "agent.ping")
47 | return "1";
48 |
49 | try
50 | {
51 | if (WindowsPerformanceCounters.IsCounter(key))
52 | {
53 | return WindowsPerformanceCounters.GetNextValue(key);
54 | }
55 |
56 | Item item;
57 | if (RegisteredProperties.TryGetValue(key, out item))
58 | {
59 | return item.GetValue(key);
60 | }
61 | }
62 | catch (Exception e)
63 | {
64 | Log.ErrorFormat("Exception occurred querying key {0}", e, key);
65 | }
66 |
67 |
68 |
69 | return Item.NotSupported;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/Nabbix/Items/BaseTypeHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Nabbix.Items
4 | {
5 | public static class BaseTypeHelper
6 | {
7 | public static string GetPropertyValue(object propertyValue)
8 | {
9 | if (propertyValue is float)
10 | return GetFloatValue((float) propertyValue);
11 | if (propertyValue is double)
12 | return GetDoubleValue((double) propertyValue);
13 | if (propertyValue is decimal)
14 | return GetDecimalValue((decimal) propertyValue);
15 | return propertyValue.ToString();
16 | }
17 |
18 | public static string GetPropertyValue(ValueType propertyValue)
19 | {
20 | if (propertyValue is float)
21 | return GetFloatValue((float)propertyValue);
22 | if (propertyValue is double)
23 | return GetDoubleValue((double)propertyValue);
24 | if (propertyValue is decimal)
25 | return GetDecimalValue((decimal)propertyValue);
26 | return propertyValue.ToString();
27 | }
28 |
29 | // Not perfect, but it's close to the maximum values of
30 | // https://www.zabbix.com/documentation/2.0/manual/config/items/item
31 |
32 | public const double MinDoubleValue = -999000000000.0D;
33 | public const double MaxDoubleValue = 999000000000.0D;
34 |
35 | public static string GetDoubleValue(double value)
36 | {
37 | value = Math.Min(MaxDoubleValue, value);
38 | value = Math.Max(MinDoubleValue, value);
39 |
40 | return value.ToString("0.0000");
41 | }
42 |
43 | public const decimal MinDecimalValue = -999000000000.0m;
44 | public const decimal MaxDecimalValue = 999000000000.0m;
45 |
46 | public static string GetDecimalValue(decimal value)
47 | {
48 | value = Math.Min(MaxDecimalValue, value);
49 | value = Math.Max(MinDecimalValue, value);
50 |
51 | return value.ToString("0.0000");
52 | }
53 |
54 | public const float MinFloatValue = -990000000000.0f;
55 | public const float MaxFloatValue = 990000000000.0f;
56 |
57 | public static string GetFloatValue(float value)
58 | {
59 | value = Math.Min(MaxFloatValue, value);
60 | value = Math.Max(MinFloatValue, value);
61 |
62 | return value.ToString("0.0000");
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading;
4 |
5 | namespace Nabbix.ConsoleApp
6 | {
7 | internal class Program
8 | {
9 | private static volatile bool _stopped;
10 |
11 | private static void IncrementCounters(RandomGenerator random, int millisecondsTimeout, SimpleCounters counters, AdvancedCounters moreCounters)
12 | {
13 | while (_stopped == false)
14 | {
15 | counters.Increment();
16 | counters.FloatExample = random.NextFloat();
17 | counters.DoubleExample = random.NextDouble();
18 | counters.DecimalExample = random.NextDecimal();
19 | counters.StringExample = random.NextString();
20 |
21 | if (millisecondsTimeout == -1)
22 | {
23 | millisecondsTimeout = random.NextInt(1, 10);
24 | }
25 | else
26 | {
27 | Thread.Sleep(millisecondsTimeout);
28 | }
29 |
30 | }
31 | }
32 |
33 | // ReSharper disable once UseObjectOrCollectionInitializer
34 | private static Thread IncrementCountersOnBackgroundThread(int millisecondsTimeout, SimpleCounters counters, AdvancedCounters moreCounters)
35 | {
36 | var increaseCounters = new Thread(() => IncrementCounters(new RandomGenerator(), millisecondsTimeout, counters, moreCounters));
37 | increaseCounters.IsBackground = true;
38 | increaseCounters.Start();
39 |
40 | return increaseCounters;
41 | }
42 |
43 | private static void Main(string[] args)
44 | {
45 | var counters = new SimpleCounters();
46 | var moreCounters = new AdvancedCounters();
47 | INabbixAgent agent = new NabbixAgent(10052, counters, moreCounters);
48 |
49 | // 100,000 requests/s for an extended period of time will run out of memory.
50 |
51 | const int numThreads = 16;
52 | Thread[] threads = new Thread[numThreads];
53 |
54 | for (int i = 0; i < numThreads; i++)
55 | {
56 | threads[i] = IncrementCountersOnBackgroundThread(10, counters, moreCounters);
57 | }
58 |
59 | Console.ReadKey();
60 | _stopped = true;
61 | foreach (var thread in threads)
62 | {
63 | thread.Join();
64 | }
65 |
66 | agent.Stop();
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/Nabbix/WindowsPerformanceCounters.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using Nabbix.Items;
5 |
6 | namespace Nabbix
7 | {
8 | public class WindowsPerformanceCounters
9 | {
10 | private static readonly Dictionary Counters = new Dictionary();
11 |
12 | public static bool IsCounter(string key)
13 | {
14 | return IsWindows() && key.StartsWith("perf_counter");
15 | }
16 |
17 | public static string GetNextValue(string key)
18 | {
19 | if (!IsWindows()) throw new NotSupportedException();
20 |
21 | PerformanceCounter counter;
22 | if (!Counters.TryGetValue(key, out counter))
23 | {
24 | counter = ParseCounter(key);
25 | Counters.Add(key, counter);
26 | }
27 |
28 | float value = counter.NextValue();
29 | return BaseTypeHelper.GetFloatValue(value);
30 | }
31 |
32 | // https://www.zabbix.com/documentation/1.8/manual/config/windows_performance_counters
33 | public static PerformanceCounter ParseCounter(string key)
34 | {
35 | if (!IsWindows()) throw new NotSupportedException();
36 |
37 | int start = key.IndexOf('"');
38 | int end = key.LastIndexOf('"');
39 | int middle = key.LastIndexOf('\\');
40 | if (start == -1 || middle == -1 || end == -1)
41 | {
42 | throw new FormatException($"Performance Counter format is invalid {key}");
43 | }
44 |
45 | int instanceStart = key.IndexOf('(');
46 | int instanceEnd = key.IndexOf(')');
47 |
48 | int categoryLength = instanceStart == -1
49 | ? middle - start
50 | : instanceStart - start;
51 |
52 | const bool readOnly = true;
53 | string category = key.Substring(start + 2, categoryLength - 2);
54 | string counterName = key.Substring(middle + 1, end - middle - 1);
55 |
56 | if (instanceStart == -1 || instanceEnd == -1)
57 | {
58 | return new PerformanceCounter(category, counterName, readOnly);
59 | }
60 |
61 | string instance = key.Substring(instanceStart + 1, instanceEnd - instanceStart - 1);
62 | return new PerformanceCounter(category, counterName, instance, readOnly);
63 | }
64 |
65 | private static bool IsWindows()
66 | {
67 | OperatingSystem os = Environment.OSVersion;
68 | PlatformID pid = os.Platform;
69 |
70 | return pid == PlatformID.Win32S ||
71 | pid == PlatformID.Win32Windows ||
72 | pid == PlatformID.Win32NT ||
73 | pid == PlatformID.WinCE;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/Examples/Examples.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.15
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nabbix.ConsoleApp", "Nabbix.Console\Nabbix.ConsoleApp.csproj", "{04307925-07FA-42D0-A1A4-81F5AB643F64}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nabbix", "..\Nabbix\Nabbix.csproj", "{7E4C9B6B-E0A7-4909-B97C-D24D3EE1C477}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplestExample", "SimplestExample\SimplestExample.csproj", "{F055C4FA-1232-406B-A98E-ACF3002E17E2}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nabbix.Tests", "..\Nabbix.Tests\Nabbix.Tests.csproj", "{4FCDCBEB-68D6-43BD-BE7B-71099765AD2E}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nabbix.ConsoleApp.NetCore", "Nabbix.ConsoleApp.NetCore\Nabbix.ConsoleApp.NetCore.csproj", "{6C270573-402F-43B4-AFF0-623FA77895B9}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplestExample.NetCore", "SimplestExample.NetCore\SimplestExample.NetCore.csproj", "{93C6BF7A-B2A0-483D-8322-05FDAE1DC194}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {04307925-07FA-42D0-A1A4-81F5AB643F64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {04307925-07FA-42D0-A1A4-81F5AB643F64}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {04307925-07FA-42D0-A1A4-81F5AB643F64}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {04307925-07FA-42D0-A1A4-81F5AB643F64}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {7E4C9B6B-E0A7-4909-B97C-D24D3EE1C477}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {7E4C9B6B-E0A7-4909-B97C-D24D3EE1C477}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {7E4C9B6B-E0A7-4909-B97C-D24D3EE1C477}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {7E4C9B6B-E0A7-4909-B97C-D24D3EE1C477}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {F055C4FA-1232-406B-A98E-ACF3002E17E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {F055C4FA-1232-406B-A98E-ACF3002E17E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {F055C4FA-1232-406B-A98E-ACF3002E17E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {F055C4FA-1232-406B-A98E-ACF3002E17E2}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {4FCDCBEB-68D6-43BD-BE7B-71099765AD2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {4FCDCBEB-68D6-43BD-BE7B-71099765AD2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {4FCDCBEB-68D6-43BD-BE7B-71099765AD2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {4FCDCBEB-68D6-43BD-BE7B-71099765AD2E}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {6C270573-402F-43B4-AFF0-623FA77895B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {6C270573-402F-43B4-AFF0-623FA77895B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {6C270573-402F-43B4-AFF0-623FA77895B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {6C270573-402F-43B4-AFF0-623FA77895B9}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {93C6BF7A-B2A0-483D-8322-05FDAE1DC194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {93C6BF7A-B2A0-483D-8322-05FDAE1DC194}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {93C6BF7A-B2A0-483D-8322-05FDAE1DC194}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {93C6BF7A-B2A0-483D-8322-05FDAE1DC194}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {BEF4CE05-8104-4768-A3BC-B1686FDFFAD7}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/Examples/SimplestExample/SimplestExample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F055C4FA-1232-406B-A98E-ACF3002E17E2}
8 | Exe
9 | Properties
10 | SimplestExample
11 | SimplestExample
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\Common.Logging.3.4.1\lib\net40\Common.Logging.dll
38 |
39 |
40 | ..\packages\Common.Logging.Core.3.4.1\lib\net40\Common.Logging.Core.dll
41 |
42 |
43 | ..\packages\fasterflect.2.1.3\lib\net40\Fasterflect.dll
44 | True
45 |
46 |
47 | ..\packages\Fasterflect.Netstandard.1.0.8\lib\net45\Fasterflect.Netstandard.dll
48 |
49 |
50 |
51 | ..\packages\Nabbix.0.2.4\lib\net45\Nabbix.dll
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Designer
64 |
65 |
66 |
67 |
74 |
--------------------------------------------------------------------------------
/Examples/Nabbix.Console/Nabbix.ConsoleApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {04307925-07FA-42D0-A1A4-81F5AB643F64}
8 | Exe
9 | Properties
10 | Nabbix.ConsoleApp
11 | Nabbix.ConsoleApp
12 | v4.5.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\Common.Logging.3.4.1\lib\net40\Common.Logging.dll
38 |
39 |
40 | ..\packages\Common.Logging.Core.3.4.1\lib\net40\Common.Logging.Core.dll
41 |
42 |
43 | ..\packages\Fasterflect.Netstandard.1.0.8\lib\net45\Fasterflect.Netstandard.dll
44 |
45 |
46 | ..\packages\Nabbix.0.2.4\lib\net45\Nabbix.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
77 |
--------------------------------------------------------------------------------
/Nabbix/QueryHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Sockets;
5 | using System.Text;
6 | using Common.Logging;
7 |
8 | namespace Nabbix
9 | {
10 | internal class QueryHandler
11 | {
12 | private static readonly ILog Log = LogManager.GetLogger(typeof(QueryHandler));
13 |
14 | private static byte[] GetResponseLength(string response)
15 | {
16 | var responseLength = new byte[8];
17 | long length = response.Length;
18 | for (int i = 0; i < 8; i++)
19 | {
20 | responseLength[i] = (byte)(int)(length & 0xFF);
21 | length >>= 8;
22 | }
23 | return responseLength;
24 | }
25 |
26 | private static string GetRequest(NetworkStream stream)
27 | {
28 | const int lengthSize = 4;
29 | const int reservedSize = 4;
30 |
31 | var dataLength = 0;
32 |
33 | var oldProtocol = false;
34 | var bytes = new List();
35 | int currentByte;
36 | do
37 | {
38 | currentByte = stream.ReadByte();
39 | bytes.Add((byte) currentByte);
40 |
41 | if (bytes.Count <= Header.Length)
42 | {
43 | if (!bytes.Take(bytes.Count).SequenceEqual(Header.Take(bytes.Count)))
44 | oldProtocol = true;
45 | }
46 | else if (bytes.Count == Header.Length + lengthSize)
47 | {
48 | var lengthBytes = bytes.Skip(Header.Length).Take(lengthSize).ToArray();
49 | dataLength = GetLittleEndianIntegerFromByteArray(lengthBytes, 0);
50 | }
51 | else if (bytes.Count == Header.Length + lengthSize + reservedSize + dataLength)
52 | {
53 | var dataBytes = bytes.Skip(Header.Length + lengthSize + reservedSize).Take(dataLength).ToArray();
54 | var dataString = Encoding.ASCII.GetString(dataBytes);
55 |
56 | return dataString;
57 | }
58 |
59 | if(oldProtocol && currentByte == 10)
60 | return Encoding.ASCII.GetString(bytes.ToArray());
61 | }
62 | while (currentByte != -1);
63 |
64 | return "ZBX_INVALID_DATA_ERR"; // never occurs i think
65 | }
66 |
67 | private static readonly byte[] Header = {(byte)'Z', (byte)'B', (byte)'X', (byte)'D', 1};
68 | private static void SendResponse(NetworkStream stream, string response)
69 | {
70 | byte[] responseLength = GetResponseLength(response);
71 | byte[] responseBytes = Encoding.ASCII.GetBytes(response);
72 |
73 | byte[] buffer = new byte[Header.Length + responseLength.Length + responseBytes.Length];
74 |
75 | // https://www.zabbix.com/documentation/1.8/protocols
76 | Array.Copy(Header, 0, buffer, 0, Header.Length);
77 | Array.Copy(responseLength, 0, buffer, Header.Length, responseLength.Length);
78 | Array.Copy(responseBytes, 0, buffer, Header.Length + responseLength.Length, responseBytes.Length);
79 |
80 | Log.Debug("Response sending...");
81 | stream.Write(buffer, 0, buffer.Length);
82 | stream.Flush();
83 | Log.Debug("Response sent.");
84 | }
85 |
86 | internal static void Run(TcpClient client, ItemRegistry registry)
87 | {
88 | Log.Debug("Run... .");
89 |
90 | var stream = client.GetStream();
91 | do
92 | {
93 | Log.Debug("Request recieving...");
94 | var request = GetRequest(stream);
95 | Log.DebugFormat("Request received: {0}", request);
96 |
97 | var response = registry.GetItemValue(request);
98 | Log.DebugFormat("Response: {0}", response);
99 | SendResponse(stream, response);
100 | } while (stream.DataAvailable);
101 |
102 | Log.Debug("Run. Ended.");
103 | }
104 |
105 | static int GetLittleEndianIntegerFromByteArray(byte[] data, int startIndex)
106 | {
107 | return (data[startIndex + 3] << 24)
108 | | (data[startIndex + 2] << 16)
109 | | (data[startIndex + 1] << 8)
110 | | data[startIndex];
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Nabbix.Tests/NabbixItemAttributeTests.cs:
--------------------------------------------------------------------------------
1 | using Nabbix.Items;
2 | using Xunit;
3 |
4 | namespace Nabbix.Tests
5 | {
6 | public class BaseTypeHelperTests
7 | {
8 | [Fact]
9 | public void GetFloatValue_SizeExceedsMax_ReturnsMaxAllowedValue()
10 | {
11 | const string maxZabbixMySqlValue = "990000000000.0000";
12 | const float exceedsMaxSize = 999999999999.9999f;
13 |
14 | string result = BaseTypeHelper.GetFloatValue(exceedsMaxSize);
15 | Assert.Equal(maxZabbixMySqlValue, result);
16 | }
17 |
18 | [Fact]
19 | public void GetFloatValue_SizeExceedsMin_ReturnsMinAllowedValue()
20 | {
21 | const string minZabbixMySqlValue = "-990000000000.0000";
22 | const float exceedsMinSize = -999999999999.9999f;
23 |
24 | string result = BaseTypeHelper.GetFloatValue(exceedsMinSize);
25 | Assert.Equal(minZabbixMySqlValue, result);
26 | }
27 |
28 | [Fact]
29 | public void GetFloatValue_SizeIsAcceptable_ReturnsSize()
30 | {
31 | const float validSize = 124.99f;
32 |
33 | string result = BaseTypeHelper.GetFloatValue(validSize);
34 | Assert.Equal("124.9900", result);
35 | }
36 |
37 | [Fact]
38 | public void GetFloatValue_TooManyDecimals_DecimalIsTrimmed()
39 | {
40 | const float validSize = 124.99543f;
41 |
42 | string result = BaseTypeHelper.GetFloatValue(validSize);
43 | Assert.Equal("124.9954", result);
44 | }
45 |
46 | [Fact]
47 | public void GetDoubleValue_SizeExceedsMax_ReturnsMaxAllowedValue()
48 | {
49 | const string maxZabbixMySqlValue = "999000000000.0000";
50 | const double exceedsMaxSize = 999999999999.9999D;
51 |
52 | string result = BaseTypeHelper.GetDoubleValue(exceedsMaxSize);
53 | Assert.Equal(maxZabbixMySqlValue, result);
54 | }
55 |
56 | [Fact]
57 | public void GetDoubleValue_SizeExceedsMin_ReturnsMinAllowedValue()
58 | {
59 | const string minZabbixMySqlValue = "-999000000000.0000";
60 | const double exceedsMinSize = -999999999999.9999D;
61 |
62 | string result = BaseTypeHelper.GetDoubleValue(exceedsMinSize);
63 | Assert.Equal(minZabbixMySqlValue, result);
64 | }
65 |
66 | [Fact]
67 | public void GetDoubleValue_SizeIsAcceptable_ReturnsSize()
68 | {
69 | const double validSize = 124.99D;
70 |
71 | string result = BaseTypeHelper.GetDoubleValue(validSize);
72 | Assert.Equal("124.9900", result);
73 | }
74 |
75 | [Fact]
76 | public void GetDoubleValue_TooManyDecimals_DecimalIsTrimmed()
77 | {
78 | const double validSize = 124.99543D;
79 |
80 | string result = BaseTypeHelper.GetDoubleValue(validSize);
81 | Assert.Equal("124.9954", result);
82 | }
83 |
84 | [Fact]
85 | public void GetDecimalValue_SizeExceedsMax_ReturnsMaxAllowedValue()
86 | {
87 | const string maxZabbixMySqlValue = "999000000000.0000";
88 | const decimal exceedsMaxSize = 999999999999.9999m;
89 |
90 | string result = BaseTypeHelper.GetDecimalValue(exceedsMaxSize);
91 | Assert.Equal(maxZabbixMySqlValue, result);
92 | }
93 |
94 | [Fact]
95 | public void GetDecimalValue_SizeExceedsMin_ReturnsMinAllowedValue()
96 | {
97 | const string minZabbixMySqlValue = "-999000000000.0000";
98 | const decimal exceedsMinSize = -999999999999.9999m;
99 |
100 | string result = BaseTypeHelper.GetDecimalValue(exceedsMinSize);
101 | Assert.Equal(minZabbixMySqlValue, result);
102 | }
103 |
104 | [Fact]
105 | public void GetDecimalValue_SizeIsAcceptable_ReturnsSize()
106 | {
107 | const decimal validSize = 124.99m;
108 |
109 | string result = BaseTypeHelper.GetDecimalValue(validSize);
110 | Assert.Equal("124.9900", result);
111 | }
112 |
113 | [Fact]
114 | public void GetDecimalValue_TooManyDecimals_DecimalIsTrimmed()
115 | {
116 | const decimal validSize = 124.99543m;
117 |
118 | string result = BaseTypeHelper.GetDecimalValue(validSize);
119 | Assert.Equal("124.9954", result);
120 | }
121 | }
122 | }
--------------------------------------------------------------------------------
/Nabbix/NabbixAgent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Threading;
5 | using Common.Logging;
6 |
7 | namespace Nabbix
8 | {
9 | public interface INabbixAgent
10 | {
11 | void Start();
12 | void Stop();
13 | void RegisterInstance(object instance);
14 | }
15 |
16 | public class NabbixAgent : INabbixAgent
17 | {
18 | private static readonly ILog Log = LogManager.GetLogger(typeof(NabbixAgent));
19 |
20 | private readonly ItemRegistry _registry;
21 | private readonly IPAddress _address;
22 | private readonly int _port;
23 |
24 | private TcpListener _listener;
25 | private CancellationTokenSource _source;
26 |
27 | public int LocalEndpointPort
28 | {
29 | get
30 | {
31 | return ((IPEndPoint)_listener.LocalEndpoint).Port;
32 | }
33 | }
34 |
35 | public NabbixAgent(ItemRegistry registry, string address, int port, bool startImmediately = true)
36 | {
37 | if (registry == null) throw new ArgumentNullException(nameof(registry));
38 |
39 | _registry = registry;
40 | _address = address == null
41 | ? IPAddress.Any
42 | : IPAddress.Parse(address);
43 | _port = port;
44 |
45 | if (startImmediately)
46 | {
47 | Start();
48 | }
49 | }
50 |
51 | public NabbixAgent(string address, int port, bool startImmediately = true, params object[] instances)
52 | : this(new ItemRegistry(), address, port, startImmediately)
53 | {
54 | if (instances == null)
55 | {
56 | return;
57 | }
58 |
59 | foreach (object instance in instances)
60 | {
61 | RegisterInstance(instance);
62 | }
63 | }
64 |
65 | public NabbixAgent(int port, params object[] instances)
66 | : this(null, port, true, instances)
67 | {
68 | }
69 |
70 | public NabbixAgent(string address, int port, params object[] instances)
71 | : this(address, port, true, instances)
72 | {
73 | }
74 |
75 | public void Start()
76 | {
77 | Log.Info("Starting NabbixAgent.");
78 | _source = new CancellationTokenSource();
79 |
80 | _listener = new TcpListener(_address, _port);
81 | _listener.Start();
82 |
83 | _listener.BeginAcceptTcpClient(ProcessRequest, _listener);
84 | }
85 |
86 | private void ProcessRequest(IAsyncResult ar)
87 | {
88 | if (_source.Token.IsCancellationRequested)
89 | {
90 | return;
91 | }
92 |
93 | if (!(ar.AsyncState is TcpListener listener))
94 | {
95 | Log.Debug("TcpLister is Null. This is impossible");
96 | return;
97 | }
98 |
99 | if (_source.Token.IsCancellationRequested)
100 | {
101 | Log.Debug("Token is already cancelled");
102 | return;
103 | }
104 |
105 | listener.BeginAcceptTcpClient(ProcessRequest, listener);
106 |
107 | try
108 | {
109 | using (TcpClient client = listener.EndAcceptTcpClient(ar))
110 | {
111 | QueryHandler.Run(client, _registry);
112 | }
113 | }
114 | catch (Exception e)
115 | {
116 | Log.Error("Error in QueryHandler Run", e);
117 | }
118 | }
119 |
120 | public void Stop()
121 | {
122 | if (_source.Token.IsCancellationRequested)
123 | {
124 | Log.Debug("Cancellation has already been requested.");
125 | return;
126 | }
127 |
128 | Log.Info("Stopping TCP connections.");
129 | _source.Cancel();
130 |
131 | Thread.Sleep(100);
132 | Log.Info("Stopping TCP Listener.");
133 | _listener.Stop();
134 |
135 | Log.Info("Stopped successfully.");
136 | }
137 |
138 | public void RegisterInstance(object instance)
139 | {
140 | if (instance == null)
141 | {
142 | Log.WarnFormat("instance is null");
143 | return;
144 | }
145 |
146 | Log.InfoFormat("Registering instance {0}.", instance.GetType());
147 |
148 | _registry.RegisterInstance(instance);
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | *.idea
14 | *.DS_Store
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # DNX
46 | project.lock.json
47 | artifacts/
48 |
49 | *_i.c
50 | *_p.c
51 | *_i.h
52 | *.ilk
53 | *.meta
54 | *.obj
55 | *.pch
56 | *.pdb
57 | *.pgc
58 | *.pgd
59 | *.rsp
60 | *.sbr
61 | *.tlb
62 | *.tli
63 | *.tlh
64 | *.tmp
65 | *.tmp_proj
66 | *.log
67 | *.vspscc
68 | *.vssscc
69 | .builds
70 | *.pidb
71 | *.svclog
72 | *.scc
73 |
74 | # Chutzpah Test files
75 | _Chutzpah*
76 |
77 | # Visual C++ cache files
78 | ipch/
79 | *.aps
80 | *.ncb
81 | *.opendb
82 | *.opensdf
83 | *.sdf
84 | *.cachefile
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # NuGet Packages
149 | *.nupkg
150 | # The packages folder can be ignored because of Package Restore
151 | **/packages/*
152 | # except build/, which is used as an MSBuild target.
153 | !**/packages/build/
154 | # Uncomment if necessary however generally it will be regenerated when needed
155 | #!**/packages/repositories.config
156 | # NuGet v3's project.json files produces more ignoreable files
157 | *.nuget.props
158 | *.nuget.targets
159 |
160 | # Microsoft Azure Build Output
161 | csx/
162 | *.build.csdef
163 |
164 | # Microsoft Azure Emulator
165 | ecf/
166 | rcf/
167 |
168 | # Microsoft Azure ApplicationInsights config file
169 | ApplicationInsights.config
170 |
171 | # Windows Store app package directory
172 | AppPackages/
173 | BundleArtifacts/
174 |
175 | # Visual Studio cache files
176 | # files ending in .cache can be ignored
177 | *.[Cc]ache
178 | # but keep track of directories ending in .cache
179 | !*.[Cc]ache/
180 |
181 | # Others
182 | ClientBin/
183 | ~$*
184 | *~
185 | *.dbmdl
186 | *.dbproj.schemaview
187 | *.pfx
188 | *.publishsettings
189 | node_modules/
190 | orleans.codegen.cs
191 |
192 | # RIA/Silverlight projects
193 | Generated_Code/
194 |
195 | # Backup & report files from converting an old project file
196 | # to a newer Visual Studio version. Backup files are not needed,
197 | # because we have git ;-)
198 | _UpgradeReport_Files/
199 | Backup*/
200 | UpgradeLog*.XML
201 | UpgradeLog*.htm
202 |
203 | # SQL Server files
204 | *.mdf
205 | *.ldf
206 |
207 | # Business Intelligence projects
208 | *.rdl.data
209 | *.bim.layout
210 | *.bim_*.settings
211 |
212 | # Microsoft Fakes
213 | FakesAssemblies/
214 |
215 | # GhostDoc plugin setting file
216 | *.GhostDoc.xml
217 |
218 | # Node.js Tools for Visual Studio
219 | .ntvs_analysis.dat
220 |
221 | # Visual Studio 6 build log
222 | *.plg
223 |
224 | # Visual Studio 6 workspace options file
225 | *.opt
226 |
227 | # Visual Studio LightSwitch build output
228 | **/*.HTMLClient/GeneratedArtifacts
229 | **/*.DesktopClient/GeneratedArtifacts
230 | **/*.DesktopClient/ModelManifest.xml
231 | **/*.Server/GeneratedArtifacts
232 | **/*.Server/ModelManifest.xml
233 | _Pvt_Extensions
234 |
235 | # Paket dependency manager
236 | .paket/paket.exe
237 |
238 | # FAKE - F# Make
239 | .fake/
240 |
--------------------------------------------------------------------------------
/Nabbix.Tests/NabbixAgentTests.cs:
--------------------------------------------------------------------------------
1 | using Nabbix.Items;
2 | using System;
3 | using System.Net.Sockets;
4 | using System.Text;
5 | using Xunit;
6 |
7 | namespace Nabbix.Tests
8 | {
9 | public class NabbixAgentTests : IDisposable
10 | {
11 | private readonly NabbixAgent _agent;
12 |
13 | private const string OneCharacterKey = "h";
14 | private const string TwoCharacterKey = "he";
15 | private const string ThreeCharacterKey = "hel";
16 | private const string FourCharacterKey = "ruok";
17 | private const string FiveCharacterKey = "hello";
18 | private const string FakeZabbixHeader = "ZBXD1";
19 | private const string MediumSizedKey = "hello world";
20 |
21 | private const string LongKey =
22 | "In cyberspace where lines of code do dwell, /A timeless phrase in bytes and bits unfurled, /It whispers softly, breaking coder's spell, /The universal hymn: \"Hello, World!\"/From languages diverse, its voice resounds, /In Python's embrace or C's stern gaze, /In Java's tortuous syntax, or HTML's bounds, /In every tongue, its message finds its ways. /In but a single line, its tale is told, /A greeting, a beginning, and a sign, /In zeroes and ones, its truth behold, /A spark ignites, a code divine. //So let it ring in every realm unfurled, /The programmer's anthem: \"Hello, World!\"";
23 |
24 | public NabbixAgentTests()
25 | {
26 | _agent = new NabbixAgent("127.0.0.1", 0, new MyCounter());
27 | _agent.Start();
28 | }
29 |
30 | public void Dispose()
31 | {
32 | _agent.Stop();
33 | }
34 |
35 | private class MyCounter
36 | {
37 | [NabbixItem(OneCharacterKey)]
38 | // ReSharper disable UnusedMember.Local
39 | public long OneCharacterReturn => 1234;
40 |
41 | [NabbixItem(TwoCharacterKey)]
42 | public long ReturnConstant => 1234;
43 |
44 | [NabbixItem(ThreeCharacterKey)]
45 | public long ThreeCharacterReturn => 1234;
46 |
47 | [NabbixItem(FourCharacterKey)]
48 | public long FourCharacterReturn => 1234;
49 |
50 | [NabbixItem(FiveCharacterKey)]
51 | public long FiveCharacterReturn => 1234;
52 |
53 | [NabbixItem(FakeZabbixHeader)]
54 | public long FakeZabbixHeaderReturn => 1234;
55 |
56 | [NabbixItem(MediumSizedKey)]
57 | public long MediumSizedKeyReturn => 1234;
58 |
59 | [NabbixItem(LongKey)]
60 | public long LongSizedKeyPattern => 1234;
61 | // ReSharper enable UnusedMember.Local
62 |
63 | }
64 |
65 | private static byte[] CreateOldProtocolRequest(string key)
66 | {
67 | var sb = new StringBuilder();
68 | sb.Append(key);
69 | sb.Append((char)10);
70 |
71 | return Encoding.ASCII.GetBytes(sb.ToString());
72 | }
73 |
74 | private static byte[] CreateNewProtocolRequest(string key)
75 | {
76 | byte[] header = Encoding.ASCII.GetBytes("ZBXD\x01");
77 | byte[] dataLen = BitConverter.GetBytes((long)key.Length);
78 | byte[] content = Encoding.ASCII.GetBytes(key);
79 | byte[] message = new byte[header.Length + dataLen.Length + content.Length];
80 | Buffer.BlockCopy(header, 0, message, 0, header.Length);
81 | Buffer.BlockCopy(dataLen, 0, message, header.Length, dataLen.Length);
82 | Buffer.BlockCopy(content, 0, message, header.Length + dataLen.Length, content.Length);
83 |
84 | return message;
85 | }
86 |
87 | private static string ReadResponse(NetworkStream stream)
88 | {
89 | byte[] data = new byte[1024];
90 | int totalRead = 0;
91 |
92 | int read;
93 | while ((read = stream.Read(data, totalRead, data.Length - totalRead)) > 0)
94 | {
95 | totalRead += read;
96 |
97 | if (!stream.DataAvailable)
98 | break;
99 | }
100 |
101 | return Encoding.ASCII.GetString(data, 0, totalRead);
102 | }
103 |
104 | [Theory]
105 | [InlineData(true, MediumSizedKey)]
106 | [InlineData(true, OneCharacterKey)]
107 | [InlineData(true, TwoCharacterKey)]
108 | [InlineData(true, ThreeCharacterKey)]
109 | [InlineData(true, FourCharacterKey)]
110 | [InlineData(true, FiveCharacterKey)]
111 | [InlineData(true, FakeZabbixHeader)]
112 | [InlineData(true, LongKey)]
113 | [InlineData(false, MediumSizedKey)]
114 | [InlineData(false, OneCharacterKey)]
115 | [InlineData(false, TwoCharacterKey)]
116 | [InlineData(false, ThreeCharacterKey)]
117 | [InlineData(false, FourCharacterKey)]
118 | [InlineData(false, FiveCharacterKey)]
119 | [InlineData(false, FakeZabbixHeader)]
120 | [InlineData(false, LongKey)]
121 | public void Integration_OneConnection_SendReceiveResults(bool useOldProtocol, string key)
122 | {
123 | var port = _agent.LocalEndpointPort;
124 |
125 | for (int i = 0; i < 3; i++)
126 | {
127 | var client = new TcpClient("127.0.0.1", port);
128 | using (NetworkStream stream = client.GetStream())
129 | {
130 | byte[] request = useOldProtocol ? CreateOldProtocolRequest(key) : CreateNewProtocolRequest(key);
131 | stream.Write(request, 0, request.Length);
132 | stream.Flush();
133 |
134 | string results = ReadResponse(stream);
135 | Assert.EndsWith("1234", results);
136 | }
137 | }
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------