Merge pull request #2796 from elfiesmelfie/feat_ipmi_SEL_ignore_list
authorPavel Rochnyak <pavel2000@ngs.ru>
Wed, 30 May 2018 12:59:32 +0000 (19:59 +0700)
committerGitHub <noreply@github.com>
Wed, 30 May 2018 12:59:32 +0000 (19:59 +0700)
ipmi plugin: Add SELSensor and SELIgnoreSelected config options.

1  2 
src/collectd.conf.in
src/collectd.conf.pod
src/ipmi.c

diff --combined src/collectd.conf.in
@@@ -90,7 -90,6 +90,7 @@@
  
  #@BUILD_PLUGIN_AGGREGATION_TRUE@LoadPlugin aggregation
  #@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp
 +#@BUILD_PLUGIN_AMQP1_TRUE@LoadPlugin amqp1
  #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache
  #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups
  #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors
  #  </Publish>
  #</Plugin>
  
 +#<Plugin amqp1>
 +#  <Transport "name">
 +#    Host "localhost"
 +#    Port "5672"
 +#    User "guest"
 +#    Password "guest"
 +#    Address "collectd"
 +#    RetryDelay 1
 +#    <Instance "log">
 +#        Format JSON
 +#        PreSettle false
 +#    </Instance>
 +#    <Instance "notify">
 +#        Format JSON
 +#        PreSettle true
 +#    </Instance>
 +#    <Instance "telemetry">
 +#        Format JSON
 +#        PreSettle false
 +#    </Instance>
 +#  </Transport>
 +#</Plugin>
 +
  #<Plugin apache>
  #  <Instance "local">
  #    URL "http://localhost/status?auto"
  #    ReportSoftwareEvents true
  #    EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json"
  #    HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD"
 +#    Cores "[0-3]"
  #</Plugin>
  
  #<Plugin "intel_rdt">
  #             NotifySensorNotPresent false
  #             NotifyIPMIConnectionState false
  #             SELEnabled false
+ #             SELSensor "some_sensor"
+ #             SELSensor "another_one"
+ #             SELIgnoreSelected false
  #             SELClearEvent false
  #     </Instance>
  #     <Instance "remote">
  #             NotifySensorNotPresent false
  #             NotifyIPMIConnectionState false
  #             SELEnabled false
+ #             SELSensor "some_sensor"
+ #             SELSensor "another_one"
+ #             SELIgnoreSelected false
  #             SELClearEvent false
  #     </Instance>
  #</Plugin>
  #             RegisterType float
  #             Type gauge
  #             Instance "..."
 +#             #Scale 1.0
 +#             #Shift 0.0
  #     </Data>
  #
  #     <Host "name">
  #      Host "redis.example.com"
  #      Port "6379"
  #      Timeout 2000
 +#      <Query "LLEN myqueue">
 +#        #Database 0
 +#        Type "queue_length"
 +#        Instance "myqueue"
 +#      <Query>
  #   </Node>
  #</Plugin>
  
  #     PluginInstanceFormat name
  #     Instances 1
  #     ExtraStats "cpu_util disk disk_err domain_state fs_info job_stats_background pcpu perf vcpupin"
 +#     PersistentNotification false
  #</Plugin>
  
  #<Plugin vmem>
diff --combined src/collectd.conf.pod
@@@ -530,9 -530,9 +530,9 @@@ are disabled by default
  =head2 Plugin C<amqp>
  
  The I<AMQP plugin> can be used to communicate with other instances of
 -I<collectd> or third party applications using an AMQP message broker. Values
 -are sent to or received from the broker, which handles routing, queueing and
 -possibly filtering out messages.
 +I<collectd> or third party applications using an AMQP 0.9.1 message broker.
 +Values are sent to or received from the broker, which handles routing,
 +queueing and possibly filtering out messages.
  
  B<Synopsis:>
  
@@@ -738,171 -738,6 +738,171 @@@ is preserved, i.e. passed through
  
  =back
  
 +=head2 Plugin C<amqp1>
 +
 +The I<AMQP1 plugin> can be used to communicate with other instances of
 +I<collectd> or third party applications using an AMQP 1.0 message
 +intermediary. Metric values or notifications are sent to the
 +messaging intermediary which may handle direct messaging or
 +queue based transfer.
 +
 +B<Synopsis:>
 +
 + <Plugin "amqp1">
 +   # Send values to an AMQP 1.0 intermediary
 +  <Transport "name">
 +    Host "localhost"
 +    Port "5672"
 +    User "guest"
 +    Password "guest"
 +    Address "collectd"
 +#    RetryDelay 1
 +    <Instance "some_name">
 +        Format "command"
 +        PreSettle false
 +        Notify false
 + #      StoreRates false
 + #      GraphitePrefix "collectd."
 + #      GraphiteEscapeChar "_"
 + #      GraphiteSeparateInstances false
 + #      GraphiteAlwaysAppendDS false
 + #      GraphitePreserveSeparator false
 +    </Instance>
 +  </Transport>
 + </Plugin>
 +
 +The plugin's configuration consists of a I<Transport> that configures
 +communications to the AMQP 1.0 messaging bus and one or more I<Instance>
 +corresponding to metric or event publishers to the messaging system.
 +
 +The address in the I<Transport> block concatenated with the name given in the
 +I<Instance> block starting tag will be used as the send-to address for
 +communications over the messaging link.
 +
 +The following options are accepted within each I<Transport> block:
 +
 +=over 4
 +
 +=item B<Host> I<Host>
 +
 +Hostname or IP-address of the AMQP 1.0 intermediary. Defaults to the
 +default behavior of the underlying communications library,
 +I<libqpid-proton>, which is "localhost".
 +
 +=item B<Port> I<Port>
 +
 +Service name or port number on which the AMQP 1.0 intermediary accepts
 +connections. This argument must be a string, even if the numeric form
 +is used. Defaults to "5672".
 +
 +=item B<User> I<User>
 +
 +=item B<Password> I<Password>
 +
 +Credentials used to authenticate to the AMQP 1.0 intermediary. By
 +default "guest"/"guest" is used.
 +
 +=item B<Address> I<Address>
 +
 +This option specifies the prefix for the send-to value in the message.
 +By default, "collectd" will be used.
 +
 +=item B<RetryDelay> I<RetryDelay>
 +
 +When the AMQP1 connection is lost, defines the time in seconds to wait
 +before attempting to reconnect. Defaults to 1, which implies attempt
 +to reconnect at 1 second intervals.
 +
 +=back
 +
 +The following options are accepted within each I<Instance> block:
 +
 +=over 4
 +
 +=item B<Format> B<Command>|B<JSON>|B<Graphite>
 +
 +Selects the format in which messages are sent to the intermediary. If set to
 +B<Command> (the default), values are sent as C<PUTVAL> commands which are
 +identical to the syntax used by the I<Exec> and I<UnixSock plugins>. In this
 +case, the C<Content-Type> header field will be set to C<text/collectd>.
 +
 +If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
 +an easy and straight forward exchange format. The C<Content-Type> header field
 +will be set to C<application/json>.
 +
 +If set to B<Graphite>, values are encoded in the I<Graphite> format, which is
 +"<metric> <value> <timestamp>\n". The C<Content-Type> header field will be set to
 +C<text/graphite>.
 +
 +A subscribing client I<should> use the C<Content-Type> header field to
 +determine how to decode the values.
 +
 +=item B<PreSettle> B<true>|B<false>
 +
 +If set to B<false> (the default), the plugin will wait for a message
 +acknowledgement from the messaging bus before sending the next
 +message. This indicates transfer of ownership to the messaging
 +system. If set to B<true>, the plugin will not wait for a message
 +acknowledgement and the message may be dropped prior to transfer of
 +ownership.
 +
 +=item B<Notify> B<true>|B<false>
 +
 +If set to B<false> (the default), the plugin will service the
 +instance write call back as a value list. If set to B<true> the
 +plugin will service the instance as a write notification callback
 +for alert formatting.
 +
 +=item B<StoreRates> B<true>|B<false>
 +
 +Determines whether or not C<COUNTER>, C<DERIVE> and C<ABSOLUTE> data sources
 +are converted to a I<rate> (i.e. a C<GAUGE> value). If set to B<false> (the
 +default), no conversion is performed. Otherwise the conversion is performed
 +using the internal value cache.
 +
 +Please note that currently this option is only used if the B<Format> option has
 +been set to B<JSON>.
 +
 +=item B<GraphitePrefix>
 +
 +A prefix can be added in the metric name when outputting in the I<Graphite> format.
 +It's added before the I<Host> name.
 +Metric name will be "<prefix><host><postfix><plugin><type><name>"
 +
 +=item B<GraphitePostfix>
 +
 +A postfix can be added in the metric name when outputting in the I<Graphite> format.
 +It's added after the I<Host> name.
 +Metric name will be "<prefix><host><postfix><plugin><type><name>"
 +
 +=item B<GraphiteEscapeChar>
 +
 +Specify a character to replace dots (.) in the host part of the metric name.
 +In I<Graphite> metric name, dots are used as separators between different
 +metric parts (host, plugin, type).
 +Default is "_" (I<Underscore>).
 +
 +=item B<GraphiteSeparateInstances> B<true>|B<false>
 +
 +If set to B<true>, the plugin instance and type instance will be in their own
 +path component, for example C<host.cpu.0.cpu.idle>. If set to B<false> (the
 +default), the plugin and plugin instance (and likewise the type and type
 +instance) are put into one component, for example C<host.cpu-0.cpu-idle>.
 +
 +=item B<GraphiteAlwaysAppendDS> B<true>|B<false>
 +
 +If set to B<true>, append the name of the I<Data Source> (DS) to the "metric"
 +identifier. If set to B<false> (the default), this is only done when there is
 +more than one DS.
 +
 +=item B<GraphitePreserveSeparator> B<false>|B<true>
 +
 +If set to B<false> (the default) the C<.> (dot) character is replaced with
 +I<GraphiteEscapeChar>. Otherwise, if set to B<true>, the C<.> (dot) character
 +is preserved, i.e. passed through.
 +
 +=back
 +
  =head2 Plugin C<apache>
  
  To configure the C<apache>-plugin you first need to configure the Apache
@@@ -3347,7 -3182,6 +3347,7 @@@ B<Synopsis:
      ReportSoftwareEvents true
      EventList "/var/cache/pmu/GenuineIntel-6-2D-core.json"
      HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD"
 +    Cores "0-3" "4,6" "[12-15]"
    </Plugin>
  
  B<Options:>
@@@ -3419,23 -3253,6 +3419,23 @@@ event_download.py script to download ev
  This field is a list of event names or groups of comma separated event names.
  This option requires B<EventList> option to be configured.
  
 +=item B<Cores> I<cores groups>
 +
 +All events are reported on a per core basis. Monitoring of the events can be
 +configured for a group of cores (aggregated statistics). This field defines
 +groups of cores on which to monitor supported events. The field is represented
 +as list of strings with core group values. Each string represents a list of
 +cores in a group. If a group is enclosed in square brackets each core is added
 +individually to a separate group (that is statistics are not aggregated).
 +Allowed formats are:
 +    0,1,2,3
 +    0-10,20-18
 +    1,3,5-8,10,0x10-12
 +    [4-15,32-63]
 +
 +If an empty string is provided as value for this field default cores
 +configuration is applied - that is separate group is created for each core.
 +
  =back
  
  =head2 Plugin C<intel_rdt>
@@@ -3636,8 -3453,23 +3636,23 @@@ a notification is sent. Defaults to B<f
  
  If system event log (SEL) is enabled, plugin will listen for sensor threshold
  and discrete events. When event is received the notification is sent.
+ SEL event filtering can be configured using B<SELSensor> and B<SELIgnoreSelected>
+ config options.
  Defaults to B<false>.
  
+ =item B<SELSensor> I<SELSensor>
+ Selects sensors to get events from or to ignore, depending on B<SELIgnoreSelected>.
+ See F</"IGNORELISTS"> for details.
+ =item B<SELIgnoreSelected> I<true>|I<false>
+ If no configuration is given, the B<ipmi> plugin will pass events from all
+ sensors. This option enables you to do that: By setting B<SELIgnoreSelected>
+ to I<true> the effect of B<SELSensor> is inverted: All events from selected
+ sensors are ignored and all events from other sensors are passed.
  =item B<SELClearEvent> I<true>|I<false>
  
  If SEL clear event is enabled, plugin will delete event from SEL list after
@@@ -4242,9 -4074,8 +4257,9 @@@ which the sizes of physical memory vary
  
  The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP or Modbus/RTU and
  reads register values. It supports reading single registers (unsigned 16E<nbsp>bit
 -values), large integer values (unsigned 32E<nbsp>bit values) and floating point
 -values (two registers interpreted as IEEE floats in big endian notation).
 +values), large integer values (unsigned 32E<nbsp>bit and 64E<nbsp>bit values) and
 +floating point values (two registers interpreted as IEEE floats in big endian
 +notation).
  
  B<Synopsis:>
  
     RegisterCmd ReadHolding
     Type voltage
     Instance "input-1"
 +   #Scale 1.0
 +   #Shift 0.0
   </Data>
  
   <Data "voltage-input-2">
@@@ -4314,7 -4143,7 +4329,7 @@@ Configures the base register to read fr
  B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
  register will be read (the register number is increased by one).
  
 -=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>|B<Int32LE>|B<Uint32LE>|B<FloatLE>
 +=item B<RegisterType> B<Int16>|B<Int32>|B<Int64>|B<Uint16>|B<Uint32>|B<UInt64>|B<Float>|B<Int32LE>|B<Uint32LE>|B<FloatLE>
  
  Specifies what kind of data is returned by the device. This defaults to
  B<Uint16>.  If the type is B<Int32>, B<Int32LE>, B<Uint32>, B<Uint32LE>,
@@@ -4326,10 -4155,7 +4341,10 @@@ significant 16E<nbsp>bits are in the re
  For B<Int32LE>, B<Uint32LE>, or B<Float32LE>, the high and low order
  registers are swapped with the most significant 16E<nbsp>bits in
  the B<RegisterBase+1> and the least significant 16E<nbsp>bits in
 -B<RegisterBase>.
 +B<RegisterBase>. If the type is B<Int64> or B<UInt64>, four 16E<nbsp>bit
 +registers at B<RegisterBase>, B<RegisterBase+1>, B<RegisterBase+2> and
 +B<RegisterBase+3> will be read and the data combined into one
 +64E<nbsp>value.
  
  =item B<RegisterCmd> B<ReadHolding>|B<ReadInput>
  
@@@ -4344,19 -4170,9 +4359,19 @@@ supported
  
  =item B<Instance> I<Instance>
  
 -Sets the type instance to use when dispatching the value to I<collectd>. If
 +Sets the type instance to use when dispatching the value to I<Instance>. If
  unset, an empty string (no type instance) is used.
  
 +=item B<Scale> I<Value>
 +
 +The values taken from device are multiplied by I<Value>. The field is optional
 +and the default is B<1.0>.
 +
 +=item B<Shift> I<Value>
 +
 +I<Value> is added to values from device after they have been multiplied by
 +B<Scale> value. The field is optional and the default value is B<0.0>.
 +
  =back
  
  =item E<lt>B<Host> I<Name>E<gt> blocks
@@@ -7276,7 -7092,6 +7291,7 @@@ which configures the connection paramet
          Port "6379"
          Timeout 2000
          <Query "LLEN myqueue">
 +          #Database 0
            Type "queue_length"
            Instance "myqueue"
          <Query>
@@@ -7322,11 -7137,6 +7337,11 @@@ than B<Interval> defined globally
  The B<Query> block identifies a query to execute against the redis server.
  There may be an arbitrary number of queries to execute.
  
 +=item B<Database> I<Index>
 +
 +This index selects the Redis logical database to use for query. Defaults
 +to C<0>.
 +
  =item B<Type> I<Collectd type>
  
  Within a query definition, a valid collectd type to use as when submitting
@@@ -9128,12 -8938,6 +9143,12 @@@ B<Note>: I<perf> metrics can't be colle
  
  =back
  
 +=item B<PersistentNotification> B<true>|B<false>
 +Override default configuration to only send notifications when there is a change
 +in the lifecycle state of a domain. When set to true notifications will be sent
 +for every read cycle. Default is false. Does not affect the stats being
 +dispatched.
 +
  =back
  
  =head2 Plugin C<vmem>
diff --combined src/ipmi.c
@@@ -49,6 -49,7 +49,7 @@@ typedef struct c_ipmi_sensor_list_s c_i
  struct c_ipmi_instance_s {
    char *name;
    ignorelist_t *ignorelist;
+   ignorelist_t *sel_ignorelist;
    bool notify_add;
    bool notify_remove;
    bool notify_notpresent;
@@@ -357,7 -358,7 +358,7 @@@ static const char *sensor_unit_to_type(
  
    /* find the db type by using sensor base unit type */
    enum ipmi_unit_type_e ipmi_type = ipmi_sensor_get_base_unit(sensor);
 -  for (int i = 0; i < STATIC_ARRAY_SIZE(ipmi_db_type_map); i++)
 +  for (size_t i = 0; i < STATIC_ARRAY_SIZE(ipmi_db_type_map); i++)
      if (ipmi_db_type_map[i].type == ipmi_type)
        return ipmi_db_type_map[i].type_name;
  
@@@ -770,6 -771,40 +771,40 @@@ static int sensor_discrete_event_handle
    return IPMI_EVENT_NOT_HANDLED;
  } /* int sensor_discrete_event_handler */
  
+ static int sel_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
+   char sensor_name[DATA_MAX_NAME_LEN] = {0};
+   int status = 0;
+   /* Check if sensor on sel_ignorelist */
+   sensor_get_name(sensor, sensor_name, sizeof(sensor_name));
+   if (ignorelist_match(st->sel_ignorelist, sensor_name) != 0)
+     return 0;
+   /* register threshold event if threshold sensor support events */
+   if (ipmi_sensor_get_event_reading_type(sensor) ==
+       IPMI_EVENT_READING_TYPE_THRESHOLD)
+     status = ipmi_sensor_add_threshold_event_handler(
+         sensor, sensor_threshold_event_handler, st);
+   /* register discrete handler if discrete/specific sensor support events */
+   else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
+     status = ipmi_sensor_add_discrete_event_handler(
+         sensor, sensor_discrete_event_handler, st);
+   if (status)
+     ERROR("Unable to add sensor %s event handler, status: %d", sensor_name,
+           status);
+   return status;
+ }
+ static void sel_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
+   if (ipmi_sensor_get_event_reading_type(sensor) ==
+       IPMI_EVENT_READING_TYPE_THRESHOLD)
+     ipmi_sensor_remove_threshold_event_handler(
+         sensor, sensor_threshold_event_handler, st);
+   else
+     ipmi_sensor_remove_discrete_event_handler(
+         sensor, sensor_discrete_event_handler, st);
+ }
  /*
   * Entity handlers
   */
@@@ -782,37 -817,12 +817,12 @@@ entity_sensor_update_handler(enum ipmi_
    if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
      /* Will check for duplicate entries.. */
      sensor_list_add(st, sensor);
-     if (st->sel_enabled) {
-       int status = 0;
-       /* register threshold event handler */
-       if (ipmi_sensor_get_event_reading_type(sensor) ==
-           IPMI_EVENT_READING_TYPE_THRESHOLD)
-         status = ipmi_sensor_add_threshold_event_handler(
-             sensor, sensor_threshold_event_handler, st);
-       /* register discrete handler if discrete/specific sensor support events */
-       else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
-         status = ipmi_sensor_add_discrete_event_handler(
-             sensor, sensor_discrete_event_handler, st);
-       if (status) {
-         char buf[DATA_MAX_NAME_LEN] = {0};
-         sensor_get_name(sensor, buf, sizeof(buf));
-         ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
-       }
-     }
+     if (st->sel_enabled)
+       sel_list_add(st, sensor);
    } else if (op == IPMI_DELETED) {
      sensor_list_remove(st, sensor);
-     if (st->sel_enabled) {
-       if (ipmi_sensor_get_event_reading_type(sensor) ==
-           IPMI_EVENT_READING_TYPE_THRESHOLD)
-         ipmi_sensor_remove_threshold_event_handler(
-             sensor, sensor_threshold_event_handler, st);
-       else
-         ipmi_sensor_remove_discrete_event_handler(
-             sensor, sensor_discrete_event_handler, st);
-     }
+     if (st->sel_enabled)
+       sel_list_remove(st, sensor);
    }
  } /* void entity_sensor_update_handler */
  
@@@ -1000,6 -1010,15 +1010,15 @@@ static c_ipmi_instance_t *c_ipmi_init_i
      return NULL;
    }
  
+   st->sel_ignorelist = ignorelist_create(/* invert = */ 1);
+   if (st->sel_ignorelist == NULL) {
+     ignorelist_free(st->ignorelist);
+     sfree(st->name);
+     sfree(st);
+     ERROR("ipmi plugin: SEL ignorelist_create() failed.");
+     return NULL;
+   }
    st->sensor_list = NULL;
    pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
  
@@@ -1026,6 -1045,7 +1045,7 @@@ static void c_ipmi_free_instance(c_ipmi
    sfree(st->username);
    sfree(st->password);
  
+   ignorelist_free(st->sel_ignorelist);
    ignorelist_free(st->ignorelist);
    pthread_mutex_destroy(&st->sensor_list_lock);
    sfree(st);
@@@ -1083,6 -1103,19 +1103,19 @@@ static int c_ipmi_config_add_instance(o
        status = cf_util_get_boolean(child, &st->notify_remove);
      } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
        status = cf_util_get_boolean(child, &st->notify_notpresent);
+     } else if (strcasecmp("SELSensor", child->key) == 0) {
+       char *value = NULL;
+       status = cf_util_get_string(child, &value);
+       if (status != 0)
+         break;
+       ignorelist_add(st->sel_ignorelist, value);
+       sfree(value);
+     } else if (strcasecmp("SELIgnoreSelected", child->key) == 0) {
+       bool t;
+       status = cf_util_get_boolean(child, &t);
+       if (status != 0)
+         break;
+       ignorelist_set_invert(st->sel_ignorelist, /* invert = */ !t);
      } else if (strcasecmp("SELEnabled", child->key) == 0) {
        status = cf_util_get_boolean(child, &st->sel_enabled);
      } else if (strcasecmp("SELClearEvent", child->key) == 0) {