Merge pull request 'refactor' (#1) from refactor into master

Reviewed-on: lbailly/Netconf-Module#1
This commit is contained in:
catvayor 2024-05-22 13:34:29 +02:00
commit 841ff3a69e
8 changed files with 522 additions and 351 deletions

View file

@ -2,82 +2,66 @@
pkgs ? (import <nixpkgs> { }), pkgs ? (import <nixpkgs> { }),
}: }:
let let
moduleEX2300 = import ./moduleMaker.nix [ lib = pkgs.lib;
"ge-0/0/0" hive_mod =
"ge-0/0/1" {
"ge-0/0/2" lib,
"ge-0/0/3" config,
"ge-0/0/4" name,
"ge-0/0/5" ...
"ge-0/0/6" }:
"ge-0/0/7" with lib;
"ge-0/0/8" {
"ge-0/0/9" options.deployment = {
"ge-0/0/10" targetHost = mkOption { type = types.str; };
"ge-0/0/11" rpc = mkOption {
"ge-0/0/12" type = types.package;
"ge-0/0/13" readOnly = true;
"ge-0/0/14" };
"ge-0/0/15" cmd = mkOption {
"ge-0/0/16" type = types.package;
"ge-0/0/17" readOnly = true;
"ge-0/0/18" };
"ge-0/0/19" };
"ge-0/0/20" config.deployment = rec {
"ge-0/0/21" rpc = pkgs.writeText "config-${name}_rpc.xml" ''
"ge-0/0/22" <rpc>
"ge-0/0/23" <edit-config>
"ge-0/0/24" <config>
"ge-0/0/25" ${config.netconf.xmls.configuration}
"ge-0/0/26" </config>
"ge-0/0/27" <target>
"ge-0/0/28" <candidate/>
"ge-0/0/29" </target>
"ge-0/0/30" </edit-config>
"ge-0/0/31" </rpc>
"ge-0/0/32" <rpc>
"ge-0/0/33" <commit/>
"ge-0/0/34" </rpc>
"ge-0/0/35" '';
"ge-0/0/36" cmd = pkgs.writeShellApplication {
"ge-0/0/37" name = "deploy-${name}.sh";
"ge-0/0/38" runtimeInputs = with pkgs; [ openssh ];
"ge-0/0/39" text = ''ssh "${config.deployment.targetHost}" -p 830 -s netconf < ${rpc}'';
"ge-0/0/40" };
"ge-0/0/41" };
"ge-0/0/42" };
"ge-0/0/43"
"ge-0/0/44"
"ge-0/0/45"
"ge-0/0/46"
"ge-0/0/47"
"ge-0/1/0"
"ge-0/1/1"
"ge-0/1/2"
"ge-0/1/3"
"xe-0/1/0"
"xe-0/1/1"
"xe-0/1/2"
"xe-0/1/3"
"me0"
];
evaluator = evaluator =
name: module_inst: name: module_inst:
let let
cfg = pkgs.lib.evalModules { cfg = pkgs.lib.evalModules {
specialArgs = { specialArgs = {
inherit pkgs name; inherit name;
}; };
modules = [ modules = [
moduleEX2300 ./junos
./ex2300.nix
hive_mod
module_inst module_inst
]; ];
}; };
in in
"ln -s ${cfg.config.deployement.cmd} $out/${name}"; "ln -s ${lib.getExe cfg.config.deployment.cmd} $out/${name}";
hive = import ./netconf-hive.nix; hive = import ./netconf-hive.nix;
cmds = builtins.attrValues (builtins.mapAttrs evaluator hive); cmds = builtins.attrValues (builtins.mapAttrs evaluator hive);
in in

62
ex2300.nix Normal file
View file

@ -0,0 +1,62 @@
{
netconf.mandatoryInterfaces = [
"ge-0/0/0"
"ge-0/0/1"
"ge-0/0/2"
"ge-0/0/3"
"ge-0/0/4"
"ge-0/0/5"
"ge-0/0/6"
"ge-0/0/7"
"ge-0/0/8"
"ge-0/0/9"
"ge-0/0/10"
"ge-0/0/11"
"ge-0/0/12"
"ge-0/0/13"
"ge-0/0/14"
"ge-0/0/15"
"ge-0/0/16"
"ge-0/0/17"
"ge-0/0/18"
"ge-0/0/19"
"ge-0/0/20"
"ge-0/0/21"
"ge-0/0/22"
"ge-0/0/23"
"ge-0/0/24"
"ge-0/0/25"
"ge-0/0/26"
"ge-0/0/27"
"ge-0/0/28"
"ge-0/0/29"
"ge-0/0/30"
"ge-0/0/31"
"ge-0/0/32"
"ge-0/0/33"
"ge-0/0/34"
"ge-0/0/35"
"ge-0/0/36"
"ge-0/0/37"
"ge-0/0/38"
"ge-0/0/39"
"ge-0/0/40"
"ge-0/0/41"
"ge-0/0/42"
"ge-0/0/43"
"ge-0/0/44"
"ge-0/0/45"
"ge-0/0/46"
"ge-0/0/47"
"ge-0/1/0"
"ge-0/1/1"
"ge-0/1/2"
"ge-0/1/3"
"xe-0/1/0"
"xe-0/1/1"
"xe-0/1/2"
"xe-0/1/3"
];
}

36
junos/default.nix Normal file
View file

@ -0,0 +1,36 @@
{
name,
lib,
config,
...
}:
with lib;
{
imports = [
./protocols.nix
./interfaces.nix
./vlans.nix
];
options = {
netconf.xmls.configuration = mkOption {
type = types.str;
readOnly = true;
};
netconf.mandatoryInterfaces = mkOption { type = types.listOf types.str; };
};
config.interfaces =
let
mkIntf = name: {
inherit name;
value.enable = mkDefault false;
};
in
listToAttrs (map mkIntf config.netconf.mandatoryInterfaces);
config.netconf.xmls.configuration = ''
<configuration>
${config.netconf.xmls.interfaces}
${config.netconf.xmls.protocols}
${config.netconf.xmls.vlans}
</configuration>
'';
}

131
junos/interfaces.nix Normal file
View file

@ -0,0 +1,131 @@
{ lib, config, ... }:
with lib;
let
interface =
{ name, config, ... }:
let
intf-name = name;
unit =
{ name, config, ... }:
{
options = {
enable = mkEnableOption "the logical interface ${intf-name}.${name}" // {
default = true;
};
family = {
ethernet-switching = {
enable = mkEnableOption "the ethernet on the logical interface ${intf-name}.${name}";
interface-mode = mkOption {
type = types.nullOr (
types.enum [
"trunk"
"access"
]
);
default = null;
};
vlans = mkOption {
type = types.listOf (types.either types.str types.ints.unsigned);
default = [ ];
};
};
#TODO : DHCP
inet = {
enable = mkEnableOption "the IPv4 configuration of the logical interface ${intf-name}.${name}";
address = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
inet6 = {
enable = mkEnableOption "the IPv6 configuration of the logical interface ${intf-name}.${name}";
address = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
};
xml = mkOption {
type = types.str;
visible = false;
readOnly = true;
};
};
config.xml =
let
members = map (
vlan: "<members>${builtins.toString vlan}</members>"
) config.family.ethernet-switching.vlans;
eth = optionalString config.family.ethernet-switching.enable ''
<ethernet-switching>
<interface-mode>${config.family.ethernet-switching.interface-mode}</interface-mode>
<vlan>${builtins.concatStringsSep "" members}</vlan>
<storm-control><profile-name>default</profile-name></storm-control>
</ethernet-switching>
'';
addr4 = map (addr: "<name>${addr}</name>") config.family.inet.address;
inet = optionalString config.family.inet.enable ''
<inet>
<address>${builtins.concatStringsSep "" addr4}</address>
</inet>
'';
addr6 = map (addr: "<name>${addr}</name>") config.family.inet6.address;
inet6 = optionalString config.family.inet6.enable ''
<inet6>
<address>${builtins.concatStringsSep "" addr6}</address>
</inet6>
'';
in
''
<unit>
<name>${name}</name>
${optionalString (!config.enable) "<disable/>"}
<family>
${eth}${inet}${inet6}
</family>
</unit>'';
};
in
{
options = {
enable = mkEnableOption "the physical interface ${intf-name}";
unit = mkOption {
type = types.attrsOf (types.submodule unit);
default = { };
};
xml = mkOption {
type = types.str;
visible = false;
readOnly = true;
};
};
config.xml =
let
units = attrsets.mapAttrsToList (_: unit: unit.xml) config.unit;
in
''
<interface>
<name>${name}</name>
${optionalString (!config.enable) "<disable/>"}
${builtins.concatStringsSep "" units}
</interface>
'';
};
in
{
options = {
interfaces = mkOption { type = types.attrsOf (types.submodule interface); };
netconf.xmls.interfaces = mkOption {
type = types.str;
visible = false;
readOnly = true;
};
};
config.netconf.xmls.interfaces = ''
<interfaces operation="replace">
${builtins.concatStringsSep "" (attrsets.mapAttrsToList (_: intf: intf.xml) config.interfaces)}
</interfaces>
'';
}

23
junos/protocols.nix Normal file
View file

@ -0,0 +1,23 @@
{ lib, config, ... }:
with lib;
{
options = {
protocols.rstp = mkOption { type = types.listOf types.str; };
netconf.xmls.protocols = mkOption {
type = types.str;
visible = false;
readOnly = true;
};
};
config.netconf.xmls.protocols =
let
rstps = map (intf: "<interface><name>${intf}</name></interface>") config.protocols.rstp;
in
''
<protocols>
<rstp operation="replace">
${concatStringsSep "" rstps}
</rstp>
</protocols>
'';
}

76
junos/vlans.nix Normal file
View file

@ -0,0 +1,76 @@
{ lib, config, ... }:
with lib;
let
vlan =
{ name, config, ... }:
{
options = {
id = mkOption {
type = types.nullOr types.ints.unsigned;
default = null;
};
id-list = mkOption {
type =
let
range_type =
{ config, ... }:
{
config.__toString = _: "${toString config.begin}-${toString config.end}";
options = {
begin = mkOption { type = types.ints.unsigned; };
end = mkOption { type = types.ints.unsigned; };
__toString = mkOption {
visible = false;
internal = true;
readOnly = true;
type = types.unspecified;
};
};
};
in
types.listOf (types.either types.ints.unsigned (types.submodule range_type));
default = [ ];
};
l3-interface = mkOption {
type = types.nullOr types.str;
default = null;
};
xml = mkOption {
type = types.str;
readOnly = true;
visible = false;
};
};
config.xml =
let
id = optionalString (!isNull config.id) "<vlan-id>${toString config.id}</vlan-id>";
id-list = concatStringsSep "" (
map (vlan: "<vlan-id-list>${toString vlan}</vlan-id-list>") config.id-list
);
l3-intf = optionalString (
!isNull config.l3-interface
) "<l3-interface>${config.l3-interface}</l3-interface>";
in
''
<vlan>
<name>${name}</name>
${id}${id-list}${l3-intf}
</vlan>
'';
};
in
{
options = {
vlans = mkOption { type = types.attrsOf (types.submodule vlan); };
netconf.xmls.vlans = mkOption {
type = types.str;
visible = false;
readOnly = true;
};
};
config.netconf.xmls.vlans = ''
<vlans operation="replace">
${builtins.concatStringsSep "" (attrsets.mapAttrsToList (_: vlan: vlan.xml) config.vlans)}
</vlans>
'';
}

View file

@ -1,241 +0,0 @@
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}
'';
}

View file

@ -1,103 +1,203 @@
let let
vlansPlan = mgmt: { vlansPlan = {
"uplink-cri".ids = 223; "uplink-cri".id = 223;
"admin-core" = { "admin-core" = {
ids = 3000; id = 3000;
management = mgmt; l3-interface = "irb.0";
}; };
"admin-ap".ids = 3001; "admin-ap".id = 3001;
"users".ids = [ "users".id-list = [
{ {
begin = 3045; begin = 3045;
end = 4094; end = 4094;
} }
]; ];
"ap-staging".ids = 2000; "ap-staging".id = 2000;
}; };
AP = { AP = {
enable = true; enable = true;
interface-mode = "trunk"; unit."0".family.ethernet-switching = {
vlans = [ enable = true;
"users" interface-mode = "trunk";
"admin-ap" vlans = [
]; "users"
"admin-ap"
];
};
}; };
AP-staging = { AP-staging = {
enable = true; enable = true;
interface-mode = "access"; unit."0".family.ethernet-switching = {
vlans = [ "ap-staging" ]; enable = true;
interface-mode = "access";
vlans = [ "ap-staging" ];
};
}; };
in in
{ {
netcore01 = { netcore02 = {
deployement.targetHost = "jourdan01.dgn"; deployment.targetHost = "netcore02.dgn";
vlans = vlansPlan;
protocols.rstp = [
"ge-0/0/0"
"ge-0/0/1"
"ge-0/0/2"
"ge-0/0/3"
"ge-0/0/4"
"ge-0/0/5"
"ge-0/0/6"
"ge-0/0/7"
"ge-0/0/8"
"ge-0/0/9"
"ge-0/0/10"
"ge-0/0/11"
"ge-0/0/12"
"ge-0/0/13"
"ge-0/0/14"
"ge-0/0/15"
"ge-0/0/16"
"ge-0/0/17"
"ge-0/0/42"
"ge-0/0/43"
"ge-0/0/47"
vlans = vlansPlan "fd26:baf9:d250:8000::1001/64"; "xe-0/1/0"
"xe-0/1/1"
"ge-0/1/3"
];
interfaces = { interfaces = {
"ge-0/0/12" = AP; "ge-0/0/0" = AP-staging;
"ge-0/0/13" = AP; "ge-0/0/1" = AP-staging;
"ge-0/0/14" = AP; "ge-0/0/2" = AP-staging;
"ge-0/0/15" = AP; "ge-0/0/3" = AP-staging;
"ge-0/0/16" = AP; "ge-0/0/4" = AP-staging;
"ge-0/0/17" = AP; "ge-0/0/5" = AP-staging;
"ge-0/0/6" = AP-staging;
"ge-0/0/7" = AP-staging;
"ge-0/0/8" = AP-staging;
"ge-0/0/9" = AP-staging;
"ge-0/0/10" = AP-staging;
"ge-0/0/11" = AP-staging;
"ge-0/0/12" = AP-staging;
"ge-0/0/13" = AP-staging;
"ge-0/0/14" = AP-staging;
"ge-0/0/15" = AP-staging;
"ge-0/0/16" = AP-staging;
"ge-0/0/17" = AP-staging;
"ge-0/0/42" = { "ge-0/0/42" = {
enable = true; enable = true;
interface-mode = "access"; unit."0".family.ethernet-switching = {
vlans = [ "admin-core" ]; enable = true;
interface-mode = "trunk";
vlans = [ "all" ];
};
}; };
"ge-0/0/43" = AP-staging; "ge-0/0/43" = AP-staging;
"ge-0/0/47" = { "ge-0/0/47" = {
# ilo # ilo
enable = true; enable = true;
interface-mode = "access"; unit."0".family.ethernet-switching = {
vlans = [ "admin-core" ]; enable = true;
interface-mode = "access";
vlans = [ "admin-core" ];
};
}; };
"xe-0/1/0" = { "xe-0/1/0" = {
enable = true; enable = true;
interface-mode = "trunk"; unit."0".family.ethernet-switching = {
vlans = [ "all" ]; enable = true;
dhcp_trusted = true; interface-mode = "trunk";
vlans = [ "all" ];
};
}; };
"xe-0/1/1" = { "xe-0/1/1" = {
enable = true; enable = true;
interface-mode = "trunk"; unit."0".family.ethernet-switching = {
vlans = [ enable = true;
"users" interface-mode = "trunk";
"admin-ap" vlans = [
"admin-core" "users"
]; "admin-ap"
"admin-core"
];
};
}; };
"ge-0/1/3" = { "ge-0/1/3" = {
enable = true; enable = true;
interface-mode = "trunk"; unit."0".family.ethernet-switching = {
vlans = [ "uplink-cri" ]; enable = true;
interface-mode = "trunk";
vlans = [ "uplink-cri" ];
};
}; };
"me0" = { "me0" = {
enable = true; enable = true;
management = "192.168.42.6/24"; unit."0".family.inet = {
enable = true;
address = [ "192.168.42.6/24" ];
};
};
"irb" = {
enable = true;
unit."0".family.inet6 = {
enable = true;
address = [ "fd26:baf9:d250:8000::1001/64" ];
};
}; };
}; };
}; };
netaccess01 = { netaccess01 = {
deployement.targetHost = "root@192.168.42.6"; deployment.targetHost = "netaccess01.dgn";
vlans = vlansPlan "fd26:baf9:d250:8000::2001/64"; vlans = vlansPlan;
protocols.rstp = [
"ge-0/0/0"
"ge-0/0/1"
"ge-0/0/2"
"ge-0/0/3"
"ge-0/0/4"
"ge-0/0/5"
"xe-0/1/0"
];
interfaces = { interfaces = {
"ge-0/0/0" = AP-staging;
"ge-0/0/1" = AP-staging;
"ge-0/0/2" = AP-staging;
"ge-0/0/3" = AP-staging;
"ge-0/0/4" = AP-staging;
"ge-0/0/5" = AP-staging;
"xe-0/1/0" = { "xe-0/1/0" = {
enable = true; enable = true;
interface-mode = "trunk"; unit."0".family.ethernet-switching = {
vlans = [ "all" ]; enable = true;
dhcp_trusted = true; interface-mode = "trunk";
vlans = [ "all" ];
};
}; };
"me0" = { "me0" = {
enable = true; enable = true;
management = "192.168.42.6/24"; unit."0".family.inet = {
enable = true;
address = [ "192.168.42.6/24" ];
};
};
"irb" = {
enable = true;
unit."0".family.inet6 = {
enable = true;
address = [ "fd26:baf9:d250:8000::2001/64" ];
};
}; };
}; };
}; };