zyxel-gs1900-automation/main.py

154 lines
4.1 KiB
Python
Executable file

#!/usr/bin/env python3
from bs4 import BeautifulSoup
import time
import requests
import pdb
import os
import random
import re
from enum import Enum
IP = "192.168.42.100"
class API_CMD_IDS(Enum):
READ_PORTS = 768
READ_VLANS = 1283
READ_VLAN_PORTS = 1290
READ_VLAN_PORT_CFG = 1293 # this needs vid as a parameter and is parsed by parse_vlan_ports
READ_STP_PORTS = 4098
class VLAN_PORT_MODE(Enum):
GENERAL = 0
ACCESS = 1
TRUNK = 2
DOT1QTUNNEL = 3
class VLAN_PORT_ACL(Enum):
EXCLUDED = 0
FORBIDDEN = 1
TAGGED = 2
UNTAGGED = 3
def zyxel_password(pw: str) -> str:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
result = ""
l = len(pw)
for i in range(1, 322 - len(pw)):
if i % 5 == 0 and l > 0:
l -= 1
result += pw[l]
elif i == 123:
if len(pw) < 10:
result += "0"
else:
c = str(len(pw) // 10)
result += c
elif i == 289:
result += str(len(pw) % 10)
else:
rnd = random.choice(list(alphabet))
result += rnd
return result
def get_login_cookie(host: str, username: str, password: str) -> str:
auth_id = requests.get(
f"http://{host}/cgi-bin/dispatcher.cgi",
params={"login": 1, "username": username, "password": zyxel_password(password)},
)
if auth_id.status_code != 200:
raise Exception("error while getting auth_id {auth_id.text}")
time.sleep(0.5)
cookie = requests.post(
f"http://{host}/cgi-bin/dispatcher.cgi",
data={"authId": auth_id.text.strip(), "login_chk": "true"},
)
if cookie.text.strip() != "OK,":
raise Exception("error while getting cookie {cookie.text}")
return cookie.cookies.get("HTTP_XSSID")
def get_cmd(host: str, xssid: str, cmd: int, **params) -> str:
params.update({"cmd": cmd})
return requests.get(
f"http://{host}/cgi-bin/dispatcher.cgi",
params=params,
cookies={"HTTP_XSSID": xssid},
).text
def parse_vlan_ports(response: str) -> dict:
soup = BeautifulSoup(response, "html.parser")
data = {}
port_settings = soup.find_all(
"input", attrs={"type": "hidden", "id": re.compile(r"vlanMode_\d+")}
)
for port_setting in port_settings:
port = port_setting.find_previous().text.strip()
port_type = int(port_setting.attrs.get("value"))
acl = list(
filter(
lambda p: "checked" in p.attrs,
port_setting.find_next().find_all("input"),
)
)[0]
data.update(
{
port: {
"mode": VLAN_PORT_MODE(port_type),
"acl": VLAN_PORT_ACL(int(acl.attrs.get("value"))),
}
}
)
return data
def extract_data_from_table(response: str) -> dict:
soup = BeautifulSoup(response, "html.parser")
data = {}
table_body = soup.find_all("table")[-1]
rows = table_body.find_all("tr")
keys = [ele.text.strip() for ele in rows[0].find_all("td")]
entries = []
for row in rows[1:]:
cols = row.find_all("td")
cols = [ele.text.strip() for ele in cols]
entries.append({k: v for (k, v) in zip(keys, cols) if k.strip() != ""})
return list(filter(lambda e: e, entries))
def dictify(data: list, key: str) -> dict:
result = {}
for d in data:
k = d[key]
del d[key]
result.update({k: d})
return result
if __name__ == "__main__":
cookie = get_login_cookie(IP, "admin", os.environ.get("ADMIN_PW"))
print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_PORTS.value)), "Port"))
print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_VLANS.value)), "VLAN ID"))
print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_VLAN_PORTS.value)), "Port"))
print(dictify(extract_data_from_table(get_cmd(IP, cookie, API_CMD_IDS.READ_STP_PORTS.value)), "Port"))
print(parse_vlan_ports(get_cmd(IP, cookie, API_CMD_IDS.READ_VLAN_PORT_CFG.value, vid=2)))