Compare commits
35 Commits
229bef99f6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 32e99dacf7 | |||
|
|
ae1e69d5fd | ||
|
|
260c02f81d | ||
| 180e830fed | |||
|
|
6bc47f1b4f | ||
|
|
9575450811 | ||
|
|
27d8931dee | ||
|
|
099869ff6d | ||
|
|
472508d51d | ||
|
|
663cbad352 | ||
|
|
79dbb99186 | ||
|
|
6846b30df0 | ||
|
|
6009f0bf9c | ||
|
|
721d4b27ca | ||
|
|
da2bf2f876 | ||
|
|
a521c60d76 | ||
|
|
62530980aa | ||
|
|
fb2d135187 | ||
| 53b79ce04f | |||
|
|
074f2e9340 | ||
|
|
dd7f117380 | ||
|
|
3c0e70b320 | ||
|
|
c40cae1561 | ||
| 7b09612313 | |||
|
|
8edd1a296c | ||
|
|
686cd6d715 | ||
|
|
acb3a6291c | ||
|
|
9c90279868 | ||
|
|
2ea056aa17 | ||
|
|
a617bd6ecd | ||
|
|
0ef5e7798a | ||
|
|
1bc01c9c1b | ||
|
|
170a2ebf85 | ||
|
|
168111e23c | ||
|
|
d329ddc4ad |
50
.gitea/workflows/ci.yml
Normal file
50
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: rassbery
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: Sync dependencies
|
||||
run: uv sync --group dev
|
||||
- name: Ruff lint
|
||||
run: uv run ruff check --output-format=github .
|
||||
- name: Ruff format check
|
||||
run: uv run ruff format --check .
|
||||
|
||||
test:
|
||||
runs-on: rassbery
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Sync dependencies
|
||||
run: uv sync --group dev
|
||||
- name: Run tests
|
||||
run: uv run pytest -q
|
||||
|
||||
ci-success:
|
||||
if: always()
|
||||
needs: [lint, test]
|
||||
runs-on: rassbery
|
||||
container: catthehacker/ubuntu:act-22.04
|
||||
steps:
|
||||
- name: Verify all jobs succeeded
|
||||
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
|
||||
run: exit 1
|
||||
47
.github/workflows/ci.yml
vendored
Normal file
47
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
- name: Sync dependencies
|
||||
run: uv sync --group dev
|
||||
- name: Ruff lint
|
||||
run: uv run ruff check --output-format=github .
|
||||
- name: Ruff format check
|
||||
run: uv run ruff format --check .
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Sync dependencies
|
||||
run: uv sync --group dev
|
||||
- name: Run tests
|
||||
run: uv run pytest -q
|
||||
|
||||
ci-success:
|
||||
if: always()
|
||||
needs: [lint, test]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Verify all jobs succeeded
|
||||
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
|
||||
run: exit 1
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,10 +6,12 @@ dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
main.py
|
||||
.coverage
|
||||
# Virtual environments
|
||||
.venv
|
||||
.python-version
|
||||
.idea
|
||||
.DS_Store
|
||||
|
||||
.vscode
|
||||
# etc files
|
||||
*.txt
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
@@ -1,6 +1,5 @@
|
||||
from .core import OxiAPI
|
||||
|
||||
|
||||
__all__ = [
|
||||
"OxiAPI",
|
||||
]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from typing import Optional
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util import Retry
|
||||
|
||||
@@ -6,7 +5,7 @@ from urllib3.util import Retry
|
||||
class OxiAdapter(HTTPAdapter):
|
||||
def __init__(
|
||||
self,
|
||||
timeout: Optional[int] = None,
|
||||
timeout: int | None = None,
|
||||
max_retries: int = 3,
|
||||
*args,
|
||||
**kwargs,
|
||||
|
||||
45
oxi/conf.py
45
oxi/conf.py
@@ -1,6 +1,7 @@
|
||||
from functools import cached_property
|
||||
import json
|
||||
from typing import TYPE_CHECKING, Generic, Iterator, TypeVar
|
||||
from collections.abc import Iterator
|
||||
from functools import cached_property
|
||||
from typing import TYPE_CHECKING, Generic, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -9,6 +10,9 @@ from .interfaces import BaseDevice, device_registry
|
||||
if TYPE_CHECKING:
|
||||
from requests import Session
|
||||
|
||||
from oxi.interfaces.contract import Interfaces, System, Vlans
|
||||
|
||||
|
||||
TModel = TypeVar("TModel", bound=BaseModel)
|
||||
|
||||
|
||||
@@ -19,7 +23,7 @@ class ModelView(Generic[TModel]):
|
||||
def dump_json(self) -> str:
|
||||
if isinstance(self._model, list):
|
||||
return json.dumps(
|
||||
[item.model_dump(by_alias=True) for item in self._model],
|
||||
[item.model_dump_json(by_alias=True) for item in self._model],
|
||||
ensure_ascii=False,
|
||||
)
|
||||
return self._model.model_dump_json(by_alias=True)
|
||||
@@ -82,13 +86,42 @@ class NodeConfig:
|
||||
return self.text
|
||||
|
||||
@property
|
||||
def vlans(self):
|
||||
def vlans(self) -> list["Vlans"]:
|
||||
"""
|
||||
Returns the list of VLAN configurations for the node.
|
||||
|
||||
:return: List of Vlans associated with the device.
|
||||
vlan_id: int
|
||||
name: str | None = Field(default=None, alias="description")
|
||||
:rtype: list[oxi.interfaces.contract.Vlans]
|
||||
"""
|
||||
return ModelView(self._parsed_data.vlans)
|
||||
return ModelView(self._parsed_data.vlans)
|
||||
|
||||
@property
|
||||
def interfaces(self):
|
||||
def interfaces(self) -> list["Interfaces"]:
|
||||
"""
|
||||
Returns the list of Interfaces configurations for the node.
|
||||
|
||||
:return: List of Interfaces associated with the device.
|
||||
name: str = Field(alias="interface")
|
||||
ip_address: IPv4Address | None = None
|
||||
mask: int | None = None
|
||||
description: str | None = None
|
||||
shutdown: bool = False
|
||||
:rtype: list[oxi.interfaces.contract.Interfaces]
|
||||
"""
|
||||
return ModelView(self._parsed_data.interfaces)
|
||||
|
||||
@property
|
||||
def system(self):
|
||||
def system(self) -> "System":
|
||||
"""
|
||||
Returns the Systems configurations for the node.
|
||||
|
||||
:return: System associated with the device.
|
||||
model: str
|
||||
serial_number: str
|
||||
version: str
|
||||
:rtype: oxi.interfaces.contract.System
|
||||
"""
|
||||
return ModelView(self._parsed_data.system)
|
||||
|
||||
10
oxi/core.py
10
oxi/core.py
@@ -1,8 +1,8 @@
|
||||
from typing import Optional
|
||||
from requests import HTTPError, Session
|
||||
|
||||
from oxi.adapter import OxiAdapter
|
||||
from oxi.exception import OxiAPIError
|
||||
|
||||
from .node import Node
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ class OxiAPI:
|
||||
def __init__(
|
||||
self,
|
||||
url: str,
|
||||
username: Optional[str] = None,
|
||||
password: Optional[str] = None,
|
||||
username: str | None = None,
|
||||
password: str | None = None,
|
||||
verify: bool = True,
|
||||
):
|
||||
self.base_url = url.rstrip("/")
|
||||
@@ -20,8 +20,8 @@ class OxiAPI:
|
||||
|
||||
def __create_session(
|
||||
self,
|
||||
username: Optional[str] = None,
|
||||
password: Optional[str] = None,
|
||||
username: str | None = None,
|
||||
password: str | None = None,
|
||||
verify: bool = True,
|
||||
) -> Session:
|
||||
session = Session()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from requests import HTTPError
|
||||
@@ -35,7 +35,7 @@ def _looks_like_node_not_found_html(e: "HTTPError") -> bool:
|
||||
|
||||
|
||||
class OxiAPIError(Exception):
|
||||
def __init__(self, message: str, status_code: Optional[int] = None):
|
||||
def __init__(self, message: str, status_code: int | None = None):
|
||||
super().__init__(message)
|
||||
self.status_code = status_code
|
||||
self.message = message
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Callable, Type
|
||||
from collections.abc import Callable
|
||||
|
||||
from .base import BaseDevice
|
||||
|
||||
@@ -7,7 +7,7 @@ device_registry = {}
|
||||
|
||||
def register_parser(
|
||||
name: list[str] | str,
|
||||
) -> Callable[[Type[BaseDevice]], Type[BaseDevice]]:
|
||||
) -> Callable[[type[BaseDevice]], type[BaseDevice]]:
|
||||
def wrapper(cls):
|
||||
name_list = []
|
||||
if isinstance(name, str):
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import xml.etree.ElementTree as ET
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
|
||||
from ttp import ttp
|
||||
|
||||
from oxi.exception import OxiAPIError
|
||||
from oxi.interfaces.contract import Device
|
||||
import xml.etree.ElementTree as ET
|
||||
from oxi.interfaces.contract import Interfaces, System, Vlans
|
||||
from oxi.interfaces.contract import Device, Interfaces, System, Vlans
|
||||
|
||||
|
||||
class BaseDevice(ABC):
|
||||
@@ -40,7 +41,7 @@ class BaseDevice(ABC):
|
||||
|
||||
Raises:
|
||||
ValueError: if raw data cannot be validated by the contract.
|
||||
"""
|
||||
""" # noqa: E501
|
||||
return self.raw.get("vlans", [])
|
||||
|
||||
def interfaces(self) -> list[dict]:
|
||||
@@ -52,7 +53,7 @@ class BaseDevice(ABC):
|
||||
|
||||
Raises:
|
||||
ValueError: if raw data cannot be validated by the contract.
|
||||
"""
|
||||
""" # noqa: E501
|
||||
return self.raw.get("interfaces", [])
|
||||
|
||||
def system(self) -> dict:
|
||||
@@ -67,16 +68,25 @@ class BaseDevice(ABC):
|
||||
"""
|
||||
return self.raw.get("system", None)
|
||||
|
||||
@staticmethod
|
||||
def _as_list(data) -> list:
|
||||
"""Normalize a TTP group result to a list.
|
||||
|
||||
TTP returns a single dict when a group matches exactly one entry and a
|
||||
list when it matches several. Callers always expect a list.
|
||||
"""
|
||||
if data is None:
|
||||
return []
|
||||
if isinstance(data, dict):
|
||||
return [data]
|
||||
return data
|
||||
|
||||
def _validate_contract(self) -> dict:
|
||||
if self.raw is None:
|
||||
msg = (
|
||||
f"Node {self.name} not found"
|
||||
if self.name
|
||||
else "Node not found"
|
||||
)
|
||||
msg = f"Node {self.name} not found" if self.name else "Node not found"
|
||||
raise OxiAPIError(msg, status_code=404)
|
||||
system_data = self.system()
|
||||
interfaces_data = self.interfaces() or []
|
||||
interfaces_data = self._as_list(self.interfaces())
|
||||
result = {
|
||||
"system": System(**system_data),
|
||||
"interfaces": [Interfaces(**item) for item in interfaces_data],
|
||||
@@ -86,10 +96,10 @@ class BaseDevice(ABC):
|
||||
if "vlans" in self._declared_sections:
|
||||
if "vlans" not in self.raw:
|
||||
raise ValueError(
|
||||
f"{self.__class__.__name__}: template '{self.template}' declares optional group "
|
||||
f"'vlans', but TTP did not return it."
|
||||
f"{self.__class__.__name__}: template '{self.template}' "
|
||||
f"declares optional group 'vlans', but TTP did not return it."
|
||||
)
|
||||
vlans_data = self.vlans() or []
|
||||
vlans_data = self._as_list(self.vlans())
|
||||
result["vlans"] = [Vlans(**item) for item in vlans_data]
|
||||
return result
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
@@ -25,6 +26,7 @@ class Interfaces(Base):
|
||||
ip_address: IPv4Address | None = None
|
||||
mask: int | None = None
|
||||
description: str | None = None
|
||||
shutdown: bool = False
|
||||
|
||||
|
||||
class Vlans(Base):
|
||||
|
||||
@@ -3,5 +3,5 @@ import pkgutil
|
||||
|
||||
package = __package__
|
||||
|
||||
for loader, module_name, is_pkg in pkgutil.iter_modules(__path__):
|
||||
for _, module_name, _ in pkgutil.iter_modules(__path__):
|
||||
importlib.import_module(f"{package}.{module_name}")
|
||||
|
||||
@@ -1,29 +1,6 @@
|
||||
from oxi.interfaces import register_parser
|
||||
from oxi.interfaces.base import BaseDevice
|
||||
|
||||
|
||||
def _expand_vlan_range(value: str | list[str]) -> list[str]:
|
||||
if isinstance(value, list):
|
||||
value = ",".join(str(item) for item in value)
|
||||
|
||||
result: list[str] = []
|
||||
for part in value.split(","):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
if "-" not in part:
|
||||
result.append(part)
|
||||
continue
|
||||
start_s, end_s = part.split("-", 1)
|
||||
try:
|
||||
start, end = int(start_s), int(end_s)
|
||||
except ValueError:
|
||||
result.append(part)
|
||||
continue
|
||||
if start > end:
|
||||
start, end = end, start
|
||||
result.extend(str(vlan_id) for vlan_id in range(start, end + 1))
|
||||
return result
|
||||
from oxi.interfaces.utils import expand_vlan_range
|
||||
|
||||
|
||||
@register_parser("eltex")
|
||||
@@ -54,15 +31,8 @@ class Eltex(BaseDevice):
|
||||
tail = item.get("vlan_tail")
|
||||
if tail:
|
||||
ids = [*ids, tail] if isinstance(ids, list) else f"{ids},{tail}"
|
||||
for vid in _expand_vlan_range(ids):
|
||||
for vid in expand_vlan_range(ids):
|
||||
if vid in named_vlan:
|
||||
continue
|
||||
vlans.append({"vlan_id": vid})
|
||||
return vlans
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./test_not_found.txt") as file:
|
||||
data = file.read()
|
||||
eltex = Eltex(data)
|
||||
print(eltex.parse())
|
||||
|
||||
@@ -15,10 +15,3 @@ class H3C(BaseDevice):
|
||||
continue
|
||||
vlans.extend({"vlan_id": vlan_id} for vlan_id in vlan_ids)
|
||||
return vlans
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./test5.txt") as file:
|
||||
data = file.read()
|
||||
h3c = H3C(data)
|
||||
print(h3c.parse())
|
||||
@@ -9,10 +9,3 @@ class Huawei(BaseDevice):
|
||||
def vlans(self) -> list[dict]:
|
||||
vlan_ids = self.raw.get("vlans", {}).get("vlan_ids", [])
|
||||
return [{"vlan_id": vlan} for vlan in vlan_ids]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./test4.txt") as file:
|
||||
data = file.read()
|
||||
huawei = Huawei(data)
|
||||
print(huawei.parse())
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
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 +19,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 +27,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())
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import os
|
||||
from oxi.interfaces import register_parser
|
||||
from oxi.interfaces.base import BaseDevice
|
||||
|
||||
@@ -6,11 +5,3 @@ from oxi.interfaces.base import BaseDevice
|
||||
@register_parser(["routeros", "ros", "mikrotik"])
|
||||
class Mikrotik(BaseDevice):
|
||||
template = "mikrotik.ttp"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(os.path.abspath(os.curdir))
|
||||
with open("./test.txt") as file:
|
||||
data = file.read()
|
||||
mikr = Mikrotik(data)
|
||||
print(mikr.parse().json())
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
from oxi.interfaces import register_parser
|
||||
from oxi.interfaces.base import BaseDevice
|
||||
|
||||
|
||||
def _expand_vlan_range(value: str | list[str]) -> list[str]:
|
||||
"""Expand values like '1,7,14-15' into individual VLAN IDs."""
|
||||
if isinstance(value, list):
|
||||
value = ",".join(str(item) for item in value)
|
||||
|
||||
result: list[str] = []
|
||||
if not value:
|
||||
return result
|
||||
for part in value.split(","):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
if "-" in part:
|
||||
start_s, end_s = part.split("-", 1)
|
||||
try:
|
||||
start, end = int(start_s), int(end_s)
|
||||
except ValueError:
|
||||
result.append(part)
|
||||
continue
|
||||
if start > end:
|
||||
start, end = end, start
|
||||
result.extend(str(i) for i in range(start, end + 1))
|
||||
else:
|
||||
result.append(part)
|
||||
return result
|
||||
from oxi.interfaces.utils import expand_vlan_range
|
||||
|
||||
|
||||
@register_parser(["QTECH"])
|
||||
@@ -48,21 +22,8 @@ class Qtech(BaseDevice):
|
||||
tail = item.get("vlan_tail")
|
||||
if tail:
|
||||
ids = [*ids, tail] if isinstance(ids, list) else f"{ids},{tail}"
|
||||
for vid in _expand_vlan_range(ids):
|
||||
for vid in expand_vlan_range(ids):
|
||||
if vid in named_vlan:
|
||||
continue
|
||||
vlans.append({"vlan_id": vid})
|
||||
return vlans
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./test3.txt") as file:
|
||||
data = file.read()
|
||||
qtech = Qtech(data)
|
||||
qt = qtech.parse()
|
||||
print(qt)
|
||||
with open("./test3-1.txt") as file:
|
||||
data = file.read()
|
||||
qtech = Qtech(data)
|
||||
qt = qtech.parse()
|
||||
print(qt)
|
||||
|
||||
@@ -21,17 +21,3 @@ class Quasar(BaseDevice):
|
||||
if ether_interface:
|
||||
interfaces.append(ether_interface)
|
||||
return interfaces
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open("./test7.txt") as file:
|
||||
data = file.read()
|
||||
quasar = Quasar(data)
|
||||
qt = quasar.parse()
|
||||
print(qt)
|
||||
print()
|
||||
with open("./test8.txt") as file:
|
||||
data = file.read()
|
||||
quasar = Quasar(data)
|
||||
qt = quasar.parse()
|
||||
print(qt)
|
||||
|
||||
@@ -25,12 +25,15 @@ Active-image: {{ ignore }} {{ _start_ }}
|
||||
|
||||
<group name="interfaces">
|
||||
interface {{ interface | ORPHRASE }}
|
||||
shutdown {{ shutdown | set(True) }}
|
||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||
name {{ description | ORPHRASE}}
|
||||
description {{ description | ORPHRASE }}
|
||||
</group>
|
||||
|
||||
<group name="vlans">
|
||||
interface vlan {{ vlan_id | _start_ }}
|
||||
name {{ name }}
|
||||
name {{ name | ORPHRASE }}
|
||||
|
||||
vlan {{ _db_ | _start_ }}
|
||||
vlan {{ vlan_ids | joinmatches(',') | unrange("-", ",") | split(",")}}
|
||||
|
||||
@@ -21,7 +21,9 @@ default_system = {
|
||||
|
||||
<group name="interfaces">
|
||||
interface {{ interface }}
|
||||
description {{ description | ORPHRASE }}
|
||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||
shutdown {{ shutdown | set(True) }}
|
||||
</group>
|
||||
|
||||
<group name="vlans">
|
||||
|
||||
@@ -20,7 +20,8 @@ default_system = {
|
||||
|
||||
<group name="interfaces">
|
||||
interface {{ interface }}
|
||||
description {{ description }}
|
||||
description {{ description | ORPHRASE }}
|
||||
shutdown {{ shutdown | set(True) }}
|
||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||
</group>
|
||||
<group name="vlans">
|
||||
|
||||
@@ -28,9 +28,9 @@ add address={{ ip_address | _start_ }}/{{ mask }} interface={{ name }} network={
|
||||
## not disabled and comment with/without quotes
|
||||
add address={{ ip_address | _start_ }}/{{ mask }} comment={{ description | ORPHRASE | exclude("disabled=") | strip('"')}} interface={{ name }} network={{ network }}
|
||||
## disabled no comment
|
||||
add address={{ ip_address | _start_ }}/{{ mask }} disabled={{ disabled | replace("yes","True") | strip('"')}} interface={{ name }} network={{ network }}
|
||||
add address={{ ip_address | _start_ }}/{{ mask }} disabled={{ shutdown | replace("yes","True") | strip('"')}} interface={{ name }} network={{ network }}
|
||||
## disabled with comment with/without quotes
|
||||
add address={{ ip_address | _start_ }}/{{ mask }} comment={{ description | ORPHRASE | exclude("disabled=") | strip('"') }} disabled={{ disabled }} interface={{ name }} network={{ network }}
|
||||
add address={{ ip_address | _start_ }}/{{ mask }} comment={{ description | ORPHRASE | exclude("disabled=") | strip('"') }} disabled={{ shutdown | replace("yes","True") | strip('"')}} interface={{ name }} network={{ network }}
|
||||
</group>
|
||||
|
||||
<group name="vlans">
|
||||
@@ -42,6 +42,6 @@ add comment={{ comment | _start_ | ORPHRASE | exclude("disabled=") | strip('"')}
|
||||
## disabled with comment with/without quotes
|
||||
add comment={{ comment | _start_ | ORPHRASE | exclude("disabled=") | strip('"')}} disabled={{ disabled | replace("yes","True") | strip('"') }} interface={{ interface }} name={{ name | ORPHRASE | strip('"') }} vlan-id={{ vlan_id }}
|
||||
## disabled no comment
|
||||
add interface={{ interface | _start_ }} name={{ name | ORPHRASE | strip('"') }} vlan-id={{ vlan_id }} disabled={{ disabled | replace("yes","True") | strip('"') }}
|
||||
add disabled={{ disabled | _start_ | replace("yes","True") | strip('"') }} interface={{ interface }} name={{ name | ORPHRASE | strip('"') }} vlan-id={{ vlan_id }}
|
||||
</group>
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ default_system = {
|
||||
|
||||
<group name="interfaces">
|
||||
interface {{ interface | ORPHRASE }}
|
||||
shutdown {{ shutdown | set(True) }}
|
||||
description {{ description | ORPHRASE }}
|
||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||
</group>
|
||||
|
||||
@@ -6,7 +6,6 @@ from oxi.exception import OxiAPIError
|
||||
|
||||
from .view import NodeView
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from requests import Session
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from .conf import NodeConfig
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from requests import Session
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "oxipy"
|
||||
version = "0.1.0"
|
||||
version = "0.1.4"
|
||||
description = "Python client for Oxidized API with TTP-based config parsing"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
@@ -22,8 +22,8 @@ dependencies = [
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=8",
|
||||
"responses>=0.25",
|
||||
"pytest>=9.0.3",
|
||||
"responses>=0.26.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -47,4 +47,12 @@ include-package-data = true
|
||||
dev = [
|
||||
"pytest>=9.0.3",
|
||||
"responses>=0.26.1",
|
||||
"ruff>=0.15.17",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py310"
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I", "UP", "B"]
|
||||
|
||||
55
tests/fixtures/eltex/config.conf
vendored
Normal file
55
tests/fixtures/eltex/config.conf
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
!
|
||||
Active-image: flash://system/images/mes3300-669-3R3.ros
|
||||
! Version: 6.6.9.3
|
||||
! Commit: 3a5c2e39
|
||||
! Build: 3 (master)
|
||||
! MD5 Digest: 7bc289cc18be560954bd5cb0afd9b2d5
|
||||
! Date: 22-Sep-2025
|
||||
! Time: 12:38:20
|
||||
! Inactive-image: flash://system/images/_image1.bin
|
||||
! Version: 6.6.2
|
||||
! Commit: 3ebc7503
|
||||
! Build: 5 (master)
|
||||
! MD5 Digest: a3f15a788c97c71e07e90d84c0ff3b12
|
||||
! Date: 20-Nov-2023
|
||||
! Time: 16:39:20
|
||||
!
|
||||
! Unit MAC address Hardware version Serial number
|
||||
! ---- ----------------- ---------------- -------------
|
||||
! 1 90:54:b7:6b:9d:40 01.01.01 ESG7007778
|
||||
! 2 90:54:b7:6b:bb:80 01.01.01 ESG7007777
|
||||
!
|
||||
!
|
||||
!
|
||||
interface TenGigabitEthernet1/0/2
|
||||
shutdown
|
||||
description FREE
|
||||
storm-control broadcast pps 3000
|
||||
storm-control multicast pps 3000
|
||||
exit
|
||||
!
|
||||
interface TenGigabitEthernet1/0/11
|
||||
shutdown
|
||||
description FREE
|
||||
storm-control broadcast pps 3000
|
||||
storm-control multicast pps 3000
|
||||
exit
|
||||
!
|
||||
interface vlan 1700
|
||||
name sw-test_HW
|
||||
ip address 13.36.8.1 255.255.255.0
|
||||
exit
|
||||
!
|
||||
vlan database
|
||||
vlan 114-115,120,130,414,610,999-1000,1701-1703,1705,1801,2001,2011
|
||||
vlan 2021-2022,3157-3158,3333-3334
|
||||
exit
|
||||
!
|
||||
interface vlan 666
|
||||
name test
|
||||
exit
|
||||
!
|
||||
interface vlan 777
|
||||
name test2
|
||||
exit
|
||||
!
|
||||
142
tests/fixtures/eltex/config.expected.json
vendored
Normal file
142
tests/fixtures/eltex/config.expected.json
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "",
|
||||
"serial_number": "ESG7007778",
|
||||
"version": "6.6.9.3"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "TenGigabitEthernet1/0/2",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "FREE"
|
||||
},
|
||||
{
|
||||
"interface": "TenGigabitEthernet1/0/11",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "FREE"
|
||||
},
|
||||
{
|
||||
"interface": "vlan 1700",
|
||||
"shutdown": false,
|
||||
"ip_address": "13.36.8.1",
|
||||
"mask": 24,
|
||||
"description": "sw-test_HW"
|
||||
},
|
||||
{
|
||||
"interface": "vlan 666",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "test"
|
||||
},
|
||||
{
|
||||
"interface": "vlan 777",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "test2"
|
||||
}
|
||||
],
|
||||
"vlans": [
|
||||
{
|
||||
"vlan_id": 1700,
|
||||
"description": "sw-test_HW"
|
||||
},
|
||||
{
|
||||
"vlan_id": 114,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 115,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 120,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 130,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 414,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 610,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 999,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1000,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1701,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1702,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1703,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1705,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1801,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2001,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2011,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2021,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2022,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 3157,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 3158,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 3333,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 3334,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 666,
|
||||
"description": "test"
|
||||
},
|
||||
{
|
||||
"vlan_id": 777,
|
||||
"description": "test2"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
tests/fixtures/eltex/not_found.conf
vendored
Normal file
1
tests/fixtures/eltex/not_found.conf
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node not found
|
||||
170
tests/fixtures/h3c/config.conf
vendored
Normal file
170
tests/fixtures/h3c/config.conf
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
# H3C Comware Software, Version 7.1.070, Release 6616P01
|
||||
# Copyright (c) 2004-2021 New H3C Technologies Co., Ltd. All rights reserved.
|
||||
# Last reboot reason : User reboot
|
||||
#
|
||||
# Boot image: flash:/s9820-cmw710-boot-r6616p01.bin
|
||||
# Boot image version: 7.1.070, Release 6616P01
|
||||
# Compiled May 06 2021 11:00:00
|
||||
# System image: flash:/s9820-cmw710-system-r6616p01.bin
|
||||
# System image version: 7.1.070, Release 6616P01
|
||||
# Compiled May 06 2021 11:00:00
|
||||
#
|
||||
#
|
||||
# MPU(M) Slot 1:
|
||||
# H3C S9820-64H MPU(M) with 1 C2538 Processor(s)
|
||||
# BOARD TYPE: S9820-64H
|
||||
# DRAM: 8192M bytes
|
||||
# FLASH: 3630M bytes
|
||||
# NVRAM: 0K bytes
|
||||
# PCB 1 Version: VER.A
|
||||
# PCB 2 Version: VER.B
|
||||
# PCB 3 Version: VER.A
|
||||
# PCB 4 Version: VER.A
|
||||
# Basic BootWare Version: 105
|
||||
# Extended BootWare Version: 108
|
||||
# CPLD 1 Version: 002
|
||||
# CPLD 2 Version: 027
|
||||
# CPLD 3 Version: 002
|
||||
# CPLD 4 Version: 002
|
||||
# FPGA 1 Version: 001
|
||||
# Release Version: H3C S9820-64H-6616P01
|
||||
# Patch Version: None
|
||||
# Reboot Cause: UserReboot
|
||||
# [SubSlot 0] 64QSFP28
|
||||
#
|
||||
# MPU(S) Slot 2:
|
||||
# H3C S9820-64H MPU(S) with 1 C2538 Processor(s)
|
||||
# BOARD TYPE: S9820-64H
|
||||
# DRAM: 8192M bytes
|
||||
# FLASH: 3630M bytes
|
||||
# NVRAM: 0K bytes
|
||||
# PCB 1 Version: VER.A
|
||||
# PCB 2 Version: VER.B
|
||||
# PCB 3 Version: VER.A
|
||||
# PCB 4 Version: VER.A
|
||||
# Basic BootWare Version: 105
|
||||
# Extended BootWare Version: 108
|
||||
# CPLD 1 Version: 002
|
||||
# CPLD 2 Version: 027
|
||||
# CPLD 3 Version: 002
|
||||
# CPLD 4 Version: 002
|
||||
# FPGA 1 Version: 001
|
||||
# Release Version: H3C S9820-64H-6616P01
|
||||
# Patch Version: None
|
||||
# Reboot Cause: IRFMergeReboot
|
||||
# [SubSlot 0] 64QSFP28
|
||||
# Slot Type State Subslot Soft Ver Patch Ver
|
||||
# 1 S9820-64H Master 0 S9820-64H-6616P01 None
|
||||
# 2 S9820-64H Standby 0 S9820-64H-6616P01 None
|
||||
#
|
||||
vlan 1
|
||||
#
|
||||
vlan 12
|
||||
name BGP to OSPF1
|
||||
#
|
||||
vlan 13
|
||||
name BGP to OSPF2
|
||||
#
|
||||
vlan 15
|
||||
name HW_TEST_1
|
||||
description HW_TEST_1
|
||||
#
|
||||
vlan 222
|
||||
name MGMT
|
||||
description MGMT
|
||||
#
|
||||
vlan 222
|
||||
#
|
||||
vlan 1112 to 1116
|
||||
#
|
||||
vlan 1122
|
||||
name DATA
|
||||
#
|
||||
vlan 1123
|
||||
#
|
||||
vlan 1200
|
||||
#
|
||||
vlan 1512
|
||||
#
|
||||
vlan 1513
|
||||
description cluster HW_TEST_1
|
||||
#
|
||||
vlan 2000
|
||||
description cluster HW_TEST_2
|
||||
#
|
||||
vlan 3377
|
||||
name VRF3377
|
||||
#
|
||||
irf-port 1/2
|
||||
port group interface HundredGigE1/0/63
|
||||
port group interface HundredGigE1/0/64
|
||||
#
|
||||
irf-port 2/1
|
||||
port group interface HundredGigE2/0/63
|
||||
port group interface HundredGigE2/0/64
|
||||
#
|
||||
interface Bridge-Aggregation1
|
||||
description HW_TEST_1
|
||||
port link-type trunk
|
||||
undo port trunk permit vlan 1
|
||||
port trunk permit vlan 221 1112 to 1116 1512 2000
|
||||
link-aggregation mode dynamic
|
||||
#
|
||||
interface Bridge-Aggregation2
|
||||
description HW_TEST_2
|
||||
port link-type trunk
|
||||
undo port trunk permit vlan 1
|
||||
port trunk permit vlan 221 1112 to 1116 1512
|
||||
link-aggregation mode dynamic
|
||||
#
|
||||
interface Bridge-Aggregation3
|
||||
description HW_TEST_3
|
||||
port link-type trunk
|
||||
undo port trunk permit vlan 1
|
||||
port trunk permit vlan 221 1112 to 1116 1512 2000
|
||||
link-aggregation mode dynamic
|
||||
#
|
||||
interface Bridge-Aggregation4
|
||||
description HW_TEST_4
|
||||
port link-type trunk
|
||||
undo port trunk permit vlan 1
|
||||
port trunk permit vlan 221 1112 to 1116 1512
|
||||
link-aggregation mode dynamic
|
||||
#
|
||||
interface NULL0
|
||||
#
|
||||
interface Vlan-interface1
|
||||
dhcp client identifier ascii 0098a92d5735b0-VLAN0001
|
||||
#
|
||||
interface Vlan-interface12
|
||||
description BGP to OSPF1
|
||||
mtu 9008
|
||||
ip address 15.12.16.246 255.255.255.252
|
||||
#
|
||||
interface Vlan-interface3000
|
||||
description L3 to HW_TEST_3
|
||||
ip binding vpn-instance HW_TEST_3
|
||||
ip address 192.168.19.254 255.255.255.128
|
||||
#
|
||||
interface HundredGigE1/0/3
|
||||
port link-mode bridge
|
||||
description HW_TEST_1
|
||||
port link-type trunk
|
||||
undo port trunk permit vlan 1
|
||||
port trunk permit vlan 221 1112 to 1116 1512
|
||||
shutdown
|
||||
storm-constrain broadcast pps 1100 1000
|
||||
storm-constrain multicast pps 1100 1000
|
||||
storm-constrain control shutdown
|
||||
port link-aggregation group 2
|
||||
#
|
||||
interface HundredGigE1/0/63
|
||||
description HW_TEST_2
|
||||
#
|
||||
interface M-GigabitEthernet0/0/0
|
||||
ip address 192.168.10.101 255.255.255.0
|
||||
dhcp client identifier hex 0298a92d5735b0
|
||||
#
|
||||
interface M-GigabitEthernet0/0/1
|
||||
dhcp client identifier hex 0298a92d5735b0
|
||||
#
|
||||
167
tests/fixtures/h3c/config.expected.json
vendored
Normal file
167
tests/fixtures/h3c/config.expected.json
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "S9820-64H",
|
||||
"serial_number": "",
|
||||
"version": "7.1.070"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "Bridge-Aggregation1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "HW_TEST_1"
|
||||
},
|
||||
{
|
||||
"interface": "Bridge-Aggregation2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "HW_TEST_2"
|
||||
},
|
||||
{
|
||||
"interface": "Bridge-Aggregation3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "HW_TEST_3"
|
||||
},
|
||||
{
|
||||
"interface": "Bridge-Aggregation4",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "HW_TEST_4"
|
||||
},
|
||||
{
|
||||
"interface": "NULL0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "Vlan-interface1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "Vlan-interface12",
|
||||
"shutdown": false,
|
||||
"ip_address": "15.12.16.246",
|
||||
"mask": 30,
|
||||
"description": "BGP to OSPF1"
|
||||
},
|
||||
{
|
||||
"interface": "Vlan-interface3000",
|
||||
"shutdown": false,
|
||||
"ip_address": "192.168.19.254",
|
||||
"mask": 25,
|
||||
"description": "L3 to HW_TEST_3"
|
||||
},
|
||||
{
|
||||
"interface": "HundredGigE1/0/3",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "HW_TEST_1"
|
||||
},
|
||||
{
|
||||
"interface": "HundredGigE1/0/63",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "HW_TEST_2"
|
||||
},
|
||||
{
|
||||
"interface": "M-GigabitEthernet0/0/0",
|
||||
"shutdown": false,
|
||||
"ip_address": "192.168.10.101",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "M-GigabitEthernet0/0/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"vlans": [
|
||||
{
|
||||
"vlan_id": 1,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 12,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 13,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 15,
|
||||
"description": "HW_TEST_1"
|
||||
},
|
||||
{
|
||||
"vlan_id": 222,
|
||||
"description": "MGMT"
|
||||
},
|
||||
{
|
||||
"vlan_id": 222,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1122,
|
||||
"description": "DATA"
|
||||
},
|
||||
{
|
||||
"vlan_id": 1123,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1200,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1512,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1513,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2000,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 3377,
|
||||
"description": "HW_TEST_1"
|
||||
},
|
||||
{
|
||||
"vlan_id": 1112,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1113,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1114,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1115,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1116,
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
49
tests/fixtures/huawei/config.conf
vendored
Normal file
49
tests/fixtures/huawei/config.conf
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
# Huawei Versatile Routing Platform Software
|
||||
# VRP (R) software, Version 5.170 (S5731 V200R019C00SPC500)
|
||||
# Copyright (C) 2000-2019 HUAWEI TECH Co., Ltd.
|
||||
#
|
||||
# DDR Memory Size : 4096 M bytes
|
||||
# FLASH Total Memory Size : 1024 M bytes
|
||||
# FLASH Available Memory Size : 739 M bytes
|
||||
# Pcb Version : VER.B
|
||||
# BootROM Version : 0000.04e4
|
||||
# BootLoad Version : 0213.0000
|
||||
# CPLD Version : 0104
|
||||
# Software Version : VRP (R) Software, Version 5.170 (V200R019C00SPC500)
|
||||
# FLASH Version : 0000.0000
|
||||
# PWR1 information
|
||||
# Pcb Version : PWR VER.D
|
||||
# PWR2 information
|
||||
# Pcb Version : PWR VER.D
|
||||
# FAN1 information
|
||||
# Pcb Version : NA
|
||||
# FAN2 information
|
||||
# Pcb Version : NA
|
||||
# ESN of slot 1: 102266666666
|
||||
# ESN of slot 2: 102288888888
|
||||
interface GigabitEthernet0/0/33
|
||||
port link-type access
|
||||
port default vlan 101
|
||||
loopback-detect enable
|
||||
stp disable
|
||||
storm-control broadcast min-rate 1500 max-rate 2500
|
||||
storm-control multicast min-rate 1000 max-rate 2000
|
||||
storm-control action error-down
|
||||
#
|
||||
interface GigabitEthernet1/0/43
|
||||
description FREE
|
||||
shutdown
|
||||
port link-type access
|
||||
port default vlan 102
|
||||
loopback-detect enable
|
||||
stp disable
|
||||
storm-control broadcast min-rate 1500 max-rate 2500
|
||||
storm-control multicast min-rate 1000 max-rate 2000
|
||||
storm-control action error-down
|
||||
#
|
||||
interface Vlanif120
|
||||
description SSH
|
||||
ip address 10.26.196.254 255.255.255.0
|
||||
#
|
||||
vlan batch 13 26 101 to 103 110 120 130 201 to 204 209 to 212 350 360
|
||||
#
|
||||
104
tests/fixtures/huawei/config.expected.json
vendored
Normal file
104
tests/fixtures/huawei/config.expected.json
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "S5731",
|
||||
"serial_number": "102266666666",
|
||||
"version": "5.170"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "GigabitEthernet0/0/33",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "GigabitEthernet1/0/43",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "FREE"
|
||||
},
|
||||
{
|
||||
"interface": "Vlanif120",
|
||||
"shutdown": false,
|
||||
"ip_address": "10.26.196.254",
|
||||
"mask": 24,
|
||||
"description": "SSH"
|
||||
}
|
||||
],
|
||||
"vlans": [
|
||||
{
|
||||
"vlan_id": 13,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 26,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 101,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 102,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 103,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 110,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 120,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 130,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 201,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 202,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 203,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 204,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 209,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 210,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 211,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 212,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 350,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 360,
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
341
tests/fixtures/keenetic/config.conf
vendored
Normal file
341
tests/fixtures/keenetic/config.conf
vendored
Normal file
@@ -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
|
||||
!
|
||||
184
tests/fixtures/keenetic/config.expected.json
vendored
Normal file
184
tests/fixtures/keenetic/config.expected.json
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "Sprinter (KN-3710)",
|
||||
"serial_number": "7777777",
|
||||
"version": "4.03.C.6.2-7"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "GigabitEthernet0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "GigabitEthernet0/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "GigabitEthernet0/2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "GigabitEthernet0/3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "GigabitEthernet0/0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster0/AccessPoint0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster0/AccessPoint1",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster0/AccessPoint2",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster0/WifiStation0",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster1/AccessPoint0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster1/AccessPoint1",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster1/AccessPoint2",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "WifiMaster1/WifiStation0",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "Bridge0",
|
||||
"shutdown": false,
|
||||
"ip_address": "17.36.1.1",
|
||||
"mask": 24,
|
||||
"description": "Home network"
|
||||
},
|
||||
{
|
||||
"interface": "Bridge1",
|
||||
"shutdown": true,
|
||||
"ip_address": "10.1.30.1",
|
||||
"mask": 24,
|
||||
"description": "Guest network"
|
||||
},
|
||||
{
|
||||
"interface": "Bridge2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "OpenVPN0",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "test_HW-udp"
|
||||
},
|
||||
{
|
||||
"interface": "OpenVPN2",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "test_HW-tcp"
|
||||
},
|
||||
{
|
||||
"interface": "Wireguard0",
|
||||
"shutdown": false,
|
||||
"ip_address": "10.3.100.1",
|
||||
"mask": 24,
|
||||
"description": "test_HW"
|
||||
},
|
||||
{
|
||||
"interface": "Wireguard1",
|
||||
"shutdown": false,
|
||||
"ip_address": "10.1.100.1",
|
||||
"mask": 24,
|
||||
"description": "test_HW"
|
||||
},
|
||||
{
|
||||
"interface": "Wireguard2",
|
||||
"shutdown": false,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
123
tests/fixtures/mikrotik/config.conf
vendored
Normal file
123
tests/fixtures/mikrotik/config.conf
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# 2026-02-18 22:32:59 by RouterOS 7.19.3
|
||||
# software id = 0V5S-56MC
|
||||
# version: 7.12
|
||||
# model = C52iG-5HaxD2HaxD
|
||||
# serial number = HE108BBGW0B
|
||||
/interface bridge
|
||||
add name=bridge.LAN
|
||||
/interface ethernet
|
||||
set [ find default-name=ether1 ] mac-address=C4:AD:32:B2:A1:9A poe-out=off
|
||||
set [ find default-name=ether4 ] comment=test
|
||||
/interface vlan
|
||||
add comment="super test vlan" interface=ether4 name="test vlan" vlan-id=255
|
||||
add disabled=yes interface=ether5 name="test test vlan" vlan-id=254
|
||||
/interface list
|
||||
add name=LAN
|
||||
add name=WAN
|
||||
/interface wifi channel
|
||||
add band=2ghz-ax disabled=no name=ch-2ghz width=20/40mhz
|
||||
add band=5ghz-ax disabled=no name=ch-5ghz width=20/40mhz
|
||||
/interface wifi security
|
||||
add authentication-types=wpa2-psk name=common-auth wps=disable
|
||||
/interface wifi configuration
|
||||
add name=common-auth security=common-auth ssid=test_HW
|
||||
/interface wifi
|
||||
set [ find default-name=wifi1 ] channel=ch-5ghz configuration=common-auth configuration.mode=ap disabled=no
|
||||
set [ find default-name=wifi2 ] channel=ch-2ghz configuration=common-auth configuration.mode=ap disabled=no
|
||||
/ip pool
|
||||
add name=dhcp_pool0 ranges=172.16.3.2-172.16.3.254
|
||||
add name=dhcp_pool1 ranges=192.168.3.2-192.168.3.254
|
||||
/ip dhcp-server
|
||||
add address-pool=dhcp_pool0 interface=bridge.LAN lease-time=10h name=dhcp1
|
||||
/ppp profile
|
||||
add name=new_antizapret on-down="/ip dns cache flush\r\
|
||||
\n" on-up="/ip dns cache flush\r\
|
||||
\n"
|
||||
add name=robovps on-down="/ip dns cache flush" on-up="/ip dns cache flush" use-ipv6=no
|
||||
/routing table
|
||||
add disabled=no fib name=test_HW_table
|
||||
/interface bridge port
|
||||
add bridge=bridge.LAN interface=ether2 learn=yes
|
||||
add bridge=bridge.LAN interface=ether3
|
||||
add bridge=bridge.LAN interface=ether4
|
||||
add bridge=bridge.LAN interface=ether5
|
||||
add bridge=bridge.LAN interface=wifi1
|
||||
add bridge=bridge.LAN interface=wifi2
|
||||
/ipv6 settings
|
||||
set disable-ipv6=yes forward=no
|
||||
/interface detect-internet
|
||||
set detect-interface-list=all
|
||||
/interface list member
|
||||
add interface=bridge.LAN list=LAN
|
||||
add interface=ether1 list=WAN
|
||||
/interface ovpn-server server
|
||||
add mac-address=FE:25:E0:B8:66:01 name=ovpn-server1
|
||||
/ip address
|
||||
add address=172.16.3.1/24 interface=bridge.LAN network=172.16.3.0
|
||||
add address=10.38.3.245/24 interface=ether1 network=10.38.3.0
|
||||
add address=10.1.100.2/24 interface=wireguard2 network=10.1.100.0
|
||||
add address=100.10.10.1/24 disabled=yes interface=ether4 network=100.10.10.0
|
||||
/ip dhcp-server lease
|
||||
add address=172.16.3.20 client-id=1:d8:3a:dd:22:28:1d mac-address=D8:3A:DD:21:28:1D server=dhcp1
|
||||
add address=172.16.3.4 client-id=1:2c:cd:29:1a:ea:6d comment=test_HW mac-address=1E:CD:29:8A:EA:6D server=dhcp1
|
||||
/ip dhcp-server network
|
||||
add address=172.16.3.0/24 dns-server=172.16.3.1 gateway=172.16.3.1
|
||||
/ip dns
|
||||
set allow-remote-requests=yes servers=217.10.44.35
|
||||
/ip dns static
|
||||
add address=172.16.3.20 regexp=".*\\.home\$" type=A
|
||||
add address=172.16.3.20 regexp=".*\\.home.uk\$" type=A
|
||||
add address=172.16.3.20 disabled=yes regexp=".*\\.home.uk\$" type=A
|
||||
/ip firewall address-list
|
||||
add address=172.16.3.0/24 list=test_HW
|
||||
add address=172.16.2.0/24 list=test_HW
|
||||
add address=172.16.1.0/24 list=test_HW
|
||||
add address=255.255.255.255 list=test_HW
|
||||
/ip firewall filter
|
||||
add action=drop chain=forward out-interface=ether1 src-address=172.16.3.11
|
||||
add action=accept chain=input src-address=255.255.255.255
|
||||
add action=accept chain=forward in-interface=wireguard2
|
||||
add action=accept chain=forward connection-state=established,related in-interface=ether1
|
||||
add action=accept chain=input connection-state=established,related in-interface=ether1
|
||||
add action=accept chain=input in-interface=bridge.LAN
|
||||
add action=drop chain=input in-interface=ether1
|
||||
add action=drop chain=forward in-interface=ether1
|
||||
/ip firewall mangle
|
||||
add action=passthrough chain=prerouting connection-mark=test_HW src-address=172.16.3.20
|
||||
/ip firewall nat
|
||||
add action=masquerade chain=srcnat out-interface=ether1
|
||||
add action=masquerade chain=srcnat comment=test_HW out-interface=test_HW
|
||||
/ip firewall service-port
|
||||
set ftp disabled=yes
|
||||
set tftp disabled=yes
|
||||
set h323 disabled=yes
|
||||
/ip route
|
||||
add disabled=no distance=1 dst-address=172.16.1.0/24 gateway=10.1.100.1
|
||||
add disabled=no distance=1 dst-address=172.16.2.0/24 gateway=10.1.100.1
|
||||
add disabled=no distance=1 dst-address=255.255.255.255/32 gateway=10.38.3.1
|
||||
add disabled=no distance=3 dst-address=0.0.0.0/0 gateway=10.38.3.1 routing-table=main scope=30 suppress-hw-offload=no target-scope=10
|
||||
add disabled=yes distance=2 dst-address=0.0.0.0/0 gateway=10.38.3.1 routing-table=main scope=30 suppress-hw-offload=no target-scope=10
|
||||
add comment=test_HW disabled=no distance=1 dst-address=185.255.255.255/32 gateway=10.38.3.1 routing-table=main scope=30 suppress-hw-offload=no target-scope=10
|
||||
add comment=test_HW disabled=no distance=1 dst-address=192.168.255.255/32 gateway=10.38.3.1 routing-table=main scope=30 suppress-hw-offload=no target-scope=10
|
||||
/ip service
|
||||
set telnet disabled=yes
|
||||
/system clock
|
||||
set time-zone-name=Europe/Berlin
|
||||
/system identity
|
||||
set name=test_HW
|
||||
/system logging
|
||||
set 0 topics=info,!wireless
|
||||
/system note
|
||||
set show-at-login=no
|
||||
/system ntp client
|
||||
set enabled=yes
|
||||
/system ntp client servers
|
||||
add address=pool.ntp.org
|
||||
/tool mac-server
|
||||
set allowed-interface-list=LAN
|
||||
/tool mac-server mac-winbox
|
||||
set allowed-interface-list=LAN
|
||||
/tool mac-server ping
|
||||
set enabled=no
|
||||
/user aaa
|
||||
set default-group=full use-radius=yes
|
||||
47
tests/fixtures/mikrotik/config.expected.json
vendored
Normal file
47
tests/fixtures/mikrotik/config.expected.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "C52iG-5HaxD2HaxD",
|
||||
"serial_number": "HE108BBGW0B",
|
||||
"version": "7.12"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "bridge.LAN",
|
||||
"shutdown": false,
|
||||
"ip_address": "172.16.3.1",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "ether1",
|
||||
"shutdown": false,
|
||||
"ip_address": "10.38.3.245",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "wireguard2",
|
||||
"shutdown": false,
|
||||
"ip_address": "10.1.100.2",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "ether4",
|
||||
"shutdown": true,
|
||||
"ip_address": "100.10.10.1",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"vlans": [
|
||||
{
|
||||
"vlan_id": 255,
|
||||
"description": "test vlan"
|
||||
},
|
||||
{
|
||||
"vlan_id": 254,
|
||||
"description": "test test vlan"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
tests/fixtures/qtech/config_1.conf
vendored
Normal file
42
tests/fixtures/qtech/config_1.conf
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
! QTECH LLC Internetwork Operating System Software
|
||||
! QSW-8330-40T-DC Series Software, Version 2.2.0C Build 96279, RELEASE SOFTWARE
|
||||
! ROM: System Bootstrap, Version 0.4.7,hardware version:A
|
||||
! Serial num:6060606060606060, ID num:555555555555
|
||||
! System image file is "Switch.bin"
|
||||
! QTECH LLC QSW-8330-40T-DC RISC
|
||||
! 524288K bytes of memory,16384K bytes of flash
|
||||
! Base ethernet MAC Address: 08:c6:b3:08:cf:ff
|
||||
! snmp info:
|
||||
! vend_ID:27514 product_ID:404 system_ID:1.3.6.1.4.1.27514
|
||||
interface GigaEthernet1/0/9
|
||||
shutdown
|
||||
description FREE
|
||||
switchport pvid 102
|
||||
storm-control broadcast threshold 15
|
||||
storm-control broadcast action shutdown
|
||||
storm-control broadcast auto_resume 60s
|
||||
storm-control multicast threshold 10
|
||||
storm-control multicast action shutdown
|
||||
storm-control multicast auto_resume 60s
|
||||
qos policy IPP3 ingress
|
||||
!
|
||||
interface VLAN1
|
||||
ip address 192.168.0.1 255.255.0.0
|
||||
ip mtu 1500
|
||||
no ip directed-broadcast
|
||||
!
|
||||
interface VLAN1002
|
||||
description test-1002
|
||||
ip address 13.36.8.1 255.255.255.0
|
||||
ip mtu 1500
|
||||
no ip directed-broadcast
|
||||
!
|
||||
vlan 772
|
||||
name test
|
||||
!
|
||||
vlan 888
|
||||
name test_super
|
||||
!
|
||||
vlan 1,7,14-15,44,101-102,115,117-124,130-136,139,167,200-205,772
|
||||
,1607
|
||||
vlan 888,2016,2085-2088
|
||||
188
tests/fixtures/qtech/config_1.expected.json
vendored
Normal file
188
tests/fixtures/qtech/config_1.expected.json
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "QSW-8330-40T-DC",
|
||||
"serial_number": "6060606060606060",
|
||||
"version": "96279"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "GigaEthernet1/0/9",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "FREE"
|
||||
},
|
||||
{
|
||||
"interface": "VLAN1",
|
||||
"shutdown": false,
|
||||
"ip_address": "192.168.0.1",
|
||||
"mask": 16,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "VLAN1002",
|
||||
"shutdown": false,
|
||||
"ip_address": "13.36.8.1",
|
||||
"mask": 24,
|
||||
"description": "test-1002"
|
||||
}
|
||||
],
|
||||
"vlans": [
|
||||
{
|
||||
"vlan_id": 772,
|
||||
"description": "test"
|
||||
},
|
||||
{
|
||||
"vlan_id": 888,
|
||||
"description": "test_super"
|
||||
},
|
||||
{
|
||||
"vlan_id": 1,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 7,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 14,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 15,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 44,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 101,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 102,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 115,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 117,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 118,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 119,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 120,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 121,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 122,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 123,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 124,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 130,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 131,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 132,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 133,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 134,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 135,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 136,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 139,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 167,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 200,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 201,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 202,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 203,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 204,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 205,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1607,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2016,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2085,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2086,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2087,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2088,
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
36
tests/fixtures/qtech/config_2.conf
vendored
Normal file
36
tests/fixtures/qtech/config_2.conf
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
! QTECH LLC Internetwork Operating System Software
|
||||
! QSW-8330-40T-DC Series Software, Version 2.2.0C Build 96279, RELEASE SOFTWARE
|
||||
! ROM: System Bootstrap, Version 0.4.7,hardware version:A
|
||||
! Serial num:6060606060606060, ID num:555555555555
|
||||
! System image file is "Switch.bin"
|
||||
! QTECH LLC QSW-8330-40T-DC RISC
|
||||
! 524288K bytes of memory,16384K bytes of flash
|
||||
! Base ethernet MAC Address: 08:c6:b3:08:cf:f7
|
||||
! snmp info:
|
||||
! vend_ID:27514 product_ID:404 system_ID:1.3.6.1.4.1.27514
|
||||
interface GigaEthernet1/0/9
|
||||
shutdown
|
||||
description FREE
|
||||
switchport pvid 102
|
||||
storm-control broadcast threshold 15
|
||||
storm-control broadcast action shutdown
|
||||
storm-control broadcast auto_resume 60s
|
||||
storm-control multicast threshold 10
|
||||
storm-control multicast action shutdown
|
||||
storm-control multicast auto_resume 60s
|
||||
qos policy IPP3 ingress
|
||||
!
|
||||
interface VLAN1
|
||||
ip address 192.168.0.1 255.255.0.0
|
||||
ip mtu 1500
|
||||
no ip directed-broadcast
|
||||
!
|
||||
interface VLAN1002
|
||||
description test-1002
|
||||
ip address 13.36.8.1 255.255.255.0
|
||||
ip mtu 1500
|
||||
no ip directed-broadcast
|
||||
!
|
||||
vlan 1,7,14-15,44,101-102,115,117-124,130-136,139,167,200-205,772
|
||||
,1607
|
||||
vlan 888,2016,2085-2088
|
||||
188
tests/fixtures/qtech/config_2.expected.json
vendored
Normal file
188
tests/fixtures/qtech/config_2.expected.json
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "QSW-8330-40T-DC",
|
||||
"serial_number": "6060606060606060",
|
||||
"version": "96279"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "GigaEthernet1/0/9",
|
||||
"shutdown": true,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "FREE"
|
||||
},
|
||||
{
|
||||
"interface": "VLAN1",
|
||||
"shutdown": false,
|
||||
"ip_address": "192.168.0.1",
|
||||
"mask": 16,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"interface": "VLAN1002",
|
||||
"shutdown": false,
|
||||
"ip_address": "13.36.8.1",
|
||||
"mask": 24,
|
||||
"description": "test-1002"
|
||||
}
|
||||
],
|
||||
"vlans": [
|
||||
{
|
||||
"vlan_id": 1,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 7,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 14,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 15,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 44,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 101,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 102,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 115,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 117,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 118,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 119,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 120,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 121,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 122,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 123,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 124,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 130,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 131,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 132,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 133,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 134,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 135,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 136,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 139,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 167,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 200,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 201,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 202,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 203,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 204,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 205,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 772,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 1607,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 888,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2016,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2085,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2086,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2087,
|
||||
"description": null
|
||||
},
|
||||
{
|
||||
"vlan_id": 2088,
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
||||
41
tests/fixtures/quasar/config_1.conf
vendored
Normal file
41
tests/fixtures/quasar/config_1.conf
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright © 2021-2022, TechArgos LLC
|
||||
# ----------- -----------------
|
||||
# Subsystem Version
|
||||
# ----------- -----------------
|
||||
# Engine 0.2.17.2022-10-21
|
||||
# DPlane 0.2.18.2022-05-16
|
||||
# BfMonitor 1.1.2.2022-08-25
|
||||
# CLI.core 1.0.3.2022-09-30
|
||||
# CLI.engine 1.2.2.2022-10-12
|
||||
# RConsole 0.3.4.2022-01-12
|
||||
# RcAppParams 0.3.1.2022-01-12
|
||||
# SNMP 0.0.11.2022-04-26
|
||||
# Zabbix 0.2.13.2022-01-26
|
||||
# WebUI 1.1.3.2022-10-19
|
||||
# BF.core 9.3.1.2021-01-30
|
||||
#
|
||||
# ------------------------- ---------------
|
||||
# Platform EEPROM field Value
|
||||
# ------------------------- ---------------
|
||||
# Product Name Quasar-T-Q-0002
|
||||
# Product Number HB4NC011234M
|
||||
# Local MAC N/A
|
||||
# Product Serial Number WEE1C1CC0004A
|
||||
# Product Version 0.0
|
||||
# System Manufacturing Date 2026-06-07
|
||||
config ethernet ipv4 address 25.25.1.221/24 gateway 25.25.1.254 enable
|
||||
|
||||
config interface 7/4 fec none mode force-up enable
|
||||
config interface 8/1 fec none mode force-up enable
|
||||
config interface 8/2 fec none mode force-up enable
|
||||
config interface 8/3 fec none mode force-up enable
|
||||
config interface 8/4 fec none mode force-up enable
|
||||
config interface 9/1 fec none mode force-up enable
|
||||
config interface 9/2 fec none mode force-up enable
|
||||
config interface 7/4 description "IN DWDM / OUT TEST_HW_08_N0_p1"
|
||||
config interface 8/1 description "IN DWDM / OUT TEST_HW_09_N1_p0"
|
||||
config interface 8/2 description "IN DWDM / OUT TEST_HW_09_N0_p0"
|
||||
config interface 8/3 description "IN DWDM / OUT TEST_HW_10_N1_p0"
|
||||
config interface 8/4 description "IN DWDM / OUT TEST_HW_10_N0_p0"
|
||||
config interface 9/1 description "IN DWDM / OUT TEST_HW_11_N0_p0"
|
||||
config interface 9/2 description "IN DWDM / OUT TEST_HW_11_N0_p1"
|
||||
66
tests/fixtures/quasar/config_1.expected.json
vendored
Normal file
66
tests/fixtures/quasar/config_1.expected.json
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "Quasar-T-Q-0002",
|
||||
"serial_number": "WEE1C1CC0004A",
|
||||
"version": "0.2.17.2022-10-21"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "7/4",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_08_N0_p1"
|
||||
},
|
||||
{
|
||||
"interface": "8/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_09_N1_p0"
|
||||
},
|
||||
{
|
||||
"interface": "8/2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_09_N0_p0"
|
||||
},
|
||||
{
|
||||
"interface": "8/3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_10_N1_p0"
|
||||
},
|
||||
{
|
||||
"interface": "8/4",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_10_N0_p0"
|
||||
},
|
||||
{
|
||||
"interface": "9/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_11_N0_p0"
|
||||
},
|
||||
{
|
||||
"interface": "9/2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "IN DWDM / OUT TEST_HW_11_N0_p1"
|
||||
},
|
||||
{
|
||||
"interface": "ethernet",
|
||||
"shutdown": false,
|
||||
"ip_address": "25.25.1.221",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"vlans": []
|
||||
}
|
||||
43
tests/fixtures/quasar/config_2.conf
vendored
Normal file
43
tests/fixtures/quasar/config_2.conf
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Copyright © 2021-2024, TechArgos LLC
|
||||
# ----------- ------------------
|
||||
# Component Version
|
||||
# ----------- ------------------
|
||||
# Assembly 0.2.23_9.9.1_GA
|
||||
# Engine 2.23.15.2024-08-22
|
||||
# DPlane 0.2.20.2023-10-17
|
||||
# BfMonitor 1.4.0.2024-08-27
|
||||
# CLI.core 1.0.3.2022-09-30
|
||||
# CLI.engine 1.3.4.2024-08-22
|
||||
# RConsole 1.0.1.2022-12-23
|
||||
# RcAppParams 1.0.1.2022-12-23
|
||||
# SNMP 0.3.0.2024-07-08
|
||||
# Zabbix 0.2.13.2022-01-26
|
||||
# WebUI 1.3.34.2024-08-30
|
||||
# NMS.agent 1.2.12.2024-08-21
|
||||
# SysLog 0.7.12.2024-02-02
|
||||
# NTPd 0.1.5.2024-07-15
|
||||
# AAA 0.0.8.2023-11-16
|
||||
#
|
||||
# ------------------------- --------------------
|
||||
# Platform EEPROM field Value
|
||||
# ------------------------- --------------------
|
||||
# Product Name D5232C-T
|
||||
# Part Number TA-PB-D5232C-T-AC-PI
|
||||
# Local MAC N/A
|
||||
# Product Serial Number WHF1C87123456A
|
||||
# Product Version 0.0
|
||||
# System Manufacturing Date 2026-06-07
|
||||
config ethernet ipv4 address 25.25.18.19/24 gateway 25.25.18.254 enable
|
||||
config interface 1/1 description "TEST_HW_1_1"
|
||||
config interface 1/3 description "TEST_HW_1_3"
|
||||
config interface 2/1 description "TEST_HW_2_1"
|
||||
config interface 2/2 description "TEST_HW_2_2"
|
||||
config interface 2/3 description "TEST_HW_2_3"
|
||||
config interface 2/4 description "TEST_HW_2_4"
|
||||
config interface 3/1 description "TEST_HW_3_1"
|
||||
config interface 3/3 description "TEST_HW_3_3"
|
||||
config interface 3/4 description "TEST_HW_3_4"
|
||||
config interface 4/1 description "TEST_HW_4_1"
|
||||
config interface 4/2 description "TEST_HW_4_2"
|
||||
config interface 4/3 description "TEST_HW_4_3"
|
||||
config interface 4/4 description "TEST_HW_4_4"
|
||||
108
tests/fixtures/quasar/config_2.expected.json
vendored
Normal file
108
tests/fixtures/quasar/config_2.expected.json
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"system": {
|
||||
"model": "D5232C-T",
|
||||
"serial_number": "WHF1C87123456A",
|
||||
"version": "0.2.23_9.9.1_GA"
|
||||
},
|
||||
"interfaces": [
|
||||
{
|
||||
"interface": "1/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_1_1"
|
||||
},
|
||||
{
|
||||
"interface": "1/3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_1_3"
|
||||
},
|
||||
{
|
||||
"interface": "2/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_2_1"
|
||||
},
|
||||
{
|
||||
"interface": "2/2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_2_2"
|
||||
},
|
||||
{
|
||||
"interface": "2/3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_2_3"
|
||||
},
|
||||
{
|
||||
"interface": "2/4",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_2_4"
|
||||
},
|
||||
{
|
||||
"interface": "3/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_3_1"
|
||||
},
|
||||
{
|
||||
"interface": "3/3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_3_3"
|
||||
},
|
||||
{
|
||||
"interface": "3/4",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_3_4"
|
||||
},
|
||||
{
|
||||
"interface": "4/1",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_4_1"
|
||||
},
|
||||
{
|
||||
"interface": "4/2",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_4_2"
|
||||
},
|
||||
{
|
||||
"interface": "4/3",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_4_3"
|
||||
},
|
||||
{
|
||||
"interface": "4/4",
|
||||
"shutdown": false,
|
||||
"ip_address": null,
|
||||
"mask": null,
|
||||
"description": "TEST_HW_4_4"
|
||||
},
|
||||
{
|
||||
"interface": "ethernet",
|
||||
"shutdown": false,
|
||||
"ip_address": "25.25.18.19",
|
||||
"mask": 24,
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"vlans": []
|
||||
}
|
||||
39
tests/test_models.py
Normal file
39
tests/test_models.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from conftest import FIXTURES, load
|
||||
|
||||
from oxi.interfaces import device_registry
|
||||
|
||||
MODEL_CASES = [
|
||||
("mikrotik", "config.conf", "config.expected.json"),
|
||||
("keenetic", "config.conf", "config.expected.json"),
|
||||
("qtech", "config_1.conf", "config_1.expected.json"),
|
||||
("qtech", "config_2.conf", "config_2.expected.json"),
|
||||
("huawei", "config.conf", "config.expected.json"),
|
||||
("eltex", "config.conf", "config.expected.json"),
|
||||
("h3c", "config.conf", "config.expected.json"),
|
||||
("quasar", "config_1.conf", "config_1.expected.json"),
|
||||
("quasar", "config_2.conf", "config_2.expected.json"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model_key, fixture, expected_file", MODEL_CASES)
|
||||
def test_parse_matches_golden(model_key, fixture, expected_file):
|
||||
cls = device_registry[model_key]
|
||||
raw = load(model_key, fixture)
|
||||
|
||||
parsed = cls(raw).parse().model_dump(by_alias=True, mode="json")
|
||||
|
||||
expected = json.loads((FIXTURES / model_key / expected_file).read_text("utf-8"))
|
||||
assert parsed == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model_key, fixture, _expected", MODEL_CASES)
|
||||
def test_parse_has_required_sections(model_key, fixture, _expected):
|
||||
cls = device_registry[model_key]
|
||||
device = cls(load(model_key, fixture)).parse()
|
||||
|
||||
assert device.system is not None
|
||||
assert isinstance(device.interfaces, list)
|
||||
assert isinstance(device.vlans, list)
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
import responses
|
||||
|
||||
from conftest import load
|
||||
|
||||
from oxi import OxiAPI
|
||||
from oxi.exception import OxiAPIError
|
||||
|
||||
@@ -84,4 +84,4 @@ def test_unknown_model_raises_value_error():
|
||||
|
||||
api = OxiAPI(url=BASE)
|
||||
with pytest.raises(ValueError, match="not found in registry"):
|
||||
api.node("HQ").config
|
||||
_ = api.node("HQ").config
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import pytest
|
||||
|
||||
from conftest import load
|
||||
|
||||
from oxi.exception import OxiAPIError
|
||||
from oxi.interfaces import device_registry
|
||||
from oxi.interfaces.base import BaseDevice
|
||||
from oxi.interfaces.contract import Interfaces, System
|
||||
from oxi.interfaces.utils import decode_utf, expand_vlan_range
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -66,6 +67,10 @@ class TestListModelView:
|
||||
def test_dump_uses_aliases(self, interfaces_view):
|
||||
dumped = interfaces_view.dump()
|
||||
assert dumped[0]["interface"] == "eth0"
|
||||
assert dumped[0]["ip_address"] == IPv4Address("192.168.1.1")
|
||||
|
||||
def test_dump_json(self, interfaces_view):
|
||||
assert interfaces_view.dump_json()
|
||||
|
||||
def test_dump_json_keeps_unicode(self):
|
||||
view = ModelView([Interfaces(interface="eth0", description="Дом")])
|
||||
|
||||
38
uv.lock
generated
38
uv.lock
generated
@@ -158,23 +158,34 @@ dependencies = [
|
||||
{ name = "ttp" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
[package.optional-dependencies]
|
||||
dev = [
|
||||
{ name = "pytest" },
|
||||
{ name = "responses" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "pytest" },
|
||||
{ name = "responses" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "pydantic", specifier = ">=2.12.5" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.3" },
|
||||
{ name = "requests", specifier = ">=2.32.5" },
|
||||
{ name = "responses", marker = "extra == 'dev'", specifier = ">=0.26.1" },
|
||||
{ name = "ttp", specifier = ">=0.10.0" },
|
||||
]
|
||||
provides-extras = ["dev"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "pytest", specifier = ">=9.0.3" },
|
||||
{ name = "responses", specifier = ">=0.26.1" },
|
||||
{ name = "ruff", specifier = ">=0.15.17" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -448,6 +459,31 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/31/6a620b4427d546b9e7cca8b3b8c5f0559d9cef2bb9eedcda7f73c1473c19/responses-0.26.1-py3-none-any.whl", hash = "sha256:8aacc4586eb08fb2208ef64a9eb4258d9b0c6e6f4260845f2f018ab847495345", size = 35502, upload-time = "2026-05-21T19:56:38.046Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.17"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.1"
|
||||
|
||||
Reference in New Issue
Block a user