diff --git a/README.md b/README.md index faa24c1ae..de490c657 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,7 @@ documentation for each plugin for configurable attributes. * `memcached`(see [collectd::plugin::memcached](#class-collectdpluginmemcached) below ) * `memory`(see [collectd::plugin::memory](#class-collectdpluginmemory) below ) +* `modbus` (see [collectd::plugin::modbus](#class-collectdpluginmodbus) below) * `mongodb`(see [collectd::plugin::mongodb](#class-collectdpluginmongodb) below ) * `mysql` (see [collectd::plugin::mysql](#class-collectdpluginmysql) below) * `netlink` (see [collectd::plugin::netlink](#class-collectdpluginnetlink) below) @@ -1073,6 +1074,35 @@ class { 'collectd::plugin::memory': } ``` +#### Class: `collectd::plugin::modbus` + +```puppet +class {'collectd::plugin::modbus': + ensure => 'present', + data => { + current_phase_a => { + 'type' => 'gauge', + 'instance' => 'current phase A', + 'register_base' => 1234, + 'register_type' => 'Float', + } + }, + hosts => { + meter123 => { + 'address' => '127.0.0.1', + 'port' => 502, + 'interval' => 10, + 'slaves' => { + 255 => { + 'instance' => 'power meter 255', + 'collect' => ['current_phase_a'], + } + }, + } + }, +} +``` + #### Class: `collectd::plugin::mysql` ```puppet diff --git a/REFERENCE.md b/REFERENCE.md index 6f1127127..b5294fe53 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -60,6 +60,7 @@ * [`collectd::plugin::mcelog`](#collectd--plugin--mcelog): https://collectd.org/documentation/manpages/collectd.conf.5.shtml#plugin_mcelog * [`collectd::plugin::memcached`](#collectd--plugin--memcached): https://collectd.org/wiki/index.php/Plugin:memcached * [`collectd::plugin::memory`](#collectd--plugin--memory): https://collectd.org/wiki/index.php/Plugin:Memory +* [`collectd::plugin::modbus`](#collectd--plugin--modbus): Install and configure the modbus plugin * [`collectd::plugin::mongodb`](#collectd--plugin--mongodb): Class: collectd::plugin::mongodb * [`collectd::plugin::mysql`](#collectd--plugin--mysql): MySQL plugin https://collectd.org/wiki/index.php/Plugin:MySQL * [`collectd::plugin::netlink`](#collectd--plugin--netlink): https://collectd.org/wiki/index.php/Plugin:Netlink @@ -188,6 +189,9 @@ * [`Collectd::LOGPARSER::Message`](#Collectd--LOGPARSER--Message): https://wiki.opnfv.org/display/fastpath/Logparser+plugin+HLD * [`Collectd::MCELOG::Memory`](#Collectd--MCELOG--Memory): https://collectd.org/documentation/manpages/collectd.conf.5.shtml#plugin_mcelog * [`Collectd::Manifests::Init`](#Collectd--Manifests--Init) +* [`Collectd::Modbus::Data`](#Collectd--Modbus--Data): represents a modbus data entry +* [`Collectd::Modbus::Host`](#Collectd--Modbus--Host): represents a modbus host entry +* [`Collectd::Modbus::Slave`](#Collectd--Modbus--Slave): Represents a modbus host's slave entry * [`Collectd::Network::SecurityLevel`](#Collectd--Network--SecurityLevel) * [`Collectd::Redis::Node`](#Collectd--Redis--Node) * [`Collectd::SNMP::AuthProtocol`](#Collectd--SNMP--AuthProtocol) @@ -3591,6 +3595,54 @@ Data type: `Any` Default value: `undef` +### `collectd::plugin::modbus` + +Install and configure the modbus plugin + +* **See also** + * https://collectd.org/wiki/index.php/Plugin:Modbus + +#### Parameters + +The following parameters are available in the `collectd::plugin::modbus` class: + +* [`ensure`](#-collectd--plugin--modbus--ensure) +* [`manage_package`](#-collectd--plugin--modbus--manage_package) +* [`data`](#-collectd--plugin--modbus--data) +* [`hosts`](#-collectd--plugin--modbus--hosts) + +##### `ensure` + +Data type: `Enum['present', 'absent']` + +Enable/Disable modbus support + +Default value: `'present'` + +##### `manage_package` + +Data type: `Optional[Boolean]` + +Install collectd-modbus package? Currently supports RedHat and Debian os family. + +Default value: `undef` + +##### `data` + +Data type: `Hash[String[1], Collectd::Modbus::Data]` + +modbus data entries + +Default value: `{}` + +##### `hosts` + +Data type: `Hash[String[1], Collectd::Modbus::Host]` + +modbus host entries + +Default value: `{}` + ### `collectd::plugin::mongodb` Class: collectd::plugin::mongodb @@ -10312,6 +10364,65 @@ The Collectd::Manifests::Init data type. Alias of `Pattern[/(^5.4|^5.5|^5.6|^5.7|^5.8|^master)/]` +### `Collectd::Modbus::Data` + +https://github.com/collectd/collectd/blob/main/src/modbus.c + +Alias of + +```puppet +Struct[{ + Optional['instance'] => String, + NotUndef['type'] => String[1], + NotUndef['register_base'] => Integer[0], + NotUndef['register_type'] => Enum[ + 'Int16', + 'Int32', + 'Int32LE', + 'Uint16', + 'Uint32', + 'Uint32LE', + 'Float', + 'FloatLE', + 'Uint64', + 'Int64', + 'Double', + ], + Optional['register_cmd'] => Enum['ReadHolding', 'ReadInput'], +}] +``` + +### `Collectd::Modbus::Host` + +represents a modbus host entry + +Alias of + +```puppet +Struct[{ + NotUndef['address'] => String[1], + NotUndef['port'] => Stdlib::Port, + NotUndef['slaves'] => Hash[Integer, Collectd::Modbus::Slave], + Optional['interval'] => Integer[0] +}] +``` + +### `Collectd::Modbus::Slave` + +Represents a modbus host's slave entry + +Alias of + +```puppet +Struct[{ + NotUndef['instance'] => String[1], + NotUndef['collect'] => Variant[ + String[1], + Array[String[1], 1] + ] +}] +``` + ### `Collectd::Network::SecurityLevel` The Collectd::Network::SecurityLevel data type. diff --git a/manifests/plugin/modbus.pp b/manifests/plugin/modbus.pp new file mode 100644 index 000000000..b76cd2338 --- /dev/null +++ b/manifests/plugin/modbus.pp @@ -0,0 +1,38 @@ +# @summary Install and configure the modbus plugin +# +# @see https://collectd.org/wiki/index.php/Plugin:Modbus +# +# @param ensure Enable/Disable modbus support +# @param manage_package Install collectd-modbus package? Currently supports RedHat and Debian os family. +# @param data modbus data entries +# @param hosts modbus host entries +class collectd::plugin::modbus ( + Enum['present', 'absent'] $ensure = 'present', + Optional[Boolean] $manage_package = undef, + Hash[String[1], Collectd::Modbus::Data] $data = {}, + Hash[String[1], Collectd::Modbus::Host] $hosts = {}, +) { + include collectd + + $_manage_package = pick($manage_package, $collectd::manage_package) + + $_package_name = $facts['os']['family'] ? { + 'RedHat' => 'collectd-modbus', + 'Debian' => 'libmodbus5', + default => undef, + } + + if $_package_name and $_manage_package { + package { $_package_name: + ensure => $ensure, + } + } + + collectd::plugin { 'modbus': + ensure => $ensure, + content => epp('collectd/plugin/modbus.conf', { + 'data' => $data, + 'hosts' => $hosts, + }), + } +} diff --git a/spec/classes/collectd_plugin_modbus_spec.rb b/spec/classes/collectd_plugin_modbus_spec.rb new file mode 100644 index 000000000..35e014ef5 --- /dev/null +++ b/spec/classes/collectd_plugin_modbus_spec.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'collectd::plugin::modbus' do + on_supported_os(baseline_os_hash).each do |os, os_facts| + context "on #{os}" do + let :facts do + os_facts + end + let :pre_condition do + 'include collectd' + end + + options = os_specific_options(os_facts) + + context ':ensure => present and dataset for Current Phase A' do + let :params do + { + data: { + 'current_phase_a' => { + 'type' => 'gauge', + 'instance' => 'Current Phase A', + 'register_base' => 1234, + 'register_type' => 'Float', + } + }, + hosts: { + 'power123' => { + 'address' => '127.0.0.1', + 'port' => 502, + 'interval' => 10, + 'slaves' => { + 255 => { + 'instance' => 'power meter 255', + 'collect' => ['current_phase_a'], + } + } + } + } + } + end + + it "Will create #{options[:plugin_conf_dir]}/10-modbus.conf" do + is_expected.to contain_file('modbus.load').with( + ensure: 'present', + path: "#{options[:plugin_conf_dir]}/10-modbus.conf", + content: %r{Data "current_phase_a".+Instance "Current Phase A".+Host "power123".+Slave 255}m + ) + end + end + + context ':ensure => absent' do + let :params do + { + ensure: 'absent', + data: { + 'current_phase_a' => { + 'type' => 'gauge', + 'instance' => 'Current Phase A', + 'register_base' => 1234, + 'register_type' => 'Float', + } + }, + hosts: { + 'power123' => { + 'address' => '127.0.0.1', + 'port' => 502, + 'interval' => 10, + 'slaves' => { + 255 => { + 'instance' => 'power meter 255', + 'collect' => ['current_phase_a'], + } + } + } + } + } + end + + it "Will not create #{options[:plugin_conf_dir]}/10-modbus.conf" do + is_expected.to contain_file('modbus.load').with( + ensure: 'absent', + path: "#{options[:plugin_conf_dir]}/10-modbus.conf" + ) + end + end + end + end +end diff --git a/spec/type_aliases/collectd_modbus_data_spec.rb b/spec/type_aliases/collectd_modbus_data_spec.rb new file mode 100644 index 000000000..86b5e9a17 --- /dev/null +++ b/spec/type_aliases/collectd_modbus_data_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Collectd::Modbus::Data' do + it do + is_expected.to allow_values({ + 'type' => 'foo', + 'register_base' => 123, + 'register_type' => 'Int32', + }) + end + + it do + is_expected.to allow_values({ + 'type' => 'foo', + 'register_base' => 123, + 'register_type' => 'Int32', + 'register_cmd' => 'ReadInput', + }) + end + + it do + is_expected.to allow_values({ + 'instance' => 'foobar', + 'type' => 'foo', + 'register_base' => 123, + 'register_type' => 'Int32', + }) + end + + it { is_expected.not_to allow_values(nil) } + it { is_expected.not_to allow_values({ 'type' => 'foo', 'register_base' => 123 }) } +end diff --git a/spec/type_aliases/collectd_modbus_host_spec.rb b/spec/type_aliases/collectd_modbus_host_spec.rb new file mode 100644 index 000000000..99d9d5a4f --- /dev/null +++ b/spec/type_aliases/collectd_modbus_host_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Collectd::Modbus::Host' do + let(:slaves) do + { + 1 => { 'instance' => 'foo1', 'collect' => 'bar1' }, + 2 => { 'instance' => 'foo2', 'collect' => %w[bar1 bar2] }, + } + end + + it { is_expected.to allow_values({ 'address' => '127.0.0.1', 'port' => 1234, 'slaves' => slaves }) } + it { is_expected.to allow_values({ 'address' => '127.0.0.1', 'port' => 1234, 'slaves' => slaves, 'interval' => 120 }) } + + it { is_expected.not_to allow_values(nil) } + it { is_expected.not_to allow_values({ 'address' => '127.0.0.1', 'port' => '1234', 'slaves' => slaves, 'interval' => 120 }) } + it { is_expected.not_to allow_values({ 'port' => 1234, 'slaves' => slaves, 'interval' => 120 }) } +end diff --git a/spec/type_aliases/collectd_modbus_slave_spec.rb b/spec/type_aliases/collectd_modbus_slave_spec.rb new file mode 100644 index 000000000..1e2fb3322 --- /dev/null +++ b/spec/type_aliases/collectd_modbus_slave_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Collectd::Modbus::Slave' do + it { is_expected.to allow_values({ 'instance' => 'foo1', 'collect' => 'bar1' }) } + it { is_expected.to allow_values({ 'instance' => 'foo1', 'collect' => %w[bar1 bar2] }) } + + it { is_expected.not_to allow_values(nil) } + it { is_expected.not_to allow_values({ 'collect' => ['bar1'] }) } + it { is_expected.not_to allow_values({ 'instance' => 'foo1' }) } + it { is_expected.not_to allow_values({ 'instance' => 'foo1', 'collect' => [] }) } +end diff --git a/templates/plugin/modbus.conf.epp b/templates/plugin/modbus.conf.epp new file mode 100644 index 000000000..6f9671337 --- /dev/null +++ b/templates/plugin/modbus.conf.epp @@ -0,0 +1,52 @@ +<%- | + Hash[String[1], Collectd::Modbus::Host] $hosts = {}, + Hash[String[1], Collectd::Modbus::Data] $data = {}, +| -%> +<% if $data or $hosts { -%> + +<% $data.keys.sort.each |String $key| { + $val = $data[$key] -%> + "> + Type "<%= $val['type'] %>" +<% if $val['instance'] { -%> + Instance "<%= $val['instance'] %>" +<% } -%> +<% if $val['register_base'] { -%> + RegisterBase <%= $val['register_base'] %> +<% } -%> +<% if $val['register_type'] { -%> + RegisterType <%= $val['register_type'] %> +<% } -%> +<% if $val['register_cmd'] { -%> + RegisterCmd <%= $val['register_cmd'] %> +<% } -%> + +<% } -%> +<% $hosts.keys.sort.each |String $key| { + $val = $hosts[$key] -%> + "> + Address "<%= $val['address'] %>" +<% if $val['port'] { -%> + Port <%= $val['port'] %> +<% } -%> +<% if $val['interval'] { -%> + Interval <%= $val['interval'] %> +<% } -%> +<% if $val['slaves'] { + $val['slaves'].keys.sort.each |$slave_key| { + $slave_val = $val['slaves'][$slave_key] +-%> + > +<% if $slave_val['instance'] { -%> + Instance "<%= $slave_val['instance'] %>" +<% } -%> +<% $slave_val['collect'].sort.each |String $data_name| { -%> + Collect "<%= $data_name %>" +<% } -%> + +<% } -%> +<% } -%> + +<% } -%> + +<% } -%> diff --git a/types/modbus/data.pp b/types/modbus/data.pp new file mode 100644 index 000000000..d3bcc63b7 --- /dev/null +++ b/types/modbus/data.pp @@ -0,0 +1,22 @@ +# @summary represents a modbus data entry +# +# https://github.com/collectd/collectd/blob/main/src/modbus.c +type Collectd::Modbus::Data = Struct[{ + Optional['instance'] => String, + NotUndef['type'] => String[1], + NotUndef['register_base'] => Integer[0], + NotUndef['register_type'] => Enum[ + 'Int16', + 'Int32', + 'Int32LE', + 'Uint16', + 'Uint32', + 'Uint32LE', + 'Float', + 'FloatLE', + 'Uint64', + 'Int64', + 'Double', + ], + Optional['register_cmd'] => Enum['ReadHolding', 'ReadInput'], +}] diff --git a/types/modbus/host.pp b/types/modbus/host.pp new file mode 100644 index 000000000..b59eff92b --- /dev/null +++ b/types/modbus/host.pp @@ -0,0 +1,7 @@ +# @summary represents a modbus host entry +type Collectd::Modbus::Host = Struct[{ + NotUndef['address'] => String[1], + NotUndef['port'] => Stdlib::Port, + NotUndef['slaves'] => Hash[Integer, Collectd::Modbus::Slave], + Optional['interval'] => Integer[0] +}] diff --git a/types/modbus/slave.pp b/types/modbus/slave.pp new file mode 100644 index 000000000..7e31e6d5e --- /dev/null +++ b/types/modbus/slave.pp @@ -0,0 +1,8 @@ +# @summary Represents a modbus host's slave entry +type Collectd::Modbus::Slave = Struct[{ + NotUndef['instance'] => String[1], + NotUndef['collect'] => Variant[ + String[1], + Array[String[1], 1] + ] +}]