241 lines
7.1 KiB
Nix
241 lines
7.1 KiB
Nix
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 "<disable/>" else "";
|
|
# FIXME : need to enforce address in reality
|
|
mgmt_fam =
|
|
if !builtins.isNull cfg.interfaces.${name}.management then
|
|
''
|
|
<inet>
|
|
<address>
|
|
<name>${cfg.interfaces.${name}.management}</name>
|
|
</address>
|
|
</inet>''
|
|
else
|
|
"";
|
|
members = map (vlan: "<members>${builtins.toString vlan}</members>") cfg.interfaces.${name}.vlans;
|
|
eth_switch =
|
|
if builtins.isNull cfg.interfaces.${name}.interface-mode then
|
|
""
|
|
else
|
|
''
|
|
<ethernet-switching>
|
|
<interface-mode>${cfg.interfaces.${name}.interface-mode}</interface-mode>
|
|
<vlan>${builtins.concatStringsSep "" members}</vlan>
|
|
<storm-control><profile-name>default</profile-name></storm-control>
|
|
</ethernet-switching>'';
|
|
in
|
|
''
|
|
<interface>
|
|
<name>${name}</name>
|
|
${disable_flag}
|
|
<unit>
|
|
<name>0</name>
|
|
<family>
|
|
${mgmt_fam}
|
|
${eth_switch}
|
|
</family>
|
|
</unit>
|
|
</interface>
|
|
'';
|
|
interface_xmls = map intf_xmlGen interfaces;
|
|
rstp_gen =
|
|
name:
|
|
if cfg.interfaces.${name}.enable && !builtins.isNull cfg.interfaces.${name}.interface-mode then
|
|
"<interface><name>${name}</name></interface>"
|
|
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
|
|
''<vlan-id-list>${list}</vlan-id-list>'';
|
|
vlan_map =
|
|
vlan:
|
|
let
|
|
ids =
|
|
if !builtins.isList cfg.vlans.${vlan}.ids then
|
|
[ "<vlan-id>${builtins.toString cfg.vlans.${vlan}.ids}</vlan-id>" ]
|
|
else
|
|
map id_map cfg.vlans.${vlan}.ids;
|
|
mgmt_flag =
|
|
if !builtins.isNull cfg.vlans.${vlan}.management then "<l3-interface>irb.0</l3-interface>" else "";
|
|
in
|
|
''
|
|
<vlan>
|
|
<name>${vlan}</name>
|
|
${mgmt_flag}
|
|
${builtins.concatStringsSep "\n" ids}
|
|
</vlan>'';
|
|
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
|
|
''
|
|
<interface>
|
|
<name>irb</name>
|
|
<unit>
|
|
<name>0</name>
|
|
<family>
|
|
<inet6>
|
|
<address><name>${addr}</name></address>
|
|
</inet6>
|
|
</family>
|
|
</unit>
|
|
</interface>
|
|
''
|
|
else
|
|
"";
|
|
config = ''
|
|
<interfaces operation="replace">
|
|
${builtins.concatStringsSep "\n" interface_xmls}
|
|
${irb_intf}
|
|
</interfaces>
|
|
<protocols>
|
|
<rstp operation="replace">
|
|
${builtins.concatStringsSep "\n" rstps}
|
|
</rstp>
|
|
</protocols>
|
|
<vlans operation="replace">
|
|
${builtins.concatStringsSep "\n" vlans}
|
|
</vlans>
|
|
'';
|
|
rpc_requests = pkgs.writeText "config-${name}_rpc.xml" ''
|
|
<rpc>
|
|
<edit-config>
|
|
<config>
|
|
<configuration>
|
|
${config}
|
|
</configuration>
|
|
</config>
|
|
<target>
|
|
<candidate/>
|
|
</target>
|
|
</edit-config>
|
|
</rpc>
|
|
<rpc>
|
|
<commit/>
|
|
</rpc>
|
|
'';
|
|
in
|
|
pkgs.writeShellScript "deploy-${name}.sh" ''
|
|
${pkgs.openssh}/bin/ssh ${cfg.deployement.targetHost} -p 830 -s netconf < ${rpc_requests}
|
|
'';
|
|
}
|