pkgs/switches: switch from salt to nix

This commit is contained in:
Astro 2021-11-03 01:07:44 +01:00
parent f54fa47fd8
commit 3072e1e78c
13 changed files with 811 additions and 630 deletions

View File

@ -4,6 +4,16 @@ let
mainServers = [ "server1" "server2" ];
pillar = self.lib.saltPillarFor "*";
clusterServerNets = [
"mgmt" "pub" "core" "serv"
"c3d2" "cluster" "bmx" "priv23"
];
clusterServerInterfaces = builtins.foldl' (result: net:
result // {
"${net}".type = "bridge";
}
) {} clusterServerNets;
renameAttr = from: to: attrset:
builtins.foldl' (result: name:
if name == from
@ -317,6 +327,26 @@ in
proto = "tcp";
sourcePort = port;
}) [ 25 465 587 110 143 993 995 ];
server3.interfaces = clusterServerInterfaces;
server5.interfaces = clusterServerInterfaces;
server6.interfaces = clusterServerInterfaces;
server7.interfaces = clusterServerInterfaces;
server8.interfaces = clusterServerInterfaces;
server9.interfaces = clusterServerInterfaces;
ap-test1.interfaces = {
mgmt.type = "phys";
pub.type = "bridge";
c3d2.type = "bridge";
bmx.type = "bridge";
};
ap-test2.interfaces = {
mgmt.type = "phys";
pub.type = "bridge";
c3d2.type = "bridge";
bmx.type = "bridge";
};
}
# host priv*-gw settings
@ -344,18 +374,47 @@ in
builtins.foldl' (result: hosts: result // hosts) {} (builtins.attrValues pillar.hosts-inet6)
)
)
);
) // builtins.foldl' (result: container:
result // builtins.mapAttrs (net: interface: {
type = "bridge";
}) container.interfaces
) {} (builtins.attrValues pillar.containers);
};
}) {} mainServers)
(builtins.mapAttrs (_: switch: {
inherit (switch) model location password;
role = "switch";
links = builtins.mapAttrs (_: { ports, group ? null, ... }: {
group = if group != null
then toString group
else null;
ports = map toString (
if builtins.isList ports
then ports
else [ ports ]
);
}) switch.ports;
}) pillar.switches)
(builtins.mapAttrs (_: ap: {
inherit (ap) model location password;
role = "ap";
interfaces = builtins.foldl' (interfaces: net: interfaces // {
"${net}" = {
type = "bridge";
};
}) {
mgmt = {
type = "phys";
gw4 = "mgmt-gw";
gw6 = "mgmt-gw";
};
} (
builtins.concatMap ({ ssids, ... }:
map ({ net, ... }: net) (builtins.attrValues ssids)
) (builtins.attrValues ap.radios)
);
}) pillar.cpe)
(builtins.mapAttrs (name: container:
@ -395,7 +454,7 @@ in
in lib.optionalAttrs (ctPillar ? ospf && ospfConf ? stubnets-inet) {
stubNets4 = ospfConf.stubnets-inet;
} // lib.optionalAttrs (ctPillar ? ospf && ospfConf ? stubnets-inet6) {
stubNets6 = ospfConf.stubnets-inet6;
stubNets6 = ospfConf.stubnets-inet6;
};
bgp =

View File

@ -2,6 +2,79 @@
with lib;
let
getLinkNets = link:
let
hostConfig = config.site.hosts.${link};
sort = builtins.sort (net1: net2:
config.site.net.${net1}.vlan < config.site.net.${net2}.vlan
);
in
if config.site.hosts ? ${link}
then
if builtins.elem hostConfig.role [ "container" "server" "ap" ]
then sort (builtins.attrNames hostConfig.interfaces)
else if hostConfig.role == "switch"
then sort (getSwitchNets link)
else if hostConfig.role == "client"
then if hostConfig.interfaces == {}
then builtins.trace "No known networks implemented for client \"${link}\"" []
else sort (builtins.attrNames hostConfig.interfaces)
else throw "getHostNets not implemented for role \"${hostConfig.role}\""
else if config.site.net ? ${link}
then [ link ]
else builtins.trace "Don't know what nets to configure for link to \"${link}\"" [];
# breaks the getLinkNets recursion for switches,
# requiring any net to be used by at >1 links
getSwitchNets = hostName:
let
linksNets = builtins.mapAttrs (link: _:
unique (
getSwitchNets' { "${hostName}" = true; } [ link ]
)
) config.site.hosts.${hostName}.links;
allNets = unique (
builtins.concatLists (builtins.attrValues linksNets)
);
netLinkCount = net:
builtins.foldl' (netLinkCount: nets:
if builtins.elem net nets
then netLinkCount + 1
else netLinkCount
) 0 (builtins.attrValues linksNets);
nets = builtins.filter (net:
netLinkCount net > 1
) allNets;
in nets; #builtins.trace "getSwitchNets ${hostName} = ${concatStringsSep "," nets}" nets;
getSwitchNets' = seen: links:
if links == []
then []
else
let
link = builtins.head links;
seen' = seen // {
"${link}" = true;
};
links' = builtins.tail links;
in
if config.site.hosts ? ${link} &&
config.site.hosts.${link}.role == "switch"
then getSwitchNets' seen' (
links' ++ (builtins.attrNames config.site.hosts.${link}.links)
)
else getLinkNets link ++
getSwitchNets' seen' (
builtins.filter (link: ! seen' ? ${link})
links'
);
dhcpOpts = {
start = mkOption {
description = "First IP address in pool";
@ -136,7 +209,7 @@ let
description = "Static MAC address";
};
type = mkOption {
type = types.enum [ "phys" "veth" "pppoe" ];
type = types.enum [ "phys" "veth" "pppoe" "bridge" ];
description = ''
veth: Virtual ethernet to be attached to a bridge.
@ -307,6 +380,11 @@ let
type = types.bool;
default = false;
};
links = mkOption {
description = "Which port is connected to what other device? Keys are either network names or known hostnames.";
default = {};
type = with types; attrsOf (submodule linkOpts);
};
};
};
@ -325,6 +403,32 @@ let
default = {};
};
};
linkOpts = { name, ... }: {
options = {
ports = mkOption {
type = with types; listOf str;
description = "Port names";
};
group = mkOption {
type = with types; nullOr str;
default = null;
description = "Link aggregation group with a fixed number";
};
nets = mkOption {
type = with types; listOf str;
description = "Set for clients, automatically generated for others";
default = getLinkNets name;
};
vlans = mkOption {
type = with types; listOf int;
description = "Automatically generated, do not set";
default = map (net:
config.site.net.${net}.vlan
) (getLinkNets name);
};
};
};
in
{
options.site = {

View File

@ -1,18 +1,20 @@
#!/usr/bin/env bash
{% macro uci_network_mgmt(ifname) -%}
set network.mgmt=interface
set network.mgmt.ifname={{ ifname }}
set network.mgmt.proto=static
set network.mgmt.ipaddr={{ pillar['hosts-inet']['mgmt'][hostname] }}
set network.mgmt.netmask=255.255.255.0
set network.mgmt.gateway={{ pillar['hosts-inet']['mgmt']['mgmt-gw'] }}
set network.mgmt.ip6addr={{ pillar['hosts-inet6']['dn42']['mgmt'][hostname] }}/64
set network.mgmt.ip6gw={{ pillar['hosts-inet6']['dn42']['mgmt']['mgmt-gw'] }}
delete network.mgmt.dns
add_list network.mgmt.dns={{ pillar['hosts-inet']['serv']['dnscache'] }}
add_list network.mgmt.dns={{ pillar['hosts-inet6']['dn42']['serv']['dnscache'] }}
{%- endmacro %}
{ pkgs, hostName, config, hostConfig, ... }:
let
uciNetworkMgmt = ifname: ''
set network.mgmt=interface
set network.mgmt.ifname={{ ifname }}
set network.mgmt.proto=static
set network.mgmt.ipaddr={{ pillar['hosts-inet']['mgmt'][hostName] }}
set network.mgmt.netmask=255.255.255.0
set network.mgmt.gateway={{ pillar['hosts-inet']['mgmt']['mgmt-gw'] }}
set network.mgmt.ip6addr={{ pillar['hosts-inet6']['dn42']['mgmt'][hostName] }}/64
set network.mgmt.ip6gw={{ pillar['hosts-inet6']['dn42']['mgmt']['mgmt-gw'] }}
delete network.mgmt.dns
add_list network.mgmt.dns={{ pillar['hosts-inet']['serv']['dnscache'] }}
add_list network.mgmt.dns={{ pillar['hosts-inet6']['dn42']['serv']['dnscache'] }}
'';
in ''
#! ${pkgs.runtimeShell} -e
{%- if conf.get('firstboot') %}
ssh-keygen -R 192.168.1.1
@ -20,7 +22,7 @@ ssh-keygen -R 192.168.1.1
ssh root@192.168.1.1 \
"ash -e -x" <<__SSH__
{%- else %}
ssh root@{{ pillar['hosts-inet']['mgmt'][hostname] }} \
ssh root@{{ pillar['hosts-inet']['mgmt'][hostName] }} \
"ash -e -x" <<__SSH__
{%- endif %}
@ -32,7 +34,7 @@ echo "{{ pillar['ssh']['pubkey'] }}" > /etc/dropbear/authorized_keys
# System configuration
uci batch <<__UCI__
set system.@system[0].hostname={{ hostname }}
set system.@system[0].hostName={{ hostName }}
set dhcp.@dnsmasq[0].enabled=0
set system.@system[0].log_ip={{ pillar['hosts-inet']['mgmt']['logging'] }}
set system.@system[0].log_proto=udp
@ -78,7 +80,7 @@ set network.@switch_vlan[{{ switchnum }}].ports='0t 1t'
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
@ -112,7 +114,7 @@ set network.@switch_vlan[{{ switchnum }}].ports='5t 6t'
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
@ -150,7 +152,7 @@ set network.@switch_vlan[{{ switchnum }}].ports='1t 6t'
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
@ -187,7 +189,7 @@ set network.@switch_vlan[{{ switchnum }}].ports='0t 1t'
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
@ -220,7 +222,7 @@ set network.@switch_vlan[{{ switchnum }}].ports='0t 5t'
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
@ -235,7 +237,7 @@ set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=0
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
@ -260,7 +262,7 @@ set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=0
{{ uci_network_mgmt('eth1.1') }}
${uciNetworkMgmt "eth1.1"}
{%- for net in bridges.keys() %}
@ -280,7 +282,7 @@ set network.{{ net }}.ifname='{{ ' '.join(ports) }}'
{%- elif conf['model'] == 'TL-WA901NDv3' or conf['model'] == 'Ubnt-UniFi-AP-AC-LR' %}
{# Only eth0 exists, no switch #}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
@ -294,7 +296,7 @@ set network.{{ net }}.ifname='{{ 'eth0.' ~ pillar['vlans'][net] }}'
{%- elif conf['model'] == 'Ubnt-UAP-nanoHD' %}
{# no switch, eth0 exists but is not usable, using "lan" instead #}
{{ uci_network_mgmt('lan.1') }}
${uciNetworkMgmt "lan.1"}
{%- for net in bridges.keys() %}
@ -332,7 +334,7 @@ set network.@switch_vlan[{{ switchnum }}].ports='4t 6t'
set network.@switch_vlan[{{ switchnum }}].comment='{{ net }}'
{%- endfor %}
{{ uci_network_mgmt('eth0.1') }}
${uciNetworkMgmt "eth0.1"}
{%- for net in bridges.keys() %}
set network.{{ net }}=interface
@ -348,7 +350,7 @@ set network.@switch[0].reset=1
set network.@switch[0].enable=1
set network.@switch[0].enable_vlan=0
{{ uci_network_mgmt('eth1.1') }}
${uciNetworkMgmt "eth1.1"}
{%- for net in bridges.keys() %}
@ -401,71 +403,71 @@ set wireless.wifi{{ index.iface }}=wifi-iface
{%- set ifsuffix = '-eap' %}
{%- endif %}
{%- else %}
{%- set ifsuffix = '' %}
{%- set ifsuffix = "" %}
{%- endif %}
set wireless.wifi{{ index.iface }}.ifname={{ ifprefix }}{{ ssidconf['net'] }}{{ ifsuffix }}
set wireless.wifi{{ index.iface }}.device=radio{{ index.radio }}
set wireless.wifi{{ index.iface }}.ssid='{{ ssid }}'
set wireless.wifi{{ index.iface }}.mode=ap
set wireless.wifi{{ index.iface }}.network={{ ssidconf['net'] }}
{%- if ssidconf.get('psk') %}
set wireless.wifi{{ index.iface }}.encryption=psk2
set wireless.wifi{{ index.iface }}.key='{{ ssidconf['psk'] }}'
{%- elif ssidconf.get('wpa-eap') %}
set wireless.wifi{{ index.iface }}.encryption=wpa2
set wireless.wifi{{ index.iface }}.server='{{ ssidconf['wpa-eap']['server'] }}'
set wireless.wifi{{ index.iface }}.port='{{ ssidconf['wpa-eap']['port'] }}'
set wireless.wifi{{ index.iface }}.auth_secret='{{ ssidconf['wpa-eap']['secret'] }}'
{%- else %}
set wireless.wifi{{ index.iface }}.encryption=none
delete wireless.wifi{{ index.iface }}.key
{%- endif %}
set wireless.wifi{{ index.iface }}.mcast_rate=18000
set wireless.wifi{{ index.iface }}.ifname={{ ifprefix }}{{ ssidconf['net'] }}{{ ifsuffix }}
set wireless.wifi{{ index.iface }}.device=radio{{ index.radio }}
set wireless.wifi{{ index.iface }}.ssid='{{ ssid }}'
set wireless.wifi{{ index.iface }}.mode=ap
set wireless.wifi{{ index.iface }}.network={{ ssidconf['net'] }}
{%- if ssidconf.get('psk') %}
set wireless.wifi{{ index.iface }}.encryption=psk2
set wireless.wifi{{ index.iface }}.key='{{ ssidconf['psk'] }}'
{%- elif ssidconf.get('wpa-eap') %}
set wireless.wifi{{ index.iface }}.encryption=wpa2
set wireless.wifi{{ index.iface }}.server='{{ ssidconf['wpa-eap']['server'] }}'
set wireless.wifi{{ index.iface }}.port='{{ ssidconf['wpa-eap']['port'] }}'
set wireless.wifi{{ index.iface }}.auth_secret='{{ ssidconf['wpa-eap']['secret'] }}'
{%- else %}
set wireless.wifi{{ index.iface }}.encryption=none
delete wireless.wifi{{ index.iface }}.key
{%- endif %}
set wireless.wifi{{ index.iface }}.mcast_rate=18000
{%- set x = index.update({ 'iface': index.iface + 1 }) %}
{%- endfor %}
{%- set x = index.update({ 'radio': index.radio + 1 }) %}
{%- endfor %}
{%- set x = index.update({ 'iface': index.iface + 1 }) %}
{%- endfor %}
{%- set x = index.update({ 'radio': index.radio + 1 }) %}
{%- endfor %}
commit
__UCI__
commit
__UCI__
# Cronjob that makes sure WiFi is only visible when server with all
# the gateways is reachable
cat >/etc/crontabs/root <<__CRON__
* * * * * /usr/sbin/wifi-on-link.sh
__CRON__
cat >/usr/sbin/wifi-on-link.sh <<__SH__
#!/bin/sh
# Cronjob that makes sure WiFi is only visible when server with all
# the gateways is reachable
cat >/etc/crontabs/root <<__CRON__
* * * * * /usr/sbin/wifi-on-link.sh
__CRON__
cat >/usr/sbin/wifi-on-link.sh <<__SH__
#!/bin/sh
if (ping -c 1 -W 3 {{ pillar['hosts-inet']['mgmt']['mgmt-gw'] }}) ; then
REACHABLE=y
else
REACHABLE=n
fi
if (ping -c 1 -W 3 {{ pillar['hosts-inet']['mgmt']['mgmt-gw'] }}) ; then
REACHABLE=y
else
REACHABLE=n
fi
if [ "\\\$(cat /sys/class/net/wlan2-pub/operstate)" == "up" ] ; then
UP=y
else
UP=n
fi
if [ "\\\$(cat /sys/class/net/wlan2-pub/operstate)" == "up" ] ; then
UP=y
else
UP=n
fi
{%- if conf.get("error-led") %}
ERROR_LED=/sys/class/leds/{{ conf["error-led"] }}/brightness
[ \\\$REACHABLE = y ] && echo 0 > \\\$ERROR_LED
[ \\\$REACHABLE = n ] && echo 1 > \\\$ERROR_LED
{%- endif %}
{%- if conf.get("error-led") %}
ERROR_LED=/sys/class/leds/{{ conf["error-led"] }}/brightness
[ \\\$REACHABLE = y ] && echo 0 > \\\$ERROR_LED
[ \\\$REACHABLE = n ] && echo 1 > \\\$ERROR_LED
{%- endif %}
[ \\\$REACHABLE = y ] && [ \\\$UP = n ] && wifi up
[ \\\$REACHABLE = n ] && [ \\\$UP = y ] && wifi down
[ \\\$REACHABLE = y ] && [ \\\$UP = n ] && wifi up
[ \\\$REACHABLE = n ] && [ \\\$UP = y ] && wifi down
exit 0
__SH__
chmod a+rx /usr/sbin/wifi-on-link.sh
/etc/init.d/cron restart
exit 0
__SH__
chmod a+rx /usr/sbin/wifi-on-link.sh
/etc/init.d/cron restart
for svc in dnsmasq uhttpd ; do
rm /etc/rc.d/*\$svc
for svc in dnsmasq uhttpd ; do
rm /etc/rc.d/*\$svc
/etc/init.d/\$svc stop
done
@ -476,4 +478,7 @@ reboot
__SSH__
echo "Base configuration done \\o/"
echo "Later run: `dirname $0`/ap_install_collectd.sh {{ pillar['hosts-inet']['mgmt'][hostname] }}"
echo "Later run: `dirname $0`/ap_install_collectd.sh {{ pillar['hosts-inet']['mgmt'][hostName] }}"
''

View File

@ -4,43 +4,17 @@ let
pkgs = nixpkgs.legacyPackages.${system};
config = self.lib.config;
templates = role: {
ap = _: ./ap.sh;
switch = model: ../../salt/switches + "/${model}.expect";
}.${role};
replaceNetmasks = template:
builtins.toFile (builtins.baseNameOf template) (
builtins.replaceStrings [''{%- import_yaml "netmasks.yaml" as netmasks -%}''] [""] (
builtins.readFile template
)
);
expandTemplate = name: template: data:
self.lib.expandSaltTemplate name (replaceNetmasks template) data;
wrapNixShell = script:
pkgs.runCommand (builtins.baseNameOf script) {
src = script;
} ''
(
echo '#! /usr/bin/env nix-shell'
echo '#! nix-shell -i "expect -f" -p expect telnet'
cat $src
) > $out
chmod a+x $out
'';
device-scripts =
builtins.mapAttrs (hostname: { role, model, ... }:
wrapNixShell (
expandTemplate "${hostname}.sh" (templates role model) ({
inherit hostname;
pillar = config.salt-pillar;
netmasks = self.lib.netmasks;
logging = config.salt-pillar.hosts-inet.mgmt.logging;
} // optionalAttrs (config.salt-pillar.switches ? ${hostname}) {
switch = config.salt-pillar.switches.${hostname};
} // optionalAttrs (config.salt-pillar.cpe ? ${hostname}) {
conf = config.salt-pillar.cpe.${hostname};
})
builtins.mapAttrs (hostName: hostConfig@{ role, model, ... }:
pkgs.writeScript "${hostName}.sh" (
let
args = {
inherit pkgs self hostName config hostConfig;
};
in {
ap = import ./ap.nix args;
switch = import (./switches + "/${model}.nix") args;
}.${role}
)
) (
filterAttrs (_: { role, ... }:
@ -58,8 +32,8 @@ let
chmod a+x $out/bin/ap_install_collectd.sh
'' +
builtins.concatStringsSep "\n" (
map (hostname:
"ln -s ${device-scripts.${hostname}} $out/bin/${hostname}.sh"
map (hostName:
"ln -s ${device-scripts.${hostName}} $out/bin/${hostName}.sh"
) (builtins.attrNames device-scripts)
)
);

View File

@ -0,0 +1,160 @@
# http://h20628.www2.hp.com/km-ext/kmcsdirect/emr_na-c02586144-1.pdf
{ self, pkgs, hostName, config, hostConfig, ... }:
with pkgs;
with lib;
''
#! ${expect}/bin/expect -f
spawn ${inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Password:"
send "${hostConfig.password}\r"
expect ">"
send "system-view\r"
expect "]"
send "sysname ${hostName}\r"
expect "]"
send "user-interface vty 0 4\r"
expect "ui-vty0-4]"
send "screen-length 0\r"
expect "ui-vty0-4]"
send "user privilege level 3\r"
expect "ui-vty0-4]"
send "set authentication password simple ${hostConfig.password}\r"
expect "ui-vty0-4]"
send "quit\r"
expect "${hostName}]"
send "local-user admin\r"
expect -- "-luser-admin]"
send "password simple ${hostConfig.password}\r"
expect -- "-luser-admin]"
send "quit\r"
expect "${hostName}]"
# Enable logging
send "info-center enable\r"
expect "]"
send "info-center loghost ${config.site.net.mgmt.hosts4.logging} channel loghost facility local6\r"
expect "]"
send "info-center source default channel loghost log level informational\r"
expect "]"
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in ''
send "vlan ${vlan}\r"
expect -- "-vlan${vlan}]"
send "name ${net}\r"
expect -- "-vlan${vlan}]"
${optionalString (net == "mgmt") ''
# Actually only used for mgmt_vlan, switches are not routers
send "interface Vlan-interface ${vlan}\r"
expect "]"
${optionalString (hosts4 ? ${hostName}) ''
send "ip address ${hostAddr4} ${netmask}\r"
expect "]"
''}
''}
send "quit\r"
expect "${hostName}]"
'') (builtins.attrNames config.site.net)
}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
isBond = isTrunk && builtins.length linkConfig.ports > 1;
in
if isTrunk
then ''
${optionalString isBond ''
send "link-aggregation group ${linkConfig.group} mode static\r"
expect {
"This aggregation will be modified to static mode. Continue ?" {
send "Y\r"
}
"]" {}
}
send "link-aggregation group ${linkConfig.group} description ${name}\r"
expect "]"
''}
${concatMapStrings (port: ''
send "interface ${port}\r"
expect "]"
send "undo stp edged-port\r"
expect "]"
${if isBond
then ''
send "lacp enable\r"
expect "]"
send "undo port link-aggregation group\r"
expect "]"
send "port link-aggregation group ${linkConfig.group}\r"
'' else ''
send "undo lacp enable\r"
''}
expect "]"
send "jumboframe enable\r"
expect "]"
send "port link-type trunk\r"
expect "]"
# Set dummy default vlan
send "port trunk pvid vlan 4094\r"
expect "]"
# Deconfigure all but mgmt vlan
send "undo port trunk permit vlan 2 to 4094\r"
expect "]"
${concatMapStrings (vlan: ''
send "port trunk permit vlan ${toString vlan}\r"
expect "]"
'') linkConfig.vlans}
send "quit\r"
expect "${hostName}]"
'') linkConfig.ports}
'' else
concatMapStrings (port: ''
send "interface ${port}\r"
expect "]"
send "undo port link-aggregation group\r"
expect "]"
send "port link-type access\r"
expect "]"
${if name == "mgmt"
then ''
send "undo port access vlan\r"
expect "]"
'' else ''
send "port access vlan ${toString netConfig.vlan}\r"
expect "]"
''}
send "quit\r"
expect "${hostName}]"
'') (linkConfig.ports)
) (builtins.attrNames hostConfig.links)}
send "save main\r"
expect "Y/N]"
send "YES\r"
expect "press the enter key):"
send "\r"
expect "]"
send "quit\r"
expect ">"
send "quit\r"
''

View File

@ -0,0 +1,141 @@
# http://ftp.hp.com/pub/networking/software/2600-2800-4100-6108-MgmtConfig-Oct2005-59906023.pdf
{ self, pkgs, hostName, config, hostConfig, ... }:
with pkgs;
with lib;
''
#! ${expect}/bin/expect -f
spawn ${inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Press any key to continue"
send "\r"
expect "assword: "
send "${hostConfig.password}\r"
expect "#"
send "configure terminal\r"
expect "(config)# "
send "hostname ${hostName}\r"
expect "(config)# "
send "snmp-server location \"${hostConfig.location}\"\r"
expect "(config)# "
send "snmp-server contact \"astro@spaceboyz.net\"\r"
expect "(config)# "
send "password manager\r"
expect "New password for Manager: "
send "${hostConfig.password}\r"
expect "Please retype new password for Manager: "
send "${hostConfig.password}\r"
expect "(config)# "
# TODO: ssh, password
# Enable Logging
send "logging ${config.site.net.mgmt.hosts4.logging}\r"
expect "(config)# "
send "logging facility local6\r"
expect "(config)# "
# todo ntp
# timesync sntp
# ip timep manual {#ntp#} interval 10
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in ''
send "vlan ${vlan}\r"
expect "(vlan-${vlan})#"
send "name ${hostName}\r"
expect "(vlan-${vlan})#"
send "jumbo\r"
expect "(vlan-${vlan})#"
# Actually only used for mgmt_vlan, switches are not routers
${optionalString (hosts4 ? ${hostName}) ''
send "ip address ${hostAddr4} ${netmask}\r"
expect "(vlan-${vlan})#"
''}
send "exit\r"
expect "(config)# "
${if net == "mgmt"
then ''
send "management-vlan ${vlan}\r"
expect "(config)# "
'' else ''
# If not mgmt, reset all VLAN mappings
send "no vlan ${vlan} tagged all\r"
expect "(config)# "
send "no vlan ${vlan} untagged all\r"
expect "(config)# "
''}
'') (builtins.attrNames config.site.net)
}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
isBond = isTrunk && builtins.length linkConfig.ports > 1;
ports = concatStringsSep "," linkConfig.ports;
in
if isTrunk && isBond
then ''
send "interface ${ports} lacp active\r"
expect "(config)# "
send "trunk ${ports} trk${linkConfig.group} lacp\r"
expect "(config)# "
${concatMapStrings (vlan: ''
send "vlan ${toString vlan} tagged trk${linkConfig.group}\r"
expect "(config)# "
'') linkConfig.vlans}
''
else if isTrunk
then ''
send "no trunk ${ports}\r"
expect "(config)# "
send "no interface ${ports} lacp\r"
expect "(config)# "
${concatMapStrings (vlan: ''
send "vlan ${toString vlan} tagged ${ports}\r"
expect "(config)# "
'') linkConfig.vlans}
''
else ''
send "no trunk ${ports}\r"
expect "(config)# "
send "vlan ${toString netConfig.vlan} untagged ${ports}\r"
expect "(config)# "
''
) (builtins.attrNames hostConfig.links)}
send "exit\r"
expect "${hostName}# "
send "write memory\r"
expect "${hostName}# "
send "exit\r"
expect "${hostName}> "
send "exit\r"
expect "Do you want to log out "
expect "y/n]? "
send "y"
''

View File

@ -0,0 +1,131 @@
# http://static.tp-link.com/res/down/doc/TL-SG3210(UN)_V2.0_CLI_.pdf
{ self, pkgs, hostName, config, hostConfig, ... }:
with pkgs;
with lib;
''
#! ${pkgs.expect}/bin/expect -f
spawn ${pkgs.inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Password:"
send "${hostConfig.password}\r"
expect ">"
send "\r"
expect ">"
send "enable\r"
expect "Password:"
send "${hostConfig.password}\r"
expect "#"
send "configure\r"
expect "(config)#"
send "enable secret 0 ${hostConfig.password}\r"
expect "(config)#"
#send "enable password 0 ${hostConfig.password}\r"
#expect "(config)#"
send "service password-encryption\r"
expect "(config)#"
send "user name admin privilege admin secret 0 ${hostConfig.password}\r"
expect "(config)#"
send "hostname \"${hostName}\"\r"
expect "(config)#"
send "location \"${hostConfig.location}\"\r"
expect "(config)#"
send "logging host index 1 ${config.site.net.mgmt.hosts4.logging} 6\r"
expect "(config)#"
send "ip management-vlan ${toString config.site.net.mgmt.vlan}\r"
expect "(config)#"
send "ip ssh server\r"
expect "(config)#"
send "telnet enable\r"
expect "(config)#"
send "line vty 0 15\r"
expect "(config-line)#"
send "password 0 ${hostConfig.password}\r"
expect "(config-line)#"
send "exit\r"
expect "(config)#"
${concatMapStrings (net:
let
netConfig = config.site.net.${net};
vlan = toString netConfig.vlan;
inherit (config.site.net.${net}) hosts4;
hostAddr4 = hosts4.${hostName};
prefixLength = elemAt (
builtins.split "/" netConfig.subnet4
) 2;
netmask = self.lib.netmasks.${prefixLength};
in ''
${optionalString (net != "mgmt") ''
send "vlan ${vlan}\r"
expect "(config-vlan)#"
send "name \"${net}\"\r"
expect "(config-vlan)#"
send "exit\r"
expect "(config)#"
''}
# Actually only used for mgmt_vlan, switches are not routers
send "interface vlan ${vlan}\r"
expect "(config-if)#"
${optionalString (hosts4 ? ${hostName}) ''
send "ip address ${hostAddr4} ${netmask}\r"
expect "(config-if)#"
''}
send "exit\r"
expect "(config)#"
''
) (builtins.attrNames config.site.net)}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
isBond = isTrunk && builtins.length linkConfig.ports > 1;
ports = concatStringsSep "," linkConfig.ports;
in
if isTrunk
then ''
send "interface range gigabitEthernet 1/0/${ports}\r"
expect "(config-if-range)#"
send "switchport mode trunk\r"
expect "(config-if-range)#"
${if (builtins.length linkConfig.ports > 1)
then ''
send "channel-group ${linkConfig.group} mode active\r"
expect "(config-if-range)#"
#send "port-channel load-balance src-dst-ip\r"
#expect "(config-if-range)#"
'' else ''
send "no channel-group\r"
expect "(config-if-range)#"
''}
send "switchport trunk allowed vlan ${concatStringsSep "," (map toString linkConfig.vlans)}\r"
expect "(config-if-range)#"
send "exit\r"
expect "(config)#"
'' else ''
send "interface range gigabitEthernet 1/0/${ports}\r"
expect "(config-if-range)#"
send "switchport mode access\r"
expect "(config-if-range)#"
send "switchport access vlan ${toString netConfig.vlan}\r"
expect "(config-if-range)#"
send "exit\r"
expect "(config)#"
''
) (builtins.attrNames hostConfig.links)}
send "exit\r"
expect "#"
send "copy running-config startup-config\r"
expect "#"
send "exit\r"
expect ">"
send "exit\r"
''

View File

@ -0,0 +1,105 @@
# https://www.crc.id.au/real-console-on-linksys-srw2024-switch/
{ self, pkgs, hostName, config, hostConfig, ... }:
with pkgs;
with lib;
''
#! ${expect}/bin/expect -f
spawn ${inetutils}/bin/telnet ${config.site.net.mgmt.hosts4.${hostName}}
expect "Password:"
send "admin\t${hostConfig.password}\r"
# ^z
send "\x1A"
expect ">"
send "lcli\r"
expect "User Name:"
send "admin\r"
expect "Password:"
send "${hostConfig.password}\r"
expect "# "
send "configure\r"
expect "(config)# "
send "hostname ${hostName}\r"
expect "(config)# "
send "management vlan 4094\r"
expect "(config)# "
send "vlan database\r"
expect "(config-vlan)# "
${concatMapStrings (net: ''
send "vlan ${toString config.site.net.${net}.vlan}\r"
expect "(config-vlan)#"
'') (builtins.attrNames config.site.net)}
send "exit\r"
expect "(config)#"
${concatMapStrings (net: ''
send "interface vlan ${toString config.site.net.${net}.vlan}\r"
expect "(config-if)#"
send "name ${net}\r"
expect "(config-if)#"
send "exit\r"
expect "(config)#"
'') (builtins.attrNames config.site.net)}
${concatMapStrings (name:
let
linkConfig = hostConfig.links.${name};
isAccess = config.site.net ? ${name};
netConfig = config.site.net.${name};
isTrunk = !isAccess;
isBond = isTrunk && builtins.length linkConfig.ports > 1;
ports = concatStringsSep "," linkConfig.ports;
in
if isTrunk && isBond
then ''
send "interface range ethernet ${ports}\r"
expect "(config-if)#"
send "switchport trunk allowed vlan remove all\r"
expect "(config-if)#"
send "channel-group ${linkConfig.group} mode auto\r"
expect "(config-if)#"
send "interface port-channel ${linkConfig.group}\r"
expect "(config-if)#"
send "exit\r"
send "interface port-channel ${linkConfig.group}\r"
expect "(config-if)#"
send "switchport mode trunk\r"
expect "(config-if)#"
send "switchport trunk allowed vlan add ${concatStringsSep "," (map toString linkConfig.vlans)}\r"
expect "(config-if)#"
send "exit\r"
expect "(config)#"
''
else if isTrunk
then concatMapStrings (port: ''
send "interface ethernet ${port}\r"
expect "(config-if)#"
send "no channel-group\r"
expect "(config-if)#"
send "switchport mode trunk\r"
expect "(config-if)#"
send "switchport trunk allowed vlan add ${concatStringsSep "," (map toString linkConfig.vlans)}\r"
expect "(config-if)#"
send "exit\r"
expect "(config)#"
'') linkConfig.ports
else concatMapStrings (port: ''
send "interface ethernet ${port}\r"
expect "(config-if)#"
send "no channel-group\r"
expect "(config-if)#"
send "switchport mode access\r"
expect "(config-if)#"
send "switchport access vlan ${toString netConfig.vlan}\r"
expect "(config-if)#"
send "exit\r"
expect "(config)#"
'') linkConfig.ports
) (builtins.attrNames hostConfig.links)}
''

View File

@ -220,7 +220,7 @@ switches:
- g39
- g40
vlans: *server1_vlans
c3d2-server5:
server5:
mode: bond
group: 6
ports:
@ -237,7 +237,7 @@ switches:
- cluster
- bmx
- priv23
c3d2-server6:
server6:
mode: bond
group: 8
ports:
@ -246,7 +246,7 @@ switches:
- g7
- g8
vlans: *server_vlans
c3d2-server7:
server7:
mode: bond
group: 7
ports:
@ -255,7 +255,7 @@ switches:
- g14
- g15
vlans: *server_vlans
c3d2-server8:
server8:
mode: bond
group: 5
ports:
@ -421,14 +421,14 @@ switches:
- mgmt
- pub
- priv17
c3d2-server3:
server3:
mode: bond
group: 1
ports:
- GigabitEthernet1/0/30
- GigabitEthernet1/0/31
vlans: *server_vlans
c3d2-server9:
server9:
mode: bond
group: 3
ports:
@ -734,6 +734,7 @@ switches:
switch-b1:
mode: bond
ports: 1
group: 1
vlans:
- mgmt
- pub

View File

@ -1,151 +0,0 @@
{# http://h20628.www2.hp.com/km-ext/kmcsdirect/emr_na-c02586144-1.pdf #}
{%- macro increment(dct, key, inc=1) -%}
{% if dct.update({key: dct[key] + inc}) %} {% endif %}
{%- endmacro -%}
{%- import_yaml "netmasks.yaml" as netmasks -%}
#!/usr/bin/expect -f