=head1 NAME

gdnsd-plugin-multifo - gdnsd plugin for multi-address, all-active failover
groups

=head1 SYNOPSIS

Example plugin config:

  plugins => {
    multifo => {
      up_thresh => 0.3
      service_types => default,
      v4www => {
        lb01 => 192.0.2.200,
        lb02 => 192.0.2.201,
        lb03 => 192.0.2.202,
      }
      v6smtp => {
        service_types => [ smtp ],
        up_thresh => 0.1,
        lb01_v6 => 2001:DB8::1,
        lb02_v6 => 2001:DB8::2,
        lb03_v6 => 2001:DB8::3,
      }
      pubwww => {
        up_thresh => 0.5
        service_types => [ corpwww_type, default ],
        addrs_v4 => [ 192.0.2.100, 192.0.2.101, 192.0.2.102 ]
        addrs_v6 => {
          service_types => [ up ],
          up_thresh => 0.7
          lb01_v6 => 2001:DB8::1,
          lb02_v6 => 2001:DB8::2,
          lb03_v6 => 2001:DB8::3,
        }
      }
    }
  }

Example zonefile RRs:

  web4 180 DYNA multifo!v4www
  smtp 180 DYNA multifo!v6smtp
  www 180 DYNA multifo!pubwww

=head1 DESCRIPTION

B<gdnsd-plugin-multifo> is designed to do multi-address all-active
failover grouping.  Basically, for each configured resource name, you supply
a labeled list of addresses.  multifo monitors these addresses
according to C<service_types>, and answers C<DYNA> address queries
using the non-C<DOWN> subset.  The core gdnsd code will round-robin
rotate the records on the way out, as it does for all address
RR-sets.

=head1 TOP-LEVEL PLUGIN CONFIG

At the top level of the plugin's configuration stanza, two special
parameters C<up_thresh> and C<service_types> are supported. These set
default per-resource options of the same name for any resources which do
not define them explicitly.

The rest of the hash entries at the top level are the names of the
resources you define.  Each resource gets a configuration hash of its own
for containing resource-specific parameters as well as the actual address
data.

=head1 RESOURCE CONFIG

Within a resource, you have two basic options.  You can either directly
specify a set of C<label =E<gt> address> pairs which are all the same
family (IPv4 or IPv6), or you can use the sub-stanzas C<addrs_v4> and/or
C<addrs_v6> to specify one or both families in the same resource.

The C<up_thresh> and C<service_types> parameters are inherited through
every level, and can be overridden at any level (even per-address-family):

=over 4

=item B<up_thresh>

Floating point, default 0.5, range (0.0 - 1.0].  This configures the
per-resource C<up_thresh> threshold.  More details in L</UP THRESH> below.

=item B<service_types>

Array of strings, or single string.  Default C<default>.  This sets the
monitored service_types for this resource.  If an array of more than one is
provided, all will be monitored for each address, and the net monitored
state will be the minimum (worst) of the set.  See L<gdnsd.config(8)> for
more details on service_types.

=back

=head1 SHORTCUT CONFIG

If you have no parameters (service_types, up_thresh) to configure in a
given stanza (single-family direct resource config, or addrs_v[46]), and do
not care about the descriptive per-address labels used in monitoring, you
can replace the hash with an array of addresses.  The labels will be
generated for you as a series of integers starting with C<1>.  For example,
the following are equivalent:

   res1 => { addrs_v4 => [ 192.0.2.1, 192.0.2.2 ] }
   res1 => { addrs_v4 => { 1 => 192.0.2.1, 2 => 192.0.2.2 } }

=head1 OPERATIONAL MECHANICS

All of the addresses for all of the resources are monitored using the
per-address-family inherited C<service_types> specified (default would be
simple HTTP/1.0 monitoring for C<200 OK> on port 80).  When the core daemon
requests a lookup for address records of a given family on one of this
plugin's resources, it goes through essentially the following process to
determine the set of response addresses for that address family: 1) Add all
non-DOWN addresses to the result set.  2) If the set of non-DOWN addresses
fail the up_thresh check, add *all* addresses to the result set as a
fallback.  3) If any address is in the DOWN or even DANGER state, cut the
zonefile-specified TTL in half

This process is repeated independently for each of the IPv4 and IPv6
address subsets, in the case that a resource has both address families
configured (the TTL is only cut in half once of course).  Details on the
up_thresh check follow:

=head1 UP THRESH

Any non-DOWN address is considered an acceptable result for up_thresh
purposes, even ones in the transitional DANGER state.  If there are not
"enough" non-DOWN addresses to pass the threshold (per address family), all
addresses (of a given address family) will be returned as a fallback.

The threshold is implemented mathematically as in the following pseudo-code
C<if(non_down E<gt>= ceil(thresh * total)) threshold_passed;>.  For
example, if thresh is at the default value of C<0.5>, and there are 3 total
IPv4 addresses, then 2 of them must be non-down to pass the threshold.  The
net result is that with the default threshold, the plugin will never return
an isolated single address from a set of 3.  It will either return all 3,
or it will return 2/3 if a single address from the set has failed.

When the threshold check fails (and all addresses are returned) for either
address family, resource-level total failure will also be signaled to any
applicable upstream meta-plugins such as metafo or geoip.

General rules for the results of the up_thresh formula:

=over 4

=item * A threshold of 1.0 will only pass if B<all> addresses are not-down.
This is mostly pointless, you might as well not monitor anything and set
up these addresses as a static set in a zonefile.

=item * A threshold of 0.01 will pass even if only one address is alive and
return just that one address, even if it's e.g. the only one left out of 40.

=item * Because a threshold of 0.0 is illegal, if all addresses are down
the threshold will always fail, returning all addresses.

=back

Intermediate value examples: (threshold: non-down/total required to pass
threshold):

=over 4

=item * 0.1: 1/1 1/2 1/3 1/4 1/5 1/6 1/7 1/8 2/16

=item * 0.2: 1/1 1/2 1/3 1/4 1/5 2/6 2/7 2/8 4/16

=item * 0.3: 1/1 1/2 1/3 2/4 2/5 2/6 3/7 3/8 5/16

=item * 0.4: 1/1 1/2 2/3 2/4 2/5 3/6 3/7 4/8 7/16

=item * 0.5: 1/1 1/2 2/3 2/4 3/5 3/6 4/7 4/8 8/16

=item * 0.6: 1/1 2/2 2/3 3/4 3/5 4/6 5/7 5/8 10/16

=item * 0.7: 1/1 2/2 3/3 3/4 4/5 5/6 5/7 6/8 12/16

=item * 0.8: 1/1 2/2 3/3 4/4 4/5 5/6 6/7 7/8 13/16

=item * 0.9: 1/1 2/2 3/3 4/4 5/5 6/6 7/7 8/8 15/16

=back

=head1 SEE ALSO

L<gdnsd.config(5)>, L<gdnsd.zonefile(5)>, L<gdnsd(8)>,
L<gdnsd-plugin-simplefo(8)>

The gdnsd manual.

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2012 Brandon L Black <blblack@gmail.com>

This file is part of gdnsd.

gdnsd is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

gdnsd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gdnsd.  If not, see <http://www.gnu.org/licenses/>.

=cut
