Merge pull request #18207 from tavyc/quagga-module

quagga service: init
This commit is contained in:
Joachim F 2016-09-03 16:23:23 +02:00 committed by GitHub
commit 3db5311be9
4 changed files with 286 additions and 0 deletions

View File

@ -388,6 +388,7 @@
./services/networking/prayer.nix
./services/networking/privoxy.nix
./services/networking/prosody.nix
./services/networking/quagga.nix
./services/networking/quassel.nix
./services/networking/racoon.nix
./services/networking/radicale.nix

View File

@ -0,0 +1,187 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.quagga;
services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ];
allServices = services ++ [ "zebra" ];
isEnabled = service: cfg.${service}.enable;
daemonName = service: if service == "zebra" then service else "${service}d";
configFile = service:
let
scfg = cfg.${service};
in
if scfg.configFile != null then scfg.configFile
else pkgs.writeText "${daemonName service}.conf"
''
! Quagga ${daemonName service} configuration
!
hostname ${config.networking.hostName}
log syslog
service password-encryption
!
${scfg.config}
!
end
'';
serviceOptions = service:
{
enable = mkEnableOption "the Quagga ${toUpper service} routing protocol";
configFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/etc/quagga/${daemonName service}.conf";
description = ''
Configuration file to use for Quagga ${daemonName service}.
By default the NixOS generated files are used.
'';
};
config = mkOption {
type = types.lines;
default = "";
example =
let
examples = {
rip = ''
router rip
network 10.0.0.0/8
'';
ospf = ''
router ospf
network 10.0.0.0/8 area 0
'';
bgp = ''
router bgp 65001
neighbor 10.0.0.1 remote-as 65001
'';
};
in
examples.${service} or "";
description = ''
${daemonName service} configuration statements.
'';
};
vtyListenAddress = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Address to bind to for the VTY interface.
'';
};
vtyListenPort = mkOption {
type = types.nullOr types.int;
default = null;
description = ''
TCP Port to bind to for the VTY interface.
'';
};
};
in
{
###### interface
options.services.quagga =
{
zebra = (serviceOptions "zebra") // {
enable = mkOption {
type = types.bool;
default = any isEnabled services;
example = true;
description = ''
Whether to enable the Zebra routing manager.
The Zebra routing manager is automatically enabled
if any routing protocols are configured.
'';
};
};
} // (genAttrs services serviceOptions);
###### implementation
config = mkIf (any isEnabled allServices) {
environment.systemPackages = [
pkgs.quagga # for the vtysh tool
];
users.users.quagga = {
description = "Quagga daemon user";
isSystemUser = true;
group = "quagga";
};
users.groups = {
quagga = {};
# Members of the quaggavty group can use vtysh to inspect the Quagga daemons
quaggavty = {};
};
systemd.services =
let
quaggaService = service:
let
scfg = cfg.${service};
daemon = daemonName service;
in
nameValuePair daemon ({
wantedBy = [ "multi-user.target" ];
restartTriggers = [ (configFile service) ];
serviceConfig = {
Type = "forking";
PIDFile = "/run/quagga/${daemon}.pid";
ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}"
+ optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}"
+ optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Restart = "on-abort";
};
} // (
if service == "zebra" then
{
description = "Quagga Zebra routing manager";
unitConfig.Documentation = "man:zebra(8)";
after = [ "network.target" ];
preStart = ''
install -m 0755 -o quagga -g quagga -d /run/quagga
${pkgs.iproute}/bin/ip route flush proto zebra
'';
}
else
{
description = "Quagga ${toUpper service} routing daemon";
unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)";
bindsTo = [ "zebra.service" ];
after = [ "network.target" "zebra.service" ];
}
));
in
listToAttrs (map quaggaService (filter isEnabled allServices));
};
meta.maintainers = with lib.maintainers; [ tavyc ];
}

View File

@ -279,6 +279,7 @@ in rec {
tests.printing = callTest tests/printing.nix {};
tests.proxy = callTest tests/proxy.nix {};
tests.pumpio = callTest tests/pump.io.nix {};
tests.quagga = callTest tests/quagga.nix {};
tests.quake3 = callTest tests/quake3.nix {};
tests.runInMachine = callTest tests/run-in-machine.nix {};
tests.sddm = callTest tests/sddm.nix {};

97
nixos/tests/quagga.nix Normal file
View File

@ -0,0 +1,97 @@
# This test runs Quagga and checks if OSPF routing works.
#
# Network topology:
# [ client ]--net1--[ router1 ]--net2--[ router2 ]--net3--[ server ]
#
# All interfaces are in OSPF Area 0.
import ./make-test.nix ({ pkgs, ... }:
let
ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ip4).address;
ospfConf = ''
interface eth2
ip ospf hello-interval 1
ip ospf dead-interval 5
!
router ospf
network 192.168.0.0/16 area 0
'';
in
{
name = "quagga";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ tavyc ];
};
nodes = {
client =
{ config, pkgs, nodes, ... }:
{
virtualisation.vlans = [ 1 ];
networking.defaultGateway = ifAddr nodes.router1 "eth1";
};
router1 =
{ config, pkgs, nodes, ... }:
{
virtualisation.vlans = [ 1 2 ];
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT";
services.quagga.ospf = {
enable = true;
config = ospfConf;
};
};
router2 =
{ config, pkgs, nodes, ... }:
{
virtualisation.vlans = [ 3 2 ];
boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
networking.firewall.extraCommands = "iptables -A nixos-fw -i eth2 -p ospf -j ACCEPT";
services.quagga.ospf = {
enable = true;
config = ospfConf;
};
};
server =
{ config, pkgs, nodes, ... }:
{
virtualisation.vlans = [ 3 ];
networking.defaultGateway = ifAddr nodes.router2 "eth1";
networking.firewall.allowedTCPPorts = [ 80 ];
networking.firewall.allowPing = true;
services.httpd.enable = true;
services.httpd.adminAddr = "foo@example.com";
};
};
testScript =
{ nodes, ... }:
''
startAll;
# Wait for the networking to start on all machines
$_->waitForUnit("network.target") foreach values %vms;
# Wait for OSPF to form adjacencies
for my $gw ($router1, $router2) {
$gw->waitForUnit("ospfd");
$gw->waitUntilSucceeds("vtysh -c 'show ip ospf neighbor' | grep Full");
$gw->waitUntilSucceeds("vtysh -c 'show ip route' | grep '^O>'");
}
# Test ICMP.
$client->succeed("ping -c 3 server >&2");
# Test whether HTTP works.
$server->waitForUnit("httpd");
$client->succeed("curl --fail http://server/ >&2");
'';
})