Compare commits
22 Commits
686cd6d715
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae1e69d5fd | ||
|
|
260c02f81d | ||
|
|
6bc47f1b4f | ||
|
|
9575450811 | ||
|
|
27d8931dee | ||
|
|
099869ff6d | ||
|
|
472508d51d | ||
|
|
663cbad352 | ||
|
|
79dbb99186 | ||
|
|
6846b30df0 | ||
|
|
6009f0bf9c | ||
|
|
721d4b27ca | ||
|
|
da2bf2f876 | ||
|
|
a521c60d76 | ||
|
|
62530980aa | ||
|
|
fb2d135187 | ||
|
|
074f2e9340 | ||
|
|
dd7f117380 | ||
|
|
3c0e70b320 | ||
|
|
c40cae1561 | ||
| 7b09612313 | |||
|
|
8edd1a296c |
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/
|
wheels/
|
||||||
*.egg-info
|
*.egg-info
|
||||||
main.py
|
main.py
|
||||||
|
.coverage
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
.venv
|
.venv
|
||||||
|
.python-version
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.vscode
|
||||||
# etc files
|
# etc files
|
||||||
*.txt
|
*.txt
|
||||||
@@ -1 +0,0 @@
|
|||||||
3.13
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
from .core import OxiAPI
|
from .core import OxiAPI
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"OxiAPI",
|
"OxiAPI",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from typing import Optional
|
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
from urllib3.util import Retry
|
from urllib3.util import Retry
|
||||||
|
|
||||||
@@ -6,7 +5,7 @@ from urllib3.util import Retry
|
|||||||
class OxiAdapter(HTTPAdapter):
|
class OxiAdapter(HTTPAdapter):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
timeout: Optional[int] = None,
|
timeout: int | None = None,
|
||||||
max_retries: int = 3,
|
max_retries: int = 3,
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
|||||||
45
oxi/conf.py
45
oxi/conf.py
@@ -1,6 +1,7 @@
|
|||||||
from functools import cached_property
|
|
||||||
import json
|
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
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -9,6 +10,9 @@ from .interfaces import BaseDevice, device_registry
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
|
from oxi.interfaces.contract import Interfaces, System, Vlans
|
||||||
|
|
||||||
|
|
||||||
TModel = TypeVar("TModel", bound=BaseModel)
|
TModel = TypeVar("TModel", bound=BaseModel)
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +23,7 @@ class ModelView(Generic[TModel]):
|
|||||||
def dump_json(self) -> str:
|
def dump_json(self) -> str:
|
||||||
if isinstance(self._model, list):
|
if isinstance(self._model, list):
|
||||||
return json.dumps(
|
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,
|
ensure_ascii=False,
|
||||||
)
|
)
|
||||||
return self._model.model_dump_json(by_alias=True)
|
return self._model.model_dump_json(by_alias=True)
|
||||||
@@ -82,13 +86,42 @@ class NodeConfig:
|
|||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
@property
|
@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)
|
return ModelView(self._parsed_data.vlans)
|
||||||
|
|
||||||
@property
|
@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)
|
return ModelView(self._parsed_data.interfaces)
|
||||||
|
|
||||||
@property
|
@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)
|
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 requests import HTTPError, Session
|
||||||
|
|
||||||
from oxi.adapter import OxiAdapter
|
from oxi.adapter import OxiAdapter
|
||||||
from oxi.exception import OxiAPIError
|
from oxi.exception import OxiAPIError
|
||||||
|
|
||||||
from .node import Node
|
from .node import Node
|
||||||
|
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ class OxiAPI:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
url: str,
|
url: str,
|
||||||
username: Optional[str] = None,
|
username: str | None = None,
|
||||||
password: Optional[str] = None,
|
password: str | None = None,
|
||||||
verify: bool = True,
|
verify: bool = True,
|
||||||
):
|
):
|
||||||
self.base_url = url.rstrip("/")
|
self.base_url = url.rstrip("/")
|
||||||
@@ -20,8 +20,8 @@ class OxiAPI:
|
|||||||
|
|
||||||
def __create_session(
|
def __create_session(
|
||||||
self,
|
self,
|
||||||
username: Optional[str] = None,
|
username: str | None = None,
|
||||||
password: Optional[str] = None,
|
password: str | None = None,
|
||||||
verify: bool = True,
|
verify: bool = True,
|
||||||
) -> Session:
|
) -> Session:
|
||||||
session = Session()
|
session = Session()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from requests import HTTPError
|
from requests import HTTPError
|
||||||
@@ -35,7 +35,7 @@ def _looks_like_node_not_found_html(e: "HTTPError") -> bool:
|
|||||||
|
|
||||||
|
|
||||||
class OxiAPIError(Exception):
|
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)
|
super().__init__(message)
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Callable, Type
|
from collections.abc import Callable
|
||||||
|
|
||||||
from .base import BaseDevice
|
from .base import BaseDevice
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ device_registry = {}
|
|||||||
|
|
||||||
def register_parser(
|
def register_parser(
|
||||||
name: list[str] | str,
|
name: list[str] | str,
|
||||||
) -> Callable[[Type[BaseDevice]], Type[BaseDevice]]:
|
) -> Callable[[type[BaseDevice]], type[BaseDevice]]:
|
||||||
def wrapper(cls):
|
def wrapper(cls):
|
||||||
name_list = []
|
name_list = []
|
||||||
if isinstance(name, str):
|
if isinstance(name, str):
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import xml.etree.ElementTree as ET
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from ttp import ttp
|
from ttp import ttp
|
||||||
|
|
||||||
from oxi.exception import OxiAPIError
|
from oxi.exception import OxiAPIError
|
||||||
from oxi.interfaces.contract import Device
|
from oxi.interfaces.contract import Device, Interfaces, System, Vlans
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
from oxi.interfaces.contract import Interfaces, System, Vlans
|
|
||||||
|
|
||||||
|
|
||||||
class BaseDevice(ABC):
|
class BaseDevice(ABC):
|
||||||
@@ -40,7 +41,7 @@ class BaseDevice(ABC):
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: if raw data cannot be validated by the contract.
|
ValueError: if raw data cannot be validated by the contract.
|
||||||
"""
|
""" # noqa: E501
|
||||||
return self.raw.get("vlans", [])
|
return self.raw.get("vlans", [])
|
||||||
|
|
||||||
def interfaces(self) -> list[dict]:
|
def interfaces(self) -> list[dict]:
|
||||||
@@ -52,7 +53,7 @@ class BaseDevice(ABC):
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: if raw data cannot be validated by the contract.
|
ValueError: if raw data cannot be validated by the contract.
|
||||||
"""
|
""" # noqa: E501
|
||||||
return self.raw.get("interfaces", [])
|
return self.raw.get("interfaces", [])
|
||||||
|
|
||||||
def system(self) -> dict:
|
def system(self) -> dict:
|
||||||
@@ -82,11 +83,7 @@ class BaseDevice(ABC):
|
|||||||
|
|
||||||
def _validate_contract(self) -> dict:
|
def _validate_contract(self) -> dict:
|
||||||
if self.raw is None:
|
if self.raw is None:
|
||||||
msg = (
|
msg = f"Node {self.name} not found" if self.name else "Node not found"
|
||||||
f"Node {self.name} not found"
|
|
||||||
if self.name
|
|
||||||
else "Node not found"
|
|
||||||
)
|
|
||||||
raise OxiAPIError(msg, status_code=404)
|
raise OxiAPIError(msg, status_code=404)
|
||||||
system_data = self.system()
|
system_data = self.system()
|
||||||
interfaces_data = self._as_list(self.interfaces())
|
interfaces_data = self._as_list(self.interfaces())
|
||||||
@@ -99,8 +96,8 @@ class BaseDevice(ABC):
|
|||||||
if "vlans" in self._declared_sections:
|
if "vlans" in self._declared_sections:
|
||||||
if "vlans" not in self.raw:
|
if "vlans" not in self.raw:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"{self.__class__.__name__}: template '{self.template}' declares optional group "
|
f"{self.__class__.__name__}: template '{self.template}' "
|
||||||
f"'vlans', but TTP did not return it."
|
f"declares optional group 'vlans', but TTP did not return it."
|
||||||
)
|
)
|
||||||
vlans_data = self._as_list(self.vlans())
|
vlans_data = self._as_list(self.vlans())
|
||||||
result["vlans"] = [Vlans(**item) for item in vlans_data]
|
result["vlans"] = [Vlans(**item) for item in vlans_data]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from ipaddress import IPv4Address
|
from ipaddress import IPv4Address
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ class Interfaces(Base):
|
|||||||
ip_address: IPv4Address | None = None
|
ip_address: IPv4Address | None = None
|
||||||
mask: int | None = None
|
mask: int | None = None
|
||||||
description: str | None = None
|
description: str | None = None
|
||||||
|
shutdown: bool = False
|
||||||
|
|
||||||
|
|
||||||
class Vlans(Base):
|
class Vlans(Base):
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ import pkgutil
|
|||||||
|
|
||||||
package = __package__
|
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}")
|
importlib.import_module(f"{package}.{module_name}")
|
||||||
|
|||||||
@@ -15,10 +15,3 @@ class H3C(BaseDevice):
|
|||||||
continue
|
continue
|
||||||
vlans.extend({"vlan_id": vlan_id} for vlan_id in vlan_ids)
|
vlans.extend({"vlan_id": vlan_id} for vlan_id in vlan_ids)
|
||||||
return vlans
|
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]:
|
def vlans(self) -> list[dict]:
|
||||||
vlan_ids = self.raw.get("vlans", {}).get("vlan_ids", [])
|
vlan_ids = self.raw.get("vlans", {}).get("vlan_ids", [])
|
||||||
return [{"vlan_id": vlan} for vlan in 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,4 +1,5 @@
|
|||||||
from ipaddress import ip_interface
|
from ipaddress import ip_interface
|
||||||
|
|
||||||
from oxi.interfaces import register_parser
|
from oxi.interfaces import register_parser
|
||||||
from oxi.interfaces.base import BaseDevice
|
from oxi.interfaces.base import BaseDevice
|
||||||
from oxi.interfaces.utils import decode_utf
|
from oxi.interfaces.utils import decode_utf
|
||||||
|
|||||||
@@ -25,14 +25,15 @@ Active-image: {{ ignore }} {{ _start_ }}
|
|||||||
|
|
||||||
<group name="interfaces">
|
<group name="interfaces">
|
||||||
interface {{ interface | ORPHRASE }}
|
interface {{ interface | ORPHRASE }}
|
||||||
|
shutdown {{ shutdown | set(True) }}
|
||||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||||
name {{ description }}
|
name {{ description | ORPHRASE}}
|
||||||
description {{ description }}
|
description {{ description | ORPHRASE }}
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group name="vlans">
|
<group name="vlans">
|
||||||
interface vlan {{ vlan_id | _start_ }}
|
interface vlan {{ vlan_id | _start_ }}
|
||||||
name {{ name }}
|
name {{ name | ORPHRASE }}
|
||||||
|
|
||||||
vlan {{ _db_ | _start_ }}
|
vlan {{ _db_ | _start_ }}
|
||||||
vlan {{ vlan_ids | joinmatches(',') | unrange("-", ",") | split(",")}}
|
vlan {{ vlan_ids | joinmatches(',') | unrange("-", ",") | split(",")}}
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ default_system = {
|
|||||||
|
|
||||||
<group name="interfaces">
|
<group name="interfaces">
|
||||||
interface {{ interface }}
|
interface {{ interface }}
|
||||||
|
description {{ description | ORPHRASE }}
|
||||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||||
|
shutdown {{ shutdown | set(True) }}
|
||||||
</group>
|
</group>
|
||||||
|
|
||||||
<group name="vlans">
|
<group name="vlans">
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ default_system = {
|
|||||||
|
|
||||||
<group name="interfaces">
|
<group name="interfaces">
|
||||||
interface {{ interface }}
|
interface {{ interface }}
|
||||||
description {{ description }}
|
description {{ description | ORPHRASE }}
|
||||||
|
shutdown {{ shutdown | set(True) }}
|
||||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||||
</group>
|
</group>
|
||||||
<group name="vlans">
|
<group name="vlans">
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ add address={{ ip_address | _start_ }}/{{ mask }} interface={{ name }} network={
|
|||||||
## not disabled and comment with/without quotes
|
## not disabled and comment with/without quotes
|
||||||
add address={{ ip_address | _start_ }}/{{ mask }} comment={{ description | ORPHRASE | exclude("disabled=") | strip('"')}} interface={{ name }} network={{ network }}
|
add address={{ ip_address | _start_ }}/{{ mask }} comment={{ description | ORPHRASE | exclude("disabled=") | strip('"')}} interface={{ name }} network={{ network }}
|
||||||
## disabled no comment
|
## 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
|
## 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>
|
||||||
|
|
||||||
<group name="vlans">
|
<group name="vlans">
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ default_system = {
|
|||||||
|
|
||||||
<group name="interfaces">
|
<group name="interfaces">
|
||||||
interface {{ interface | ORPHRASE }}
|
interface {{ interface | ORPHRASE }}
|
||||||
|
shutdown {{ shutdown | set(True) }}
|
||||||
description {{ description | ORPHRASE }}
|
description {{ description | ORPHRASE }}
|
||||||
ip address {{ ip_address }} {{ mask | to_cidr }}
|
ip address {{ ip_address }} {{ mask | to_cidr }}
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from oxi.exception import OxiAPIError
|
|||||||
|
|
||||||
from .view import NodeView
|
from .view import NodeView
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING
|
|||||||
|
|
||||||
from .conf import NodeConfig
|
from .conf import NodeConfig
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "oxipy"
|
name = "oxipy"
|
||||||
version = "0.1.0"
|
version = "0.1.4"
|
||||||
description = "Python client for Oxidized API with TTP-based config parsing"
|
description = "Python client for Oxidized API with TTP-based config parsing"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
@@ -22,8 +22,8 @@ dependencies = [
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest>=8",
|
"pytest>=9.0.3",
|
||||||
"responses>=0.25",
|
"responses>=0.26.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
@@ -47,4 +47,12 @@ include-package-data = true
|
|||||||
dev = [
|
dev = [
|
||||||
"pytest>=9.0.3",
|
"pytest>=9.0.3",
|
||||||
"responses>=0.26.1",
|
"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"]
|
||||||
|
|||||||
5
tests/fixtures/eltex/config.expected.json
vendored
5
tests/fixtures/eltex/config.expected.json
vendored
@@ -7,30 +7,35 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "TenGigabitEthernet1/0/2",
|
"interface": "TenGigabitEthernet1/0/2",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "FREE"
|
"description": "FREE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "TenGigabitEthernet1/0/11",
|
"interface": "TenGigabitEthernet1/0/11",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "FREE"
|
"description": "FREE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "vlan 1700",
|
"interface": "vlan 1700",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "13.36.8.1",
|
"ip_address": "13.36.8.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "sw-test_HW"
|
"description": "sw-test_HW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "vlan 666",
|
"interface": "vlan 666",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "test"
|
"description": "test"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "vlan 777",
|
"interface": "vlan 777",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "test2"
|
"description": "test2"
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
11
tests/fixtures/huawei/config.conf
vendored
11
tests/fixtures/huawei/config.conf
vendored
@@ -30,6 +30,17 @@ interface GigabitEthernet0/0/33
|
|||||||
storm-control multicast min-rate 1000 max-rate 2000
|
storm-control multicast min-rate 1000 max-rate 2000
|
||||||
storm-control action error-down
|
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
|
interface Vlanif120
|
||||||
description SSH
|
description SSH
|
||||||
ip address 10.26.196.254 255.255.255.0
|
ip address 10.26.196.254 255.255.255.0
|
||||||
|
|||||||
9
tests/fixtures/huawei/config.expected.json
vendored
9
tests/fixtures/huawei/config.expected.json
vendored
@@ -7,12 +7,21 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "GigabitEthernet0/0/33",
|
"interface": "GigabitEthernet0/0/33",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"interface": "GigabitEthernet1/0/43",
|
||||||
|
"shutdown": true,
|
||||||
|
"ip_address": null,
|
||||||
|
"mask": null,
|
||||||
|
"description": "FREE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"interface": "Vlanif120",
|
"interface": "Vlanif120",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "10.26.196.254",
|
"ip_address": "10.26.196.254",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "SSH"
|
"description": "SSH"
|
||||||
|
|||||||
23
tests/fixtures/keenetic/config.expected.json
vendored
23
tests/fixtures/keenetic/config.expected.json
vendored
@@ -7,138 +7,161 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "GigabitEthernet0",
|
"interface": "GigabitEthernet0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "GigabitEthernet0/1",
|
"interface": "GigabitEthernet0/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "GigabitEthernet0/2",
|
"interface": "GigabitEthernet0/2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "GigabitEthernet0/3",
|
"interface": "GigabitEthernet0/3",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "GigabitEthernet0/0",
|
"interface": "GigabitEthernet0/0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster0",
|
"interface": "WifiMaster0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster0/AccessPoint0",
|
"interface": "WifiMaster0/AccessPoint0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster0/AccessPoint1",
|
"interface": "WifiMaster0/AccessPoint1",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster0/AccessPoint2",
|
"interface": "WifiMaster0/AccessPoint2",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster0/WifiStation0",
|
"interface": "WifiMaster0/WifiStation0",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster1",
|
"interface": "WifiMaster1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster1/AccessPoint0",
|
"interface": "WifiMaster1/AccessPoint0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster1/AccessPoint1",
|
"interface": "WifiMaster1/AccessPoint1",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster1/AccessPoint2",
|
"interface": "WifiMaster1/AccessPoint2",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "WifiMaster1/WifiStation0",
|
"interface": "WifiMaster1/WifiStation0",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "Bridge0",
|
"interface": "Bridge0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "17.36.1.1",
|
"ip_address": "17.36.1.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "Home network"
|
"description": "Home network"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "Bridge1",
|
"interface": "Bridge1",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": "10.1.30.1",
|
"ip_address": "10.1.30.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "Guest network"
|
"description": "Guest network"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "Bridge2",
|
"interface": "Bridge2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "OpenVPN0",
|
"interface": "OpenVPN0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "test_HW-udp"
|
"description": "test_HW-udp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "OpenVPN2",
|
"interface": "OpenVPN2",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "test_HW-tcp"
|
"description": "test_HW-tcp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "Wireguard0",
|
"interface": "Wireguard0",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "10.3.100.1",
|
"ip_address": "10.3.100.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "test_HW"
|
"description": "test_HW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "Wireguard1",
|
"interface": "Wireguard1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "10.1.100.1",
|
"ip_address": "10.1.100.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "test_HW"
|
"description": "test_HW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "Wireguard2",
|
"interface": "Wireguard2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "10.2.100.1",
|
"ip_address": "10.2.100.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "test_HW"
|
"description": "test_HW"
|
||||||
|
|||||||
4
tests/fixtures/mikrotik/config.expected.json
vendored
4
tests/fixtures/mikrotik/config.expected.json
vendored
@@ -7,24 +7,28 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "bridge.LAN",
|
"interface": "bridge.LAN",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "172.16.3.1",
|
"ip_address": "172.16.3.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "ether1",
|
"interface": "ether1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "10.38.3.245",
|
"ip_address": "10.38.3.245",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "wireguard2",
|
"interface": "wireguard2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "10.1.100.2",
|
"ip_address": "10.1.100.2",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "ether4",
|
"interface": "ether4",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": "100.10.10.1",
|
"ip_address": "100.10.10.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": null
|
"description": null
|
||||||
|
|||||||
3
tests/fixtures/qtech/config_1.expected.json
vendored
3
tests/fixtures/qtech/config_1.expected.json
vendored
@@ -7,18 +7,21 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "GigaEthernet1/0/9",
|
"interface": "GigaEthernet1/0/9",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "FREE"
|
"description": "FREE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "VLAN1",
|
"interface": "VLAN1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "192.168.0.1",
|
"ip_address": "192.168.0.1",
|
||||||
"mask": 16,
|
"mask": 16,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "VLAN1002",
|
"interface": "VLAN1002",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "13.36.8.1",
|
"ip_address": "13.36.8.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "test-1002"
|
"description": "test-1002"
|
||||||
|
|||||||
3
tests/fixtures/qtech/config_2.expected.json
vendored
3
tests/fixtures/qtech/config_2.expected.json
vendored
@@ -7,18 +7,21 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "GigaEthernet1/0/9",
|
"interface": "GigaEthernet1/0/9",
|
||||||
|
"shutdown": true,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "FREE"
|
"description": "FREE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "VLAN1",
|
"interface": "VLAN1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "192.168.0.1",
|
"ip_address": "192.168.0.1",
|
||||||
"mask": 16,
|
"mask": 16,
|
||||||
"description": null
|
"description": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "VLAN1002",
|
"interface": "VLAN1002",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "13.36.8.1",
|
"ip_address": "13.36.8.1",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": "test-1002"
|
"description": "test-1002"
|
||||||
|
|||||||
8
tests/fixtures/quasar/config_1.expected.json
vendored
8
tests/fixtures/quasar/config_1.expected.json
vendored
@@ -7,48 +7,56 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "7/4",
|
"interface": "7/4",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_08_N0_p1"
|
"description": "IN DWDM / OUT TEST_HW_08_N0_p1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "8/1",
|
"interface": "8/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_09_N1_p0"
|
"description": "IN DWDM / OUT TEST_HW_09_N1_p0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "8/2",
|
"interface": "8/2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_09_N0_p0"
|
"description": "IN DWDM / OUT TEST_HW_09_N0_p0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "8/3",
|
"interface": "8/3",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_10_N1_p0"
|
"description": "IN DWDM / OUT TEST_HW_10_N1_p0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "8/4",
|
"interface": "8/4",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_10_N0_p0"
|
"description": "IN DWDM / OUT TEST_HW_10_N0_p0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "9/1",
|
"interface": "9/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_11_N0_p0"
|
"description": "IN DWDM / OUT TEST_HW_11_N0_p0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "9/2",
|
"interface": "9/2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "IN DWDM / OUT TEST_HW_11_N0_p1"
|
"description": "IN DWDM / OUT TEST_HW_11_N0_p1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "ethernet",
|
"interface": "ethernet",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "25.25.1.221",
|
"ip_address": "25.25.1.221",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": null
|
"description": null
|
||||||
|
|||||||
14
tests/fixtures/quasar/config_2.expected.json
vendored
14
tests/fixtures/quasar/config_2.expected.json
vendored
@@ -7,84 +7,98 @@
|
|||||||
"interfaces": [
|
"interfaces": [
|
||||||
{
|
{
|
||||||
"interface": "1/1",
|
"interface": "1/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_1_1"
|
"description": "TEST_HW_1_1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "1/3",
|
"interface": "1/3",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_1_3"
|
"description": "TEST_HW_1_3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "2/1",
|
"interface": "2/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_2_1"
|
"description": "TEST_HW_2_1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "2/2",
|
"interface": "2/2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_2_2"
|
"description": "TEST_HW_2_2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "2/3",
|
"interface": "2/3",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_2_3"
|
"description": "TEST_HW_2_3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "2/4",
|
"interface": "2/4",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_2_4"
|
"description": "TEST_HW_2_4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "3/1",
|
"interface": "3/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_3_1"
|
"description": "TEST_HW_3_1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "3/3",
|
"interface": "3/3",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_3_3"
|
"description": "TEST_HW_3_3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "3/4",
|
"interface": "3/4",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_3_4"
|
"description": "TEST_HW_3_4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "4/1",
|
"interface": "4/1",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_4_1"
|
"description": "TEST_HW_4_1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "4/2",
|
"interface": "4/2",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_4_2"
|
"description": "TEST_HW_4_2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "4/3",
|
"interface": "4/3",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_4_3"
|
"description": "TEST_HW_4_3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "4/4",
|
"interface": "4/4",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": null,
|
"ip_address": null,
|
||||||
"mask": null,
|
"mask": null,
|
||||||
"description": "TEST_HW_4_4"
|
"description": "TEST_HW_4_4"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"interface": "ethernet",
|
"interface": "ethernet",
|
||||||
|
"shutdown": false,
|
||||||
"ip_address": "25.25.18.19",
|
"ip_address": "25.25.18.19",
|
||||||
"mask": 24,
|
"mask": 24,
|
||||||
"description": null
|
"description": null
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from conftest import FIXTURES, load
|
from conftest import FIXTURES, load
|
||||||
|
|
||||||
from oxi.interfaces import device_registry
|
from oxi.interfaces import device_registry
|
||||||
|
|
||||||
MODEL_CASES = [
|
MODEL_CASES = [
|
||||||
@@ -12,6 +12,7 @@ MODEL_CASES = [
|
|||||||
("qtech", "config_2.conf", "config_2.expected.json"),
|
("qtech", "config_2.conf", "config_2.expected.json"),
|
||||||
("huawei", "config.conf", "config.expected.json"),
|
("huawei", "config.conf", "config.expected.json"),
|
||||||
("eltex", "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_1.conf", "config_1.expected.json"),
|
||||||
("quasar", "config_2.conf", "config_2.expected.json"),
|
("quasar", "config_2.conf", "config_2.expected.json"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import responses
|
import responses
|
||||||
|
|
||||||
from conftest import load
|
from conftest import load
|
||||||
|
|
||||||
from oxi import OxiAPI
|
from oxi import OxiAPI
|
||||||
from oxi.exception import OxiAPIError
|
from oxi.exception import OxiAPIError
|
||||||
|
|
||||||
@@ -84,4 +84,4 @@ def test_unknown_model_raises_value_error():
|
|||||||
|
|
||||||
api = OxiAPI(url=BASE)
|
api = OxiAPI(url=BASE)
|
||||||
with pytest.raises(ValueError, match="not found in registry"):
|
with pytest.raises(ValueError, match="not found in registry"):
|
||||||
api.node("HQ").config
|
_ = api.node("HQ").config
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from conftest import load
|
from conftest import load
|
||||||
|
|
||||||
from oxi.exception import OxiAPIError
|
from oxi.exception import OxiAPIError
|
||||||
from oxi.interfaces import device_registry
|
from oxi.interfaces import device_registry
|
||||||
from oxi.interfaces.base import BaseDevice
|
from oxi.interfaces.base import BaseDevice
|
||||||
from oxi.interfaces.contract import Interfaces, System
|
|
||||||
from oxi.interfaces.utils import decode_utf, expand_vlan_range
|
from oxi.interfaces.utils import decode_utf, expand_vlan_range
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
from ipaddress import IPv4Address
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@@ -66,6 +67,10 @@ class TestListModelView:
|
|||||||
def test_dump_uses_aliases(self, interfaces_view):
|
def test_dump_uses_aliases(self, interfaces_view):
|
||||||
dumped = interfaces_view.dump()
|
dumped = interfaces_view.dump()
|
||||||
assert dumped[0]["interface"] == "eth0"
|
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):
|
def test_dump_json_keeps_unicode(self):
|
||||||
view = ModelView([Interfaces(interface="eth0", description="Дом")])
|
view = ModelView([Interfaces(interface="eth0", description="Дом")])
|
||||||
|
|||||||
38
uv.lock
generated
38
uv.lock
generated
@@ -158,23 +158,34 @@ dependencies = [
|
|||||||
{ name = "ttp" },
|
{ name = "ttp" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dev-dependencies]
|
[package.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "responses" },
|
{ name = "responses" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[package.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "responses" },
|
||||||
|
{ name = "ruff" },
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "pydantic", specifier = ">=2.12.5" },
|
{ name = "pydantic", specifier = ">=2.12.5" },
|
||||||
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.3" },
|
||||||
{ name = "requests", specifier = ">=2.32.5" },
|
{ name = "requests", specifier = ">=2.32.5" },
|
||||||
|
{ name = "responses", marker = "extra == 'dev'", specifier = ">=0.26.1" },
|
||||||
{ name = "ttp", specifier = ">=0.10.0" },
|
{ name = "ttp", specifier = ">=0.10.0" },
|
||||||
]
|
]
|
||||||
|
provides-extras = ["dev"]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "pytest", specifier = ">=9.0.3" },
|
{ name = "pytest", specifier = ">=9.0.3" },
|
||||||
{ name = "responses", specifier = ">=0.26.1" },
|
{ name = "responses", specifier = ">=0.26.1" },
|
||||||
|
{ name = "ruff", specifier = ">=0.15.17" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user