diff options
Diffstat (limited to 'smoketest')
| -rw-r--r-- | smoketest/config-tests/dialup-router-medium-vpn | 5 | ||||
| -rw-r--r-- | smoketest/configs/basic-vyos | 18 | ||||
| -rw-r--r-- | smoketest/configs/dialup-router-wireguard-ipv6 | 1629 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_protocols_bgp.py | 15 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_protocols_segment_routing.py | 70 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_service_dhcp-server.py | 455 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_service_dhcpv6-server.py | 156 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_service_dns_dynamic.py | 41 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_service_https.py | 69 |
9 files changed, 2249 insertions, 209 deletions
diff --git a/smoketest/config-tests/dialup-router-medium-vpn b/smoketest/config-tests/dialup-router-medium-vpn index e10adbbc6..039a50594 100644 --- a/smoketest/config-tests/dialup-router-medium-vpn +++ b/smoketest/config-tests/dialup-router-medium-vpn @@ -257,7 +257,6 @@ set service dhcp-server shared-network-name LAN authoritative set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 default-router '192.168.0.1' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 domain-name 'vyos.net' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 domain-search 'vyos.net' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 enable-failover set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 lease '86400' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 name-server '192.168.0.1' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 range LANDynamic start '192.168.0.200' @@ -268,16 +267,12 @@ set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-map set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping IPTV mac-address '00:50:01:31:b5:f6' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping McPrintus ip-address '192.168.0.60' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping McPrintus mac-address '00:50:01:58:ac:95' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping McPrintus static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Mobile01 ip-address '192.168.0.109' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Mobile01 mac-address '00:50:01:bc:ac:51' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping Mobile01 static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera1 ip-address '192.168.0.11' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera1 mac-address '00:50:01:70:b9:4d' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera1 static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera2 ip-address '192.168.0.12' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera2 mac-address '00:50:01:70:b7:4f' -set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping camera2 static-mapping-parameters 'option domain-name-servers 192.168.0.6,192.168.0.17;' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping pearTV ip-address '192.168.0.101' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping pearTV mac-address '00:50:01:ba:62:79' set service dhcp-server shared-network-name LAN subnet 192.168.0.0/24 static-mapping sand ip-address '192.168.0.110' diff --git a/smoketest/configs/basic-vyos b/smoketest/configs/basic-vyos index 78dba3ee2..fca4964bf 100644 --- a/smoketest/configs/basic-vyos +++ b/smoketest/configs/basic-vyos @@ -1,6 +1,7 @@ interfaces { ethernet eth0 { address 192.168.0.1/24 + address fe88::1/56 duplex auto smp-affinity auto speed auto @@ -90,6 +91,23 @@ service { } } } + dhcpv6-server { + shared-network-name LAN6 { + subnet fe88::/56 { + address-range { + prefix fe88::/56 { + temporary + } + } + prefix-delegation { + start fe88:0000:0000:0001:: { + prefix-length 64 + stop fe88:0000:0000:0010:: + } + } + } + } + } dns { forwarding { allow-from 192.168.0.0/16 diff --git a/smoketest/configs/dialup-router-wireguard-ipv6 b/smoketest/configs/dialup-router-wireguard-ipv6 new file mode 100644 index 000000000..33afb9b04 --- /dev/null +++ b/smoketest/configs/dialup-router-wireguard-ipv6 @@ -0,0 +1,1629 @@ +firewall { + all-ping enable + broadcast-ping disable + config-trap disable + group { + address-group DMZ-WEBSERVER { + address 172.16.36.10 + address 172.16.36.40 + address 172.16.36.20 + } + address-group DMZ-RDP-SERVER { + address 172.16.33.40 + } + address-group DOMAIN-CONTROLLER { + address 172.16.100.10 + address 172.16.100.20 + address 172.16.110.30 + } + address-group VIDEO { + address 172.16.33.211 + address 172.16.33.212 + address 172.16.33.213 + address 172.16.33.214 + } + ipv6-network-group LOCAL-ADDRESSES { + network ff02::/64 + network fe80::/10 + } + network-group SSH-IN-ALLOW { + network 100.65.150.0/23 + network 100.64.69.205/32 + network 100.64.8.67/32 + network 100.64.55.1/32 + } + } + ipv6-name ALLOW-ALL-6 { + default-action accept + } + ipv6-name ALLOW-BASIC-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + protocol icmpv6 + } + } + ipv6-name ALLOW-ESTABLISHED-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + destination { + group { + network-group LOCAL-ADDRESSES + } + } + protocol icmpv6 + source { + address fe80::/10 + } + } + rule 20 { + action accept + icmpv6 { + type echo-request + } + protocol icmpv6 + } + rule 21 { + action accept + icmpv6 { + type destination-unreachable + } + protocol icmpv6 + } + rule 22 { + action accept + icmpv6 { + type packet-too-big + } + protocol icmpv6 + } + rule 23 { + action accept + icmpv6 { + type time-exceeded + } + protocol icmpv6 + } + rule 24 { + action accept + icmpv6 { + type parameter-problem + } + protocol icmpv6 + } + } + ipv6-name WAN-LOCAL-6 { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + state { + invalid enable + } + } + rule 10 { + action accept + destination { + address ff02::/64 + } + protocol icmpv6 + source { + address fe80::/10 + } + } + rule 50 { + action accept + destination { + address fe80::/10 + port 546 + } + protocol udp + source { + address fe80::/10 + port 547 + } + } + } + ipv6-receive-redirects disable + ipv6-src-route disable + ip-src-route disable + log-martians enable + name DMZ-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name DMZ-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + group { + address-group DOMAIN-CONTROLLER + } + port 123,389,636 + } + protocol tcp_udp + } + rule 300 { + action accept + destination { + group { + address-group DMZ-RDP-SERVER + } + port 3389 + } + protocol tcp_udp + source { + address 172.16.36.20 + } + } + } + name DMZ-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 50 { + action accept + destination { + address 172.16.254.30 + port 53 + } + protocol tcp_udp + } + rule 123 { + action accept + destination { + port 123 + } + protocol udp + } + } + name DMZ-WAN { + default-action accept + } + name GUEST-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name GUEST-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name GUEST-LOCAL { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + destination { + address 172.31.0.254 + port 53 + } + protocol tcp_udp + } + rule 11 { + action accept + destination { + port 67 + } + protocol udp + } + rule 15 { + action accept + destination { + address 172.31.0.254 + } + protocol icmp + } + rule 100 { + action accept + destination { + address 172.31.0.254 + port 80,443 + } + protocol tcp + } + } + name GUEST-WAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 25 { + action accept + destination { + port 25,587 + } + protocol tcp + } + rule 53 { + action accept + destination { + port 53 + } + protocol tcp_udp + } + rule 60 { + action accept + source { + address 172.31.0.200 + } + } + rule 80 { + action accept + source { + address 172.31.0.200 + } + } + rule 100 { + action accept + protocol icmp + } + rule 110 { + action accept + destination { + port 110,995 + } + protocol tcp + } + rule 123 { + action accept + destination { + port 123 + } + protocol udp + } + rule 143 { + action accept + destination { + port 143,993 + } + protocol tcp + } + rule 200 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 500 { + action accept + destination { + port 500,4500 + } + protocol udp + } + rule 600 { + action accept + destination { + port 5222-5224 + } + protocol tcp + } + rule 601 { + action accept + destination { + port 3478-3497,4500,16384-16387,16393-16402 + } + protocol udp + } + rule 1000 { + action accept + source { + address 172.31.0.184 + } + } + } + name LAN-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 22 { + action accept + destination { + port 22 + } + protocol tcp + } + rule 100 { + action accept + destination { + group { + address-group DMZ-WEBSERVER + } + port 22 + } + protocol tcp + } + } + name LAN-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + } + name LAN-LOCAL { + default-action accept + } + name LAN-WAN { + default-action accept + rule 90 { + action accept + destination { + address 100.65.150.0/23 + port 25 + } + protocol tcp_udp + source { + group { + address-group VIDEO + } + } + } + rule 100 { + action drop + source { + group { + address-group VIDEO + } + } + } + } + name LOCAL-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + address 172.16.36.40 + port 80,443 + } + protocol tcp + } + } + name LOCAL-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 5 { + action accept + protocol icmp + } + rule 300 { + action accept + destination { + port 1900 + } + protocol udp + } + } + name LOCAL-LAN { + default-action accept + } + name LOCAL-WAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 10 { + action accept + protocol icmp + } + rule 50 { + action accept + destination { + port 53 + } + protocol tcp_udp + } + rule 80 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 123 { + action accept + destination { + port 123 + } + protocol udp + } + rule 800 { + action accept + destination { + address 100.65.151.213 + } + protocol udp + } + rule 805 { + action accept + destination { + address 100.65.151.2 + } + protocol all + } + rule 1010 { + action accept + destination { + address 100.64.69.205 + port 7705 + } + protocol udp + source { + port 7705 + } + } + rule 1990 { + action accept + destination { + address 100.64.55.1 + port 10666 + } + protocol udp + } + rule 2000 { + action accept + destination { + address 100.64.39.249 + } + } + rule 10200 { + action accept + destination { + address 100.64.89.98 + port 10200 + } + protocol udp + source { + port 10200 + } + } + } + name WAN-DMZ { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 100 { + action accept + destination { + address 172.16.36.10 + port 80,443 + } + protocol tcp + } + } + name WAN-GUEST { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 1000 { + action accept + destination { + address 172.31.0.184 + } + } + rule 8000 { + action accept + destination { + address 172.31.0.200 + port 10000 + } + protocol udp + } + } + name WAN-LAN { + default-action drop + enable-default-log + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 1000 { + action accept + destination { + address 172.16.33.40 + port 3389 + } + protocol tcp + source { + group { + network-group SSH-IN-ALLOW + } + } + } + } + name WAN-LOCAL { + default-action drop + rule 1 { + action accept + state { + established enable + related enable + } + } + rule 2 { + action drop + log enable + state { + invalid enable + } + } + rule 22 { + action accept + destination { + port 22 + } + protocol tcp + source { + group { + network-group SSH-IN-ALLOW + } + } + } + rule 1990 { + action accept + destination { + port 10666 + } + protocol udp + source { + address 100.64.55.1 + } + } + rule 10000 { + action accept + destination { + port 80,443 + } + protocol tcp + } + rule 10100 { + action accept + destination { + port 10100 + } + protocol udp + source { + port 10100 + } + } + rule 10200 { + action accept + destination { + port 10200 + } + protocol udp + source { + address 100.64.89.98 + port 10200 + } + } + } + options { + interface pppoe0 { + adjust-mss 1452 + adjust-mss6 1432 + } + } + receive-redirects disable + send-redirects enable + source-validation disable + syn-cookies enable + twa-hazards-protection disable +} +interfaces { + dummy dum0 { + address 172.16.254.30/32 + } + ethernet eth0 { + duplex auto + offload { + gro + gso + sg + tso + } + ring-buffer { + rx 256 + tx 256 + } + speed auto + vif 5 { + address 172.16.37.254/24 + ip { + ospf { + authentication { + md5 { + key-id 10 { + md5-key ospf + } + } + } + dead-interval 40 + hello-interval 10 + priority 1 + retransmit-interval 5 + transmit-delay 1 + } + } + } + vif 10 { + address 172.16.33.254/24 + address 172.16.40.254/24 + } + vif 50 { + address 172.16.36.254/24 + } + } + ethernet eth1 { + duplex auto + offload { + gro + gso + sg + tso + } + speed auto + vif 20 { + address 172.31.0.254/24 + } + } + ethernet eth2 { + disable + duplex auto + offload { + gro + gso + sg + tso + } + speed auto + } + ethernet eth3 { + duplex auto + offload { + gro + gso + sg + tso + } + ring-buffer { + rx 256 + tx 256 + } + speed auto + vif 7 { + } + } + loopback lo { + address 172.16.254.30/32 + } + pppoe pppoe0 { + authentication { + password vyos + user vyos + } + default-route force + dhcpv6-options { + pd 0 { + interface eth0.10 { + address 1 + sla-id 10 + } + interface eth1.20 { + address 1 + sla-id 20 + } + length 56 + } + } + ipv6 { + address { + autoconf + } + } + no-peer-dns + source-interface eth3.7 + } + wireguard wg100 { + address 172.16.252.128/31 + mtu 1500 + peer HR6 { + address 100.65.151.213 + allowed-ips 0.0.0.0/0 + port 10100 + pubkey yLpi+UZuI019bmWH2h5fX3gStbpPPPLgEoYMyrdkOnQ= + } + port 10100 + } + wireguard wg200 { + address 172.16.252.130/31 + mtu 1500 + peer WH56 { + address 80.151.69.205 + allowed-ips 0.0.0.0/0 + port 10200 + pubkey XQbkj6vnKKBJfJQyThXysU0iGxCvEOEb31kpaZgkrD8= + } + port 10200 + } + wireguard wg666 { + address 172.29.0.1/31 + mtu 1500 + peer WH34 { + address 100.65.55.1 + allowed-ips 0.0.0.0/0 + port 10666 + pubkey yaTN4+xAafKM04D+Baeg5GWfbdaw35TE9HQivwRgAk0= + } + port 10666 + } +} +nat { + destination { + rule 8000 { + destination { + port 10000 + } + inbound-interface pppoe0 + protocol udp + translation { + address 172.31.0.200 + } + } + } + source { + rule 50 { + outbound-interface pppoe0 + source { + address 100.64.0.0/24 + } + translation { + address masquerade + } + } + rule 100 { + outbound-interface pppoe0 + source { + address 172.16.32.0/21 + } + translation { + address masquerade + } + } + rule 200 { + outbound-interface pppoe0 + source { + address 172.16.100.0/24 + } + translation { + address masquerade + } + } + rule 300 { + outbound-interface pppoe0 + source { + address 172.31.0.0/24 + } + translation { + address masquerade + } + } + rule 400 { + outbound-interface pppoe0 + source { + address 172.18.200.0/21 + } + translation { + address masquerade + } + } + rule 1000 { + destination { + address 192.168.189.0/24 + } + outbound-interface wg666 + source { + address 172.16.32.0/21 + } + translation { + address 172.29.0.1 + } + } + rule 1001 { + destination { + address 192.168.189.0/24 + } + outbound-interface wg666 + source { + address 172.16.100.0/24 + } + translation { + address 172.29.0.1 + } + } + } +} +policy { + route-map MAP-OSPF-CONNECTED { + rule 1 { + action deny + match { + interface eth1.20 + } + } + rule 20 { + action permit + match { + interface eth0.10 + } + } + rule 40 { + action permit + match { + interface eth0.50 + } + } + } +} +protocols { + bfd { + peer 172.16.252.129 { + } + peer 172.16.252.131 { + } + peer 172.18.254.201 { + } + } + bgp 64503 { + address-family { + ipv4-unicast { + network 172.16.32.0/21 { + } + network 172.16.100.0/24 { + } + network 172.16.252.128/31 { + } + network 172.16.252.130/31 { + } + network 172.16.254.30/32 { + } + network 172.18.0.0/16 { + } + } + } + neighbor 172.16.252.129 { + peer-group WIREGUARD + } + neighbor 172.16.252.131 { + peer-group WIREGUARD + } + neighbor 172.18.254.201 { + address-family { + ipv4-unicast { + nexthop-self { + } + } + } + bfd { + } + remote-as 64503 + update-source dum0 + } + parameters { + default { + no-ipv4-unicast + } + log-neighbor-changes + } + peer-group WIREGUARD { + address-family { + ipv4-unicast { + soft-reconfiguration { + inbound + } + } + } + bfd + remote-as external + } + timers { + holdtime 30 + keepalive 10 + } + } + ospf { + area 0 { + network 172.16.254.30/32 + network 172.16.37.0/24 + network 172.18.201.0/24 + network 172.18.202.0/24 + network 172.18.203.0/24 + network 172.18.204.0/24 + } + default-information { + originate { + always + metric-type 2 + } + } + log-adjacency-changes { + detail + } + parameters { + abr-type cisco + router-id 172.16.254.30 + } + passive-interface default + passive-interface-exclude eth0.5 + redistribute { + connected { + metric-type 2 + route-map MAP-OSPF-CONNECTED + } + } + } + static { + interface-route6 2000::/3 { + next-hop-interface pppoe0 { + } + } + route 10.0.0.0/8 { + blackhole { + distance 254 + } + } + route 169.254.0.0/16 { + blackhole { + distance 254 + } + } + route 172.16.0.0/12 { + blackhole { + distance 254 + } + } + route 172.16.32.0/21 { + blackhole { + } + } + route 172.18.0.0/16 { + blackhole { + } + } + route 172.29.0.2/31 { + next-hop 172.29.0.0 { + } + } + route 192.168.0.0/16 { + blackhole { + distance 254 + } + } + route 192.168.189.0/24 { + next-hop 172.29.0.0 { + } + } + } +} +service { + dhcp-server { + shared-network-name BACKBONE { + authoritative + subnet 172.16.37.0/24 { + default-router 172.16.37.254 + domain-name vyos.net + domain-search vyos.net + lease 86400 + name-server 172.16.254.30 + ntp-server 172.16.254.30 + range 0 { + start 172.16.37.120 + stop 172.16.37.149 + } + static-mapping AP1 { + ip-address 172.16.37.231 + mac-address 02:00:00:00:ee:18 + } + static-mapping AP2 { + ip-address 172.16.37.232 + mac-address 02:00:00:00:52:84 + } + static-mapping AP3 { + ip-address 172.16.37.233 + mac-address 02:00:00:00:51:c0 + } + static-mapping AP4 { + ip-address 172.16.37.234 + mac-address 02:00:00:00:e6:fc + } + static-mapping AP5 { + ip-address 172.16.37.235 + mac-address 02:00:00:00:c3:50 + } + } + } + shared-network-name GUEST { + authoritative + subnet 172.31.0.0/24 { + default-router 172.31.0.254 + domain-name vyos.net + domain-search vyos.net + lease 86400 + name-server 172.31.0.254 + range 0 { + start 172.31.0.101 + stop 172.31.0.199 + } + } + } + shared-network-name LAN { + authoritative + subnet 172.16.33.0/24 { + default-router 172.16.33.254 + domain-name vyos.net + domain-search vyos.net + lease 86400 + name-server 172.16.254.30 + ntp-server 172.16.254.30 + range 0 { + start 172.16.33.100 + stop 172.16.33.189 + } + static-mapping one { + ip-address 172.16.33.221 + mac-address 02:00:00:00:eb:a6 + } + static-mapping two { + ip-address 172.16.33.211 + mac-address 02:00:00:00:58:90 + } + static-mapping three { + ip-address 172.16.33.212 + mac-address 02:00:00:00:12:c7 + } + static-mapping four { + ip-address 172.16.33.214 + mac-address 02:00:00:00:c4:33 + } + } + } + } + dns { + dynamic { + interface pppoe0 { + service vyos { + host-name r1.vyos.net + login vyos-vyos + password vyos + protocol dyndns2 + server dyndns.vyos.io + } + } + } + forwarding { + allow-from 172.16.0.0/12 + domain 16.172.in-addr.arpa { + addnta + recursion-desired + server 172.16.100.10 + server 172.16.100.20 + } + domain 18.172.in-addr.arpa { + addnta + recursion-desired + server 172.16.100.10 + server 172.16.100.20 + } + domain vyos.net { + addnta + recursion-desired + server 172.16.100.20 + server 172.16.100.10 + } + ignore-hosts-file + listen-address 172.16.254.30 + listen-address 172.31.0.254 + negative-ttl 60 + } + } + lldp { + legacy-protocols { + cdp + edp + fdp + sonmp + } + snmp { + enable + } + } + router-advert { + interface eth0.10 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + interface eth1.20 { + prefix ::/64 { + preferred-lifetime 2700 + valid-lifetime 5400 + } + } + } + snmp { + community ro-community { + authorization ro + network 172.16.100.0/24 + } + contact "VyOS" + listen-address 172.16.254.30 { + port 161 + } + location "CLOUD" + } + ssh { + disable-host-validation + port 22 + } +} +system { + config-management { + commit-revisions 200 + } + conntrack { + expect-table-size 2048 + hash-size 32768 + modules { + ftp + h323 + nfs + pptp + sqlnet + tftp + } + table-size 262144 + timeout { + icmp 30 + other 600 + udp { + other 300 + stream 300 + } + } + } + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos.net + host-name r1 + login { + user vyos { + authentication { + encrypted-password $6$2Ta6TWHd/U$NmrX0x9kexCimeOcYK1MfhMpITF9ELxHcaBU/znBq.X2ukQOj61fVI2UYP/xBzP4QtiTcdkgs7WOQMHWsRymO/ + plaintext-password "" + } + } + } + name-server 172.16.254.30 + ntp { + allow-clients { + address 172.16.0.0/12 + } + server time1.vyos.net { + } + server time2.vyos.net { + } + } + option { + ctrl-alt-delete ignore + performance latency + reboot-on-panic + startup-beep + } + syslog { + global { + facility all { + level debug + } + facility protocols { + level debug + } + } + host 172.16.100.1 { + facility all { + level warning + } + } + } + time-zone Europe/Berlin +} +traffic-policy { + shaper QoS { + bandwidth 50mbit + default { + bandwidth 100% + burst 15k + queue-limit 1000 + queue-type fq-codel + } + } +} +zone-policy { + zone DMZ { + default-action drop + from GUEST { + firewall { + name GUEST-DMZ + } + } + from LAN { + firewall { + name LAN-DMZ + } + } + from LOCAL { + firewall { + name LOCAL-DMZ + } + } + from WAN { + firewall { + name WAN-DMZ + } + } + interface eth0.50 + } + zone GUEST { + default-action drop + from DMZ { + firewall { + name DMZ-GUEST + } + } + from LAN { + firewall { + name LAN-GUEST + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-GUEST + } + } + from WAN { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name WAN-GUEST + } + } + interface eth1.20 + } + zone LAN { + default-action drop + from DMZ { + firewall { + name DMZ-LAN + } + } + from GUEST { + firewall { + name GUEST-LAN + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-LAN + } + } + from WAN { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name WAN-LAN + } + } + interface eth0.5 + interface eth0.10 + interface wg100 + interface wg200 + } + zone LOCAL { + default-action drop + from DMZ { + firewall { + name DMZ-LOCAL + } + } + from GUEST { + firewall { + ipv6-name ALLOW-ESTABLISHED-6 + name GUEST-LOCAL + } + } + from LAN { + firewall { + ipv6-name ALLOW-ALL-6 + name LAN-LOCAL + } + } + from WAN { + firewall { + ipv6-name WAN-LOCAL-6 + name WAN-LOCAL + } + } + local-zone + } + zone WAN { + default-action drop + from DMZ { + firewall { + name DMZ-WAN + } + } + from GUEST { + firewall { + ipv6-name ALLOW-ALL-6 + name GUEST-WAN + } + } + from LAN { + firewall { + ipv6-name ALLOW-ALL-6 + name LAN-WAN + } + } + from LOCAL { + firewall { + ipv6-name ALLOW-ALL-6 + name LOCAL-WAN + } + } + interface pppoe0 + interface wg666 + } +} + + +// Warning: Do not remove the following line. +// vyos-config-version: "broadcast-relay@1:cluster@1:config-management@1:conntrack@3:conntrack-sync@2:container@1:dhcp-relay@2:dhcp-server@6:dhcpv6-server@1:dns-forwarding@3:firewall@5:https@2:interfaces@22:ipoe-server@1:ipsec@5:isis@1:l2tp@3:lldp@1:mdns@1:nat@5:ntp@1:pppoe-server@5:pptp@2:qos@1:quagga@8:rpki@1:salt@1:snmp@2:ssh@2:sstp@3:system@21:vrrp@2:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2:zone-policy@1" +// Release version: 1.3.4 diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 71e2142f9..97dab255e 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -1133,5 +1133,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase): self.assertIn(f' mpls bgp forwarding', frrconfig) self.cli_delete(['interfaces', 'ethernet', interface, 'vrf']) + def test_bgp_24_srv6_sid(self): + locator_name = 'VyOS_foo' + sid = 'auto' + + self.cli_set(base_path + ['srv6', 'locator', locator_name]) + self.cli_set(base_path + ['sid', 'vpn', 'per-vrf', 'export', sid]) + + self.cli_commit() + + frrconfig = self.getFRRconfig(f'router bgp {ASN}') + self.assertIn(f'router bgp {ASN}', frrconfig) + self.assertIn(f' segment-routing srv6', frrconfig) + self.assertIn(f' locator {locator_name}', frrconfig) + self.assertIn(f' sid vpn per-vrf export {sid}', frrconfig) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_protocols_segment_routing.py b/smoketest/scripts/cli/test_protocols_segment_routing.py new file mode 100755 index 000000000..81d42b925 --- /dev/null +++ b/smoketest/scripts/cli/test_protocols_segment_routing.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os +import unittest + +from base_vyostest_shim import VyOSUnitTestSHIM + +from vyos.configsession import ConfigSessionError +from vyos.utils.process import cmd +from vyos.utils.process import process_named_running + +base_path = ['protocols', 'segment-routing'] +PROCESS_NAME = 'zebra' + +class TestProtocolsSegmentRouting(VyOSUnitTestSHIM.TestCase): + @classmethod + def setUpClass(cls): + # call base-classes classmethod + super(TestProtocolsSegmentRouting, cls).setUpClass() + # Retrieve FRR daemon PID - it is not allowed to crash, thus PID must remain the same + cls.daemon_pid = process_named_running(PROCESS_NAME) + # ensure we can also run this test on a live system - so lets clean + # out the current configuration :) + cls.cli_delete(cls, base_path) + + def tearDown(self): + self.cli_delete(base_path) + self.cli_commit() + + # check process health and continuity + self.assertEqual(self.daemon_pid, process_named_running(PROCESS_NAME)) + + def test_srv6(self): + locators = { + 'foo' : { 'prefix' : '2001:a::/64' }, + 'foo' : { 'prefix' : '2001:b::/64', 'usid' : {} }, + } + + for locator, locator_config in locators.items(): + self.cli_set(base_path + ['srv6', 'locator', locator, 'prefix', locator_config['prefix']]) + if 'usid' in locator_config: + self.cli_set(base_path + ['srv6', 'locator', locator, 'behavior-usid']) + + self.cli_commit() + + frrconfig = self.getFRRconfig(f'segment-routing', daemon='zebra') + self.assertIn(f'segment-routing', frrconfig) + self.assertIn(f' srv6', frrconfig) + self.assertIn(f' locators', frrconfig) + for locator, locator_config in locators.items(): + self.assertIn(f' locator {locator}', frrconfig) + self.assertIn(f' prefix {locator_config["prefix"]} block-len 40 node-len 24 func-bits 16', frrconfig) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 093e43494..9f6e05ff3 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -14,11 +14,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os import unittest +from json import loads + from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError +from vyos.utils.dict import dict_search_recursive from vyos.utils.process import process_named_running from vyos.utils.file import read_file from vyos.template import address_from_cidr @@ -26,8 +30,10 @@ from vyos.template import inc_ip from vyos.template import dec_ip from vyos.template import netmask_from_cidr -PROCESS_NAME = 'dhcpd' -DHCPD_CONF = '/run/dhcp-server/dhcpd.conf' +PROCESS_NAME = 'kea-dhcp4' +CTRL_PROCESS_NAME = 'kea-ctrl-agent' +KEA4_CONF = '/run/kea/kea-dhcp4.conf' +KEA4_CTRL = '/run/kea/dhcp4-ctrl-socket' base_path = ['service', 'dhcp-server'] subnet = '192.0.2.0/25' router = inc_ip(subnet, 1) @@ -52,6 +58,36 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.cli_delete(base_path) self.cli_commit() + def walk_path(self, obj, path): + current = obj + + for i, key in enumerate(path): + if isinstance(key, str): + self.assertTrue(isinstance(current, dict), msg=f'Failed path: {path}') + self.assertTrue(key in current, msg=f'Failed path: {path}') + elif isinstance(key, int): + self.assertTrue(isinstance(current, list), msg=f'Failed path: {path}') + self.assertTrue(0 <= key < len(current), msg=f'Failed path: {path}') + else: + assert False, "Invalid type" + + current = current[key] + + return current + + def verify_config_object(self, obj, path, value): + base_obj = self.walk_path(obj, path) + self.assertTrue(isinstance(base_obj, list)) + self.assertTrue(any(True for v in base_obj if v == value)) + + def verify_config_value(self, obj, path, key, value): + base_obj = self.walk_path(obj, path) + if isinstance(base_obj, list): + self.assertTrue(any(True for v in base_obj if key in v and v[key] == value)) + elif isinstance(base_obj, dict): + self.assertTrue(key in base_obj) + self.assertEqual(base_obj[key], value) + def test_dhcp_single_pool_range(self): shared_net_name = 'SMOKE-1' @@ -60,15 +96,12 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): range_1_start = inc_ip(subnet, 40) range_1_stop = inc_ip(subnet, 50) - self.cli_set(base_path + ['dynamic-dns-update']) - pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] # we use the first subnet IP address as default gateway self.cli_set(pool + ['default-router', router]) self.cli_set(pool + ['name-server', dns_1]) self.cli_set(pool + ['name-server', dns_2]) self.cli_set(pool + ['domain-name', domain_name]) - self.cli_set(pool + ['ping-check']) # check validate() - No DHCP address range or active static-mapping set with self.assertRaises(ConfigSessionError): @@ -81,20 +114,37 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) - self.assertIn(f'ddns-update-style interim;', config) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'option domain-name "{domain_name}";', config) - self.assertIn(f'default-lease-time 86400;', config) - self.assertIn(f'max-lease-time 86400;', config) - self.assertIn(f'ping-check true;', config) - self.assertIn(f'range {range_0_start} {range_0_stop};', config) - self.assertIn(f'range {range_1_start} {range_1_stop};', config) - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + config = read_file(KEA4_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name', 'data': domain_name}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_1_start} - {range_1_stop}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -134,6 +184,7 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.cli_set(pool + ['static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1']) self.cli_set(pool + ['ipv6-only-preferred', ipv6_only_preferred]) + self.cli_set(pool + ['time-zone', 'Europe/London']) # check validate() - No DHCP address range or active static-mapping set with self.assertRaises(ConfigSessionError): @@ -144,38 +195,89 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) - self.assertIn(f'ddns-update-style none;', config) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'option domain-name "{domain_name}";', config) - - search = '"' + ('", "').join(search_domains) + '"' - self.assertIn(f'option domain-search {search};', config) - - self.assertIn(f'option ip-forwarding true;', config) - self.assertIn(f'option smtp-server {smtp_server};', config) - self.assertIn(f'option pop-server {smtp_server};', config) - self.assertIn(f'option time-servers {time_server};', config) - self.assertIn(f'option wpad-url "{wpad}";', config) - self.assertIn(f'option dhcp-server-identifier {server_identifier};', config) - self.assertIn(f'option tftp-server-name "{tftp_server}";', config) - self.assertIn(f'option bootfile-name "{bootfile_name}";', config) - self.assertIn(f'filename "{bootfile_name}";', config) - self.assertIn(f'next-server {bootfile_server};', config) - self.assertIn(f'default-lease-time 86400;', config) - self.assertIn(f'max-lease-time 86400;', config) - self.assertIn(f'range {range_0_start} {range_0_stop};', config) - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) - self.assertIn(f'option rfc8925-ipv6-only-preferred {ipv6_only_preferred};', config) - - # weird syntax for those static routes - self.assertIn(f'option rfc3442-static-route 24,10,0,0,192,0,2,1, 0,192,0,2,1;', config) - self.assertIn(f'option windows-static-route 24,10,0,0,192,0,2,1;', config) + config = read_file(KEA4_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'boot-file-name', bootfile_name) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'next-server', bootfile_server) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name', 'data': domain_name}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-search', 'data': ', '.join(search_domains)}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'pop-server', 'data': smtp_server}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'smtp-server', 'data': smtp_server}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'time-servers', 'data': time_server}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'dhcp-server-identifier', 'data': server_identifier}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'tftp-server-name', 'data': tftp_server}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'wpad-url', 'data': wpad}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'rfc3442-static-route', 'data': '24,10,0,0,192,0,2,1, 0,192,0,2,1'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'windows-static-route', 'data': '24,10,0,0,192,0,2,1'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'v6-only-preferred', 'data': ipv6_only_preferred}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'ip-forwarding', 'data': "true"}) + + # Time zone + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'pcode', 'data': 'GMT0BST,M3.5.0/1,M10.5.0'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'tcode', 'data': 'Europe/London'}) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -205,27 +307,39 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) - self.assertIn(f'ddns-update-style none;', config) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option domain-name-servers {dns_1}, {dns_2};', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'option domain-name "{domain_name}";', config) - self.assertIn(f'default-lease-time 86400;', config) - self.assertIn(f'max-lease-time 86400;', config) + config = read_file(KEA4_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'valid-lifetime', 86400) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'max-valid-lifetime', 86400) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name', 'data': domain_name}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'domain-name-servers', 'data': f'{dns_1}, {dns_2}'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) client_base = 10 for client in ['client1', 'client2', 'client3']: mac = '00:50:00:00:00:{}'.format(client_base) ip = inc_ip(subnet, client_base) - self.assertIn(f'host {shared_net_name}_{client}' + ' {', config) - self.assertIn(f'fixed-address {ip};', config) - self.assertIn(f'hardware ethernet {mac};', config) - client_base += 1 - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'reservations'], + {'hw-address': mac, 'ip-address': ip}) + + client_base += 1 # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -266,7 +380,9 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) + config = read_file(KEA4_CONF) + obj = loads(config) + for network in ['0', '1', '2', '3']: shared_net_name = f'VyOS-SMOKETEST-{network}' subnet = f'192.0.{network}.0/24' @@ -278,27 +394,43 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): range_1_start = inc_ip(subnet, 30) range_1_stop = inc_ip(subnet, 40) - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) - - self.assertIn(f'ddns-update-style none;', config) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option domain-name-servers {dns_1};', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'option domain-name "{domain_name}";', config) - self.assertIn(f'default-lease-time {lease_time};', config) - self.assertIn(f'max-lease-time {lease_time};', config) - self.assertIn(f'range {range_0_start} {range_0_stop};', config) - self.assertIn(f'range {range_1_start} {range_1_stop};', config) - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'subnet', subnet) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'valid-lifetime', int(lease_time)) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', int(network), 'subnet4'], 'max-valid-lifetime', int(lease_time)) + + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'], + {'name': 'domain-name', 'data': domain_name}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'], + {'name': 'domain-name-servers', 'data': dns_1}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) + + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}) + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'pools'], + {'pool': f'{range_1_start} - {range_1_stop}'}) client_base = 60 for client in ['client1', 'client2', 'client3', 'client4']: mac = '02:50:00:00:00:{}'.format(client_base) ip = inc_ip(subnet, client_base) - self.assertIn(f'host {shared_net_name}_{client}' + ' {', config) - self.assertIn(f'fixed-address {ip};', config) - self.assertIn(f'hardware ethernet {mac};', config) + + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', int(network), 'subnet4', 0, 'reservations'], + {'hw-address': mac, 'ip-address': ip}) + client_base += 1 # Check for running process @@ -319,14 +451,23 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - # VErify - config = read_file(DHCPD_CONF) - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) + config = read_file(KEA4_CONF) + obj = loads(config) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'range {range_0_start} {range_0_stop};', config) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST') + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -352,15 +493,27 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - # Verify - config = read_file(DHCPD_CONF) - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) + config = read_file(KEA4_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'EXCLUDE-TEST-2') + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'range {range_0_start} {range_0_stop_excl};', config) - self.assertIn(f'range {range_0_start_excl} {range_0_stop};', config) + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) + + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop_excl}'}) + + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start_excl} - {range_0_stop}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -384,41 +537,23 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) - # Check the relay network - self.assertIn(f'subnet {network} netmask {netmask}' + r' { }', config) - - relay_network = address_from_cidr(relay_subnet) - relay_netmask = netmask_from_cidr(relay_subnet) - self.assertIn(f'subnet {relay_network} netmask {relay_netmask}' + r' {', config) - self.assertIn(f'option routers {relay_router};', config) - self.assertIn(f'range {range_0_start} {range_0_stop};', config) - - # Check for running process - self.assertTrue(process_named_running(PROCESS_NAME)) - - def test_dhcp_invalid_raw_options(self): - shared_net_name = 'SMOKE-5' + config = read_file(KEA4_CONF) + obj = loads(config) - range_0_start = inc_ip(subnet, 10) - range_0_stop = inc_ip(subnet, 20) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', 'RELAY') + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', relay_subnet) - pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] - # we use the first subnet IP address as default gateway - self.cli_set(pool + ['default-router', router]) - self.cli_set(pool + ['range', '0', 'start', range_0_start]) - self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': relay_router}) - self.cli_set(base_path + ['global-parameters', 'this-is-crap']) - # check generate() - dhcpd should not acceot this garbage config - with self.assertRaises(ConfigSessionError): - self.cli_commit() - self.cli_delete(base_path + ['global-parameters']) - - # commit changes - self.cli_commit() + # Verify pools + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -449,41 +584,43 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): self.cli_set(base_path + ['failover', 'remote', failover_remote]) self.cli_set(base_path + ['failover', 'status', 'primary']) - # check validate() - failover needs to be enabled for at least one subnet - with self.assertRaises(ConfigSessionError): - self.cli_commit() - self.cli_set(pool + ['enable-failover']) - # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - - self.assertIn(f'failover peer "{failover_name}"' + r' {', config) - self.assertIn(f'primary;', config) - self.assertIn(f'mclt 1800;', config) - self.assertIn(f'mclt 1800;', config) - self.assertIn(f'split 128;', config) - self.assertIn(f'port 647;', config) - self.assertIn(f'peer port 647;', config) - self.assertIn(f'max-response-delay 30;', config) - self.assertIn(f'max-unacked-updates 10;', config) - self.assertIn(f'load balance max seconds 3;', config) - self.assertIn(f'address {failover_local};', config) - self.assertIn(f'peer address {failover_remote};', config) - - network = address_from_cidr(subnet) - netmask = netmask_from_cidr(subnet) - self.assertIn(f'ddns-update-style none;', config) - self.assertIn(f'subnet {network} netmask {netmask}' + r' {', config) - self.assertIn(f'option routers {router};', config) - self.assertIn(f'range {range_0_start} {range_0_stop};', config) - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) - self.assertIn(f'failover peer "{failover_name}";', config) - self.assertIn(f'deny dynamic bootp clients;', config) + config = read_file(KEA4_CONF) + obj = loads(config) + + # Verify failover + self.verify_config_value(obj, ['Dhcp4', 'control-socket'], 'socket-name', KEA4_CTRL) + + self.verify_config_object( + obj, + ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'], + {'name': os.uname()[1], 'url': f'http://{failover_local}:647/', 'role': 'primary', 'auto-failover': True}) + + self.verify_config_object( + obj, + ['Dhcp4', 'hooks-libraries', 0, 'parameters', 'high-availability', 0, 'peers'], + {'name': failover_name, 'url': f'http://{failover_remote}:647/', 'role': 'standby', 'auto-failover': True}) + + self.verify_config_value(obj, ['Dhcp4', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp4', 'shared-networks', 0, 'subnet4'], 'subnet', subnet) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'routers', 'data': router}) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'pools'], + {'pool': f'{range_0_start} - {range_0_stop}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) + self.assertTrue(process_named_running(CTRL_PROCESS_NAME)) if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_service_dhcpv6-server.py b/smoketest/scripts/cli/test_service_dhcpv6-server.py index 4d9dabc3f..175a67537 100755 --- a/smoketest/scripts/cli/test_service_dhcpv6-server.py +++ b/smoketest/scripts/cli/test_service_dhcpv6-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2022 VyOS maintainers and contributors +# Copyright (C) 2020-2023 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -16,6 +16,8 @@ import unittest +from json import loads + from base_vyostest_shim import VyOSUnitTestSHIM from vyos.configsession import ConfigSessionError @@ -23,8 +25,8 @@ from vyos.template import inc_ip from vyos.utils.process import process_named_running from vyos.utils.file import read_file -PROCESS_NAME = 'dhcpd' -DHCPD_CONF = '/run/dhcp-server/dhcpdv6.conf' +PROCESS_NAME = 'kea-dhcp6' +KEA6_CONF = '/run/kea/kea-dhcp6.conf' base_path = ['service', 'dhcpv6-server'] subnet = '2001:db8:f00::/64' @@ -52,6 +54,36 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): self.cli_delete(base_path) self.cli_commit() + def walk_path(self, obj, path): + current = obj + + for i, key in enumerate(path): + if isinstance(key, str): + self.assertTrue(isinstance(current, dict), msg=f'Failed path: {path}') + self.assertTrue(key in current, msg=f'Failed path: {path}') + elif isinstance(key, int): + self.assertTrue(isinstance(current, list), msg=f'Failed path: {path}') + self.assertTrue(0 <= key < len(current), msg=f'Failed path: {path}') + else: + assert False, "Invalid type" + + current = current[key] + + return current + + def verify_config_object(self, obj, path, value): + base_obj = self.walk_path(obj, path) + self.assertTrue(isinstance(base_obj, list)) + self.assertTrue(any(True for v in base_obj if v == value)) + + def verify_config_value(self, obj, path, key, value): + base_obj = self.walk_path(obj, path) + if isinstance(base_obj, list): + self.assertTrue(any(True for v in base_obj if key in v and v[key] == value)) + elif isinstance(base_obj, dict): + self.assertTrue(key in base_obj) + self.assertEqual(base_obj[key], value) + def test_single_pool(self): shared_net_name = 'SMOKE-1' search_domains = ['foo.vyos.net', 'bar.vyos.net'] @@ -99,34 +131,66 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - self.assertIn(f'option dhcp6.preference {preference};', config) - - self.assertIn(f'subnet6 {subnet}' + r' {', config) - search = '"' + '", "'.join(search_domains) + '"' - nissrv = ', '.join(nis_servers) - self.assertIn(f'range6 {range_start} {range_stop};', config) - self.assertIn(f'default-lease-time {lease_time};', config) - self.assertIn(f'default-lease-time {lease_time};', config) - self.assertIn(f'max-lease-time {max_lease_time};', config) - self.assertIn(f'min-lease-time {min_lease_time};', config) - self.assertIn(f'option dhcp6.domain-search {search};', config) - self.assertIn(f'option dhcp6.name-servers {dns_1}, {dns_2};', config) - self.assertIn(f'option dhcp6.nis-domain-name "{domain}";', config) - self.assertIn(f'option dhcp6.nis-servers {nissrv};', config) - self.assertIn(f'option dhcp6.nisp-domain-name "{domain}";', config) - self.assertIn(f'option dhcp6.nisp-servers {nissrv};', config) - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + config = read_file(KEA6_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet) + self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'valid-lifetime', int(lease_time)) + self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'min-valid-lifetime', int(min_lease_time)) + self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'max-valid-lifetime', int(max_lease_time)) + + # Verify options + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'dns-servers', 'data': f'{dns_1}, {dns_2}'}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'domain-search', 'data': ", ".join(search_domains)}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nis-domain-name', 'data': domain}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nis-servers', 'data': ", ".join(nis_servers)}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nisp-domain-name', 'data': domain}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'nisp-servers', 'data': ", ".join(nis_servers)}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'sntp-servers', 'data': sntp_server}) + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'option-data'], + {'name': 'sip-server-dns', 'data': sip_server}) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pools'], + {'pool': f'{range_start} - {range_stop}'}) client_base = 1 for client in ['client1', 'client2', 'client3']: cid = '00:01:00:01:12:34:56:78:aa:bb:cc:dd:ee:{}'.format(client_base) ip = inc_ip(subnet, client_base) prefix = inc_ip(subnet, client_base << 64) + '/64' - self.assertIn(f'host {shared_net_name}_{client}' + ' {', config) - self.assertIn(f'fixed-address6 {ip};', config) - self.assertIn(f'fixed-prefix6 {prefix};', config) - self.assertIn(f'host-identifier option dhcp6.client-id {cid};', config) + + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'reservations'], + {'duid': cid, 'ip-addresses': [ip], 'prefixes': [prefix]}) + client_base += 1 # Check for running process @@ -138,22 +202,34 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): range_start = inc_ip(subnet, 256) # ::100 range_stop = inc_ip(subnet, 65535) # ::ffff delegate_start = '2001:db8:ee::' - delegate_stop = '2001:db8:ee:ff00::' - delegate_len = '56' + delegate_len = '64' + prefix_len = '56' pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] self.cli_set(pool + ['address-range', 'start', range_start, 'stop', range_stop]) - self.cli_set(pool + ['prefix-delegation', 'start', delegate_start, 'stop', delegate_stop]) - self.cli_set(pool + ['prefix-delegation', 'start', delegate_start, 'prefix-length', delegate_len]) + self.cli_set(pool + ['prefix-delegation', 'prefix', delegate_start, 'delegated-length', delegate_len]) + self.cli_set(pool + ['prefix-delegation', 'prefix', delegate_start, 'prefix-length', prefix_len]) # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - self.assertIn(f'subnet6 {subnet}' + r' {', config) - self.assertIn(f'range6 {range_start} {range_stop};', config) - self.assertIn(f'prefix6 {delegate_start} {delegate_stop} /{delegate_len};', config) + config = read_file(KEA6_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet) + + # Verify pools + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pools'], + {'pool': f'{range_start} - {range_stop}'}) + + self.verify_config_object( + obj, + ['Dhcp6', 'shared-networks', 0, 'subnet6', 0, 'pd-pools'], + {'prefix': delegate_start, 'prefix-len': int(prefix_len), 'delegated-len': int(delegate_len)}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) @@ -170,10 +246,16 @@ class TestServiceDHCPv6Server(VyOSUnitTestSHIM.TestCase): # commit changes self.cli_commit() - config = read_file(DHCPD_CONF) - self.assertIn(f'option dhcp6.name-servers {ns_global_1}, {ns_global_2};', config) - self.assertIn(f'subnet6 {subnet}' + r' {', config) - self.assertIn(f'set shared-networkname = "{shared_net_name}";', config) + config = read_file(KEA6_CONF) + obj = loads(config) + + self.verify_config_value(obj, ['Dhcp6', 'shared-networks'], 'name', shared_net_name) + self.verify_config_value(obj, ['Dhcp6', 'shared-networks', 0, 'subnet6'], 'subnet', subnet) + + self.verify_config_object( + obj, + ['Dhcp6', 'option-data'], + {'name': 'dns-servers', "code": 23, "space": "dhcp6", "csv-format": True, 'data': f'{ns_global_1}, {ns_global_2}'}) # Check for running process self.assertTrue(process_named_running(PROCESS_NAME)) diff --git a/smoketest/scripts/cli/test_service_dns_dynamic.py b/smoketest/scripts/cli/test_service_dns_dynamic.py index cb3d90593..ae46b18ba 100755 --- a/smoketest/scripts/cli/test_service_dns_dynamic.py +++ b/smoketest/scripts/cli/test_service_dns_dynamic.py @@ -17,8 +17,6 @@ import os import unittest import tempfile -import random -import string from base_vyostest_shim import VyOSUnitTestSHIM @@ -67,14 +65,12 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): self.cli_set(name_path + [svc, 'address', interface]) self.cli_set(name_path + [svc, 'host-name', hostname]) self.cli_set(name_path + [svc, 'password', password]) - self.cli_set(name_path + [svc, 'zone', zone]) - self.cli_set(name_path + [svc, 'ttl', ttl]) for opt, value in details.items(): self.cli_set(name_path + [svc, opt, value]) - # 'zone' option is supported and required by 'cloudfare', but not 'freedns' and 'zoneedit' + # 'zone' option is supported by 'cloudfare' and 'zoneedit1', but not 'freedns' self.cli_set(name_path + [svc, 'zone', zone]) - if details['protocol'] == 'cloudflare': + if details['protocol'] in ['cloudflare', 'zoneedit1']: pass else: # exception is raised for unsupported ones @@ -292,9 +288,38 @@ class TestServiceDDNS(VyOSUnitTestSHIM.TestCase): self.assertIn(f'password=\'{password}\'', ddclient_conf) self.assertIn(f'{hostname}', ddclient_conf) - def test_07_dyndns_vrf(self): + def test_07_dyndns_dynamic_interface(self): + # Check if DDNS service can be configured and runs + svc_path = name_path + ['namecheap'] + proto = 'namecheap' + dyn_interface = 'pppoe587' + + self.cli_set(svc_path + ['address', dyn_interface]) + self.cli_set(svc_path + ['protocol', proto]) + self.cli_set(svc_path + ['server', server]) + self.cli_set(svc_path + ['username', username]) + self.cli_set(svc_path + ['password', password]) + self.cli_set(svc_path + ['host-name', hostname]) + + # Dynamic interface will raise a warning but still go through + # XXX: We should have idiomatic class "ConfigSessionWarning" wrapping + # "Warning" similar to "ConfigSessionError". + # with self.assertWarns(Warning): + # self.cli_commit() + self.cli_commit() + + # Check the generating config parameters + ddclient_conf = cmd(f'sudo cat {DDCLIENT_CONF}') + self.assertIn(f'ifv4={dyn_interface}', ddclient_conf) + self.assertIn(f'protocol={proto}', ddclient_conf) + self.assertIn(f'server={server}', ddclient_conf) + self.assertIn(f'login={username}', ddclient_conf) + self.assertIn(f'password=\'{password}\'', ddclient_conf) + self.assertIn(f'{hostname}', ddclient_conf) + + def test_08_dyndns_vrf(self): # Table number randomized, but should be within range 100-65535 - vrf_table = "".join(random.choices(string.digits, k=4)) + vrf_table = '58710' vrf_name = f'vyos-test-{vrf_table}' svc_path = name_path + ['cloudflare'] proto = 'cloudflare' diff --git a/smoketest/scripts/cli/test_service_https.py b/smoketest/scripts/cli/test_service_https.py index 6cb91bcf1..703e3e8c4 100755 --- a/smoketest/scripts/cli/test_service_https.py +++ b/smoketest/scripts/cli/test_service_https.py @@ -23,6 +23,7 @@ from urllib3.exceptions import InsecureRequestWarning from base_vyostest_shim import VyOSUnitTestSHIM from base_vyostest_shim import ignore_warning from vyos.utils.file import read_file +from vyos.utils.process import call from vyos.utils.process import process_named_running from vyos.configsession import ConfigSessionError @@ -51,6 +52,23 @@ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPLpD0Ohhoq0g4nhx u8/3jHMM7sDwL3aWzW/zp54/LhCWUoLMjDdDEEigK4fal4ZF9aA9F0Ww """ +# to test load config via HTTP URL +nginx_conf_smoketest = """ +server { + listen 8000; + server_name localhost; + + root /tmp; + + index index.html; + + location / { + try_files $uri $uri/ =404; + autoindex on; + } +} +""" + PROCESS_NAME = 'nginx' class TestHTTPSService(VyOSUnitTestSHIM.TestCase): @@ -375,6 +393,57 @@ class TestHTTPSService(VyOSUnitTestSHIM.TestCase): r = request('POST', url, verify=False, headers=headers, data=payload) self.assertEqual(r.status_code, 200) + @ignore_warning(InsecureRequestWarning) + def test_api_config_file_load_http(self): + """Test load config from HTTP URL + """ + address = '127.0.0.1' + key = 'VyOS-key' + url = f'https://{address}/config-file' + url_config = f'https://{address}/configure' + headers = {} + tmp_file = 'tmp-config.boot' + nginx_tmp_site = '/etc/nginx/sites-enabled/smoketest' + + self.cli_set(base_path + ['api', 'keys', 'id', 'key-01', 'key', key]) + self.cli_commit() + + # load config via HTTP requires nginx config + call(f'sudo touch {nginx_tmp_site}') + call(f'sudo chown vyos:vyattacfg {nginx_tmp_site}') + call(f'sudo chmod +w {nginx_tmp_site}') + + with open(nginx_tmp_site, 'w') as f: + f.write(nginx_conf_smoketest) + call('sudo nginx -s reload') + + # save config + payload = { + 'data': '{"op": "save", "file": "/tmp/tmp-config.boot"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + # change config + payload = { + 'data': '{"op": "set", "path": ["interfaces", "dummy", "dum1", "address", "192.0.2.31/32"]}', + 'key': f'{key}', + } + r = request('POST', url_config, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + # load config from URL + payload = { + 'data': '{"op": "load", "file": "http://localhost:8000/tmp-config.boot"}', + 'key': f'{key}', + } + r = request('POST', url, verify=False, headers=headers, data=payload) + self.assertEqual(r.status_code, 200) + + # cleanup tmp nginx conf + call(f'sudo rm -rf {nginx_tmp_site}') + call('sudo nginx -s reload') if __name__ == '__main__': unittest.main(verbosity=5) |
