From 168111e23c43cd1ff2e0bcde04b8673727bec959 Mon Sep 17 00:00:00 2001 From: IluaAir Date: Sun, 7 Jun 2026 08:41:59 +0300 Subject: [PATCH] Refactor Keenetic model to utilize centralized UTF-8 decoding utility - Removed the internal `_decode_utf` method from the `Keenetic` class and replaced its usage with the new `decode_utf` utility function for decoding interface descriptions. - Added a new configuration file `config.conf` for Keenetic devices to facilitate testing. - Introduced an expected output JSON file `config.expected.json` to validate the parsing of Keenetic configurations against expected results. --- oxi/interfaces/models/keenetic.py | 24 +- tests/fixtures/keenetic/config.conf | 341 +++++++++++++++++++ tests/fixtures/keenetic/config.expected.json | 161 +++++++++ 3 files changed, 505 insertions(+), 21 deletions(-) create mode 100644 tests/fixtures/keenetic/config.conf create mode 100644 tests/fixtures/keenetic/config.expected.json diff --git a/oxi/interfaces/models/keenetic.py b/oxi/interfaces/models/keenetic.py index 89ea3c7..5764f23 100644 --- a/oxi/interfaces/models/keenetic.py +++ b/oxi/interfaces/models/keenetic.py @@ -1,24 +1,13 @@ from ipaddress import ip_interface from oxi.interfaces import register_parser from oxi.interfaces.base import BaseDevice +from oxi.interfaces.utils import decode_utf @register_parser(["NDMS", "keenetic", "KeeneticOS"]) class Keenetic(BaseDevice): template = "keenetic.ttp" - def _decode_utf(self, text: str): - if "\\x" in text: - desc = text.strip('"') - decoded = ( - desc.encode("utf-8") - .decode("unicode_escape") - .encode("latin1") - .decode("utf-8") - ) - return decoded - return text - def interfaces(self): interfaces: list[dict] = self.raw["interfaces"] for item in interfaces: @@ -29,7 +18,7 @@ class Keenetic(BaseDevice): item["mask"] = ipaddress.network.prefixlen item.pop("netmask", "Key not found") if item.get("description"): - decoded = self._decode_utf(item.get("description", "")) + decoded = decode_utf(item.get("description", "")) item["description"] = decoded return interfaces @@ -37,13 +26,6 @@ class Keenetic(BaseDevice): vlans = self.raw["vlans"] for item in vlans: if item.get("description"): - decoded = self._decode_utf(item.get("description", "")) + decoded = decode_utf(item.get("description", "")) item["description"] = decoded return vlans - - -if __name__ == "__main__": - with open("./test2.txt") as file: - data = file.read() - mikr = Keenetic(data) - print(mikr.parse().model_dump_json()) diff --git a/tests/fixtures/keenetic/config.conf b/tests/fixtures/keenetic/config.conf new file mode 100644 index 0000000..dfab0d1 --- /dev/null +++ b/tests/fixtures/keenetic/config.conf @@ -0,0 +1,341 @@ +! +! release: 4.03.C.6.2-7 +! sandbox: stable +! title: 4.3.6.2 +! arch: mips +! +! ndm: +! exact: 0-a3057529fd +! cdate: 29 Sep 2025 +! +! bsp: +! exact: 0-03b50470c4 +! cdate: 30 Sep 2025 +! +! ndw: +! features: dual_image,led_control,wifi_button,wifi5ghz, +! vht2ghz,mimo2ghz,mimo5ghz,atf2ghz,atf5ghz,wifi6,wifi_ft, +! wpa3,hwnat +! components: base,cloudcontrol,corewireless,ddns,dhcpd, +! dns-filter,dns-https,dns-tls,dot1x,easyconfig,igmp,ip6, +! lang-en,lang-ru,miniupnpd,mws,nathelper-ftp,nathelper- +! h323,nathelper-pptp,nathelper-rtsp,nathelper-sip,ndmp, +! ndns,openvpn,pingcheck,ppe,pppoe,pptp,ssh,trafficcontrol, +! wireguard +! +! ndw3: +! version: 1.101.18.1 +! +! ndw4: +! version: 4.3.C.6.2 +! +! manufacturer: Keenetic Ltd. +! vendor: Keenetic +! series: KN +! model: Sprinter (KN-3710) +! hw_version: 7777777 +! hw_type: router +! hw_id: KN-3710 +! device: Sprinter +! region: EA +! description: Keenetic Sprinter (KN-3710) +! $$$ Agent: http/rci +! $$$ Last change: Fri, 3 Oct 2025 18:37:40 GMT +! $$$ Model: Keenetic Sprinter +! $$$ Username: admin +! $$$ Version: 2.06.1 +system + set net.ipv4.ip_forward 1 + set net.ipv4.neigh.default.gc_thresh1 256 + set net.ipv4.neigh.default.gc_thresh2 1024 + set net.ipv4.neigh.default.gc_thresh3 2048 + set net.ipv4.tcp_fin_timeout 30 + set net.ipv4.tcp_keepalive_time 120 + set net.ipv6.conf.all.forwarding 1 + set net.ipv6.neigh.default.gc_thresh1 256 + set net.ipv6.neigh.default.gc_thresh2 1024 + set net.ipv6.neigh.default.gc_thresh3 2048 + set net.netfilter.nf_conntrack_tcp_timeout_established 1200 + set vm.overcommit_memory 0 + set vm.vfs_cache_pressure 1000 + clock timezone Europe/Berlin + domainname WORKGROUP + hostname test_HW + caption default + description "Keenetic Sprinter (KN-3710)" + ndss dump-report disable +! +dyndns profile _WEBADMIN +! +interface GigabitEthernet0 + up +! +interface GigabitEthernet0/1 + rename 1 + switchport mode access + switchport access vlan 1 + up +! +interface GigabitEthernet0/2 + rename 2 + switchport mode access + switchport access vlan 1 + up +! +interface GigabitEthernet0/3 + rename 3 + switchport mode access + switchport access vlan 1 + up +! +interface GigabitEthernet0/Vlan1 + description "Home VLAN" + ip dhcp client dns-routes + ip name-servers + up +! +interface GigabitEthernet0/Vlan2 + rename ISP + description "\xd0\x9f\xd0\xbe\xd0\xb4\xd0\xba\xd0\xbb\xd1\x8e\xd1\x87\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 Ethernet" + dyndns nobind + mac address factory wan + security-level public + ip address dhcp + ip dhcp client hostname test_HW + ip dhcp client dns-routes + ip mtu 1500 + ip access-group _WEBADMIN_ISP in + ip global 57342 + ip no name-servers + igmp upstream + ipv6 address auto + ipv6 prefix auto + ipv6 no name-servers auto + up +! +interface GigabitEthernet0/0 + rename 0 + role inet for ISP + switchport mode access + switchport access vlan 2 + up +! +interface GigabitEthernet0/Vlan3 + dyndns nobind + ip dhcp client dns-routes + ip name-servers + up +! +interface WifiMaster0 + country-code RU + compatibility BGN+AX + rekey-interval 86400 + up +! +interface WifiMaster0/AccessPoint0 + mac access-list type none + authentication wpa-psk ns3 7777ggggddddsss + encryption enable + encryption wpa2 + ip dhcp client dns-routes + ssid test_HW_2.4G + up +! +interface WifiMaster0/AccessPoint1 + mac access-list type none + security-level private + encryption no enable + ip dhcp client dns-routes + down +! +interface WifiMaster0/AccessPoint2 + mac access-list type none + security-level private + encryption no enable + ip dhcp client dns-routes + down +! +interface WifiMaster0/WifiStation0 + security-level public + encryption no enable + ip dhcp client dns-routes + standby enable + standby timeout 600 + down +! +interface WifiMaster1 + country-code RU + compatibility AN+AC+AX + channel width 40-above/80 + rekey-interval 86400 + up +! +interface WifiMaster1/AccessPoint0 + mac access-list type none + authentication wpa-psk ns3 7777ggggddddsss + encryption enable + encryption wpa2 + ip dhcp client dns-routes + ssid test_HW_5G + up +! +interface WifiMaster1/AccessPoint1 + mac access-list type none + security-level private + encryption no enable + ip dhcp client dns-routes + down +! +interface WifiMaster1/AccessPoint2 + mac access-list type none + security-level private + encryption no enable + ip dhcp client dns-routes + down +! +interface WifiMaster1/WifiStation0 + security-level public + encryption no enable + ip dhcp client dns-routes + standby enable + standby timeout 600 + down +! +interface Bridge0 + rename Home + description "Home network" + dyndns nobind + include GigabitEthernet0/Vlan1 + include WifiMaster0/AccessPoint0 + include WifiMaster1/AccessPoint0 + mac access-list type none + security-level private + ip address 17.36.1.1 255.255.255.0 + ip dhcp client dns-routes + ip access-group _WEBADMIN_Home in + ip name-servers + band-steering + up +! +interface Bridge1 + rename Guest + description "Guest network" + traffic-shape rate 5120 + dyndns nobind + include GigabitEthernet0/Vlan3 + mac access-list type none + peer-isolation + security-level protected + ip address 10.1.30.1 255.255.255.0 + ip dhcp client dns-routes + ip name-servers + down +! +interface Bridge2 + rename Test + mac access-list type none + security-level public + ip dhcp client dns-routes + up +! +interface OpenVPN0 + description test_HW-udp + role misc + security-level public + ip dhcp client dns-routes + ip tcp adjust-mss pmtu + ip name-servers + ipv6 name-servers auto + openvpn accept-routes + openvpn connect + up +! +interface OpenVPN2 + description test_HW-tcp + role misc + dyndns nobind + security-level public + ip dhcp client dns-routes + ip tcp adjust-mss pmtu + openvpn accept-routes + openvpn connect + down +! +interface Wireguard0 + description test_HW + dyndns nobind + security-level public + ip address 10.3.100.1 255.255.255.0 + ip mtu 1324 + ip tcp adjust-mss pmtu + wireguard listen-port 65513 + wireguard peer 7777ggggddddsss= !test_HW + allow-ips 0.0.0.0 0.0.0.0 + connect + ! + up +! +interface Wireguard1 + description test_HW + dyndns nobind + security-level private + ip address 10.1.100.1 255.255.255.0 + ip mtu 1324 + ip access-group _WEBADMIN_Wireguard1 in + ip tcp adjust-mss pmtu + wireguard listen-port 65511 + wireguard peer 7777ggggddddsss= !test_HW + allow-ips 10.1.100.0 255.255.255.0 + allow-ips 17.36.3.0 255.255.255.0 + allow-ips 17.36.1.0 255.255.255.0 + allow-ips 0.0.0.0 0.0.0.0 + connect + ! + up +! +interface Wireguard2 + description test_HW + dyndns nobind + security-level private + ip address 10.2.100.1 255.255.255.0 + ip access-group _WEBADMIN_Wireguard2 in + ip tcp adjust-mss pmtu + wireguard listen-port 65512 + wireguard peer 7777ggggddddsss= !test_HW + allow-ips 0.0.0.0 0.0.0.0 + connect + ! + up +! +ip ssh + port 22 + security-level public + lockout-policy 5 15 3 +! +ip hotspot + policy Home permit + host 7777ggggddddsss permit + host 7777ggggddddsss priority 4 +! +ipv6 subnet Default + bind Home + mode slaac + prefix length 64 + number 0 +! +ppe software +ppe hardware +upnp lan Home +service dhcp +service dns-proxy +service http +service telnet +service ssh +service ntp +service upnp +! +easyconfig disable +components + auto-update disable + auto-update channel stable +! \ No newline at end of file diff --git a/tests/fixtures/keenetic/config.expected.json b/tests/fixtures/keenetic/config.expected.json new file mode 100644 index 0000000..fee724e --- /dev/null +++ b/tests/fixtures/keenetic/config.expected.json @@ -0,0 +1,161 @@ +{ + "system": { + "model": "Sprinter (KN-3710)", + "serial_number": "7777777", + "version": "4.03.C.6.2-7" + }, + "interfaces": [ + { + "interface": "GigabitEthernet0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "GigabitEthernet0/1", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "GigabitEthernet0/2", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "GigabitEthernet0/3", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "GigabitEthernet0/0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster0/AccessPoint0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster0/AccessPoint1", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster0/AccessPoint2", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster0/WifiStation0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster1", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster1/AccessPoint0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster1/AccessPoint1", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster1/AccessPoint2", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "WifiMaster1/WifiStation0", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "Bridge0", + "ip_address": "17.36.1.1", + "mask": 24, + "description": "Home network" + }, + { + "interface": "Bridge1", + "ip_address": "10.1.30.1", + "mask": 24, + "description": "Guest network" + }, + { + "interface": "Bridge2", + "ip_address": null, + "mask": null, + "description": null + }, + { + "interface": "OpenVPN0", + "ip_address": null, + "mask": null, + "description": "test_HW-udp" + }, + { + "interface": "OpenVPN2", + "ip_address": null, + "mask": null, + "description": "test_HW-tcp" + }, + { + "interface": "Wireguard0", + "ip_address": "10.3.100.1", + "mask": 24, + "description": "test_HW" + }, + { + "interface": "Wireguard1", + "ip_address": "10.1.100.1", + "mask": 24, + "description": "test_HW" + }, + { + "interface": "Wireguard2", + "ip_address": "10.2.100.1", + "mask": 24, + "description": "test_HW" + } + ], + "vlans": [ + { + "vlan_id": 1, + "description": "Home VLAN" + }, + { + "vlan_id": 2, + "description": "Подключение Ethernet" + }, + { + "vlan_id": 3, + "description": "Home network" + } + ] +} \ No newline at end of file