keepalived service: init (#22755)
parent
f47921f3d4
commit
6572f5e81b
@ -0,0 +1,245 @@ |
||||
{ config, lib, pkgs, ... }: |
||||
|
||||
with lib; |
||||
|
||||
let |
||||
|
||||
cfg = config.services.keepalived; |
||||
|
||||
keepalivedConf = pkgs.writeText "keepalived.conf" '' |
||||
global_defs { |
||||
${snmpGlobalDefs} |
||||
${cfg.extraGlobalDefs} |
||||
} |
||||
|
||||
${vrrpInstancesStr} |
||||
${cfg.extraConfig} |
||||
''; |
||||
|
||||
snmpGlobalDefs = with cfg.snmp; optionalString enable ( |
||||
optionalString (socket != null) "snmp_socket ${socket}\n" |
||||
+ optionalString enableKeepalived "enable_snmp_keepalived\n" |
||||
+ optionalString enableChecker "enable_snmp_checker\n" |
||||
+ optionalString enableRfc "enable_snmp_rfc\n" |
||||
+ optionalString enableRfcV2 "enable_snmp_rfcv2\n" |
||||
+ optionalString enableRfcV3 "enable_snmp_rfcv3\n" |
||||
+ optionalString enableTraps "enable_traps" |
||||
); |
||||
|
||||
vrrpInstancesStr = concatStringsSep "\n" (map (i: |
||||
'' |
||||
vrrp_instance ${i.name} { |
||||
interface ${i.interface} |
||||
state ${i.state} |
||||
virtual_router_id ${toString i.virtualRouterId} |
||||
priority ${toString i.priority} |
||||
${optionalString i.noPreempt "nopreempt"} |
||||
|
||||
${optionalString i.useVmac ( |
||||
"use_vmac" + optionalString (i.vmacInterface != null) " ${i.vmacInterface}" |
||||
)} |
||||
${optionalString i.vmacXmitBase "vmac_xmit_base"} |
||||
|
||||
${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"} |
||||
unicast_peer { |
||||
${concatStringsSep "\n" i.unicastPeers} |
||||
} |
||||
|
||||
virtual_ipaddress { |
||||
${concatMapStringsSep "\n" virtualIpLine i.virtualIps} |
||||
} |
||||
|
||||
${i.extraConfig} |
||||
} |
||||
'' |
||||
) vrrpInstances); |
||||
|
||||
virtualIpLine = (ip: |
||||
ip.addr |
||||
+ optionalString (notNullOrEmpty ip.brd) " brd ${ip.brd}" |
||||
+ optionalString (notNullOrEmpty ip.dev) " dev ${ip.dev}" |
||||
+ optionalString (notNullOrEmpty ip.scope) " scope ${ip.scope}" |
||||
+ optionalString (notNullOrEmpty ip.label) " label ${ip.label}" |
||||
); |
||||
|
||||
notNullOrEmpty = s: !(s == null || s == ""); |
||||
|
||||
vrrpInstances = mapAttrsToList (iName: iConfig: |
||||
{ |
||||
name = iName; |
||||
} // iConfig |
||||
) cfg.vrrpInstances; |
||||
|
||||
vrrpInstanceAssertions = i: [ |
||||
{ assertion = i.interface != ""; |
||||
message = "services.keepalived.vrrpInstances.${i.name}.interface option cannot be empty."; |
||||
} |
||||
{ assertion = i.virtualRouterId >= 0 && i.virtualRouterId <= 255; |
||||
message = "services.keepalived.vrrpInstances.${i.name}.virtualRouterId must be an integer between 0..255."; |
||||
} |
||||
{ assertion = i.priority >= 0 && i.priority <= 255; |
||||
message = "services.keepalived.vrrpInstances.${i.name}.priority must be an integer between 0..255."; |
||||
} |
||||
{ assertion = i.vmacInterface == null || i.useVmac; |
||||
message = "services.keepalived.vrrpInstances.${i.name}.vmacInterface has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set."; |
||||
} |
||||
{ assertion = !i.vmacXmitBase || i.useVmac; |
||||
message = "services.keepalived.vrrpInstances.${i.name}.vmacXmitBase has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set."; |
||||
} |
||||
] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps); |
||||
|
||||
virtualIpAssertions = vrrpName: ip: [ |
||||
{ assertion = ip.addr != ""; |
||||
message = "The 'addr' option for an services.keepalived.vrrpInstances.${vrrpName}.virtualIps entry cannot be empty."; |
||||
} |
||||
]; |
||||
|
||||
pidFile = "/run/keepalived.pid"; |
||||
|
||||
in |
||||
{ |
||||
|
||||
options = { |
||||
services.keepalived = { |
||||
|
||||
enable = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Whether to enable Keepalived. |
||||
''; |
||||
}; |
||||
|
||||
snmp = { |
||||
|
||||
enable = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Whether to enable the builtin AgentX subagent. |
||||
''; |
||||
}; |
||||
|
||||
socket = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
Socket to use for connecting to SNMP master agent. If this value is |
||||
set to null, keepalived's default will be used, which is |
||||
unix:/var/agentx/master, unless using a network namespace, when the |
||||
default is udp:localhost:705. |
||||
''; |
||||
}; |
||||
|
||||
enableKeepalived = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable SNMP handling of vrrp element of KEEPALIVED MIB. |
||||
''; |
||||
}; |
||||
|
||||
enableChecker = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable SNMP handling of checker element of KEEPALIVED MIB. |
||||
''; |
||||
}; |
||||
|
||||
enableRfc = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable SNMP handling of RFC2787 and RFC6527 VRRP MIBs. |
||||
''; |
||||
}; |
||||
|
||||
enableRfcV2 = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable SNMP handling of RFC2787 VRRP MIB. |
||||
''; |
||||
}; |
||||
|
||||
enableRfcV3 = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable SNMP handling of RFC6527 VRRP MIB. |
||||
''; |
||||
}; |
||||
|
||||
enableTraps = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Enable SNMP traps. |
||||
''; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
vrrpInstances = mkOption { |
||||
type = types.attrsOf (types.submodule (import ./vrrp-options.nix { |
||||
inherit lib; |
||||
})); |
||||
default = {}; |
||||
description = "Declarative vhost config"; |
||||
}; |
||||
|
||||
extraGlobalDefs = mkOption { |
||||
type = types.lines; |
||||
default = ""; |
||||
description = '' |
||||
Extra lines to be added verbatim to the 'global_defs' block of the |
||||
configuration file |
||||
''; |
||||
}; |
||||
|
||||
extraConfig = mkOption { |
||||
type = types.lines; |
||||
default = ""; |
||||
description = '' |
||||
Extra lines to be added verbatim to the configuration file. |
||||
''; |
||||
}; |
||||
|
||||
}; |
||||
}; |
||||
|
||||
config = mkIf cfg.enable { |
||||
|
||||
assertions = flatten (map vrrpInstanceAssertions vrrpInstances); |
||||
|
||||
systemd.timers.keepalived-boot-delay = { |
||||
description = "Keepalive Daemon delay to avoid instant transition to MASTER state"; |
||||
after = [ "network.target" "network-online.target" "syslog.target" ]; |
||||
requires = [ "network-online.target" ]; |
||||
wantedBy = [ "multi-user.target" ]; |
||||
timerConfig = { |
||||
OnActiveSec = "5s"; |
||||
Unit = "keepalived.service"; |
||||
}; |
||||
}; |
||||
|
||||
systemd.services.keepalived = { |
||||
description = "Keepalive Daemon (LVS and VRRP)"; |
||||
after = [ "network.target" "network-online.target" "syslog.target" ]; |
||||
wants = [ "network-online.target" ]; |
||||
serviceConfig = { |
||||
Type = "forking"; |
||||
PIDFile = pidFile; |
||||
KillMode = "process"; |
||||
ExecStart = "${pkgs.keepalived}/sbin/keepalived" |
||||
+ " -f ${keepalivedConf}" |
||||
+ " -p ${pidFile}" |
||||
+ optionalString cfg.snmp.enable " --snmp"; |
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; |
||||
Restart = "always"; |
||||
RestartSec = "1s"; |
||||
}; |
||||
}; |
||||
}; |
||||
} |
@ -0,0 +1,50 @@ |
||||
{ lib } : |
||||
|
||||
with lib; |
||||
{ |
||||
options = { |
||||
|
||||
addr = mkOption { |
||||
type = types.str; |
||||
description = '' |
||||
IP address, optionally with a netmask: IPADDR[/MASK] |
||||
''; |
||||
}; |
||||
|
||||
brd = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
The broadcast address on the interface. |
||||
''; |
||||
}; |
||||
|
||||
dev = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
The name of the device to add the address to. |
||||
''; |
||||
}; |
||||
|
||||
scope = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
The scope of the area where this address is valid. |
||||
''; |
||||
}; |
||||
|
||||
label = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
Each address may be tagged with a label string. In order to preserve |
||||
compatibility with Linux-2.0 net aliases, this string must coincide with |
||||
the name of the device or must be prefixed with the device name followed |
||||
by colon. |
||||
''; |
||||
}; |
||||
|
||||
}; |
||||
} |
@ -0,0 +1,121 @@ |
||||
{ lib } : |
||||
|
||||
with lib; |
||||
{ |
||||
options = { |
||||
|
||||
interface = mkOption { |
||||
type = types.str; |
||||
description = '' |
||||
Interface for inside_network, bound by vrrp. |
||||
''; |
||||
}; |
||||
|
||||
state = mkOption { |
||||
type = types.enum [ "MASTER" "BACKUP" ]; |
||||
default = "BACKUP"; |
||||
description = '' |
||||
Initial state. As soon as the other machine(s) come up, an election will |
||||
be held and the machine with the highest "priority" will become MASTER. |
||||
So the entry here doesn't matter a whole lot. |
||||
''; |
||||
}; |
||||
|
||||
virtualRouterId = mkOption { |
||||
type = types.int; |
||||
description = '' |
||||
Arbitrary unique number 0..255. Used to differentiate multiple instances |
||||
of vrrpd running on the same NIC (and hence same socket). |
||||
''; |
||||
}; |
||||
|
||||
priority = mkOption { |
||||
type = types.int; |
||||
default = 100; |
||||
description = '' |
||||
For electing MASTER, highest priority wins. To be MASTER, make 50 more |
||||
than other machines. |
||||
''; |
||||
}; |
||||
|
||||
noPreempt = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
VRRP will normally preempt a lower priority machine when a higher |
||||
priority machine comes online. "nopreempt" allows the lower priority |
||||
machine to maintain the master role, even when a higher priority machine |
||||
comes back online. NOTE: For this to work, the initial state of this |
||||
entry must be BACKUP. |
||||
''; |
||||
}; |
||||
|
||||
useVmac = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Use VRRP Virtual MAC. |
||||
''; |
||||
}; |
||||
|
||||
vmacInterface = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
Name of the vmac interface to use. keepalived will come up with a name |
||||
if you don't specify one. |
||||
''; |
||||
}; |
||||
|
||||
vmacXmitBase = mkOption { |
||||
type = types.bool; |
||||
default = false; |
||||
description = '' |
||||
Send/Recv VRRP messages from base interface instead of VMAC interface. |
||||
''; |
||||
}; |
||||
|
||||
unicastSrcIp = mkOption { |
||||
type = types.nullOr types.str; |
||||
default = null; |
||||
description = '' |
||||
Default IP for binding vrrpd is the primary IP on interface. If you |
||||
want to hide location of vrrpd, use this IP as src_addr for unicast |
||||
vrrp packets. |
||||
''; |
||||
}; |
||||
|
||||
unicastPeers = mkOption { |
||||
type = types.listOf types.str; |
||||
default = []; |
||||
description = '' |
||||
Do not send VRRP adverts over VRRP multicast group. Instead it sends |
||||
adverts to the following list of ip addresses using unicast design |
||||
fashion. It can be cool to use VRRP FSM and features in a networking |
||||
environment where multicast is not supported! IP Addresses specified can |
||||
IPv4 as well as IPv6. |
||||
''; |
||||
}; |
||||
|
||||
virtualIps = mkOption { |
||||
type = types.listOf (types.submodule (import ./virtual-ip-options.nix { |
||||
inherit lib; |
||||
})); |
||||
default = []; |
||||
example = literalExample '' |
||||
TODO: Example |
||||
''; |
||||
description = "Declarative vhost config"; |
||||
}; |
||||
|
||||
extraConfig = mkOption { |
||||
type = types.lines; |
||||
default = ""; |
||||
description = '' |
||||
Extra lines to be added verbatim to the vrrp_instance section. |
||||
''; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
} |
Loading…
Reference in new issue