interfaces:
{
name,
lib,
pkgs,
config,
...
}:
let
cfg = config;
in
with lib;
{
options = {
deployement = {
targetHost = mkOption { type = types.str; };
cmd = mkOption {
type = types.package;
readOnly = true;
};
};
vlans =
let
range_type.options = {
begin = mkOption { type = types.ints.unsigned; };
end = mkOption { type = types.ints.unsigned; };
};
vlan_type.options = {
ids = mkOption {
type = types.either types.ints.unsigned (
types.listOf (types.either types.ints.unsigned (types.submodule range_type))
);
default = [ ];
};
management = mkOption {
# FIXME : support ipv4, either static or dhcp (with the coffee)
type = types.nullOr types.str;
default = null;
description = ''
IP address with wich to permit management on this vlan.
Only one vlan can set an IP (this module limitation, not switch).
'';
};
};
in
mkOption { type = types.attrsOf (types.submodule vlan_type); };
interfaces =
let
template = name: {
enable = mkEnableOption "the interface ${name}";
interface-mode = mkOption {
type = types.nullOr (
types.enum [
"trunk"
"access"
]
);
default = null;
};
vlans = mkOption {
type =
let
vlan_type = types.either (types.strMatching "[^\n\r]+") (types.ints.unsigned);
in
types.listOf vlan_type;
default = [ ];
};
# TODO: use this option
dhcp_trusted = mkOption {
type = types.bool;
default = false;
};
management = mkOption {
# FIXME : support ipv6, either static or dhcp (with the coffee)
type = types.nullOr types.str;
default = null;
};
};
in
builtins.listToAttrs (
map (name: {
inherit name;
value = template name;
}) interfaces
);
};
config.deployement.cmd =
let
intf_xmlGen =
name:
let
disable_flag = if !cfg.interfaces.${name}.enable then "" else "";
# FIXME : need to enforce address in reality
mgmt_fam =
if !builtins.isNull cfg.interfaces.${name}.management then
''
${cfg.interfaces.${name}.management}
''
else
"";
members = map (vlan: "${builtins.toString vlan}") cfg.interfaces.${name}.vlans;
eth_switch =
if builtins.isNull cfg.interfaces.${name}.interface-mode then
""
else
''
${cfg.interfaces.${name}.interface-mode}
${builtins.concatStringsSep "" members}
default
'';
in
''
${name}
${disable_flag}
0
${mgmt_fam}
${eth_switch}
'';
interface_xmls = map intf_xmlGen interfaces;
rstp_gen =
name:
if cfg.interfaces.${name}.enable && !builtins.isNull cfg.interfaces.${name}.interface-mode then
"${name}"
else
"";
rstps = map rstp_gen interfaces;
vlan_trust_table =
let
vlan_map =
inter: vlan:
if builtins.isString vlan && cfg.interfaces.${inter}.enable then
if cfg.interfaces.${inter}.dhcp_trusted then
{ ${vlan}.trust = inter; }
else
{ ${vlan}.notrust = inter; }
else
{ };
int_map = inter: map (vlan_map inter) cfg.interfaces.${inter}.vlans;
in
builtins.zipAttrsWith (vlan: values: builtins.zipAttrsWith (_: ints: ints) values) (
builtins.concatMap int_map interfaces
);
vlans =
let
id_map =
id:
let
list =
if builtins.isInt id then
builtins.toString id
else
"${builtins.toString id.begin}-${builtins.toString id.end}";
in
''${list}'';
vlan_map =
vlan:
let
ids =
if !builtins.isList cfg.vlans.${vlan}.ids then
[ "${builtins.toString cfg.vlans.${vlan}.ids}" ]
else
map id_map cfg.vlans.${vlan}.ids;
mgmt_flag =
if !builtins.isNull cfg.vlans.${vlan}.management then "irb.0" else "";
in
''
${vlan}
${mgmt_flag}
${builtins.concatStringsSep "\n" ids}
'';
in
map vlan_map (builtins.attrNames cfg.vlans);
irb_intf =
let
addresses = map (vlan: vlan.management) (builtins.attrValues cfg.vlans);
addr = builtins.foldl' (acc: addr: if !builtins.isNull addr then addr else acc) null addresses;
in
if !builtins.isNull addr then
''
irb
0
${addr}
''
else
"";
config = ''
${builtins.concatStringsSep "\n" interface_xmls}
${irb_intf}
${builtins.concatStringsSep "\n" rstps}
${builtins.concatStringsSep "\n" vlans}
'';
rpc_requests = pkgs.writeText "config-${name}_rpc.xml" ''
${config}
'';
in
pkgs.writeShellScript "deploy-${name}.sh" ''
${pkgs.openssh}/bin/ssh ${cfg.deployement.targetHost} -p 830 -s netconf < ${rpc_requests}
'';
}