├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ ├── config.yml
│ └── feature_request.yaml
└── workflows
│ └── ci.yml
├── .gitignore
├── COMMANDS.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── contributing
│ ├── coding_style.md
│ ├── please_read_first.md
│ └── tests.md
├── daemon Common variables.MD
├── img
│ └── logo.png
├── index.md
├── media
│ ├── subnet_initalization.png
│ └── vscode_autocomplete.png
└── stylesheets
│ └── extra.css
├── mkdocs.yml
├── poetry.lock
├── pyisckea
├── __init__.py
├── daemons
│ ├── __init__.py
│ ├── ctrlagent.py
│ ├── ddns.py
│ ├── dhcp4.py
│ └── dhcp6.py
├── exceptions.py
├── kea.py
├── models
│ ├── __init__.py
│ ├── ctrlagent
│ │ ├── __init__.py
│ │ └── config.py
│ ├── dhcp4
│ │ ├── __init__.py
│ │ ├── client_class.py
│ │ ├── config.py
│ │ ├── lease.py
│ │ ├── reservation.py
│ │ ├── shared_network.py
│ │ └── subnet.py
│ ├── dhcp6
│ │ ├── __init__.py
│ │ ├── client_class.py
│ │ ├── config.py
│ │ ├── lease.py
│ │ ├── pd_pool.py
│ │ ├── reservation.py
│ │ ├── server_id.py
│ │ ├── shared_network.py
│ │ └── subnet.py
│ ├── enums.py
│ └── generic
│ │ ├── __init__.py
│ │ ├── api_response.py
│ │ ├── authentication.py
│ │ ├── base.py
│ │ ├── client_class.py
│ │ ├── config.py
│ │ ├── control_socket.py
│ │ ├── daemon.py
│ │ ├── database.py
│ │ ├── dhcp_common.py
│ │ ├── dhcp_ddns.py
│ │ ├── dhcp_queue_control.py
│ │ ├── high_availability.py
│ │ ├── hook.py
│ │ ├── lease.py
│ │ ├── logger.py
│ │ ├── multi_threading.py
│ │ ├── option_data.py
│ │ ├── option_def.py
│ │ ├── pool.py
│ │ ├── relay.py
│ │ ├── remote_map.py
│ │ ├── remote_server.py
│ │ ├── reservation.py
│ │ ├── sanity_check.py
│ │ ├── shared_network.py
│ │ ├── sockets.py
│ │ ├── status.py
│ │ └── subnet.py
└── parsers
│ ├── __init__.py
│ ├── ctrlagent.py
│ ├── ddns.py
│ ├── dhcp4.py
│ ├── dhcp6.py
│ ├── exceptions.py
│ └── generic.py
├── pyproject.toml
├── requirements.txt
└── tests
├── ci
├── models
│ ├── test_ci_kea_ctrlagent_config_model.py
│ ├── test_ci_kea_dhcp4_config_model.py
│ └── test_ci_kea_dhcp6_config_model.py
└── parsers
│ ├── test_ci_kea_dhcp4_parser.py
│ └── test_ci_kea_dhcp6_parser.py
├── configs
├── ctrlagent_api_config.json
├── ctrlagent_parsed_config.json
├── dhcp4_api_config.json
├── dhcp4_parsed_config.json
└── dhcp6_api_config.json
├── conftest.py
├── ctrlagent
├── test_kea_ctrlagent.py
└── test_kea_ctrlagent_parser.py
├── dhcp4
├── remote
│ ├── test_kea_dhcp4_remote.py
│ ├── test_kea_dhcp4_remote_class4.py
│ ├── test_kea_dhcp4_remote_global_parameter4.py
│ ├── test_kea_dhcp4_remote_network4.py
│ ├── test_kea_dhcp4_remote_option4_global.py
│ ├── test_kea_dhcp4_remote_option4_network.py
│ ├── test_kea_dhcp4_remote_option4_pool.py
│ ├── test_kea_dhcp4_remote_option4_subnet.py
│ ├── test_kea_dhcp4_remote_option_def4.py
│ ├── test_kea_dhcp4_remote_reservation.py
│ └── test_kea_dhcp4_remote_subnet4.py
├── test_kea_dhcp4.py
├── test_kea_dhcp4_cache.py
├── test_kea_dhcp4_class.py
├── test_kea_dhcp4_ha.py
├── test_kea_dhcp4_lease4.py
├── test_kea_dhcp4_network4.py
├── test_kea_dhcp4_parser.py
├── test_kea_dhcp4_statistics.py
└── test_kea_dhcp4_subnet4.py
├── dhcp6
├── remote
│ ├── test_kea_dhcp6_remote.py
│ ├── test_kea_dhcp6_remote_class6.py
│ ├── test_kea_dhcp6_remote_global_parameter6.py
│ ├── test_kea_dhcp6_remote_network6.py
│ ├── test_kea_dhcp6_remote_option6_global.py
│ ├── test_kea_dhcp6_remote_option6_network.py
│ ├── test_kea_dhcp6_remote_option6_pd_pool.py
│ ├── test_kea_dhcp6_remote_option6_pool.py
│ ├── test_kea_dhcp6_remote_option6_subnet.py
│ ├── test_kea_dhcp6_remote_option_def6.py
│ ├── test_kea_dhcp6_remote_reservation.py
│ └── test_kea_dhcp6_remote_subnet6.py
├── test_kea_dhcp6.py
├── test_kea_dhcp6_cache.py
├── test_kea_dhcp6_class.py
├── test_kea_dhcp6_ha.py
├── test_kea_dhcp6_lease6.py
├── test_kea_dhcp6_network6.py
├── test_kea_dhcp6_parser.py
├── test_kea_dhcp6_statistics.py
└── test_kea_dhcp6_subnet6.py
└── test_infrastructure
├── Dockerfiles
├── Dockerfile
└── Dockerfile.perfdhcp
├── configs
├── ha
│ ├── primary
│ │ ├── kea-ctrl-agent.conf
│ │ ├── kea-dhcp4-sql.conf
│ │ └── kea-dhcp6-sql.conf
│ └── standby
│ │ ├── kea-ctrl-agent.conf
│ │ ├── kea-dhcp4-sql.conf
│ │ └── kea-dhcp6-sql.conf
├── kea-ctrl-agent.conf
├── kea-ddns.conf
├── kea-dhcp4-sql.conf
├── kea-dhcp4.conf
├── kea-dhcp6-sql.conf
├── kea-dhcp6.conf
├── mysqld.cnf
└── supervisor
│ ├── conf.d
│ ├── kea-agent.conf
│ ├── kea-dhcp4.conf
│ └── kea-dhcp6.conf
│ └── supervisord.conf
├── db-init
├── 01-create-schema.sql
├── 02-create-users.sql
└── dhcpdb_create.mysql.license
├── docker-compose-ha-sql.yml
├── docker-compose-sql.yml
└── docker-compose.yml
/.github/ISSUE_TEMPLATE/bug_report.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | description: Report a bug that is currently in the latest pyisckea release
4 | labels: ["type: bug"]
5 | body:
6 | - type: dropdown
7 | attributes:
8 | label: Python version
9 | description: What version of Python are you currently running?
10 | options:
11 | - "3.9"
12 | - "3.10"
13 | - "3.11"
14 | validations:
15 | required: true
16 | - type: textarea
17 | attributes:
18 | label: How to reproduce this locally
19 | description: >
20 | Please note here the exact steps you take so that other people can reproduce the same bug and
21 | attempt to fix the issue. Ensure you are using the latest release of pyisckea and that you are
22 | not using any other modules that enhance pyisckea itself.
23 | placeholder: |
24 | 1. Setup Kea class
25 | 2. call dhcp4.subnet4_list()
26 | 3. Attempt to access .something variable from the returned subnets
27 | validations:
28 | required: true
29 | - type: textarea
30 | attributes:
31 | label: What should happen?
32 | description: Explain what you think should have happened
33 | placeholder: I should be able to access the data from `.something` when using the Subnet4 Pydantic Model
34 | validations:
35 | required: true
36 | - type: textarea
37 | attributes:
38 | label: What happened?
39 | description: Explain what happened instead
40 | placeholder: The .something variable had no data in it
41 | validations:
42 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | description: Suggest a new feature or enhancement for pyisckea
4 | labels: ["type: feature"]
5 | body:
6 | - type: dropdown
7 | attributes:
8 | label: Daemon/Area
9 | options:
10 | - ctrlagent
11 | - dhcp4
12 | - dhcp6
13 | - ddns
14 | - Pydantic Models
15 | - All Daemons
16 | validations:
17 | required: true
18 | - type: textarea
19 | attributes:
20 | label: Feature/Enhancement you are proposing
21 | description: >
22 | Describe in detail what feature or enhancement you would like to add to
23 | pyisckea.
24 | validations:
25 | required: true
26 | - type: textarea
27 | attributes:
28 | label: Reason for Feature/Enhancement
29 | description: >
30 | Describe the purpose of this change and what it solves for you or the community.
31 | validations:
32 | required: true
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Publish Python 🐍 distribution 📦 to PyPI and TestPyPI
2 | on:
3 | release:
4 | types: [created]
5 |
6 | jobs:
7 | lint:
8 | name: Lint and Check Format
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: astral-sh/ruff-action@v3
14 | with:
15 | src: "./pyisckea"
16 | - uses: astral-sh/ruff-action@v3
17 | with:
18 | args: "format --check --diff"
19 | src: "./pyisckea"
20 |
21 | #test:
22 | # name: Run tests
23 | # runs-on: ubuntu-latest
24 | #
25 | # steps:
26 | # - uses: actions/checkout@v4
27 | # with:
28 | # persist-credentials: false
29 | # - name: Set up Python
30 | # uses: actions/setup-python@v5
31 | # with:
32 | # python-version: "3.12"
33 | # - name: Ruff Check
34 | # uses: jpetrucciani/ruff-check@main
35 | # with:
36 | # path: 'cgn_ec/'
37 | # - name: Test with pytest
38 | # run: |
39 | # pip install pytest pytest-cov
40 | # pytest tests/ --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-
41 |
42 | build:
43 | name: Build distribution 📦
44 | runs-on: ubuntu-latest
45 |
46 | steps:
47 | - uses: actions/checkout@v4
48 | with:
49 | persist-credentials: false
50 | - name: Set up Python
51 | uses: actions/setup-python@v5
52 | with:
53 | python-version: "3.12"
54 | - name: Install pypa/build
55 | run: >-
56 | python3 -m
57 | pip install
58 | build
59 | --user
60 | - name: Build a binary wheel and a source tarball
61 | run: python3 -m build
62 | - name: Store the distribution packages
63 | uses: actions/upload-artifact@v4
64 | with:
65 | name: python-package-distributions
66 | path: dist/
67 |
68 | publish-to-pypi:
69 | name: >-
70 | Publish Python 🐍 distribution 📦 to PyPI
71 | if: startsWith(github.ref, 'refs/tags/') && github.event.release.prerelease == false
72 | needs:
73 | - build
74 | runs-on: ubuntu-latest
75 | environment:
76 | name: pypi
77 | url: https://pypi.org/p/cgn-ec
78 | permissions:
79 | id-token: write
80 |
81 | steps:
82 | - name: Download all the dists
83 | uses: actions/download-artifact@v4
84 | with:
85 | name: python-package-distributions
86 | path: dist/
87 | - name: Publish distribution 📦 to PyPI
88 | uses: pypa/gh-action-pypi-publish@release/v1
89 |
90 | publish-to-testpypi:
91 | name: Publish Python 🐍 distribution 📦 to TestPyPI
92 | if: startsWith(github.ref, 'refs/tags/') && github.event.release.prerelease == true
93 | needs:
94 | - build
95 | runs-on: ubuntu-latest
96 |
97 | environment:
98 | name: testpypi
99 | url: https://test.pypi.org/p/cgn-ec
100 |
101 | permissions:
102 | id-token: write
103 |
104 | steps:
105 | - name: Download all the dists
106 | uses: actions/download-artifact@v4
107 | with:
108 | name: python-package-distributions
109 | path: dist/
110 | - name: Publish distribution 📦 to TestPyPI
111 | uses: pypa/gh-action-pypi-publish@release/v1
112 | with:
113 | repository-url: https://test.pypi.org/legacy/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # venv
2 | .venv*
3 |
4 | # JetBrains
5 | .idea/
6 |
7 | # Byte-compiled / optimized / DLL files
8 | __pycache__/
9 | *.py[cod]
10 | *$py.class
11 | test.py
12 |
13 | # C extensions
14 | *.so
15 |
16 | # Distribution / packaging
17 | .Python
18 | build/
19 | develop-eggs/
20 | dist/
21 | downloads/
22 | eggs/
23 | .eggs/
24 | lib/
25 | lib64/
26 | parts/
27 | sdist/
28 | var/
29 | wheels/
30 | pip-wheel-metadata/
31 | share/python-wheels/
32 | *.egg-info/
33 | .installed.cfg
34 | *.egg
35 | MANIFEST
36 |
37 | # PyInstaller
38 | # Usually these files are written by a python script from a template
39 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
40 | *.manifest
41 | *.spec
42 |
43 | # Installer logs
44 | pip-log.txt
45 | pip-delete-this-directory.txt
46 |
47 | # Unit test / coverage reports
48 | htmlcov/
49 | .tox/
50 | .nox/
51 | .coverage
52 | .coverage.*
53 | .cache
54 | nosetests.xml
55 | coverage.xml
56 | *.cover
57 | *.py,cover
58 | .hypothesis/
59 | .pytest_cache/
60 |
61 | # Translations
62 | *.mo
63 | *.pot
64 |
65 | # Django stuff:
66 | *.log
67 | local_settings.py
68 | db.sqlite3
69 | db.sqlite3-journal
70 |
71 | # Flask stuff:
72 | instance/
73 | .webassets-cache
74 |
75 | # Scrapy stuff:
76 | .scrapy
77 |
78 | # Sphinx documentation
79 | docs/_build/
80 |
81 | # PyBuilder
82 | target/
83 |
84 | # Jupyter Notebook
85 | .ipynb_checkpoints
86 |
87 | # IPython
88 | profile_default/
89 | ipython_config.py
90 |
91 | # pyenv
92 | .python-version
93 |
94 | # pipenv
95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
98 | # install all needed dependencies.
99 | #Pipfile.lock
100 |
101 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
102 | __pypackages__/
103 |
104 | # Celery stuff
105 | celerybeat-schedule
106 | celerybeat.pid
107 |
108 | # SageMath parsed files
109 | *.sage.py
110 |
111 | # Environments
112 | .env
113 | .venv
114 | env/
115 | venv/
116 | ENV/
117 | env.bak/
118 | venv.bak/
119 |
120 | # Spyder project settings
121 | .spyderproject
122 | .spyproject
123 |
124 | # Rope project settings
125 | .ropeproject
126 |
127 | # mkdocs documentation
128 | /site
129 |
130 | # mypy
131 | .mypy_cache/
132 | .dmypy.json
133 | dmypy.json
134 |
135 | # Pyre type checker
136 | .pyre/
137 |
--------------------------------------------------------------------------------
/docs/contributing/coding_style.md:
--------------------------------------------------------------------------------
1 | # Main Guidelines
2 | - Use python type hinting in every function and class, including return types.
3 | - Always use [docstrings (google style)](https://google.github.io/styleguide/pyguide.html#381-docstrings) for Classes and Functions/Methods.
4 | - Always prefer double-quotes rather than single quotes.
5 | - Adhere as close as possible to google style docstrings, auto-generated documentation is dependent on it and a merge request will not make it into dev if they are missing.
6 | - Always indent with [4 spaces](https://peps.python.org/pep-0008/#tabs-or-spaces), no more, no less.
7 | - [Naming of classes](https://peps.python.org/pep-0008/#prescriptive-naming-conventions) should always be CamelCase and functions should be snake_case.
8 | - Never raise exceptions from the base Exception, write custom ones if required.
9 |
10 | # Other Guidelines
11 | - Minimum version of Python to use during development and testing must be 3.11.
12 | - Always use [f-strings](https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals) when possible as they are far more superior than % and .format() in most cases.
13 | - Always write [tests (pytest)](https://docs.pytest.org/en/stable/) for any new functions/API calls implemented or changed (if the logic has changed).
14 | - [List comprehensions](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions) should always be used where possible unless the code is too ugly and readable, that is for you and the reviewer to agree with :-)
15 | - [Ruff](https://github.com/astral-sh/ruff) for formatting/linting.
--------------------------------------------------------------------------------
/docs/contributing/please_read_first.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this project, please first discuss the change you wish to make via GitHub discussions or via an Issue thread.
4 |
5 | We don't currently have a dedicated code of conduct however, it is expected that you treat everyone with respect in the community.
6 |
7 | Any feedback for the contributing workflows whether it be the branch structure, coding style, tests or general feedback of the implementation is welcome and should be brought up either as a discussion/issue or informally over a chat like Slack. Do not use emails/GitHub Issues for informal discussions.
8 |
9 | ## Dev Setup
10 |
11 | `tests/test_infrastructure` contains various docker-compose configurations for different scenarios such as Database, HA, Database+HA, etc... You can run local tests on these servers to ensure everything is running as expected when you make a PR.
--------------------------------------------------------------------------------
/docs/contributing/tests.md:
--------------------------------------------------------------------------------
1 | pytest is the testing framework of choice in this project. If you are not very familiar with the basics of pytest, I would recommend reading the documentation or having a look at the tests in this project to see the structure and provided functionality.
2 |
3 | Each API endpoint or class/function that interacts with a resource which changes potential production data must implement a test and pass before a merge request is accepted.
4 |
5 | ## Test Structure
6 |
7 | All tests belong in the tests directory of the project. The structure of this folder may change from time to time but the basis is that for every API endpoint, you try to group them cleanly into their respective files according to the API endpoint path, prefixed with test_. Here are some examples:
8 |
9 |
10 | TO DO.
--------------------------------------------------------------------------------
/docs/daemon Common variables.MD:
--------------------------------------------------------------------------------
1 | ## All Daemons Common Global Params
2 |
3 | hooks_libraries
4 | loggers
5 | user_context
6 | comment
7 | unknown_map_entry
8 |
9 | ## Dhcp4 and Dhcp6 Common Global Params
10 |
11 | interfaces_config
12 | lease_database
13 | hosts_database
14 | hosts_databases
15 | host_reservation_identifiers
16 | client_classes
17 | option_def
18 | expired_leases_processing
19 | dhcp4o6_port
20 | control_socket
21 | dhcp_queue_control
22 | dhcp_ddns
23 | sanity_checks
24 | config_control
25 | server_tag
26 | hostname_char_set
27 | hostname_char_replacement
28 | statistic_default_sample_count
29 | statistic_default_sample_age
30 | dhcp_multi_threading
31 | early_global_reservations_lookup
32 | ip_reservations_unique
33 | reservations_lookup_first
34 | compatibility
35 | parked_packet_limit
36 | decline_probation_period
37 |
38 | ## Dhcp4 Specific Global Params
39 | shared_networks
40 | reservations
41 | subnet4_list
42 | echo_client_id
43 | match_client_id
44 | authoritative
45 | next_server
46 | server_hostname
47 | boot_file_name
48 |
49 | ## Dhcp6 Specific Global Params
50 | shared_networks
51 | reservations
52 | data_directory
53 | preferred_lifetime
54 | min_preferred_lifetime
55 | max_preferred_lifetime
56 | subnet6_list
57 | mac_sources
58 | relay_supplied_options
59 | server_id
60 |
61 | ## CtrlAgent Specific Global Params
62 | http_host
63 | http_port
64 | trust_anchor
65 | cert_file
66 | key_file
67 | cert_required
68 | authentication
69 | control_sockets
70 |
71 | ## DDNS Specific Global Params
72 | ip_address
73 | port
74 | dns_server_timeout
75 | ncr_protocol
76 | ncr_format
77 | forward_ddns
78 | reverse_ddns
79 | tsig_keys
80 | control_socket
81 |
82 | ## Dhcp4 and Dhcp6 Daemon Global Config, Shared Networks and Subnets common variables
83 |
84 | valid_lifetime
85 | min_valid_lifetime
86 | max_valid_lifetime
87 | renew_timer
88 | rebind_timer
89 | option_data
90 | reservation_mode
91 | reservations_global
92 | reservations_in_subnet
93 | reservations_out_of_pool
94 | calculate_tee_times
95 | t1_percent
96 | t2_percent
97 | cache_threshold
98 | cache_max_age
99 | ddns_send_updates
100 | ddns_override_no_update
101 | ddns_override_client_update
102 | ddns_replace_client_name
103 | ddns_generated_prefix
104 | ddns_qualifying_suffix
105 | ddns_update_on_renew
106 | ddns_use_conflict_resolution
107 | store_extended_info
108 | user_context
109 | comment
110 | unknown_map_entry
--------------------------------------------------------------------------------
/docs/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/docs/img/logo.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | #
2 |
3 |
4 |
5 |
6 | pyisckea is a python module used to interact with the ISC Kea DHCP daemons running on an ISC Kea server. This module also implements Pydantic to improve the developer experience when working in a code editor like VSCode and also provide super fast data validation functionality on python objects before they get serialized and sent to the Kea APIs.
7 |
8 | ## Get Started
9 |
10 | 1) Install the module.
11 |
12 | ```
13 | pip install pyisckea
14 | ```
15 |
16 | 2) Import the Kea class.
17 |
18 | ```python
19 | from pyisckea import Kea
20 |
21 | server = Kea("http://localhost:8000")
22 | ```
23 |
24 | 3) Call API commands based on the Daemon to interact with the APIs.
25 |
26 | ```python
27 | subnets_v4 = server.dhcp4.subnet4_list()
28 |
29 | for subnet in subnets_v4:
30 | print(subnet.subnet, subnet.option_data, subnet.relay, subnet.pools_list)
31 |
32 | my_subnet = server.dhcp6.subnet4_get(name="pyisckea-pytest")
33 | print(my_subnet.json(exclude_none=True, indent=4))
34 |
35 | # {
36 | # "valid_lifetime": 4000,
37 | # "renew_timer": 1000,
38 | # "rebind_timer": 2000,
39 | # "option_data": [],
40 | # "calculate_tee_times": true,
41 | # "t1_percent": 0.5,
42 | # "t2_percent": 0.8,
43 | # "store_extended_info": false,
44 | # "name": "pyisckea-pytest",
45 | # "relay": {
46 | # "ip-addresses": []
47 | # },
48 | # "subnet6": [
49 | # {
50 | # "valid_lifetime": 4000,
51 | # "renew_timer": 1000,
52 | # "rebind_timer": 2000,
53 | # "option_data": [],
54 | # "calculate_tee_times": true,
55 | # "t1_percent": 0.5,
56 | # "t2_percent": 0.8,
57 | # "store_extended_info": false,
58 | # "id": 40123,
59 | # "subnet": "2001:db8::/64",
60 | # "preferred_lifetime": 3600,
61 | # "pd_pools": [],
62 | # "rapid_commit": false
63 | # }
64 | # ],
65 | # "rapid_commit": false
66 | # }
67 | ```
68 |
69 | 4) Utilize the Pydantic models which provide basic data validation.
70 |
71 | ```python
72 | from pyisckea.models.dhcp4.subnet import Subnet4
73 |
74 | my_subnet = Subnet4(
75 | id=1234, subnet="192.0.2.32/31", option_data=[{"code": 3, "data": "192.0.2.32"}]
76 | )
77 |
78 | create_subnet = server.dhcp4.subnet4_add(subnets=[my_subnet])
79 | print(create_subnet.result, create_subnet.text)
80 |
81 | # Note because subnet_cmds hook library is not loaded, we run into an exception here:
82 | # pyisckea.exceptions.KeaHookLibraryNotConfiguredException: Hook library 'subnet_cmds' is not configured for 'dhcp4' service. Please ensure this is enabled in the configuration for the 'dhcp4' daemon
83 | ```
84 |
85 | ## Basic Authentication
86 |
87 | If you have basic authentication enabled on your Kea Servers, import the `BasicAuth` class from the `httpx` library and pass it into the `Kea` object like this:
88 |
89 | ```python
90 | from pyisckea.kea import Kea
91 | from httpx import BasicAuth
92 |
93 | auth = BasicAuth("kea", "secret123")
94 |
95 |
96 | api = Kea("http://localhost:8000", auth=auth)
97 | ```
98 |
99 | ## TLS
100 |
101 | Create a context using the `ssl` library and then pass this context into a httpx `Client` object to be used with Kea like this:
102 |
103 | ```python
104 | from httpx import Client
105 | from pyisckea.kea import Kea
106 |
107 | ctx = ssl.create_default_context(cafile="/path/to/the/ca-cert.pem")
108 | ctx.load_cert_chain(
109 | certfile="/path/to/the/agent-cert.pem", keyfile="/path/to/the/agent-key.pem"
110 | )
111 |
112 | client = Client(verify=ctx)
113 | api = Kea("http://localhost:8000", client=client)
114 | ```
115 |
116 | ## API Reference
117 |
118 | All supported commands by the daemons are in the format of the API referenced commands with the exception of replacing any hyphen or space with an underscore. Eg. the build-report API command for all daemons is implemented as build_report so it heavily ties into the Kea predefined commands when looking at their documentation. Currently everything is built towards Kea 2.2.0. Pydantic variables will replace any hyphens with an underscore however when loading/exporting the data models, it will replace all keys with the hyphen to adhere to the Kea expected variables, ensure that the KeaBaseModel (located in from pyisckea.models.generic.base import KeaBaseModel instead of from pydantic import BaseModel) is used when creating any Pydantic models to inherit this functionality.
--------------------------------------------------------------------------------
/docs/media/subnet_initalization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/docs/media/subnet_initalization.png
--------------------------------------------------------------------------------
/docs/media/vscode_autocomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/docs/media/vscode_autocomplete.png
--------------------------------------------------------------------------------
/docs/stylesheets/extra.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --md-primary-fg-color: #4c9c2d;
3 | --md-primary-fg-color--light: #4c9c2d;
4 | --md-primary-fg-color--dark: #4c9c2d;
5 | }
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: veesix ::networks - pyisckea
2 | site_url: https://docs.pyisckea.veesix-networks.co.uk
3 | repo_url: https://github.com/veesix-networks/pyisckea
4 | repo_name: veesix-networks/pyisckea
5 | logo: docs/img/logo.png
6 | theme:
7 | name: material
8 | icon:
9 | repo: fontawesome/brands/github
10 | palette:
11 | # Palette toggle for automatic mode
12 | - media: "(prefers-color-scheme)"
13 | toggle:
14 | icon: material/brightness-auto
15 | name: Switch to light mode
16 | primary: custom
17 | # Palette toggle for light mode
18 | - media: "(prefers-color-scheme: light)"
19 | scheme: default
20 | toggle:
21 | icon: material/brightness-7
22 | name: Switch to dark mode
23 | primary: custom
24 | # Palette toggle for dark mode
25 | - media: "(prefers-color-scheme: dark)"
26 | scheme: slate
27 | toggle:
28 | icon: material/brightness-4
29 | name: Switch to system preference
30 | primary: custom
31 | features:
32 | - navigation.instant
33 | - navigation.tabs
34 | - navigation.sections
35 | - content.code.copy
36 | extra_css:
37 | - stylesheets/extra.css
38 | nav:
39 | - Get Started:
40 | - Intro: index.md
41 | - Contributing:
42 | - PLEASE READ FIRST: contributing/please_read_first.md
43 | - Coding Style: contributing/coding_style.md
44 | - Tests: contributing/tests.md
45 | - Author: https://github.com/veesix-networks
46 | markdown_extensions:
47 | - tables
48 | - admonition
49 | - def_list
50 | - attr_list
51 | - pymdownx.emoji:
52 | emoji_index: !!python/name:material.extensions.emoji.twemoji
53 | emoji_generator: !!python/name:material.extensions.emoji.to_svg
54 | - pymdownx.tasklist:
55 | custom_checkbox: true
56 | - pymdownx.highlight:
57 | anchor_linenums: true
58 | line_spans: __span
59 | pygments_lang_class: true
60 | - pymdownx.inlinehilite
61 | - pymdownx.snippets
62 | - pymdownx.superfences
--------------------------------------------------------------------------------
/pyisckea/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ["Kea"]
2 |
3 | from pyisckea.kea import Kea
4 |
--------------------------------------------------------------------------------
/pyisckea/daemons/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ["CtrlAgent", "Ddns", "Dhcp4", "Dhcp6"]
2 |
3 | from pyisckea.daemons.ctrlagent import CtrlAgent
4 | from pyisckea.daemons.ddns import Ddns
5 | from pyisckea.daemons.dhcp4 import Dhcp4
6 | from pyisckea.daemons.dhcp6 import Dhcp6
7 |
--------------------------------------------------------------------------------
/pyisckea/exceptions.py:
--------------------------------------------------------------------------------
1 | class KeaException(Exception):
2 | def __init__(self, message: str = "Kea Exception encountered"):
3 | self.message = message
4 | super().__init__(self.message)
5 |
6 |
7 | class KeaGenericException(KeaException):
8 | # Result code 1
9 | def __init__(
10 | self,
11 | message: str = "Code 1: a general error or failure has occurred during the command processing.",
12 | ):
13 | self.message = message
14 | super().__init__(self.message)
15 |
16 |
17 | class KeaCommandNotSupportedException(KeaException):
18 | # Result code 2
19 | def __init__(
20 | self,
21 | message: str = "Code 2: the specified command is unsupported by the server receiving it.",
22 | ):
23 | self.message = message
24 | super().__init__(self.message)
25 |
26 |
27 | class KeaObjectNotFoundException(KeaException):
28 | # Result code 3
29 | def __init__(
30 | self,
31 | message: str = "Code 3: the requested operation has been completed but the requested resource was not found. This status code is returned when a command returns no resources or affects no resources.",
32 | ):
33 | self.message = message
34 | super().__init__(self.message)
35 |
36 |
37 | class KeaServerConflictException(KeaException):
38 | # Result code 4
39 | def __init__(
40 | self,
41 | message: str = "Code 4: the well-formed command has been processed but the requested changes could not be applied, because they were in conflict with the server state or its notion of the configuration.",
42 | ):
43 | self.message = message
44 | super().__init__(self.message)
45 |
46 |
47 | class KeaUnauthorizedAccessException(KeaException):
48 | def __init__(
49 | self,
50 | message: str = "Received 401 (Unauthorized response), please ensure username and password is configured, and use_basic_auth is set to True for the Kea object",
51 | ):
52 | self.message = message
53 | super().__init__(self.message)
54 |
55 |
56 | class KeaHookLibraryNotConfiguredException(KeaException):
57 | def __init__(self, service: str, hook: str):
58 | self.message = f"Hook library '{hook}' is not configured for '{service}' service. Please ensure this is enabled in the configuration for the '{service}' daemon"
59 | super().__init__(self.message)
60 |
61 |
62 | class KeaSharedNetworkNotFoundException(KeaException):
63 | def __init__(self, name: str):
64 | self.message = f"Shared Network '{name}' not found"
65 | super().__init__(self.message)
66 |
67 |
68 | class KeaSubnetNotFoundException(KeaException):
69 | def __init__(self, subnet_id: int):
70 | self.message = f"Subnet '{subnet_id}' not found"
71 | super().__init__(self.message)
72 |
73 |
74 | class KeaLeaseNotFoundException(KeaException):
75 | def __init__(self, lease: str):
76 | self.message = f"Lease '{lease}' not found"
77 | super().__init__(self.message)
78 |
79 |
80 | class KeaInvalidRemoteMapException(KeaException):
81 | def __init__(self, detailed_error: str):
82 | self.message = f"Remote map for cm_cmd remote API call is not correctly formatted. Detailed Error: {detailed_error}"
83 | super().__init__(self.message)
84 |
85 |
86 | class KeaRemoteServerNotFoundException(KeaException):
87 | def __init__(self, server_tag: str):
88 | self.message = f"Server with server_tag '{server_tag}' not found"
89 | super().__init__(self.message)
90 |
91 |
92 | class KeaConfigBackendNotConfiguredException(KeaException):
93 | def __init__(self):
94 | self.message = "Kea API reports that there is no configuration backends"
95 | super().__init__(self.message)
96 |
97 |
98 | class KeaUnknownHostReservationTypeException(KeaException):
99 | def __init__(self, reservation_type: str):
100 | self.message = (
101 | f"Reservation type '{reservation_type}' is not a valid reservation type"
102 | )
103 | super().__init__(self.message)
104 |
105 |
106 | class KeaReservationNotFoundException(KeaException):
107 | def __init__(self, reservation_data: str):
108 | self.message = f"Reservation '{reservation_data}' not found"
109 | super().__init__(self.message)
110 |
111 |
112 | class KeaClientClassNotFoundException(KeaException):
113 | def __init__(self, client_class: str):
114 | self.message = f"Client Class '{client_class}' not found"
115 | super().__init__(self.message)
116 |
--------------------------------------------------------------------------------
/pyisckea/models/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/pyisckea/models/__init__.py
--------------------------------------------------------------------------------
/pyisckea/models/ctrlagent/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/pyisckea/models/ctrlagent/__init__.py
--------------------------------------------------------------------------------
/pyisckea/models/ctrlagent/config.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.control_socket import ControlSockets
4 | from pyisckea.models.generic.daemon import CommonDaemonConfig
5 |
6 |
7 | class CtrlAgentDaemonConfig(CommonDaemonConfig):
8 | http_host: str
9 | http_port: int
10 | trust_anchor: Optional[str] = None
11 | cert_file: Optional[str] = None
12 | key_file: Optional[str] = None
13 | cert_required: Optional[bool] = None
14 | control_sockets: Optional[ControlSockets] = None
15 | authentication: Optional[dict] = None
16 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/pyisckea/models/dhcp4/__init__.py
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/client_class.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.client_class import ClientClass
6 | from pyisckea.models.generic.option_def import OptionDef
7 |
8 |
9 | class ClientClass4(ClientClass):
10 | option_def: Optional[List[OptionDef]] = Field(default_factory=list)
11 | next_server: Optional[str] = None
12 | server_hostname: Optional[str] = None
13 | boot_file_name: Optional[str] = None
14 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/config.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.dhcp4.client_class import ClientClass4
6 | from pyisckea.models.dhcp4.reservation import Reservation4
7 | from pyisckea.models.dhcp4.shared_network import SharedNetwork4
8 | from pyisckea.models.dhcp4.subnet import Subnet4
9 | from pyisckea.models.generic.daemon import CommonDhcpDaemonConfig
10 |
11 |
12 | class Dhcp4DaemonConfig(CommonDhcpDaemonConfig):
13 | client_classes: Optional[List[ClientClass4]] = Field(default_factory=list)
14 | shared_networks: Optional[List[SharedNetwork4]] = Field(default_factory=list)
15 | reservations: Optional[List[Reservation4]] = Field(default_factory=list)
16 | subnet4: Optional[List[Subnet4]] = Field(default_factory=list)
17 | echo_client_id: Optional[bool] = None
18 | match_client_id: Optional[bool] = None
19 | authoritative: Optional[bool] = None
20 | next_server: Optional[str] = None
21 | server_hostname: Optional[str] = None
22 | boot_file_name: Optional[str] = None
23 | stash_agent_options: Optional[bool] = None
24 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/lease.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.lease import Lease, LeasePage
6 |
7 |
8 | class Lease4(Lease):
9 | pass
10 |
11 |
12 | class Lease4Page(LeasePage):
13 | count: int
14 | leases: Optional[List[Lease4]] = Field(default_factory=list)
15 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/reservation.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.reservation import Reservation
4 |
5 |
6 | class Reservation4(Reservation):
7 | client_id: Optional[str] = None
8 | circuit_id: Optional[str] = None
9 | ip_address: str
10 | next_server: Optional[str] = None
11 | server_hostname: Optional[str] = None
12 | boot_file_name: Optional[str] = None
13 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/shared_network.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.dhcp4.subnet import Subnet4
6 | from pyisckea.models.generic.shared_network import SharedNetwork
7 |
8 |
9 | class SharedNetwork4(SharedNetwork):
10 | subnet4: Optional[List[Subnet4]] = Field(default_factory=list)
11 | match_client_id: Optional[bool] = None
12 | authoritative: Optional[bool] = None
13 | next_server: Optional[str] = None
14 | server_hostname: Optional[str] = None
15 | boot_file_name: Optional[str] = None
16 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp4/subnet.py:
--------------------------------------------------------------------------------
1 | from typing import Annotated, List, Optional
2 |
3 | from pydantic import ConfigDict, Field
4 |
5 | from pyisckea.models.dhcp4.reservation import Reservation4
6 | from pyisckea.models.generic.subnet import Subnet
7 |
8 |
9 | class Subnet4(Subnet):
10 | match_client_id: Optional[bool] = None
11 | authoritative: Optional[bool] = None
12 | next_server: Optional[str] = None
13 | boot_file_name: Optional[str] = None
14 | subnet_4o6_interface: Annotated[Optional[str], Field(alias="4o6-interface")] = None
15 | subnet_4o6_interface_id: Annotated[
16 | Optional[str], Field(alias="4o6-interface-id")
17 | ] = None
18 | subnet_4o6_subnet: Annotated[Optional[str], Field(alias="4o6-subnet")] = None
19 | reservations: Optional[List[Reservation4]] = Field(default_factory=list)
20 |
21 | model_config = ConfigDict(populate_by_name=True)
22 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/pyisckea/models/dhcp6/__init__.py
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/client_class.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.client_class import ClientClass
4 |
5 |
6 | class ClientClass6(ClientClass):
7 | preferred_lifetime: Optional[int] = None
8 | min_preferred_lifetime: Optional[int] = None
9 | max_preferred_lifetime: Optional[int] = None
10 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/config.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.dhcp6.client_class import ClientClass6
6 | from pyisckea.models.dhcp6.reservation import Reservation6
7 | from pyisckea.models.dhcp6.server_id import ServerId
8 | from pyisckea.models.dhcp6.shared_network import SharedNetwork6
9 | from pyisckea.models.dhcp6.subnet import Subnet6
10 | from pyisckea.models.generic.daemon import CommonDhcpDaemonConfig
11 |
12 |
13 | class Dhcp6DaemonConfig(CommonDhcpDaemonConfig):
14 | client_classes: Optional[List[ClientClass6]] = Field(default_factory=list)
15 | shared_networks: Optional[List[SharedNetwork6]] = Field(default_factory=list)
16 | reservations: Optional[List[Reservation6]] = Field(default_factory=list)
17 | data_directory: Optional[str] = None
18 | preferred_lifetime: Optional[int] = None
19 | min_preferred_lifetime: Optional[int] = None
20 | max_preferred_lifetime: Optional[int] = None
21 | subnet6: Optional[List[Subnet6]] = Field(default_factory=list)
22 | mac_sources: Optional[List[str]] = Field(default_factory=list)
23 | relay_supplied_options: Optional[List[str]] = Field(default_factory=list)
24 | server_id: ServerId
25 | pd_allocator: Optional[str] = None
26 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/lease.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.enums import Lease6TypeEnum
6 | from pyisckea.models.generic.lease import Lease, LeasePage
7 |
8 |
9 | class Lease6(Lease):
10 | duid: str
11 | iaid: int
12 | prefix_len: Optional[int] = None
13 | type: Optional[Lease6TypeEnum] = None
14 |
15 |
16 | class Lease6Page(LeasePage):
17 | count: int
18 | leases: Optional[List[Lease6]] = Field(default_factory=list)
19 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/pd_pool.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.base import KeaModel
6 | from pyisckea.models.generic.option_data import OptionData
7 |
8 |
9 | class PDPool(KeaModel):
10 | prefix: str
11 | prefix_len: int
12 | delegated_len: int
13 | option_data: Optional[List[OptionData]] = Field(default_factory=list)
14 | client_class: Optional[str] = None
15 | require_client_classes: Optional[List[str]] = Field(default_factory=list)
16 | excluded_prefix: Optional[str] = None
17 | excluded_prefix_len: Optional[int] = None
18 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/reservation.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.reservation import Reservation
6 |
7 |
8 | class Reservation6(Reservation):
9 | ip_addresses: Optional[List[str]] = Field(default_factory=list)
10 | prefixes: Optional[List[str]] = Field(default_factory=list)
11 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/server_id.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.enums import ServerIdTypeEnum
4 | from pyisckea.models.generic.config import CommonConfig
5 |
6 |
7 | class ServerId(CommonConfig):
8 | type: ServerIdTypeEnum
9 | htype: int
10 | identifier: Optional[str] = None
11 | time: int
12 | enterprise_id: int
13 | persist: bool
14 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/shared_network.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.dhcp6.subnet import Subnet6
6 | from pyisckea.models.generic.shared_network import SharedNetwork
7 |
8 |
9 | class SharedNetwork6(SharedNetwork):
10 | subnet6: Optional[List[Subnet6]] = Field(default_factory=list)
11 | interface_id: Optional[str] = None
12 | min_preferred_lifetime: Optional[int] = None
13 | max_preferred_lifetime: Optional[int] = None
14 | rapid_commit: Optional[bool] = None
15 |
--------------------------------------------------------------------------------
/pyisckea/models/dhcp6/subnet.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.dhcp6.pd_pool import PDPool
6 | from pyisckea.models.dhcp6.reservation import Reservation6
7 | from pyisckea.models.generic.subnet import Subnet
8 |
9 |
10 | class Subnet6(Subnet):
11 | preferred_lifetime: Optional[int] = None
12 | min_preferred_lifetime: Optional[int] = None
13 | max_preferred_lifetime: Optional[int] = None
14 | pd_allocator: Optional[str] = None
15 | pd_pools: Optional[List[PDPool]] = Field(default_factory=list)
16 | interface_id: Optional[str] = None
17 | rapid_commit: Optional[bool] = None
18 | reservations: Optional[List[Reservation6]] = Field(default_factory=list)
19 |
--------------------------------------------------------------------------------
/pyisckea/models/enums.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class StatusEnum(str, Enum):
5 | ready = "ready"
6 | retrying = "retrying"
7 | failed = "failed"
8 |
9 |
10 | class ReservationMode(str, Enum):
11 | disabled = "disabled"
12 | out_of_pool = ("out-of-pool",)
13 | r_global = "global"
14 | all = "all"
15 |
16 |
17 | class DDNSReplaceClientNameEnum(str, Enum):
18 | when_present = "when-present"
19 | never = "never"
20 | always = "always"
21 | when_not_present = "when-not-present"
22 |
23 |
24 | class Lease6TypeEnum(str, Enum):
25 | iana = "IA_NA"
26 | iapd = "IA_PD"
27 |
28 |
29 | class LoggerLevelEnum(str, Enum):
30 | critical = "CRITICAL"
31 | error = "ERROR"
32 | warning = "WARNING"
33 | info = "INFO"
34 | debug = "DEBUG"
35 |
36 |
37 | class DHCPSocketTypeEnum(str, Enum):
38 | raw = "raw"
39 | udp = "udp"
40 |
41 |
42 | class OutboundInterfaceEnum(str, Enum):
43 | same_as_inbound = "same-as-inbound"
44 | use_routing = "use-routing"
45 |
46 |
47 | class DatabaseTypeEnum(str, Enum):
48 | memfile = "memfile"
49 | mysql = "mysql"
50 | postgresql = "postgresql"
51 |
52 |
53 | class DatabaseOnFailEnum(str, Enum):
54 | stop_retry_exit = "stop-retry-exit"
55 | serve_retry_exit = "serve-retry-exit"
56 | serve_retry_continue = "serve-retry-continue"
57 |
58 |
59 | class HostReservationIdentifierEnum(str, Enum):
60 | duid = "duid"
61 | hw_address = "hw-address"
62 | circuit_id = "circuit-id"
63 | client_id = "client-id"
64 | flex_id = "flex-id"
65 |
66 |
67 | class NCRProtocolEnum(str, Enum):
68 | udp = "UDP"
69 | tcp = "TCP"
70 |
71 |
72 | class NCRFormatEnum(str, Enum):
73 | json = "JSON"
74 |
75 |
76 | class ServerIdTypeEnum(str, Enum):
77 | llt = "LLT"
78 | en = "EN"
79 | ll = "LL"
80 |
81 |
82 | class AuthenticationTypeEnum(str, Enum):
83 | basic = "basic"
84 |
85 |
86 | class RemoteMapTypeEnum(str, Enum):
87 | mysql = "mysql"
88 | postgresql = "postgresql"
89 |
90 |
91 | class HAModeTypeEnum(str, Enum):
92 | load_balancing = "load-balancing"
93 | hot_standby = "hot-standby"
94 |
95 |
96 | class HARoleTypeEnum(str, Enum):
97 | primary = "primary"
98 | secondary = "secondary"
99 | standby = "standby"
100 |
101 |
102 | class HAStateTypeEnum(str, Enum):
103 | backup = "backup"
104 | communication_recovery = "communication-recovery"
105 | hot_standby = "hot-standby"
106 | load_balancing = "load-balancing"
107 | in_maintenance = "in-maintenance"
108 | partner_down = "partner-down"
109 | partner_in_maintenance = "partner-in-maintenance"
110 | passive_backup = "passive-backup"
111 | ready = "ready"
112 | synbcing = "syncing"
113 | terminated = "terminated"
114 | waiting = "waiting"
115 | unavailable = "unavailable"
116 | null = ""
117 |
118 |
119 | class DDNSConflictResolutionModeEnum(str, Enum):
120 | check_with_dhcid = "check-with-dhcid"
121 | no_check_with_dhcid = "no-check-with-dhcid"
122 | check_exists_with_dhcid = "check-exists-with-dhcid"
123 | no_check_without_dhcid = "no-check-without-dhcid"
124 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ["KeaResponse", "Sockets", "StatusGet"]
2 |
3 | from pyisckea.models.generic.api_response import KeaResponse
4 | from pyisckea.models.generic.sockets import Sockets
5 | from pyisckea.models.generic.status import StatusGet
6 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/api_response.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Union
2 |
3 | from pydantic import BaseModel
4 |
5 |
6 | class KeaResponse(BaseModel):
7 | result: int
8 | text: Optional[str] = None
9 | arguments: Optional[Union[dict, list]] = None
10 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/authentication.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.enums import AuthenticationTypeEnum
6 | from pyisckea.models.generic.config import CommonConfig
7 |
8 |
9 | class AuthenticationClient(CommonConfig):
10 | user: Optional[str] = None
11 | user_file: Optional[str] = None
12 | password: Optional[str] = None
13 | password_file: Optional[str] = None
14 |
15 |
16 | class Authentication(CommonConfig):
17 | type: AuthenticationTypeEnum
18 | realm: str
19 | directory: Optional[str] = None
20 | clients: List[Optional[AuthenticationClient]] = Field(default_factory=list)
21 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/base.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import BaseModel, ConfigDict
4 |
5 |
6 | def normalize_keys(string: str) -> str:
7 | return string.replace("_", "-")
8 |
9 |
10 | class KeaBaseModel(BaseModel):
11 | model_config = ConfigDict(
12 | alias_generator=normalize_keys, populate_by_name=True, use_enum_values=True
13 | )
14 |
15 |
16 | class KeaModel(KeaBaseModel):
17 | user_context: Optional[dict] = None
18 | comment: Optional[str] = None
19 | unknown_map_entry: Optional[str] = None
20 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/client_class.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pyisckea.models.generic.config import CommonConfig
4 | from pyisckea.models.generic.option_data import OptionData
5 |
6 |
7 | class ClientClass(CommonConfig):
8 | name: str
9 | test: Optional[str] = None
10 | only_if_required: Optional[bool] = None
11 | option_data: Optional[List[OptionData]] = None
12 | valid_lifetime: Optional[int] = None
13 | min_valid_lifetime: Optional[int] = None
14 | max_valid_lifetime: Optional[int] = None
15 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/config.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional, Union
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.enums import DDNSReplaceClientNameEnum, ReservationMode
6 | from pyisckea.models.generic.base import KeaModel
7 | from pyisckea.models.generic.option_data import OptionData
8 |
9 |
10 | class CommonConfig(KeaModel):
11 | store_extended_info: Optional[bool] = None
12 |
13 |
14 | class CommonDhcpConfig(CommonConfig):
15 | valid_lifetime: Optional[int] = None
16 | min_valid_lifetime: Optional[int] = None
17 | max_valid_lifetime: Optional[int] = None
18 | renew_timer: Optional[int] = None
19 | rebind_timer: Optional[int] = None
20 | option_data: Optional[List[OptionData]] = Field(default_factory=list)
21 | reservation_mode: Optional[ReservationMode] = None
22 | reservations_global: Optional[bool] = None
23 | reservations_in_subnet: Optional[bool] = None
24 | reservations_out_of_pool: Optional[bool] = None
25 | calculate_tee_times: Optional[bool] = None
26 | t1_percent: Optional[float] = None
27 | t2_percent: Optional[float] = None
28 | cache_threshold: Optional[float] = None
29 | cache_max_age: Optional[int] = None
30 | ddns_send_updates: Optional[bool] = None
31 | ddns_override_no_update: Optional[bool] = None
32 | ddns_override_client_update: Optional[bool] = None
33 | ddns_replace_client_name: Optional[Union[DDNSReplaceClientNameEnum, bool]] = None
34 | ddns_generated_prefix: Optional[str] = None
35 | ddns_qualifying_suffix: Optional[str] = None
36 | ddns_update_on_renew: Optional[bool] = None
37 | ddns_use_conflict_resolution: Optional[bool] = None
38 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/control_socket.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.base import KeaBaseModel
4 | from pyisckea.models.generic.config import CommonConfig
5 |
6 |
7 | class ControlSocket(CommonConfig):
8 | socket_name: str
9 | socket_type: str
10 |
11 |
12 | class ControlSockets(KeaBaseModel):
13 | dhcp4: Optional[ControlSocket] = None
14 | dhcp6: Optional[ControlSocket] = None
15 | d2: Optional[ControlSocket] = None
16 | unknown_map_entry: Optional[str] = None
17 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/daemon.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.enums import (
6 | DDNSConflictResolutionModeEnum,
7 | DHCPSocketTypeEnum,
8 | HostReservationIdentifierEnum,
9 | OutboundInterfaceEnum,
10 | )
11 | from pyisckea.models.generic.base import KeaBaseModel
12 | from pyisckea.models.generic.config import CommonDhcpConfig
13 | from pyisckea.models.generic.control_socket import ControlSocket
14 | from pyisckea.models.generic.database import Database
15 | from pyisckea.models.generic.dhcp_ddns import DhcpDdns
16 | from pyisckea.models.generic.dhcp_queue_control import DHCPQueueControl
17 | from pyisckea.models.generic.hook import Hook
18 | from pyisckea.models.generic.logger import Logger
19 | from pyisckea.models.generic.multi_threading import MultiThreading
20 | from pyisckea.models.generic.option_def import OptionDef
21 | from pyisckea.models.generic.sanity_check import SanityCheck
22 |
23 |
24 | class CommonDaemonConfig(CommonDhcpConfig):
25 | hooks_libraries: Optional[List[Hook]] = Field(default_factory=list)
26 | loggers: Optional[List[Logger]] = Field(default_factory=list)
27 |
28 |
29 | class InterfaceListConfig(KeaBaseModel):
30 | interfaces: List[str] = Field(default_factory=list)
31 | dhcp_socket_type: Optional[DHCPSocketTypeEnum] = None
32 | outbound_interface: Optional[OutboundInterfaceEnum] = None
33 | re_detect: Optional[bool] = None
34 | service_sockets_require_all: Optional[bool] = None
35 | service_sockets_retry_wait_time: Optional[int] = None
36 | service_sockets_max_retries: Optional[int] = None
37 |
38 |
39 | class CommonDhcpDaemonConfig(CommonDaemonConfig):
40 | interfaces_config: InterfaceListConfig
41 | lease_database: Optional[Database] = None
42 | hosts_database: Optional[Database] = None
43 | hosts_databases: Optional[List[Database]] = None
44 | host_reservation_identifiers: Optional[List[HostReservationIdentifierEnum]] = Field(
45 | default_factory=list
46 | )
47 | option_def: Optional[List[OptionDef]] = Field(default_factory=list)
48 | expired_leases_processing: Optional[dict] = None
49 | dhcp4o6_port: Optional[int] = None
50 | control_socket: Optional[ControlSocket] = None
51 | ddns_conflict_resolution_mode: Optional[DDNSConflictResolutionModeEnum] = (
52 | DDNSConflictResolutionModeEnum.check_with_dhcid
53 | )
54 | dhcp_queue_control: Optional[DHCPQueueControl] = None
55 | dhcp_ddns: Optional[DhcpDdns] = None
56 | sanity_checks: Optional[SanityCheck] = None
57 | config_control: Optional[dict] = None
58 | server_tag: Optional[str] = None
59 | hostname_char_set: Optional[str] = None
60 | hostname_char_replacement: Optional[str] = None
61 | statistic_default_sample_count: Optional[int] = None
62 | statistic_default_sample_age: Optional[int] = None
63 | multi_threading: Optional[MultiThreading] = None
64 | early_global_reservations_lookup: Optional[bool] = None
65 | ip_reservations_unique: Optional[bool] = None
66 | reservations_lookup_first: Optional[bool] = None
67 | compatibility: Optional[dict] = None
68 | parked_packet_limit: Optional[int] = None
69 | decline_probation_period: Optional[int] = None
70 | allocator: Optional[str] = None
71 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/database.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.enums import DatabaseOnFailEnum, DatabaseTypeEnum
4 | from pyisckea.models.generic.base import KeaBaseModel
5 |
6 |
7 | class Database(KeaBaseModel):
8 | type: DatabaseTypeEnum
9 | user: Optional[str] = None
10 | password: Optional[str] = None
11 | host: Optional[str] = None
12 | port: Optional[int] = None
13 | name: Optional[str] = None
14 | persist: Optional[bool] = None
15 | lfc_interval: Optional[int] = None
16 | readonly: Optional[bool] = None
17 | connect_timeout: Optional[int] = None
18 | max_reconnect_tries: Optional[int] = None
19 | on_fail: Optional[DatabaseOnFailEnum] = None
20 | max_row_errors: Optional[int] = None
21 | trust_anchor: Optional[str] = None
22 | cert_file: Optional[str] = None
23 | key_file: Optional[str] = None
24 | cipher_list: Optional[str] = None
25 | unknown_map_entry: Optional[str] = None
26 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/dhcp_common.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.config import CommonDhcpConfig
6 | from pyisckea.models.generic.relay import Relay
7 |
8 |
9 | class CommonDHCPParams(CommonDhcpConfig):
10 | """Any param shared between v4/v6 Shared Networks and Subnets"""
11 |
12 | interface: Optional[str] = None
13 | client_class: Optional[str] = None
14 | require_client_classes: Optional[List[str]] = Field(default_factory=list)
15 | relay: Optional[Relay] = None
16 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/dhcp_ddns.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.enums import NCRFormatEnum, NCRProtocolEnum
4 | from pyisckea.models.generic.config import CommonConfig
5 |
6 |
7 | class DhcpDdns(CommonConfig):
8 | enable_updates: bool
9 | server_ip: str
10 | server_port: int
11 | sender_ip: str
12 | sender_port: int
13 | max_queue_size: int
14 | ncr_protocol: NCRProtocolEnum
15 | ncr_format: NCRFormatEnum
16 | dep_override_no_update: Optional[bool] = None
17 | dep_override_client_update: Optional[bool] = None
18 | dep_replace_client_name: Optional[str] = None
19 | dep_generated_prefix: Optional[str] = None
20 | dep_qualifying_suffix: Optional[str] = None
21 | dep_hostname_char_set: Optional[str] = None
22 | dep_hostname_char_replacement: Optional[str] = None
23 |
24 |
25 | """
26 | "enable-updates": false,
27 | "max-queue-size": 1024,
28 | "ncr-format": "JSON",
29 | "ncr-protocol": "UDP",
30 | "sender-ip": "0.0.0.0",
31 | "sender-port": 0,
32 | "server-ip": "127.0.0.1",
33 | "server-port": 53001
34 | """
35 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/dhcp_queue_control.py:
--------------------------------------------------------------------------------
1 | from pyisckea.models.generic.config import CommonConfig
2 |
3 |
4 | class DHCPQueueControl(CommonConfig):
5 | enable_queue: bool
6 | queue_type: str
7 | capacity: int
8 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/high_availability.py:
--------------------------------------------------------------------------------
1 | from typing import List, Union
2 |
3 | from pyisckea.models.enums import HAModeTypeEnum, HARoleTypeEnum, HAStateTypeEnum
4 | from pyisckea.models.generic.base import KeaBaseModel
5 |
6 |
7 | class HAServerLocal(KeaBaseModel):
8 | role: HARoleTypeEnum
9 | scopes: List[str]
10 | state: HAStateTypeEnum
11 |
12 |
13 | class HAServerRemote(KeaBaseModel):
14 | age: int
15 | in_touch: bool
16 | last_scopes: List[str]
17 | last_state: Union[None, HAStateTypeEnum] = None
18 | role: HARoleTypeEnum
19 |
20 |
21 | class HAServers(KeaBaseModel):
22 | local: HAServerLocal
23 | remote: HAServerRemote
24 |
25 |
26 | class HighAvailability(KeaBaseModel):
27 | ha_mode: HAModeTypeEnum
28 | ha_servers: HAServers
29 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/hook.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.base import KeaBaseModel
4 |
5 |
6 | class Hook(KeaBaseModel):
7 | library: str
8 | parameters: Optional[dict] = None
9 | name: Optional[str] = None
10 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/lease.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.base import KeaBaseModel
4 |
5 |
6 | class Lease(KeaBaseModel):
7 | cltt: Optional[int] = None
8 | fqdn_fwd: Optional[bool] = None
9 | fqdn_rev: Optional[bool] = None
10 | hostname: Optional[str] = None
11 | hw_address: Optional[str] = None
12 | ip_address: str
13 | state: Optional[int] = None
14 | subnet_id: Optional[int] = None
15 | valid_lft: Optional[int] = None
16 |
17 |
18 | class LeasePage(KeaBaseModel):
19 | count: int
20 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/logger.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 | from typing_extensions import Annotated
5 |
6 | from pyisckea.models.enums import LoggerLevelEnum
7 | from pyisckea.models.generic.base import KeaBaseModel, KeaModel
8 |
9 |
10 | class Output(KeaBaseModel):
11 | output: str
12 | flush: bool
13 | maxsize: Optional[int] = None
14 | maxver: Optional[int] = None
15 | pattern: Optional[str] = None
16 |
17 |
18 | class Logger(KeaModel):
19 | name: str
20 | output_options: Optional[List[Output]] = Field(default_factory=list)
21 | debuglevel: Annotated[int, Field(ge=0, le=100)]
22 | severity: Optional[LoggerLevelEnum] = None
23 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/multi_threading.py:
--------------------------------------------------------------------------------
1 | from pyisckea.models.generic.config import CommonConfig
2 |
3 |
4 | class MultiThreading(CommonConfig):
5 | enable_multi_threading: bool
6 | thread_pool_size: int
7 | packet_queue_size: int
8 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/option_data.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.base import KeaModel
4 |
5 |
6 | class OptionData(KeaModel):
7 | data: str
8 | name: Optional[str] = None
9 | code: Optional[int] = None
10 | space: Optional[str] = None
11 | csv_format: Optional[bool] = None
12 | always_send: Optional[bool] = None
13 | never_send: Optional[bool] = None
14 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/option_def.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.config import CommonConfig
4 |
5 |
6 | class OptionDef(CommonConfig):
7 | name: str
8 | code: Optional[int] = None
9 | type: Optional[str] = None
10 | record_types: Optional[str] = None
11 | space: Optional[str] = None
12 | encapsulate: Optional[str] = None
13 | array: Optional[bool] = None
14 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/pool.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.base import KeaModel
6 | from pyisckea.models.generic.option_data import OptionData
7 |
8 |
9 | class Pool(KeaModel):
10 | pool: str
11 | option_data: Optional[List[OptionData]] = Field(default_factory=list)
12 | client_class: Optional[str] = None
13 | require_client_classes: Optional[str] = None
14 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/relay.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.base import KeaBaseModel
6 |
7 |
8 | class Relay(KeaBaseModel):
9 | ip_addresses: List[str] = Field(default_factory=list)
10 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/remote_map.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import Field
4 | from typing_extensions import Annotated
5 |
6 | from pyisckea.models.enums import RemoteMapTypeEnum
7 | from pyisckea.models.generic.base import KeaBaseModel
8 |
9 |
10 | class RemoteMap(KeaBaseModel):
11 | type: Optional[RemoteMapTypeEnum] = None
12 | host: Optional[str] = None
13 | port: Optional[Annotated[int, Field(gt=1, le=65535)]] = None
14 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/remote_server.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pydantic import StringConstraints
4 | from typing_extensions import Annotated
5 |
6 | from pyisckea.models.generic.base import KeaBaseModel
7 |
8 |
9 | class RemoteServer(KeaBaseModel):
10 | server_tag: Annotated[str, StringConstraints(max_length=256)]
11 | description: Optional[str] = None
12 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/reservation.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic.base import KeaModel
6 | from pyisckea.models.generic.option_data import OptionData
7 |
8 |
9 | class Reservation(KeaModel):
10 | duid: Optional[str] = None
11 | client_classes: Optional[List[str]] = Field(default_factory=list)
12 | flex_id: Optional[str] = None
13 | hw_address: Optional[str] = None
14 | hostname: Optional[str] = None
15 | option_data: Optional[List[OptionData]] = Field(default_factory=list)
16 | subnet_id: Optional[int] = None # Used for reservation-add
17 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/sanity_check.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.base import KeaBaseModel
4 |
5 |
6 | class SanityCheck(KeaBaseModel):
7 | lease_checks: str
8 | extended_info_checks: Optional[str] = None
9 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/shared_network.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from pyisckea.models.generic.dhcp_common import CommonDHCPParams
4 |
5 |
6 | class SharedNetwork(CommonDHCPParams):
7 | name: str
8 | relay: Optional[dict] = None
9 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/sockets.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import BaseModel, ConfigDict, Field
4 |
5 | from pyisckea.models.enums import StatusEnum
6 |
7 |
8 | class Sockets(BaseModel):
9 | errors: Optional[List[str]] = Field(default_factory=list)
10 | status: StatusEnum
11 | model_config = ConfigDict(use_enum_values=True)
12 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/status.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 |
5 | from pyisckea.models.generic import Sockets
6 | from pyisckea.models.generic.base import KeaBaseModel
7 | from pyisckea.models.generic.high_availability import HighAvailability
8 |
9 |
10 | class StatusGet(KeaBaseModel):
11 | pid: int
12 | uptime: int
13 | reload: int
14 | multi_threading_enabled: Optional[bool] = None
15 | sockets: Optional[Sockets] = None
16 | high_availability: Optional[List[HighAvailability]] = Field(default_factory=list)
17 |
--------------------------------------------------------------------------------
/pyisckea/models/generic/subnet.py:
--------------------------------------------------------------------------------
1 | from typing import List, Optional
2 |
3 | from pydantic import Field
4 | from typing_extensions import Annotated
5 |
6 | from pyisckea.models.generic.dhcp_common import CommonDHCPParams
7 | from pyisckea.models.generic.pool import Pool
8 |
9 |
10 | # check whether id is still optional with new versions of kea
11 | class Subnet(CommonDHCPParams):
12 | id: Optional[Annotated[int, Field(gt=0, lt=4294967295)]] = None
13 | pools: Optional[List[Pool]] = Field(default_factory=list)
14 | subnet: str
15 | hostname_char_set: Optional[str] = None
16 | hostname_char_replacement: Optional[str] = None
17 | allocator: Optional[str] = None
18 |
--------------------------------------------------------------------------------
/pyisckea/parsers/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ["Dhcp4Parser", "Dhcp6Parser", "CtrlAgentParser"]
2 |
3 | from pyisckea.parsers.ctrlagent import CtrlAgentParser
4 | from pyisckea.parsers.dhcp4 import Dhcp4Parser
5 | from pyisckea.parsers.dhcp6 import Dhcp6Parser
6 |
--------------------------------------------------------------------------------
/pyisckea/parsers/ctrlagent.py:
--------------------------------------------------------------------------------
1 | from pyisckea.models.ctrlagent.config import CtrlAgentDaemonConfig
2 | from pyisckea.parsers.generic import GenericParser
3 |
4 |
5 | class CtrlAgentParser(GenericParser):
6 | """Parser for the ISC Kea CtrlAgent configuration file. This should ideally
7 | be used with the cached config stored in the Daemon class like this:
8 |
9 | parser = CtrlAgentParser(config=server.ctrlagent.cached_config)
10 | """
11 |
12 | def __init__(self, config: dict):
13 | self.config = CtrlAgentDaemonConfig.model_validate(config["Control-agent"])
14 |
--------------------------------------------------------------------------------
/pyisckea/parsers/ddns.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/veesix-networks/pyisckea/c701b965556c5e52a98d53e406d754728d993126/pyisckea/parsers/ddns.py
--------------------------------------------------------------------------------
/pyisckea/parsers/exceptions.py:
--------------------------------------------------------------------------------
1 | class GenericParserError(Exception):
2 | def __init__(self, message: str = "Encountered generic parser error"):
3 | self.message = message
4 | super().__init__(self.message)
5 |
6 |
7 | class ParserSubnetIDAlreadyExistError(GenericParserError):
8 | def __init__(self, id: int):
9 | self.message = f"Subnet with same ID {id} already exist in local configuration"
10 | super().__init__(self.message)
11 |
12 |
13 | class ParserSubnetCIDRAlreadyExistError(GenericParserError):
14 | def __init__(self, cidr: str):
15 | self.message = (
16 | f"Subnet with same CIDR {cidr} already exist in local configuration"
17 | )
18 | super().__init__(self.message)
19 |
20 |
21 | class ParserSharedNetworkAlreadyExistError(GenericParserError):
22 | def __init__(self, name: str):
23 | self.message = f"Shared Network with name {name} already exists"
24 | super().__init__(self.message)
25 |
26 |
27 | class ParserSharedNetworkNotFoundError(GenericParserError):
28 | def __init__(self, name: str):
29 | self.message = f"Shared Network with name {name} does not exist"
30 | super().__init__(self.message)
31 |
32 |
33 | class ParserSubnetNotFoundError(GenericParserError):
34 | def __init__(self, id: int):
35 | self.message = f"Subnet with ID {id} does not exist"
36 | super().__init__(self.message)
37 |
38 |
39 | class ParserReservationAlreadyExistError(GenericParserError):
40 | def __init__(self, ip_address: str):
41 | self.message = f"Reservation with IP Address {ip_address} already exist"
42 | super().__init__(self.message)
43 |
44 |
45 | class ParserReservationNotFoundError(GenericParserError):
46 | def __init__(self, ip_address: str):
47 | self.message = f"Reservation with IP Address {ip_address} not found"
48 | super().__init__(self.message)
49 |
50 |
51 | class ParserOptionDataAlreadyExistError(GenericParserError):
52 | def __init__(self, object: str, code: int):
53 | self.message = f"Option {code} already exist on object {object}"
54 | super().__init__(self.message)
55 |
56 |
57 | class ParserSubnetPoolAlreadyExistError(GenericParserError):
58 | def __init__(self, id: int, pool: str):
59 | self.message = f"Pool {pool} already exist in subnet {id}"
60 | super().__init__(self.message)
61 |
62 |
63 | class ParserPoolInvalidAddressError(GenericParserError):
64 | def __init__(self, start: str, end: str):
65 | self.message = (
66 | f"Start ({start}) or End IP ({end}) for pool is not a valid IP address"
67 | )
68 | super().__init__(self.message)
69 |
70 |
71 | class ParserPoolAddressNotInSubnetError(GenericParserError):
72 | def __init__(self, address: str, subnet: str):
73 | self.message = (
74 | f"Pool Start or End address {address} is not within subnet {subnet}"
75 | )
76 | super().__init__(self.message)
77 |
78 |
79 | class ParserInvalidHostReservationIdentifierError(GenericParserError):
80 | def __init__(self, identifier_type: str):
81 | self.message = f"Identifier Typer {identifier_type} is not a valid type"
82 | super().__init__(self.message)
83 |
84 |
85 | class ParserPDPoolAlreadyExistError(GenericParserError):
86 | def __init__(self, prefix: str, prefix_len: int, id: int):
87 | self.message = (
88 | f"PD Prefix {prefix}/{prefix_len} already exist in a subnet ({id})"
89 | )
90 | super().__init__(self.message)
91 |
92 |
93 | class ParserPDPoolNotFoundError(GenericParserError):
94 | def __init__(self, id: int, prefix: str, prefix_len: int):
95 | self.message = f"Unable to find PD Prefix {prefix}/{prefix_len} in subnet {id}"
96 | super().__init__(self.message)
97 |
--------------------------------------------------------------------------------
/pyisckea/parsers/generic.py:
--------------------------------------------------------------------------------
1 | class GenericParser:
2 | """A Parser does not interact with the ISC Kea Daemon APIs but essentially builds
3 | similar functionality for a local cached config file. If you do not pay for the
4 | premium hooks to expose the different commands like subnet4-add or network4-del then
5 | the parsers job is to provide similar functionality for the provided daemon configuration
6 | and then you should use the relevant service `config-set` functionality which is implemented
7 | for each service in pyisckea.
8 |
9 | The recommended option is to use the functions within the Daemon class (eg. server.dhcp4) and to
10 | use these parsers as a last resort as tests are not currently performed as extensively vs the API
11 | functionality"""
12 |
13 | def __init__(self, config: dict):
14 | self.config = config
15 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "pyisckea"
3 | version = "0.9.1"
4 | description = "Wrapper around requests module to query ISC Kea DHCP API Daemons (ctrlagent, dhcp4, dhcp6, ddns)"
5 | authors = ["Brandon Spendlove "]
6 | license = "Apache 2.0"
7 | readme = "README.md"
8 |
9 | [tool.poetry.dependencies]
10 | python = "^3.10"
11 | pydantic = "^2.10.6"
12 | httpx = "^0.28.1"
13 |
14 |
15 | [tool.poetry.group.dev.dependencies]
16 | black = "^24.10.0"
17 | pytest = "^7.4.0"
18 | pytest-order = "^1.1.0"
19 |
20 |
21 | [tool.poetry.group.docs.dependencies]
22 | mkdocs = "^1.6.1"
23 | mkdocs-material = "^9.6.11"
24 |
25 | [build-system]
26 | requires = ["poetry-core"]
27 | build-backend = "poetry.core.masonry.api"
28 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | mkdocs-material==9.6.11 ; python_version >= "3.10" and python_version < "4.0"
--------------------------------------------------------------------------------
/tests/ci/models/test_ci_kea_ctrlagent_config_model.py:
--------------------------------------------------------------------------------
1 | from pyisckea.models.ctrlagent.config import CtrlAgentDaemonConfig
2 | from pyisckea.models.generic.control_socket import ControlSockets
3 |
4 |
5 | def test_ci_kea_ctrlagent_config_model_load(ctrlagent_model: CtrlAgentDaemonConfig):
6 | assert ctrlagent_model
7 |
8 |
9 | def test_ci_kea_ctrlagent_config_model_by_export(
10 | ctrlagent_model: CtrlAgentDaemonConfig,
11 | ):
12 | assert ctrlagent_model.model_dump(exclude_none=True, by_alias=True)
13 |
14 |
15 | def test_ci_kea_ctrlagent_config_model_check_required(
16 | ctrlagent_model: CtrlAgentDaemonConfig,
17 | ):
18 | assert ctrlagent_model.http_host
19 | assert ctrlagent_model.http_port
20 |
21 |
22 | def test_ci_kea_ctrlagent_config_model_control_sockets(
23 | ctrlagent_model: CtrlAgentDaemonConfig,
24 | ):
25 | assert type(ctrlagent_model.control_sockets) is ControlSockets
26 |
27 | assert ctrlagent_model.control_sockets.d2.socket_name
28 | assert ctrlagent_model.control_sockets.d2.socket_type == "unix"
29 |
30 | assert ctrlagent_model.control_sockets.dhcp4.socket_name
31 | assert ctrlagent_model.control_sockets.dhcp4.socket_type == "unix"
32 |
33 | assert ctrlagent_model.control_sockets.dhcp6.socket_name
34 | assert ctrlagent_model.control_sockets.dhcp6.socket_type == "unix"
35 |
36 |
37 | def test_ci_kea_ctrlagent_config_model_loggers(
38 | ctrlagent_model: CtrlAgentDaemonConfig,
39 | ):
40 | assert len(ctrlagent_model.loggers) > 0
41 |
--------------------------------------------------------------------------------
/tests/ci/models/test_ci_kea_dhcp4_config_model.py:
--------------------------------------------------------------------------------
1 | from pyisckea.models.dhcp4.config import Dhcp4DaemonConfig
2 |
3 |
4 | def test_ci_kea_dhcp4_config_model_load(dhcp4_model: Dhcp4DaemonConfig):
5 | assert dhcp4_model
6 |
7 |
8 | def test_ci_kea_dhcp4_config_model_export(dhcp4_model: Dhcp4DaemonConfig):
9 | assert dhcp4_model.model_dump(exclude_none=True, by_alias=True)
10 |
11 |
12 | def test_ci_kea_dhcp4_config_model_hooks(dhcp4_model: Dhcp4DaemonConfig):
13 | assert len(dhcp4_model.hooks_libraries) > 0
14 | for hook_library in dhcp4_model.hooks_libraries:
15 | assert hook_library.library
16 |
17 |
18 | def test_ci_kea_dhcp4_config_client_classes(dhcp4_model: Dhcp4DaemonConfig):
19 | assert len(dhcp4_model.client_classes) > 0
20 | for client_class in dhcp4_model.client_classes:
21 | assert client_class.name
22 | assert len(client_class.option_data) > 0
23 |
24 |
25 | def test_ci_kea_dhcp4_config_model_host_reservation_identifiers(
26 | dhcp4_model: Dhcp4DaemonConfig,
27 | ):
28 | identifiers = ["hw-address", "duid", "circuit-id", "client-id"]
29 | assert len(dhcp4_model.host_reservation_identifiers) > 0
30 | for identifier in dhcp4_model.host_reservation_identifiers:
31 | assert identifier in identifiers
32 |
33 |
34 | def test_ci_kea_dhcp4_config_model_interfaces_config(dhcp4_model: Dhcp4DaemonConfig):
35 | assert len(dhcp4_model.interfaces_config.interfaces) == 1
36 | assert dhcp4_model.interfaces_config.interfaces[0] == "eth0"
37 |
38 |
39 | def test_ci_kea_dhcp4_config_model_subnet4(dhcp4_model: Dhcp4DaemonConfig):
40 | assert len(dhcp4_model.subnet4) > 0
41 | for subnet in dhcp4_model.subnet4:
42 | assert subnet.id
43 | assert subnet.subnet
44 |
45 |
46 | def test_ci_kea_dhcp4_config_model_subnet4_option_data(dhcp4_model: Dhcp4DaemonConfig):
47 | for subnet in dhcp4_model.subnet4:
48 | assert len(subnet.option_data) > 0
49 |
50 | for option in subnet.option_data:
51 | assert option.code
52 | assert option.data
53 |
54 |
55 | def test_ci_kea_dhcp4_config_model_subnet4_pool(dhcp4_model: Dhcp4DaemonConfig):
56 | for subnet in dhcp4_model.subnet4:
57 | assert len(subnet.pools) > 0
58 | for pool in subnet.pools:
59 | assert pool.pool
60 |
61 |
62 | def test_ci_kea_dhcp4_config_model_subnet4_reservations(dhcp4_model: Dhcp4DaemonConfig):
63 | for subnet in dhcp4_model.subnet4:
64 | assert len(subnet.reservations) > 0
65 | for reservation in subnet.reservations:
66 | assert reservation.ip_address
67 |
--------------------------------------------------------------------------------
/tests/ci/models/test_ci_kea_dhcp6_config_model.py:
--------------------------------------------------------------------------------
1 | from pyisckea.models.dhcp6.config import Dhcp6DaemonConfig
2 |
3 |
4 | def test_ci_kea_dhcp6_config_model_load(dhcp6_model: Dhcp6DaemonConfig):
5 | assert dhcp6_model
6 |
7 |
8 | def test_ci_kea_dhcp6_config_model_export(dhcp6_model: Dhcp6DaemonConfig):
9 | assert dhcp6_model.model_dump(exclude_none=True, by_alias=True)
10 |
11 |
12 | def test_ci_kea_dhcp6_config_model_hooks(dhcp6_model: Dhcp6DaemonConfig):
13 | assert len(dhcp6_model.hooks_libraries) > 0
14 | for hook_library in dhcp6_model.hooks_libraries:
15 | assert hook_library.library
16 |
17 |
18 | def test_ci_kea_dhcp6_config_client_classes(dhcp6_model: Dhcp6DaemonConfig):
19 | assert len(dhcp6_model.client_classes) > 0
20 | for client_class in dhcp6_model.client_classes:
21 | assert client_class.name
22 | assert len(client_class.option_data) > 0
23 |
--------------------------------------------------------------------------------
/tests/configs/ctrlagent_api_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "Control-agent": {
3 | "cert-file": "/etc/kea/cert.pem",
4 | "cert-required": false,
5 | "control-sockets": {
6 | "d2": {
7 | "socket-name": "/tmp/kea-ddns-ctrl-socket",
8 | "socket-type": "unix"
9 | },
10 | "dhcp4": {
11 | "socket-name": "/tmp/kea4-ctrl-socket",
12 | "socket-type": "unix"
13 | },
14 | "dhcp6": {
15 | "socket-name": "/tmp/kea6-ctrl-socket",
16 | "socket-type": "unix"
17 | }
18 | },
19 | "hooks-libraries": [],
20 | "http-host": "0.0.0.0",
21 | "http-port": 8080,
22 | "key-file": "/etc/kea/key.pem",
23 | "loggers": [
24 | {
25 | "debuglevel": 0,
26 | "name": "kea-ctrl-agent",
27 | "severity": "INFO"
28 | }
29 | ],
30 | "trust-anchor": "my-ca"
31 | }
32 | }
--------------------------------------------------------------------------------
/tests/configs/ctrlagent_parsed_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "Control-agent": {
3 | "control-sockets": {
4 | "d2": {
5 | "socket-name": "/tmp/kea-ddns-ctrl-socket",
6 | "socket-type": "unix"
7 | },
8 | "dhcp4": {
9 | "socket-name": "/tmp/kea4-ctrl-socket",
10 | "socket-type": "unix"
11 | },
12 | "dhcp6": {
13 | "socket-name": "/tmp/kea6-ctrl-socket",
14 | "socket-type": "unix"
15 | }
16 | },
17 | "hooks-libraries": [],
18 | "http-host": "0.0.0.0",
19 | "http-port": 8080,
20 | "loggers": [
21 | {
22 | "debuglevel": 0,
23 | "name": "kea-ctrl-agent",
24 | "severity": "INFO"
25 | }
26 | ]
27 | }
28 | }
--------------------------------------------------------------------------------
/tests/ctrlagent/test_kea_ctrlagent.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 |
4 | def test_kea_ctrlagent_build_report(kea_server: Kea):
5 | response = kea_server.ctrlagent.build_report()
6 | assert response.result == 0
7 | assert response.text
8 |
9 |
10 | def test_kea_ctrlagent_config_get(kea_server: Kea):
11 | response = kea_server.ctrlagent.config_get()
12 | assert response.result == 0
13 | assert response.arguments
14 |
15 |
16 | def test_kea_ctrlagent_config_test(kea_server: Kea):
17 | config = kea_server.ctrlagent.cached_config
18 | if config.get("hash"): # Temp workaround
19 | del config["hash"]
20 |
21 | response = kea_server.ctrlagent.config_test(config=config)
22 | assert response.result == 0
23 |
24 |
25 | def test_kea_ctrlagent_config_set(kea_server: Kea):
26 | config = kea_server.ctrlagent.cached_config
27 | if config.get("hash"): # Temp workaround
28 | del config["hash"]
29 |
30 | response = kea_server.ctrlagent.config_set(config=config)
31 | assert response.result == 0
32 |
33 |
34 | def test_kea_ctrlagent_config_write(kea_server: Kea):
35 | filename = "/usr/local/etc/kea/kea-ctrl-agent.conf"
36 | response = kea_server.ctrlagent.config_write(filename=filename)
37 | assert response.result == 0
38 | assert response.arguments["filename"] == filename
39 | assert response.arguments["size"] > 0
40 |
41 |
42 | def test_kea_ctrlagent_config_reload(kea_server: Kea):
43 | response = kea_server.ctrlagent.config_reload()
44 | assert response.result == 0
45 | assert response.text
46 |
47 |
48 | def test_kea_ctrlagent_list_commands(kea_server: Kea):
49 | response = kea_server.ctrlagent.list_commands()
50 | assert response.result == 0
51 | assert response.arguments
52 | assert "config-get" in response.arguments
53 |
54 |
55 | def test_kea_ctrlagent_status_get(kea_server: Kea):
56 | response = kea_server.ctrlagent.status_get()
57 | assert response.pid
58 | assert response.reload >= 0
59 | assert response.uptime
60 |
61 |
62 | def test_kea_ctrlagent_shutdown(kea_server: Kea):
63 | response = kea_server.ctrlagent.shutdown()
64 | assert response.result == 0
65 | assert response.text == "Control Agent is shutting down"
66 |
--------------------------------------------------------------------------------
/tests/ctrlagent/test_kea_ctrlagent_parser.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from pyisckea import Kea
4 | from pyisckea.parsers.ctrlagent import CtrlAgentParser
5 |
6 |
7 | def test_kea_ctrlagent_parser_parse_config(kea_server: Kea):
8 | cached_config = kea_server.ctrlagent.cached_config
9 | parsed = CtrlAgentParser(config=cached_config)
10 |
11 | assert parsed.config.http_host
12 | assert parsed.config.http_port
13 | assert json.dumps(
14 | parsed.config.model_dump(exclude_none=True, by_alias=True),
15 | indent=4,
16 | sort_keys=True,
17 | )
18 |
19 |
20 | def test_kea_ctrlagent_parser_config_test(kea_server: Kea):
21 | cached_config = kea_server.ctrlagent.cached_config
22 | parsed = CtrlAgentParser(config=cached_config)
23 | config_to_test = {
24 | "Control-agent": parsed.config.model_dump(
25 | exclude_none=True, exclude_unset=True, by_alias=True
26 | )
27 | }
28 |
29 | test_results = kea_server.ctrlagent.config_test(config=config_to_test)
30 | assert test_results.result == 0
31 |
32 | # Remove hash if exist for now until tests are created to take that into account
33 | if cached_config.get("hash"):
34 | del cached_config["hash"]
35 |
36 | cached_config_json = json.dumps(cached_config, indent=4)
37 | parsed_config_json = json.dumps(config_to_test, indent=4, sort_keys=True)
38 | assert cached_config_json == parsed_config_json
39 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.generic.remote_server import RemoteServer
3 |
4 | """remote generic process:
5 | remote-prepare (config backend pull test)
6 | remote-server4-set
7 | remote-server4-get
8 | remote-server4-get (non existent)
9 | remote-server4-get-all
10 | remote-server4-del
11 | """
12 |
13 |
14 | def test_kea_dhcp4_remote_prepare(kea_server: Kea):
15 | response = kea_server.dhcp4.config_backend_pull()
16 | assert response.result == 0
17 |
18 |
19 | def test_kea_dhcp4_remote_server4_set(kea_server: Kea):
20 | response = kea_server.dhcp4.remote_server4_set(
21 | servers=[RemoteServer(server_tag="pyisckea", description="pyisckea-test")]
22 | )
23 | assert response.result == 0
24 |
25 |
26 | def test_kea_dhcp4_remote_server4_get(kea_server: Kea):
27 | server = kea_server.dhcp4.remote_server4_get(server_tag="pyisckea")
28 | assert server
29 | assert server.server_tag == "pyisckea"
30 | assert server.description == "pyisckea-test"
31 |
32 |
33 | def test_kea_dhcp4_remote_server4_del(kea_server: Kea):
34 | response = kea_server.dhcp4.remote_server4_del(servers=["pyisckea"])
35 | assert response.result == 0
36 | assert "deleted" in response.text
37 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_class4.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaClientClassNotFoundException
5 | from pyisckea.models.dhcp4.client_class import ClientClass4
6 |
7 | """remote-class4 process:
8 |
9 | get (non existent)
10 | set
11 | get
12 | get-all
13 | del
14 | """
15 |
16 |
17 | def test_kea_dhcp4_remote_class4_get_non_existent(kea_server: Kea):
18 | with pytest.raises(KeaClientClassNotFoundException):
19 | kea_server.dhcp4.remote_class4_get(name="ipxe_efi_x64")
20 |
21 |
22 | def test_kea_dhcp4_remote_class4_set(kea_server: Kea):
23 | client_class = ClientClass4(
24 | name="ipxe_efi_x64",
25 | test="option[93].hex == 0x0009",
26 | next_server="192.0.2.254",
27 | server_hostname="hal9000",
28 | boot_file_name="/dev/null",
29 | )
30 | response = kea_server.dhcp4.remote_class4_set(client_class=client_class)
31 | assert response.result == 0
32 | assert len(response.arguments.get("client-classes", [])) > 0
33 |
34 |
35 | def test_kea_dhcp4_remote_class4_get(kea_server: Kea):
36 | response = kea_server.dhcp4.remote_class4_get(name="ipxe_efi_x64")
37 | assert response
38 | assert response.name == "ipxe_efi_x64"
39 |
40 |
41 | def test_kea_dhcp4_remote_class4_get_all(kea_server: Kea):
42 | response = kea_server.dhcp4.remote_class4_get_all()
43 | assert response
44 | assert len(response) > 0
45 |
46 |
47 | def test_kea_dhcp4_remote_class4_del(kea_server: Kea):
48 | response = kea_server.dhcp4.remote_class4_del(name="ipxe_efi_x64")
49 | assert response.result == 0
50 | assert response.arguments.get("count") == 1
51 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_global_parameter4.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 | """remote-global-parameter4 process:
4 |
5 | get (non existent)
6 | set
7 | get
8 | get-all
9 | del
10 | """
11 |
12 |
13 | def test_kea_dhcp4_remote_global_parameter4_get_non_existent(kea_server: Kea):
14 | response = kea_server.dhcp4.remote_global_parameter4_get(
15 | parameter="boot-file-name", server_tag="pyisckea-1"
16 | )
17 | assert response.result == 3
18 | assert response.arguments.get("count") == 0
19 |
20 |
21 | def test_kea_dhcp4_remote_global_parameter4_set(kea_server: Kea):
22 | response = kea_server.dhcp4.remote_global_parameter4_set(
23 | parameters={"boot-file-name": "/dev/null"}, server_tag="all"
24 | )
25 | assert response.result == 0
26 | assert response.arguments.get("count") == 1
27 |
28 |
29 | def test_kea_dhcp4_remote_global_parameter4_get(kea_server: Kea):
30 | response = kea_server.dhcp4.remote_global_parameter4_get(
31 | parameter="boot-file-name", server_tag="all"
32 | )
33 | assert response.result == 0
34 | assert response.arguments.get("count") == 1
35 |
36 |
37 | def test_kea_dhcp4_remote_global_parameter4_get_all(kea_server: Kea):
38 | response = kea_server.dhcp4.remote_global_parameter4_get_all(server_tag="all")
39 | assert response.result == 0
40 | assert len(response.arguments.get("parameters")) > 0
41 |
42 |
43 | def test_kea_dhcp4_remote_global_parameter4_del(kea_server: Kea):
44 | response = kea_server.dhcp4.remote_global_parameter4_del(
45 | parameter="boot-file-name", server_tag="all"
46 | )
47 | assert response.result == 0
48 |
49 |
50 | def test_kea_dhcp4_remote_global_parameter4_del_non_existent(kea_server: Kea):
51 | response = kea_server.dhcp4.remote_global_parameter4_del(
52 | parameter="boot-file-name", server_tag="all"
53 | )
54 | assert response.result == 3
55 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_network4.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSharedNetworkNotFoundException
5 | from pyisckea.models.dhcp4.shared_network import SharedNetwork4
6 | from pyisckea.models.dhcp4.subnet import Subnet4
7 |
8 | """remote-network4 process:
9 | get (non-existent network)
10 | add
11 | list
12 | get
13 | add and delete subnet (for existing shared-network)
14 | del
15 | del (non-existent)
16 | """
17 |
18 |
19 | def test_kea_dhcp4_remote_network4_get_non_existent(kea_server: Kea):
20 | name = "pyisckea-pytest"
21 | with pytest.raises(KeaSharedNetworkNotFoundException):
22 | kea_server.dhcp4.remote_network4_get(name=name)
23 |
24 |
25 | def test_kea_dhcp4_remote_network4_add(kea_server: Kea, db_remote_map: dict):
26 | name = "pyisckea-pytest"
27 | data = SharedNetwork4(name=name)
28 | shared_networks = [data]
29 | response = kea_server.dhcp4.remote_network4_set(
30 | shared_networks=shared_networks, server_tags=["all"], remote_map=db_remote_map
31 | )
32 |
33 | assert response.result == 0
34 | assert len(response.arguments.get("shared-networks", [])) > 0
35 |
36 |
37 | def test_kea_dhcp4_remote_network4_list(kea_server: Kea, db_remote_map: dict):
38 | shared_networks = kea_server.dhcp4.remote_network4_list(
39 | server_tags=["pyisckea-1"], remote_map=db_remote_map
40 | )
41 | assert shared_networks
42 | assert len(shared_networks) > 0
43 |
44 |
45 | def test_kea_dhcp4_remote_network4_get(kea_server: Kea, db_remote_map: dict):
46 | name = "pyisckea-pytest"
47 | shared_network = kea_server.dhcp4.remote_network4_get(
48 | name=name, remote_map=db_remote_map
49 | )
50 | assert shared_network
51 | assert shared_network.name == name
52 |
53 |
54 | def test_kea_dhcp4_remote_subnet4_add_subnet(kea_server: Kea, db_remote_map: dict):
55 | name = "pyisckea-pytest"
56 |
57 | # Create Temporary Subnet
58 | subnet = Subnet4(subnet="192.0.2.32/31", id=40123, shared_network_name=name)
59 |
60 | # Create subnet with shared network assosication
61 | response = kea_server.dhcp4.remote_subnet4_set(
62 | subnet=subnet, server_tags=["all"], remote_map=db_remote_map
63 | )
64 | assert response.result == 0
65 | assert len(response.arguments.get("subnets", [])) > 0
66 |
67 |
68 | def test_kea_dhcp4_remote_subnet4_del_by_id(kea_server: Kea, db_remote_map: dict):
69 | response = kea_server.dhcp4.remote_subnet4_del_by_id(
70 | subnet_id=40123, remote_map=db_remote_map
71 | )
72 | assert response.result == 0
73 | assert response.arguments.get("count") == 1
74 |
75 |
76 | def test_kea_dhcp4_remote_network4_del(kea_server: Kea, db_remote_map: dict):
77 | response = kea_server.dhcp4.remote_network4_del(
78 | name="pyisckea-pytest", keep_subnets=False, remote_map=db_remote_map
79 | )
80 | assert response.result == 0
81 | assert response.arguments.get("count") == 1
82 |
83 |
84 | def test_kea_dhcp4_remote_network4_del_non_existent(
85 | kea_server: Kea, db_remote_map: dict
86 | ):
87 | response = kea_server.dhcp4.remote_network4_del(
88 | name="pyisckea-pytest", remote_map=db_remote_map
89 | )
90 | assert response.result == 3
91 | assert response.arguments["count"] == 0
92 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_option4_global.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.generic.option_data import OptionData
3 |
4 | """remote-option-def4 process:
5 |
6 | get (non existent)
7 | set
8 | get
9 | get-all
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp4_remote_option4_global_get_non_existent(kea_server: Kea):
15 | response = kea_server.dhcp4.remote_option4_global_get(
16 | option_code=6, option_space="dhcp4", server_tag="all"
17 | )
18 | assert response.result == 3
19 | assert response.arguments.get("count") == 0
20 |
21 |
22 | def test_kea_dhcp4_remote_option4_global_set(kea_server: Kea):
23 | option_data = OptionData(name="domain-name-servers", data="192.0.2.111")
24 | response = kea_server.dhcp4.remote_option4_global_set(
25 | option_data=option_data, server_tag="all"
26 | )
27 | assert response.result == 0
28 | assert len(response.arguments.get("options", [])) > 0
29 |
30 |
31 | def test_kea_dhcp4_remote_option4_global_del(kea_server: Kea):
32 | response = kea_server.dhcp4.remote_option4_global_del(
33 | option_code=6, option_space="dhcp4", server_tag="all"
34 | )
35 | assert response.result == 0
36 | assert response.arguments.get("count") == 1
37 |
38 |
39 | def test_kea_dhcp4_remote_option4_global_del_non_existent(kea_server: Kea):
40 | response = kea_server.dhcp4.remote_option4_global_del(
41 | option_code=6, option_space="dhcp4", server_tag="all"
42 | )
43 | assert response.result == 3
44 | assert response.arguments.get("count") == 0
45 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_option4_network.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp4.shared_network import SharedNetwork4
3 | from pyisckea.models.generic.option_data import OptionData
4 |
5 | """remote-option4-network process:
6 |
7 | del (non existent)
8 | set
9 | del
10 | """
11 |
12 |
13 | def test_kea_dhcp4_remote_option4_network_prepare(kea_server: Kea):
14 | shared_network = SharedNetwork4(name="pyisckea-network")
15 | response = kea_server.dhcp4.remote_network4_set(
16 | shared_networks=[shared_network], server_tags=["all"]
17 | )
18 | assert response.result == 0
19 |
20 |
21 | def test_kea_dhcp4_remote_option4_network_del_non_existent(kea_server: Kea):
22 | response = kea_server.dhcp4.remote_option4_network_del(
23 | shared_network="pyisckea-network", option_code=6, option_space="dhcp4"
24 | )
25 | assert response.result == 3
26 | assert response.arguments.get("count") == 0
27 |
28 |
29 | def test_kea_dhcp4_remote_option4_network_set(kea_server: Kea):
30 | option_data = OptionData(name="domain-name-servers", data="192.0.2.111")
31 | response = kea_server.dhcp4.remote_option4_network_set(
32 | shared_network="pyisckea-network", option_data=option_data
33 | )
34 | assert response.result == 0
35 | assert len(response.arguments.get("options", [])) > 0
36 |
37 |
38 | def test_kea_dhcp4_remote_option4_network_del(kea_server: Kea):
39 | response = kea_server.dhcp4.remote_option4_network_del(
40 | shared_network="pyisckea-network", option_code=6, option_space="dhcp4"
41 | )
42 | assert response.result == 0
43 | assert response.arguments.get("count") == 1
44 |
45 |
46 | def test_kea_dhcp4_remote_option4_network_cleanup(kea_server: Kea):
47 | response = kea_server.dhcp4.remote_network4_del(
48 | name="pyisckea-network", keep_subnets=False
49 | )
50 | assert response.result == 0
51 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_option4_pool.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp4.subnet import Subnet4
3 | from pyisckea.models.generic.option_data import OptionData
4 | from pyisckea.models.generic.pool import Pool
5 |
6 | """remote-option4-pool process:
7 |
8 | del (non existent)
9 | set
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp4_remote_option4_pool_prepare(kea_server: Kea):
15 | subnet = Subnet4(
16 | id=40123, subnet="192.0.2.0/24", pools=[Pool(pool="192.0.2.100-192.0.2.200")]
17 | )
18 | response = kea_server.dhcp4.remote_subnet4_set(subnet=subnet, server_tags=["all"])
19 | assert response.result == 0
20 |
21 |
22 | def test_kea_dhcp4_remote_option4_pool_del_non_existent(kea_server: Kea):
23 | response = kea_server.dhcp4.remote_option4_pool_del(
24 | pool="192.0.2.100-192.168.0.200", option_code=6, option_space="dhcp4"
25 | )
26 | assert response.result == 3
27 | assert response.arguments.get("count") == 0
28 |
29 |
30 | def test_kea_dhcp4_remote_option4_pool_set(kea_server: Kea):
31 | option_data = OptionData(name="domain-name-servers", data="192.0.2.111")
32 | response = kea_server.dhcp4.remote_option4_pool_set(
33 | pool="192.0.2.100-192.0.2.200", option_data=option_data
34 | )
35 | assert response.result == 0
36 | assert len(response.arguments.get("options", [])) > 0
37 |
38 |
39 | def test_kea_dhcp4_remote_option4_pool_del(kea_server: Kea):
40 | response = kea_server.dhcp4.remote_option4_pool_del(
41 | pool="192.0.2.100-192.0.2.200", option_code=6, option_space="dhcp4"
42 | )
43 | assert response.result == 0
44 | assert response.arguments.get("count") == 1
45 |
46 |
47 | def test_kea_dhcp4_remote_option4_pool_cleanup(kea_server: Kea):
48 | response = kea_server.dhcp4.remote_subnet4_del_by_id(subnet_id=40123)
49 | assert response.result == 0
50 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_option4_subnet.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp4.subnet import Subnet4
3 | from pyisckea.models.generic.option_data import OptionData
4 |
5 | """remote-option4-subnet process:
6 |
7 | del (non existent)
8 | set
9 | del
10 | """
11 |
12 |
13 | def test_kea_dhcp4_remote_option4_subnet_prepare(kea_server: Kea):
14 | subnet = Subnet4(id=40123, subnet="192.0.2.0/24")
15 | response = kea_server.dhcp4.remote_subnet4_set(subnet=subnet, server_tags=["all"])
16 | assert response.result == 0
17 |
18 |
19 | def test_kea_dhcp4_remote_option4_subnet_del_non_existent(kea_server: Kea):
20 | response = kea_server.dhcp4.remote_option4_subnet_del(
21 | subnet_id=40123, option_code=6, option_space="dhcp4"
22 | )
23 | assert response.result == 3
24 | assert response.arguments.get("count") == 0
25 |
26 |
27 | def test_kea_dhcp4_remote_option4_subnet_set(kea_server: Kea):
28 | option_data = OptionData(name="domain-name-servers", data="192.0.2.111")
29 | response = kea_server.dhcp4.remote_option4_subnet_set(
30 | subnet_id=40123, option_data=option_data
31 | )
32 | assert response.result == 0
33 | assert len(response.arguments.get("options", [])) > 0
34 |
35 |
36 | def test_kea_dhcp4_remote_option4_subnet_del(kea_server: Kea):
37 | response = kea_server.dhcp4.remote_option4_subnet_del(
38 | subnet_id=40123, option_code=6, option_space="dhcp4"
39 | )
40 | assert response.result == 0
41 | assert response.arguments.get("count") == 1
42 |
43 |
44 | def test_kea_dhcp4_remote_option4_subnet_cleanup(kea_server: Kea):
45 | response = kea_server.dhcp4.remote_subnet4_del_by_id(subnet_id=40123)
46 | assert response.result == 0
47 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_option_def4.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.generic.option_def import OptionDef
3 |
4 | """remote-option-def4 process:
5 |
6 | get (non existent)
7 | set
8 | get
9 | get-all
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp4_remote_option_def4_get_non_existent(kea_server: Kea):
15 | response = kea_server.dhcp4.remote_option_def4_get(
16 | option_code=1, option_space="pyisckea", server_tag="all"
17 | )
18 | assert response.result == 3
19 | assert response.arguments.get("count") == 0
20 |
21 |
22 | def test_kea_dhcp4_remote_option_def4_set(kea_server: Kea):
23 | option_def = OptionDef(name="subopt1", code=1, space="pyisckea", type="string")
24 | response = kea_server.dhcp4.remote_option_def4_set(
25 | option_def=option_def, server_tag="all"
26 | )
27 | assert response.result == 0
28 | assert len(response.arguments.get("option-defs", [])) > 0
29 |
30 |
31 | def test_kea_dhcp4_remote_option_def4_del(kea_server: Kea):
32 | response = kea_server.dhcp4.remote_option_def4_del(
33 | option_code=1, option_space="pyisckea", server_tag="all"
34 | )
35 | assert response.result == 0
36 | assert response.arguments.get("count") == 1
37 |
38 |
39 | def test_kea_dhcp4_remote_option_def4_del_non_existent(kea_server: Kea):
40 | response = kea_server.dhcp4.remote_option_def4_del(
41 | option_code=1, option_space="pyisckea", server_tag="all"
42 | )
43 | assert response.result == 3
44 | assert response.arguments.get("count") == 0
45 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_reservation.py:
--------------------------------------------------------------------------------
1 | """The reason why I have created 'remote_reservation' although the API commands are 'reservation-
2 | is because you need a database to use these commands, they don't work with a mem file... Host reservations
3 | must be configured in the global dhcp configuration files as per the documentation:
4 |
5 | https://kea.readthedocs.io/en/kea-2.2.0/arm/hooks.html#hooks-host-cmds
6 |
7 | "To use the commands that change reservation information (i.e. reservation-add and reservation-del), the hosts database
8 | must be specified and it must not operate in read-only mode
9 | (for details, see the hosts-databases descriptions in DHCPv4 Hosts Database Configuration and DHCPv6 Hosts Database Configuration)."
10 | """
11 |
12 | import pytest
13 |
14 | from pyisckea import Kea
15 | from pyisckea.exceptions import KeaException, KeaReservationNotFoundException
16 | from pyisckea.models.dhcp4.subnet import Subnet4
17 |
18 | """reservation process:
19 | reservation-get non existent
20 | reservation-add
21 | reservation-add existing
22 | reservation-get-all
23 | reservation-get
24 | reservation-get-by-hostname
25 | reservation-get-by-id
26 | reservation-get-page
27 | reservation-del
28 | reservation-del non existent
29 | """
30 |
31 |
32 | def test_kea_dhcp4_remote_reservation_prepare(kea_server: Kea):
33 | subnet = Subnet4(id=40123, subnet="192.0.2.32/31")
34 | response = kea_server.dhcp4.remote_subnet4_set(subnet=subnet, server_tags=["all"])
35 | assert response.result == 0
36 |
37 |
38 | def test_kea_dhcp4_remote_reservation_get_non_existent(kea_server: Kea):
39 | with pytest.raises(KeaReservationNotFoundException):
40 | kea_server.dhcp4.reservation_get_by_identifier(
41 | subnet_id=40123,
42 | identifier_type="hw-address",
43 | identifier="aa:bb:cc:dd:ee:ff",
44 | )
45 |
46 |
47 | def test_kea_dhcp4_remote_reservation_add(kea_server: Kea):
48 | backend_updated = kea_server.dhcp4.config_backend_pull()
49 | assert backend_updated.result == 0
50 |
51 | response = kea_server.dhcp4.reservation_add(
52 | ip_address="192.0.2.33",
53 | hw_address="aa:bb:cc:dd:ee:ff",
54 | hostname="pyisckea-reservation",
55 | subnet_id=40123,
56 | )
57 | assert response.result == 0
58 |
59 |
60 | def test_kea_dhcp4_remote_reservation_add_existing(kea_server: Kea):
61 | response = kea_server.dhcp4.reservation_add(
62 | ip_address="192.0.2.33",
63 | hw_address="aa:bb:cc:dd:ee:ff",
64 | hostname="pyisckea-reservation",
65 | subnet_id=40123,
66 | )
67 |
68 | assert response.result == 1
69 |
70 |
71 | def test_kea_dhcp4_remote_reservation_get_all(kea_server: Kea):
72 | reservations = kea_server.dhcp4.reservation_get_all(subnet_id=40123)
73 | assert reservations
74 | assert len(reservations) > 0
75 |
76 |
77 | def test_kea_dhcp4_remote_reservation_get_by_ip(kea_server: Kea):
78 | reservation = kea_server.dhcp4.reservation_get_by_ip_address(
79 | subnet_id=40123, ip_address="192.0.2.33"
80 | )
81 | assert reservation
82 | assert reservation.hostname == "pyisckea-reservation"
83 | assert reservation.hw_address == "aa:bb:cc:dd:ee:ff"
84 | assert reservation.ip_address == "192.0.2.33"
85 |
86 |
87 | def test_kea_dhcp4_remote_reservation_get_by_hostname(kea_server: Kea):
88 | hostname = "pyisckea-reservation"
89 | reservation = kea_server.dhcp4.reservation_get_by_hostname(
90 | hostname=hostname, subnet_id=40123
91 | )
92 | assert reservation
93 | assert reservation.hostname == hostname
94 |
95 |
96 | def test_kea_dhcp4_remote_reservation_get_by_identifier(kea_server: Kea):
97 | hw_address = "aa:bb:cc:dd:ee:ff"
98 | reservation = kea_server.dhcp4.reservation_get_by_identifier(
99 | subnet_id=40123, identifier_type="hw-address", identifier=hw_address
100 | )
101 | assert reservation
102 | assert reservation.hw_address == hw_address
103 |
104 |
105 | def test_kea_dhcp4_remote_reservation_get_page(kea_server: Kea):
106 | reservations = kea_server.dhcp4.reservation_get_page()
107 | assert reservations
108 | assert len(reservations) > 0
109 |
110 |
111 | def test_kea_dhcp4_remote_reservation_get_page_bad_source(kea_server: Kea):
112 | with pytest.raises(KeaException):
113 | kea_server.dhcp4.reservation_get_page(source_index=9999)
114 |
115 |
116 | def test_kea_dhcp4_remote_reservation_del(kea_server: Kea):
117 | response = kea_server.dhcp4.reservation_del_by_ip(
118 | subnet_id=40123, ip_address="192.0.2.33"
119 | )
120 | assert response.result == 0
121 |
122 |
123 | def test_kea_dhcp4_remote_reservation_cleanup(kea_server: Kea):
124 | response = kea_server.dhcp4.remote_subnet4_del_by_id(subnet_id=40123)
125 | assert response.result == 0
126 |
--------------------------------------------------------------------------------
/tests/dhcp4/remote/test_kea_dhcp4_remote_subnet4.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSubnetNotFoundException
5 | from pyisckea.models.dhcp4.subnet import Subnet4
6 |
7 | """remote-subnet4 process:
8 | get (non-existent subnet)
9 | add
10 | list
11 | get by id
12 | get by prefix
13 | del by id
14 | del non existent by prefix??
15 | """
16 |
17 |
18 | def test_kea_dhcp4_remote_subnet4_get_non_existent(kea_server: Kea):
19 | with pytest.raises(KeaSubnetNotFoundException):
20 | kea_server.dhcp4.remote_subnet4_get_by_id(subnet_id=40123)
21 |
22 |
23 | def test_kea_dhcp4_remote_subnet4_add_subnet(kea_server: Kea):
24 | subnet = Subnet4(subnet="192.0.2.32/31", id=40123)
25 |
26 | # Create subnet
27 | response = kea_server.dhcp4.remote_subnet4_set(subnet=subnet, server_tags=["all"])
28 | assert response.result == 0
29 | assert len(response.arguments.get("subnets", [])) > 0
30 |
31 |
32 | def test_kea_dhcp4_remote_subnet4_list(kea_server: Kea):
33 | subnets = kea_server.dhcp4.remote_subnet4_list(server_tags=["pyisckea-1"])
34 | assert subnets
35 | assert len(subnets) > 0
36 |
37 |
38 | def test_kea_dhcp4_remote_subnet4_get_by_id(kea_server: Kea):
39 | subnet = kea_server.dhcp4.remote_subnet4_get_by_id(subnet_id=40123)
40 | assert subnet
41 | assert subnet.id == 40123
42 | assert subnet.subnet == "192.0.2.32/31"
43 |
44 |
45 | def test_kea_dhcp4_remote_subnet4_get_by_prefix(kea_server: Kea):
46 | subnet = kea_server.dhcp4.remote_subnet4_get_by_prefix(prefix="192.0.2.32/31")
47 | assert subnet
48 | assert subnet.id == 40123
49 | assert subnet.subnet == "192.0.2.32/31"
50 |
51 |
52 | def test_kea_dhcp4_remote_subnet4_del_by_id(kea_server: Kea):
53 | response = kea_server.dhcp4.remote_subnet4_del_by_id(subnet_id=40123)
54 | assert response.result == 0
55 | assert response.arguments.get("count") == 1
56 |
57 |
58 | def test_kea_dhcp4_remote_subnet4_del_by_prefix_non_existent(kea_server: Kea):
59 | response = kea_server.dhcp4.remote_subnet4_del_by_prefix(prefix="192.0.2.32/31")
60 | assert response.result == 3
61 | assert response.arguments.get("count") == 0
62 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 |
4 | def test_kea_dhcp4_build_report(kea_server: Kea):
5 | response = kea_server.dhcp4.build_report()
6 | assert response.result == 0
7 | assert response.text
8 |
9 |
10 | def test_kea_dhcp4_config_get(kea_server: Kea):
11 | response = kea_server.dhcp4.config_get()
12 | assert response.result == 0
13 | assert "Dhcp4" in response.arguments
14 |
15 |
16 | def test_kea_dhcp4_config_test(kea_server: Kea):
17 | config = kea_server.dhcp4.cached_config
18 | if config.get("hash"): # Temp workaround
19 | del config["hash"]
20 |
21 | response = kea_server.dhcp4.config_test(config=config)
22 | assert response.result == 0
23 |
24 |
25 | def test_kea_dhcp4_config_set(kea_server: Kea):
26 | config = kea_server.dhcp4.cached_config
27 | if config.get("hash"): # Temp workaround
28 | del config["hash"]
29 |
30 | response = kea_server.dhcp4.config_set(config=config)
31 | assert response.result == 0
32 |
33 |
34 | def test_kea_dhcp4_config_write(kea_server: Kea):
35 | filename = "/usr/local/etc/kea/kea-dhcp4.conf"
36 | response = kea_server.dhcp4.config_write(filename=filename)
37 | assert response.result == 0
38 | assert response.arguments["filename"] == filename
39 | assert response.arguments["size"] > 0
40 |
41 |
42 | def test_kea_dhcp4_config_reload(kea_server: Kea):
43 | response = kea_server.dhcp4.config_reload()
44 | assert response.result == 0
45 | assert response.text == "Configuration successful."
46 |
47 |
48 | def test_kea_dhcp4_dhcp_disable(kea_server: Kea):
49 | response = kea_server.dhcp4.dhcp_disable(max_period=60)
50 | assert response.result == 0
51 | assert response.text == "DHCPv4 service disabled for 60 seconds"
52 |
53 | status = kea_server.dhcp4.status_get()
54 | assert status.reload < 20
55 |
56 |
57 | def test_kea_dhcp4_dhcp_enable(kea_server: Kea):
58 | response = kea_server.dhcp4.dhcp_enable()
59 | assert response.result == 0
60 | assert response.text == "DHCP service successfully enabled"
61 |
62 |
63 | def test_kea_dhcp4_list_commands(kea_server: Kea):
64 | response = kea_server.dhcp4.list_commands()
65 | assert response.result == 0
66 | assert "config-get" in response.arguments
67 |
68 |
69 | def test_kea_dhcp4_status_get(kea_server: Kea):
70 | response = kea_server.dhcp4.status_get()
71 | assert response.pid
72 | assert response.uptime
73 |
74 |
75 | def test_kea_dhcp4_version_get(kea_server: Kea):
76 | response = kea_server.dhcp4.version_get()
77 | assert response.result == 0
78 |
79 |
80 | def test_kea_dhcp4_shutdown(kea_server: Kea):
81 | response = kea_server.dhcp4.shutdown()
82 | assert response == 0
83 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_cache.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp4.reservation import Reservation4
3 |
4 | """cache process:
5 | cache get (none)
6 | cache insert
7 | cache get
8 | cache get_by_id
9 | cache_size
10 | cache remove
11 | cache get_by_id (non existent)
12 | cache_clear
13 | cache_flush
14 | cache_write
15 | cache_load
16 | """
17 |
18 |
19 | def test_kea_dhcp4_cache_get_none(kea_server: Kea):
20 | response = kea_server.dhcp4.cache_get()
21 | assert len(response) == 0
22 |
23 |
24 | def test_kea_dhcp4_cache_insert(kea_server: Kea):
25 | reservation = Reservation4(ip_address="192.0.2.33", hw_address="aa:bb:cc:dd:ee:ff")
26 | response = kea_server.dhcp4.cache_insert(subnet_id=40123, reservation=reservation)
27 | assert response.result == 0
28 |
29 |
30 | def test_kea_dhcp4_cache_get(kea_server: Kea):
31 | response = kea_server.dhcp4.cache_get()
32 | assert len(response) > 0
33 |
34 |
35 | def test_kea_dhcp4_cache_get_by_id(kea_server: Kea):
36 | response = kea_server.dhcp4.cache_get_by_id(
37 | identifier_type="hw-address", identifier="aa:bb:cc:dd:ee:ff"
38 | )
39 | assert len(response) > 0
40 |
41 |
42 | def test_kea_dhcp4_cache_size(kea_server: Kea):
43 | response = kea_server.dhcp4.cache_size()
44 | assert response.result == 0
45 | assert response.arguments.get("size") > 0
46 |
47 |
48 | def test_kea_dhcp4_cache_remove(kea_server: Kea):
49 | response = kea_server.dhcp4.cache_remove(subnet_id=40123, ip_address="192.0.2.33")
50 | assert response.result == 0
51 |
52 |
53 | def test_kea_dhcp4_cache_get_by_id_non_existent(kea_server: Kea):
54 | response = kea_server.dhcp4.cache_get_by_id(
55 | identifier_type="hw-address", identifier="aa:bb:cc:dd:ee:ff"
56 | )
57 | assert len(response) == 0
58 |
59 |
60 | def test_kea_dhcp4_cache_clear(kea_server: Kea):
61 | response = kea_server.dhcp4.cache_clear()
62 | assert response.result == 0
63 |
64 |
65 | def test_kea_dhcp4_cache_flush(kea_server: Kea):
66 | response = kea_server.dhcp4.cache_flush(number=123)
67 | assert response.result == 0
68 |
69 |
70 | def test_kea_dhcp4_cache_write(kea_server: Kea):
71 | response = kea_server.dhcp4.cache_write(filepath="/tmp/kea-host-cache.json")
72 | assert response.result == 0
73 |
74 |
75 | def test_kea_dhcp4_cache_load(kea_server: Kea):
76 | response = kea_server.dhcp4.cache_load(filepath="/tmp/kea-host-cache.json")
77 | assert response.result == 0
78 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_class.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaClientClassNotFoundException
5 | from pyisckea.models.dhcp4.client_class import ClientClass4
6 |
7 | """class process:
8 | get (non-existent client-class)
9 | add
10 | add (again to check duplicate client-class)
11 | get
12 | list
13 | update
14 | del
15 | del (non-existent client-class)
16 | """
17 |
18 |
19 | def test_kea_dhcp4_class_get_non_existent(kea_server: Kea):
20 | with pytest.raises(KeaClientClassNotFoundException):
21 | kea_server.dhcp4.class_get(name="ipxe_efi_x64")
22 |
23 |
24 | def test_kea_dhcp4_class_add(kea_server: Kea):
25 | client_class = ClientClass4(
26 | name="ipxe_efi_x64",
27 | test="option[93].hex == 0x0009",
28 | next_server="192.0.2.254",
29 | server_hostname="hal9000",
30 | boot_file_name="/dev/null",
31 | )
32 | response = kea_server.dhcp4.class_add(client_class=client_class)
33 | assert response.result == 0
34 |
35 |
36 | def test_kea_dhcp4_class_get(kea_server: Kea):
37 | response = kea_server.dhcp4.class_get(name="ipxe_efi_x64")
38 | assert response
39 | assert response.name == "ipxe_efi_x64"
40 |
41 |
42 | def test_kea_dhcp4_class_list(kea_server: Kea):
43 | response = kea_server.dhcp4.class_list()
44 | assert len(response) > 0
45 |
46 |
47 | def test_kea_dhcp4_class_del(kea_server: Kea):
48 | response = kea_server.dhcp4.class_del(name="ipxe_efi_x64")
49 | assert response.result == 0
50 |
51 |
52 | def test_kea_dhcp4_class_del_non_existent(kea_server: Kea):
53 | response = kea_server.dhcp4.class_del(name="ipxe_efi_x64")
54 | assert response.result == 3
55 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_ha.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 | """ha process:
4 | status-get (get HA mode)
5 | """
6 |
7 |
8 | def test_kea_dhcp4_ha_check_primary(kea_server: Kea):
9 | status = kea_server.dhcp4.status_get()
10 |
11 | assert status.high_availability
12 | assert len(status.high_availability) > 0
13 |
14 | ha_settings = status.high_availability[0]
15 |
16 | assert ha_settings.ha_mode == "hot-standby"
17 | assert ha_settings.ha_servers.local.role == "primary"
18 | assert ha_settings.ha_servers.local.state in ["hot-standby", "waiting"]
19 | assert ha_settings.ha_servers.remote.last_state in ["hot-standby", "ready", ""]
20 |
21 |
22 | def test_kea_dhcp4_ha_continue_not_paused(kea_server: Kea):
23 | response = kea_server.dhcp4.ha_continue()
24 | assert response.result == 0
25 | assert "not paused" in response.text
26 |
27 |
28 | def test_kea_dhcp4_ha_heartbeat(kea_server: Kea):
29 | response = kea_server.dhcp4.ha_heartbeat()
30 | assert response.result == 0
31 |
32 |
33 | def test_kea_dhcp4_ha_maintenance_cancel_wrong_state(kea_server: Kea):
34 | response = kea_server.dhcp4.ha_maintenance_cancel()
35 | assert response.result == 1
36 | assert "not in the partner-in-maintenance state" in response.text
37 |
38 |
39 | def test_kea_dhcp4_ha_maintenance_notify_not_in_maintenance_mode(kea_server: Kea):
40 | response = kea_server.dhcp4.ha_maintenance_notify(cancel=True)
41 | assert response.result == 1
42 | assert "not in the in-maintenance state" in response.text
43 |
44 |
45 | def test_kea_dhcp4_ha_maintenance_start(kea_server: Kea):
46 | response = kea_server.dhcp4.ha_maintenance_start()
47 | assert response.result == 0
48 |
49 | status = kea_server.dhcp4.status_get()
50 | ha_settings = status.high_availability[0]
51 | assert ha_settings.ha_servers.local.state == "partner-in-maintenance"
52 |
53 |
54 | def test_kea_dhcp4_ha_maintenance_cancel(kea_server: Kea):
55 | response = kea_server.dhcp4.ha_maintenance_cancel()
56 | assert response.result == 0
57 |
58 |
59 | def test_kea_dhcp4_ha_maintenance_reset(kea_server: Kea):
60 | response = kea_server.dhcp4.ha_reset()
61 | assert response.result == 0
62 |
63 |
64 | def test_kea_dhcp4_ha_reset_peer(kea_server: Kea):
65 | kea_server.port = (
66 | 8081 # Need to find a better way to integrate this into pytest as a fixture...
67 | )
68 | response = kea_server.dhcp4.ha_reset()
69 | assert response.result == 0
70 |
71 | kea_server.dhcp4.ha_maintenance_cancel()
72 |
73 |
74 | def test_kea_dhcp4_ha_sync(kea_server: Kea):
75 | response = kea_server.dhcp4.ha_sync(partner_server="server2", max_period=30)
76 | assert response.result == 0
77 |
78 |
79 | def test_kea_dhcp4_ha_synbc_complete_notify(kea_server: Kea):
80 | response = kea_server.dhcp4.ha_sync_complete_notify()
81 | assert response.result == 0
82 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_lease4.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaLeaseNotFoundException
5 | from pyisckea.models.dhcp4.subnet import Subnet4
6 |
7 | """lease4 process:
8 | get (non-existent lease)
9 | add
10 | add (again to check duplicate lease)
11 | get
12 | get-all
13 | get-page
14 | get-by-client-id
15 | get-by-hw-address
16 | update
17 | del
18 | wipe
19 | reclaim
20 | """
21 |
22 |
23 | def test_kea_dhcp4_lease4_get_non_exsistent(kea_server: Kea):
24 | with pytest.raises(KeaLeaseNotFoundException):
25 | kea_server.dhcp4.lease4_get(ip_address="192.0.2.32")
26 |
27 |
28 | def test_kea_dhcp4_lease4_get_all_none(kea_server: Kea):
29 | with pytest.raises(KeaLeaseNotFoundException):
30 | kea_server.dhcp4.lease4_get_all()
31 |
32 |
33 | def test_kea_dhcp4_lease4_add(kea_server: Kea):
34 | # Add Temporary Subnet
35 | data = Subnet4(id=40123, subnet="192.0.2.32/31")
36 | subnets = [data]
37 | response = kea_server.dhcp4.subnet4_add(subnets=subnets)
38 | assert response.result == 0
39 |
40 | # Add Lease
41 | lease_response = kea_server.dhcp4.lease4_add(
42 | ip_address="192.0.2.32", hw_address="aa:bb:cc:11:22:33"
43 | )
44 |
45 | assert lease_response.result == 0
46 |
47 | delete_response = kea_server.dhcp4.subnet4_del(subnet_id=40123)
48 | assert delete_response.result == 0
49 |
50 |
51 | def test_kea_dhcp4_lease4_get(kea_server: Kea):
52 | response = kea_server.dhcp4.lease4_get(ip_address="192.0.2.32")
53 | assert response
54 | assert response.ip_address == "192.0.2.32"
55 | assert response.hw_address == "aa:bb:cc:11:22:33"
56 |
57 |
58 | def test_kea_dhcp4_lease4_get_all(kea_server: Kea):
59 | response = kea_server.dhcp4.lease4_get_all()
60 | assert len(response) > 0
61 |
62 |
63 | def test_kea_dhcp4_lease4_get_all_subnets(kea_server: Kea):
64 | response = kea_server.dhcp4.lease4_get_all(subnets=[40123])
65 | assert len(response) > 0
66 |
67 |
68 | def test_kea_dhcp4_lease4_get_page(kea_server: Kea):
69 | response = kea_server.dhcp4.lease4_get_page(limit=100, search_from="start")
70 | assert response.count > 0
71 |
72 |
73 | def test_kea_dhcp4_lease4_get_by_client_id_non_existent(kea_server: Kea):
74 | with pytest.raises(KeaLeaseNotFoundException):
75 | kea_server.dhcp4.lease4_get_by_client_id(client_id="00:00:11:00:00:22")
76 |
77 |
78 | def test_kea_dhcp4_lease4_get_by_hostname_non_existent(kea_server: Kea):
79 | with pytest.raises(KeaLeaseNotFoundException):
80 | kea_server.dhcp4.lease4_get_by_hostname(hostname="bad-hostname")
81 |
82 |
83 | def test_kea_dhcp4_lease4_get_by_hw_address(kea_server: Kea):
84 | response = kea_server.dhcp4.lease4_get_by_hw_address(hw_address="aa:bb:cc:11:22:33")
85 | assert response.ip_address == "192.0.2.32"
86 | assert response.hw_address == "aa:bb:cc:11:22:33"
87 | assert response.subnet_id == 40123
88 |
89 |
90 | def test_kea_dhcp4_lease4_get_by_hw_address_non_existent(kea_server: Kea):
91 | with pytest.raises(KeaLeaseNotFoundException):
92 | kea_server.dhcp4.lease4_get_by_hw_address(hw_address="00:00:11:00:00:22")
93 |
94 |
95 | def test_kea_dhcp4_lease4_del(kea_server: Kea):
96 | response = kea_server.dhcp4.lease4_del(ip_address="192.0.2.32")
97 | assert response.result == 0
98 |
99 |
100 | def test_kea_dhcp4_lease4_del_non_exsistent(kea_server: Kea):
101 | response = kea_server.dhcp4.lease4_del(ip_address="192.0.2.32")
102 | assert response.result == 3
103 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_network4.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSharedNetworkNotFoundException
5 | from pyisckea.models.dhcp4.shared_network import SharedNetwork4
6 | from pyisckea.models.dhcp4.subnet import Subnet4
7 |
8 | """network4 process:
9 | get (non-existent network)
10 | add
11 | add (again to check duplicate subnets)
12 | get
13 | subnet-add
14 | subnet-del
15 | update (full update without option 3... should disappear)
16 | del
17 | list
18 | """
19 |
20 |
21 | def test_kea_dhcp4_network4_get_non_existent(kea_server: Kea):
22 | name = "pyisckea-pytest"
23 | with pytest.raises(KeaSharedNetworkNotFoundException):
24 | kea_server.dhcp4.network4_get(name=name)
25 |
26 |
27 | def test_kea_dhcp4_network4_add(kea_server: Kea):
28 | name = "pyisckea-pytest"
29 | data = SharedNetwork4(name=name)
30 | shared_networks = [data]
31 | response = kea_server.dhcp4.network4_add(shared_networks)
32 | assert response.result == 0
33 |
34 |
35 | def test_kea_dhcp4_network4_add_duplicate(kea_server: Kea):
36 | name = "pyisckea-pytest"
37 | data = SharedNetwork4(name=name)
38 | shared_networks = [data]
39 | response = kea_server.dhcp4.network4_add(shared_networks)
40 | assert response.result == 1
41 |
42 |
43 | def test_kea_dhcp4_network4_get(kea_server: Kea):
44 | name = "pyisckea-pytest"
45 | response = kea_server.dhcp4.network4_get(name=name)
46 | assert response
47 | assert response.name == name
48 |
49 |
50 | def test_kea_dhcp4_network4_subnet_add(kea_server: Kea):
51 | name = "pyisckea-pytest"
52 |
53 | # Create Temporary Subnet
54 | data = Subnet4(subnet="192.0.2.32/31", id=40123)
55 | subnets = [data]
56 | subnet = kea_server.dhcp4.subnet4_add(subnets=subnets)
57 | assert subnet.result == 0
58 |
59 | # Assign Subnet to existing shared network
60 | response = kea_server.dhcp4.network4_subnet_add(name=name, subnet_id=data.id)
61 | assert response.result == 0
62 |
63 | # Confirm shared-network has at least 1 subnet
64 | shared_network = kea_server.dhcp4.network4_get(name=name)
65 | assert shared_network
66 | assert len(shared_network.subnet4) == 1
67 |
68 |
69 | def test_kea_dhcp4_network4_subnet_del(kea_server: Kea):
70 | name = "pyisckea-pytest"
71 |
72 | # Delete temporary subnet assosication
73 | response = kea_server.dhcp4.network4_subnet_del(name=name, subnet_id=40123)
74 | assert response.result == 0
75 |
76 | # Delete Temporary Subnet
77 | deleted_subnet = kea_server.dhcp4.subnet4_del(subnet_id=40123)
78 | assert deleted_subnet.result == 0
79 |
80 | # Confirm Shared Network now has 0 subnets
81 | shared_network = kea_server.dhcp4.network4_get(name=name)
82 | assert shared_network
83 | assert len(shared_network.subnet4) == 0
84 |
85 |
86 | def test_kea_dhcp4_network4_del(kea_server: Kea):
87 | name = "pyisckea-pytest"
88 | response = kea_server.dhcp4.network4_del(name=name)
89 | assert response.result == 0
90 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_parser.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from pyisckea import Kea
4 | from pyisckea.parsers.dhcp4 import Dhcp4Parser
5 |
6 |
7 | def test_kea_dhcp4_parser_parse_config(kea_server: Kea):
8 | cached_config = kea_server.dhcp4.cached_config
9 | parsed = Dhcp4Parser(config=cached_config)
10 |
11 | assert parsed.config.interfaces_config
12 | assert parsed.config.control_socket
13 | assert json.dumps(
14 | parsed.config.model_dump(exclude_none=True, by_alias=True),
15 | indent=4,
16 | sort_keys=True,
17 | )
18 |
19 |
20 | def test_kea_dhcp4_parser_config_test(kea_server: Kea):
21 | cached_config = kea_server.dhcp4.cached_config
22 | parsed = Dhcp4Parser(config=cached_config)
23 | config_to_test = {
24 | "Dhcp4": parsed.config.model_dump(
25 | exclude_none=True, exclude_unset=True, by_alias=True
26 | )
27 | }
28 |
29 | test_results = kea_server.dhcp4.config_test(config=config_to_test)
30 | assert test_results.result == 0
31 |
32 | # Remove hash if exist for now until tests are created to take that into account
33 | if cached_config.get("hash"):
34 | del cached_config["hash"]
35 |
36 | cached_config_json = json.dumps(cached_config, indent=4)
37 | parsed_config_json = json.dumps(config_to_test, indent=4, sort_keys=True)
38 | assert cached_config_json == parsed_config_json
39 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_statistics.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 |
4 | def test_kea_dhcp4_statistic_get_all(kea_server: Kea):
5 | response = kea_server.dhcp4.statistic_get_all()
6 | assert response.result == 0
7 |
8 |
9 | def test_kea_dhcp4_statistic_get(kea_server: Kea):
10 | response = kea_server.dhcp4.statistic_get(name="pkt4-received")
11 | assert response.result == 0
12 | assert response.arguments["pkt4-received"]
13 |
14 |
15 | def test_kea_dhcp4_statistic_get_bad_name(kea_server: Kea):
16 | response = kea_server.dhcp4.statistic_get(name="bad-name-argument")
17 | assert response.result == 0
18 | assert response.arguments == {}
19 |
--------------------------------------------------------------------------------
/tests/dhcp4/test_kea_dhcp4_subnet4.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSubnetNotFoundException
5 | from pyisckea.models.dhcp4.subnet import Subnet4
6 | from pyisckea.models.generic.option_data import OptionData
7 |
8 | """subnet4 process:
9 | get (non-existent subnet)
10 | add
11 | add (again to check duplicate subnets)
12 | list
13 | get
14 | delta-add (partial update code 3 option and valid-lifetime)
15 | delta-del (remove valid-lifetime)
16 | update (full update without option 3... should disappear)
17 | del
18 | """
19 |
20 |
21 | def test_kea_dhcp4_subnet4_get_non_existent(kea_server: Kea):
22 | with pytest.raises(KeaSubnetNotFoundException):
23 | kea_server.dhcp4.subnet4_get(subnet_id=40123)
24 |
25 |
26 | def test_kea_dhcp4_subnet4_add(kea_server: Kea):
27 | option_data = OptionData(data="192.0.2.32", code=3)
28 | data = Subnet4(id=40123, subnet="192.0.2.32/31", option_data=[option_data])
29 | subnets = [data]
30 | response = kea_server.dhcp4.subnet4_add(subnets=subnets)
31 | assert response.result == 0
32 |
33 |
34 | def test_kea_dhcp4_subnet4_add_existing(kea_server: Kea):
35 | data = Subnet4(id=40123, subnet="192.0.2.32/31")
36 | subnets = [data]
37 | response = kea_server.dhcp4.subnet4_add(subnets=subnets)
38 | assert response.result == 1
39 |
40 |
41 | def test_kea_dhcp4_subnet4_list(kea_server: Kea):
42 | response = kea_server.dhcp4.subnet4_list()
43 | assert response
44 |
45 |
46 | def test_kea_dhcp4_subnet4_get(kea_server: Kea):
47 | response = kea_server.dhcp4.subnet4_get(subnet_id=40123)
48 | assert response
49 | assert response.id == 40123
50 |
51 |
52 | def test_kea_dhcp4_subnet4_delta_add(kea_server: Kea):
53 | data = Subnet4(
54 | id=40123,
55 | subnet="192.0.2.32/31",
56 | min_valid_lifetime=5000,
57 | max_valid_lifetime=7000,
58 | option_data=[{"code": 3, "data": "192.0.2.32"}],
59 | )
60 | subnets = [data]
61 |
62 | response = kea_server.dhcp4.subnet4_delta_add(subnets=subnets)
63 | assert response.result == 0
64 |
65 |
66 | def test_kea_dhcp4_subnet4_delta_delete(kea_server: Kea):
67 | data = Subnet4(
68 | id=40123,
69 | subnet="192.0.2.32/31",
70 | min_valid_lifetime=5000,
71 | max_valid_lifetime=7000,
72 | option_data=[{"code": 3, "data": "192.0.2.32"}],
73 | )
74 | subnets = [data]
75 | response = kea_server.dhcp4.subnet4_delta_del(subnets=subnets)
76 | assert response.result == 0
77 |
78 | updated_subnet = kea_server.dhcp4.subnet4_get(subnet_id=40123)
79 | assert updated_subnet
80 | assert updated_subnet.max_valid_lifetime != 7000
81 |
82 |
83 | def test_kea_dhcp4_subnet4_update(kea_server: Kea):
84 | data = Subnet4(
85 | id=40123,
86 | subnet="192.0.2.32/31",
87 | )
88 | subnets = [data]
89 | response = kea_server.dhcp4.subnet4_update(subnets=subnets)
90 | assert response.result == 0
91 |
92 | updated_subnet = kea_server.dhcp4.subnet4_get(subnet_id=40123)
93 | assert updated_subnet
94 | assert len(updated_subnet.option_data) == 0
95 |
96 |
97 | def test_kea_dhcp4_subnet4_del(kea_server: Kea):
98 | response = kea_server.dhcp4.subnet4_del(subnet_id=40123)
99 | assert response.result == 0
100 |
101 |
102 | def test_kea_dhcp4_subnet4_del_non_existent(kea_server: Kea):
103 | with pytest.raises(KeaSubnetNotFoundException):
104 | kea_server.dhcp4.subnet4_del(subnet_id=40123)
105 |
106 |
107 | def test_kea_dhcp4_subnet4_get_next_available_id(kea_server: Kea):
108 | next_available_id = kea_server.dhcp4.get_next_available_subnet_id()
109 | subnets = kea_server.dhcp4.subnet4_list()
110 |
111 | if subnets:
112 | subnet_ids = [subnet.id for subnet in subnets]
113 | assert next_available_id not in subnet_ids
114 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.generic.remote_server import RemoteServer
3 |
4 | """remote generic process:
5 | remote-prepare (config backend pull test)
6 | remote-server6-set
7 | remote-server6-get
8 | remote-server6-get (non existent)
9 | remote-server6-get-all
10 | remote-server6-del
11 | """
12 |
13 |
14 | def test_kea_dhcp6_remote_prepare(kea_server: Kea):
15 | response = kea_server.dhcp6.config_backend_pull()
16 | assert response.result == 0
17 |
18 |
19 | def test_kea_dhcp6_remote_server6_set(kea_server: Kea):
20 | response = kea_server.dhcp6.remote_server6_set(
21 | servers=[RemoteServer(server_tag="pyisckea", description="pyisckea-test")]
22 | )
23 | assert response.result == 0
24 |
25 |
26 | def test_kea_dhcp6_remote_server6_get(kea_server: Kea):
27 | server = kea_server.dhcp6.remote_server6_get(server_tag="pyisckea")
28 | assert server
29 | assert server.server_tag == "pyisckea"
30 | assert server.description == "pyisckea-test"
31 |
32 |
33 | def test_kea_dhcp6_remote_server6_get_all(kea_server: Kea, db_remote_map: dict):
34 | servers = kea_server.dhcp6.remote_server6_get_all(remote_map=db_remote_map)
35 | assert servers
36 | assert len(servers) > 0
37 |
38 |
39 | def test_kea_dhcp6_remote_server6_del(kea_server: Kea):
40 | response = kea_server.dhcp6.remote_server6_del(servers=["pyisckea"])
41 | assert response.result == 0
42 | assert "deleted" in response.text
43 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_class6.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaClientClassNotFoundException
5 | from pyisckea.models.dhcp6.client_class import ClientClass6
6 |
7 | """remote-class6 process:
8 |
9 | get (non existent)
10 | set
11 | get
12 | get-all
13 | del
14 | """
15 |
16 |
17 | def test_kea_dhcp6_remote_class6_get_non_existent(kea_server: Kea):
18 | with pytest.raises(KeaClientClassNotFoundException):
19 | kea_server.dhcp6.remote_class6_get(name="ipxe_efi_x64")
20 |
21 |
22 | def test_kea_dhcp6_remote_class6_set(kea_server: Kea):
23 | client_class = ClientClass6(
24 | name="ipxe_efi_x64",
25 | test="option[93].hex == 0x0009",
26 | next_server="2001:db8::111",
27 | server_hostname="hal9000",
28 | boot_file_name="/dev/null",
29 | )
30 | response = kea_server.dhcp6.remote_class6_set(client_class=client_class)
31 | assert response.result == 0
32 | assert len(response.arguments.get("client-classes", [])) > 0
33 |
34 |
35 | def test_kea_dhcp6_remote_class6_get(kea_server: Kea):
36 | response = kea_server.dhcp6.remote_class6_get(name="ipxe_efi_x64")
37 | assert response
38 | assert response.name == "ipxe_efi_x64"
39 |
40 |
41 | def test_kea_dhcp6_remote_class6_get_all(kea_server: Kea):
42 | response = kea_server.dhcp6.remote_class6_get_all()
43 | assert response
44 | assert len(response) > 0
45 |
46 |
47 | def test_kea_dhcp6_remote_class6_del(kea_server: Kea):
48 | response = kea_server.dhcp6.remote_class6_del(name="ipxe_efi_x64")
49 | assert response.result == 0
50 | assert response.arguments.get("count") == 1
51 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_global_parameter6.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 | """remote-global-parameter6 process:
4 |
5 | get (non existent)
6 | set
7 | get
8 | get-all
9 | del
10 | """
11 |
12 |
13 | def test_kea_dhcp6_remote_global_parameter6_get_non_existent(kea_server: Kea):
14 | response = kea_server.dhcp6.remote_global_parameter6_get(
15 | parameter="t1-percent", server_tag="pyisckea-1"
16 | )
17 | assert response.result == 3
18 | assert response.arguments.get("count") == 0
19 |
20 |
21 | def test_kea_dhcp6_remote_global_parameter6_set(kea_server: Kea):
22 | response = kea_server.dhcp6.remote_global_parameter6_set(
23 | parameters={"t1-percent": 0.85}, server_tag="all"
24 | )
25 | assert response.result == 0
26 | assert response.arguments.get("count") == 1
27 |
28 |
29 | def test_kea_dhcp6_remote_global_parameter6_get(kea_server: Kea):
30 | response = kea_server.dhcp6.remote_global_parameter6_get(
31 | parameter="t1-percent", server_tag="all"
32 | )
33 | assert response.result == 0
34 | assert response.arguments.get("count") == 1
35 |
36 |
37 | def test_kea_dhcp6_remote_global_parameter6_get_all(kea_server: Kea):
38 | response = kea_server.dhcp6.remote_global_parameter6_get_all(server_tag="all")
39 | assert response.result == 0
40 | assert len(response.arguments.get("parameters")) > 0
41 |
42 |
43 | def test_kea_dhcp6_remote_global_parameter6_del(kea_server: Kea):
44 | response = kea_server.dhcp6.remote_global_parameter6_del(
45 | parameter="t1-percent", server_tag="all"
46 | )
47 | assert response.result == 0
48 |
49 |
50 | def test_kea_dhcp6_remote_global_parameter6_del_non_existent(kea_server: Kea):
51 | response = kea_server.dhcp6.remote_global_parameter6_del(
52 | parameter="t1-percent", server_tag="all"
53 | )
54 | assert response.result == 3
55 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_network6.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSharedNetworkNotFoundException
5 | from pyisckea.models.dhcp6.shared_network import SharedNetwork6
6 | from pyisckea.models.dhcp6.subnet import Subnet6
7 |
8 | """remote-network6 process:
9 | get (non-existent network)
10 | add
11 | list
12 | get
13 | add and delete subnet (for existing shared-network)
14 | del
15 | del (non-existent)
16 | """
17 |
18 |
19 | def test_kea_dhcp6_remote_network6_get_non_existent(kea_server: Kea):
20 | name = "pyisckea-pytest"
21 | with pytest.raises(KeaSharedNetworkNotFoundException):
22 | kea_server.dhcp6.remote_network6_get(name=name)
23 |
24 |
25 | def test_kea_dhcp6_remote_network6_add(kea_server: Kea, db_remote_map: dict):
26 | name = "pyisckea-pytest"
27 | data = SharedNetwork6(name=name)
28 | shared_networks = [data]
29 | response = kea_server.dhcp6.remote_network6_set(
30 | shared_networks=shared_networks, server_tags=["all"], remote_map=db_remote_map
31 | )
32 |
33 | assert response.result == 0
34 | assert len(response.arguments.get("shared-networks", [])) > 0
35 |
36 |
37 | def test_kea_dhcp6_remote_network6_list(kea_server: Kea, db_remote_map: dict):
38 | shared_networks = kea_server.dhcp6.remote_network6_list(
39 | server_tags=["pyisckea-1"], remote_map=db_remote_map
40 | )
41 | assert shared_networks
42 | assert len(shared_networks) > 0
43 |
44 |
45 | def test_kea_dhcp6_remote_network6_get(kea_server: Kea, db_remote_map: dict):
46 | name = "pyisckea-pytest"
47 | shared_network = kea_server.dhcp6.remote_network6_get(
48 | name=name, remote_map=db_remote_map
49 | )
50 | assert shared_network
51 | assert shared_network.name == name
52 |
53 |
54 | def test_kea_dhcp6_remote_subnet6_add_subnet(kea_server: Kea, db_remote_map: dict):
55 | name = "pyisckea-pytest"
56 |
57 | # Create Temporary Subnet
58 | subnet = Subnet6(subnet="2001:db8::32/127", id=40123, shared_network_name=name)
59 |
60 | # Create subnet with shared network assosication
61 | response = kea_server.dhcp6.remote_subnet6_set(
62 | subnet=subnet, server_tags=["all"], remote_map=db_remote_map
63 | )
64 | assert response.result == 0
65 | assert len(response.arguments.get("subnets", [])) > 0
66 |
67 |
68 | def test_kea_dhcp6_remote_subnet6_del_by_id(kea_server: Kea, db_remote_map: dict):
69 | response = kea_server.dhcp6.remote_subnet6_del_by_id(
70 | subnet_id=40123, remote_map=db_remote_map
71 | )
72 | assert response.result == 0
73 | assert response.arguments.get("count") == 1
74 |
75 |
76 | def test_kea_dhcp6_remote_network6_del(kea_server: Kea, db_remote_map: dict):
77 | response = kea_server.dhcp6.remote_network6_del(
78 | name="pyisckea-pytest", keep_subnets=False, remote_map=db_remote_map
79 | )
80 | assert response.result == 0
81 | assert response.arguments.get("count") == 1
82 |
83 |
84 | def test_kea_dhcp6_remote_network6_del_non_existent(
85 | kea_server: Kea, db_remote_map: dict
86 | ):
87 | response = kea_server.dhcp6.remote_network6_del(
88 | name="pyisckea-pytest", remote_map=db_remote_map
89 | )
90 | assert response.result == 3
91 | assert response.arguments["count"] == 0
92 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_option6_global.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.generic.option_data import OptionData
3 |
4 | """remote-option-def6 process:
5 |
6 | get (non existent)
7 | set
8 | get
9 | get-all
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp6_remote_option6_global_get_non_existent(kea_server: Kea):
15 | response = kea_server.dhcp6.remote_option6_global_get(
16 | option_code=23, option_space="dhcp6", server_tag="all"
17 | )
18 | assert response.result == 3
19 | assert response.arguments.get("count") == 0
20 |
21 |
22 | def test_kea_dhcp6_remote_option6_global_set(kea_server: Kea):
23 | option_data = OptionData(name="dns-servers", data="2001:db8::111")
24 | response = kea_server.dhcp6.remote_option6_global_set(
25 | option_data=option_data, server_tag="all"
26 | )
27 | assert response.result == 0
28 | assert len(response.arguments.get("options", [])) > 0
29 |
30 |
31 | def test_kea_dhcp6_remote_option6_global_del(kea_server: Kea):
32 | response = kea_server.dhcp6.remote_option6_global_del(
33 | option_code=23, option_space="dhcp6", server_tag="all"
34 | )
35 | assert response.result == 0
36 | assert response.arguments.get("count") == 1
37 |
38 |
39 | def test_kea_dhcp6_remote_option6_global_del_non_existent(kea_server: Kea):
40 | response = kea_server.dhcp6.remote_option6_global_del(
41 | option_code=23, option_space="dhcp6", server_tag="all"
42 | )
43 | assert response.result == 3
44 | assert response.arguments.get("count") == 0
45 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_option6_network.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp6.shared_network import SharedNetwork6
3 | from pyisckea.models.generic.option_data import OptionData
4 |
5 | """remote-option6-network process:
6 |
7 | del (non existent)
8 | set
9 | del
10 | """
11 |
12 |
13 | def test_kea_dhcp6_remote_option6_network_prepare(kea_server: Kea):
14 | shared_network = SharedNetwork6(name="pyisckea-network")
15 | response = kea_server.dhcp6.remote_network6_set(
16 | shared_networks=[shared_network], server_tags=["all"]
17 | )
18 | assert response.result == 0
19 |
20 |
21 | def test_kea_dhcp6_remote_option6_network_del_non_existent(kea_server: Kea):
22 | response = kea_server.dhcp6.remote_option6_network_del(
23 | shared_network="pyisckea-network", option_code=6, option_space="dhcp6"
24 | )
25 | assert response.result == 3
26 | assert response.arguments.get("count") == 0
27 |
28 |
29 | def test_kea_dhcp6_remote_option6_network_set(kea_server: Kea):
30 | option_data = OptionData(name="dns-servers", data="2001:db8::111")
31 | response = kea_server.dhcp6.remote_option6_network_set(
32 | shared_network="pyisckea-network", option_data=option_data
33 | )
34 | assert response.result == 0
35 | assert len(response.arguments.get("options", [])) > 0
36 |
37 |
38 | def test_kea_dhcp6_remote_option6_network_del(kea_server: Kea):
39 | response = kea_server.dhcp6.remote_option6_network_del(
40 | shared_network="pyisckea-network", option_code=23, option_space="dhcp6"
41 | )
42 | assert response.result == 0
43 | assert response.arguments.get("count") == 1
44 |
45 |
46 | def test_kea_dhcp6_remote_option6_network_cleanup(kea_server: Kea):
47 | response = kea_server.dhcp6.remote_network6_del(
48 | name="pyisckea-network", keep_subnets=False
49 | )
50 | assert response.result == 0
51 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_option6_pd_pool.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp6.pd_pool import PDPool
3 | from pyisckea.models.dhcp6.subnet import Subnet6
4 | from pyisckea.models.generic.option_data import OptionData
5 |
6 | """remote-option6-pd-pool process:
7 |
8 | del (non existent)
9 | set
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp6_remote_option6_pd_pool_prepare(kea_server: Kea):
15 | subnet = Subnet6(
16 | id=40123,
17 | subnet="2001:db8::/64",
18 | pd_pools=[PDPool(prefix="2001:1db8::", prefix_len=38, delegated_len=56)],
19 | )
20 | response = kea_server.dhcp6.remote_subnet6_set(subnet=subnet, server_tags=["all"])
21 | assert response.result == 0
22 |
23 |
24 | def test_kea_dhcp6_remote_option6_pd_pool_del_non_existent(kea_server: Kea):
25 | response = kea_server.dhcp6.remote_option6_pd_pool_del(
26 | prefix="2001:1db8::", prefix_len=38, option_code=23, option_space="dhcp6"
27 | )
28 | assert response.result == 3
29 | assert response.arguments.get("count") == 0
30 |
31 |
32 | def test_kea_dhcp6_remote_option6_pd_pool_set(kea_server: Kea):
33 | option_data = OptionData(name="dns-servers", data="2001:db8::111")
34 | response = kea_server.dhcp6.remote_option6_pd_pool_set(
35 | prefix="2001:1db8::", prefix_len=38, option_data=option_data
36 | )
37 | assert response.result == 0
38 | assert len(response.arguments.get("options", [])) > 0
39 |
40 |
41 | def test_kea_dhcp6_remote_option6_pd_pool_del(kea_server: Kea):
42 | response = kea_server.dhcp6.remote_option6_pd_pool_del(
43 | prefix="2001:1db8::", prefix_len=38, option_code=23, option_space="dhcp6"
44 | )
45 | assert response.result == 0
46 | assert response.arguments.get("count") == 1
47 |
48 |
49 | def test_kea_dhcp6_remote_option6_pd_pool_cleanup(kea_server: Kea):
50 | response = kea_server.dhcp6.remote_subnet6_del_by_id(subnet_id=40123)
51 | assert response.result == 0
52 |
53 |
54 | """
55 | def test_kea_dhcp6_remote_option6_pool_del_non_existent(kea_server: Kea):
56 | response = kea_server.dhcp6.remote_option6_pool_del(
57 | pool="2001:db8::2-2001:db8::ffff", option_code=23, option_space="dhcp6"
58 | )
59 | assert response.result == 3
60 | assert response.arguments.get("count") == 0
61 |
62 |
63 | def test_kea_dhcp6_remote_option6_pool_set(kea_server: Kea):
64 | option_data = OptionData(name="dns-servers", data="2001:db8::111")
65 | response = kea_server.dhcp6.remote_option6_pool_set(
66 | pool="2001:db8::2-2001:db8::ffff", option_data=option_data
67 | )
68 | assert response.result == 0
69 | assert len(response.arguments.get("options", [])) > 0
70 |
71 |
72 | def test_kea_dhcp6_remote_option6_pool_del(kea_server: Kea):
73 | response = kea_server.dhcp6.remote_option6_pool_del(
74 | pool="2001:db8::2-2001:db8::ffff", option_code=23, option_space="dhcp6"
75 | )
76 | assert response.result == 0
77 | assert response.arguments.get("count") == 1
78 |
79 | """
80 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_option6_pool.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp6.subnet import Subnet6
3 | from pyisckea.models.generic.option_data import OptionData
4 | from pyisckea.models.generic.pool import Pool
5 |
6 | """remote-option6-pool process:
7 |
8 | del (non existent)
9 | set
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp6_remote_option6_pool_prepare(kea_server: Kea):
15 | subnet = Subnet6(
16 | id=40123,
17 | subnet="2001:db8::/64",
18 | pools=[Pool(pool="2001:db8::2-2001:db8::ffff")],
19 | )
20 | response = kea_server.dhcp6.remote_subnet6_set(subnet=subnet, server_tags=["all"])
21 | assert response.result == 0
22 |
23 |
24 | def test_kea_dhcp6_remote_option6_pool_del_non_existent(kea_server: Kea):
25 | response = kea_server.dhcp6.remote_option6_pool_del(
26 | pool="2001:db8::2-2001:db8::ffff", option_code=23, option_space="dhcp6"
27 | )
28 | assert response.result == 3
29 | assert response.arguments.get("count") == 0
30 |
31 |
32 | def test_kea_dhcp6_remote_option6_pool_set(kea_server: Kea):
33 | option_data = OptionData(name="dns-servers", data="2001:db8::111")
34 | response = kea_server.dhcp6.remote_option6_pool_set(
35 | pool="2001:db8::2-2001:db8::ffff", option_data=option_data
36 | )
37 | assert response.result == 0
38 | assert len(response.arguments.get("options", [])) > 0
39 |
40 |
41 | def test_kea_dhcp6_remote_option6_pool_del(kea_server: Kea):
42 | response = kea_server.dhcp6.remote_option6_pool_del(
43 | pool="2001:db8::2-2001:db8::ffff", option_code=23, option_space="dhcp6"
44 | )
45 | assert response.result == 0
46 | assert response.arguments.get("count") == 1
47 |
48 |
49 | def test_kea_dhcp6_remote_option6_pool_cleanup(kea_server: Kea):
50 | response = kea_server.dhcp6.remote_subnet6_del_by_id(subnet_id=40123)
51 | assert response.result == 0
52 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_option6_subnet.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp6.subnet import Subnet6
3 | from pyisckea.models.generic.option_data import OptionData
4 |
5 | """remote-option6-subnet process:
6 |
7 | del (non existent)
8 | set
9 | del
10 | """
11 |
12 |
13 | def test_kea_dhcp6_remote_option6_subnet_prepare(kea_server: Kea):
14 | subnet = Subnet6(id=40123, subnet="2001:db8::/64")
15 | response = kea_server.dhcp6.remote_subnet6_set(subnet=subnet, server_tags=["all"])
16 | assert response.result == 0
17 |
18 |
19 | def test_kea_dhcp6_remote_option6_subnet_del_non_existent(kea_server: Kea):
20 | response = kea_server.dhcp6.remote_option6_subnet_del(
21 | subnet_id=40123, option_code=23, option_space="dhcp6"
22 | )
23 | assert response.result == 3
24 | assert response.arguments.get("count") == 0
25 |
26 |
27 | def test_kea_dhcp6_remote_option6_subnet_set(kea_server: Kea):
28 | option_data = OptionData(name="dns-servers", data="2001:db8::111")
29 | response = kea_server.dhcp6.remote_option6_subnet_set(
30 | subnet_id=40123, option_data=option_data
31 | )
32 | assert response.result == 0
33 | assert len(response.arguments.get("options", [])) > 0
34 |
35 |
36 | def test_kea_dhcp6_remote_option6_subnet_del(kea_server: Kea):
37 | response = kea_server.dhcp6.remote_option6_subnet_del(
38 | subnet_id=40123, option_code=23, option_space="dhcp6"
39 | )
40 | assert response.result == 0
41 | assert response.arguments.get("count") == 1
42 |
43 |
44 | def test_kea_dhcp6_remote_option6_subnet_cleanup(kea_server: Kea):
45 | response = kea_server.dhcp6.remote_subnet6_del_by_id(subnet_id=40123)
46 | assert response.result == 0
47 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_option_def6.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.generic.option_def import OptionDef
3 |
4 | """remote-option-def6 process:
5 |
6 | get (non existent)
7 | set
8 | get
9 | get-all
10 | del
11 | """
12 |
13 |
14 | def test_kea_dhcp6_remote_option_def6_get_non_existent(kea_server: Kea):
15 | response = kea_server.dhcp6.remote_option_def6_get(
16 | option_code=1, option_space="pyisckea", server_tag="all"
17 | )
18 | assert response.result == 3
19 | assert response.arguments.get("count") == 0
20 |
21 |
22 | def test_kea_dhcp6_remote_option_def6_set(kea_server: Kea):
23 | option_def = OptionDef(name="subopt1", code=1, space="pyisckea", type="string")
24 | response = kea_server.dhcp6.remote_option_def6_set(
25 | option_def=option_def, server_tag="all"
26 | )
27 | assert response.result == 0
28 | assert len(response.arguments.get("option-defs", [])) > 0
29 |
30 |
31 | def test_kea_dhcp6_remote_option_def6_del(kea_server: Kea):
32 | response = kea_server.dhcp6.remote_option_def6_del(
33 | option_code=1, option_space="pyisckea", server_tag="all"
34 | )
35 | assert response.result == 0
36 | assert response.arguments.get("count") == 1
37 |
38 |
39 | def test_kea_dhcp6_remote_option_def6_del_non_existent(kea_server: Kea):
40 | response = kea_server.dhcp6.remote_option_def6_del(
41 | option_code=1, option_space="pyisckea", server_tag="all"
42 | )
43 | assert response.result == 3
44 | assert response.arguments.get("count") == 0
45 |
--------------------------------------------------------------------------------
/tests/dhcp6/remote/test_kea_dhcp6_remote_subnet6.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSubnetNotFoundException
5 | from pyisckea.models.dhcp6.subnet import Subnet6
6 |
7 | """remote-subnet6 process:
8 | get (non-existent subnet)
9 | add
10 | list
11 | get by id
12 | get by prefix
13 | del by id
14 | del non existent by prefix??
15 | """
16 |
17 |
18 | def test_kea_dhcp6_remote_subnet6_get_non_existent(kea_server: Kea):
19 | with pytest.raises(KeaSubnetNotFoundException):
20 | kea_server.dhcp6.remote_subnet6_get_by_id(subnet_id=40123)
21 |
22 |
23 | def test_kea_dhcp6_remote_subnet6_add_subnet(kea_server: Kea):
24 | subnet = Subnet6(subnet="2001:db8::32/127", id=40123)
25 |
26 | # Create subnet
27 | response = kea_server.dhcp6.remote_subnet6_set(subnet=subnet, server_tags=["all"])
28 | assert response.result == 0
29 | assert len(response.arguments.get("subnets", [])) > 0
30 |
31 |
32 | def test_kea_dhcp6_remote_subnet6_list(kea_server: Kea):
33 | subnets = kea_server.dhcp6.remote_subnet6_list(server_tags=["pyisckea-1"])
34 | assert subnets
35 | assert len(subnets) > 0
36 |
37 |
38 | def test_kea_dhcp6_remote_subnet6_get_by_id(kea_server: Kea):
39 | subnet = kea_server.dhcp6.remote_subnet6_get_by_id(subnet_id=40123)
40 | assert subnet
41 | assert subnet.id == 40123
42 | assert subnet.subnet == "2001:db8::32/127"
43 |
44 |
45 | def test_kea_dhcp6_remote_subnet6_get_by_prefix(kea_server: Kea):
46 | subnet = kea_server.dhcp6.remote_subnet6_get_by_prefix(prefix="2001:db8::32/127")
47 | assert subnet
48 | assert subnet.id == 40123
49 | assert subnet.subnet == "2001:db8::32/127"
50 |
51 |
52 | def test_kea_dhcp6_remote_subnet6_del_by_id(kea_server: Kea):
53 | response = kea_server.dhcp6.remote_subnet6_del_by_id(subnet_id=40123)
54 | assert response.result == 0
55 | assert response.arguments.get("count") == 1
56 |
57 |
58 | def test_kea_dhcp6_remote_subnet6_del_by_prefix_non_existent(kea_server: Kea):
59 | response = kea_server.dhcp6.remote_subnet6_del_by_prefix(prefix="2001:db8::32/127")
60 | assert response.result == 3
61 | assert response.arguments.get("count") == 0
62 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 |
4 | def test_kea_dhcp6_build_report(kea_server: Kea):
5 | response = kea_server.dhcp6.build_report()
6 | assert response.result == 0
7 | assert response.text
8 |
9 |
10 | def test_kea_dhcp6_config_get(kea_server: Kea):
11 | response = kea_server.dhcp6.config_get()
12 | assert response.result == 0
13 | assert "Dhcp6" in response.arguments
14 |
15 |
16 | def test_kea_dhcp6_config_test(kea_server: Kea):
17 | config = kea_server.dhcp6.cached_config
18 | if config.get("hash"): # Temp workaround
19 | del config["hash"]
20 |
21 | response = kea_server.dhcp6.config_test(config=config)
22 | assert response.result == 0
23 |
24 |
25 | def test_kea_dhcp6_config_set(kea_server: Kea):
26 | config = kea_server.dhcp6.cached_config
27 | if config.get("hash"): # Temp workaround
28 | del config["hash"]
29 |
30 | response = kea_server.dhcp6.config_set(config=config)
31 | assert response.result == 0
32 |
33 |
34 | def test_kea_dhcp6_config_write(kea_server: Kea):
35 | filename = "/usr/local/etc/kea/kea-dhcp6.conf"
36 | response = kea_server.dhcp6.config_write(filename=filename)
37 | assert response.result == 0
38 | assert response.arguments["filename"] == filename
39 | assert response.arguments["size"] > 0
40 |
41 |
42 | def test_kea_dhcp6_config_reload(kea_server: Kea):
43 | response = kea_server.dhcp6.config_reload()
44 | assert response.result == 0
45 | assert response.text == "Configuration successful."
46 |
47 |
48 | def test_kea_dhcp6_dhcp_disable(kea_server: Kea):
49 | response = kea_server.dhcp6.dhcp_disable(max_period=60)
50 | assert response.result == 0
51 | assert response.text == "DHCPv6 service disabled for 60 seconds"
52 |
53 |
54 | def test_kea_dhcp6_dhcp_enable(kea_server: Kea):
55 | response = kea_server.dhcp6.dhcp_enable()
56 | assert response.result == 0
57 | assert response.text == "DHCP service successfully enabled"
58 |
59 |
60 | def test_kea_dhcp6_list_commands(kea_server: Kea):
61 | response = kea_server.dhcp6.list_commands()
62 | assert response.result == 0
63 | assert "config-get" in response.arguments
64 |
65 |
66 | def test_kea_dhcp6_status_get(kea_server: Kea):
67 | response = kea_server.dhcp6.status_get()
68 | assert response.pid
69 | assert response.uptime
70 |
71 |
72 | def test_kea_dhcp6_version_get(kea_server: Kea):
73 | response = kea_server.dhcp6.version_get()
74 | assert response.result == 0
75 |
76 |
77 | def test_kea_dhcp6_shutdown(kea_server: Kea):
78 | response = kea_server.dhcp6.shutdown()
79 | assert response == 0
80 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_cache.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 | from pyisckea.models.dhcp6.reservation import Reservation6
3 |
4 | """cache process:Reservation6
5 | cache get (none)
6 | cache insert
7 | cache get
8 | cache get_by_id
9 | cache_size
10 | cache remove
11 | cache get_by_id (non existent)
12 | cache_clear
13 | cache_flush
14 | cache_write
15 | cache_load
16 | """
17 |
18 |
19 | def test_kea_dhcp6_cache_get_none(kea_server: Kea):
20 | response = kea_server.dhcp6.cache_get()
21 | assert len(response) == 0
22 |
23 |
24 | def test_kea_dhcp6_cache_insert(kea_server: Kea):
25 | reservation = Reservation6(
26 | ip_addresses=["2001:db8::33"],
27 | duid="00:01:00:01:17:96:f9:3a:aa:bb:cc:dd:ee:ff",
28 | )
29 | response = kea_server.dhcp6.cache_insert(subnet_id=40123, reservation=reservation)
30 | assert response.result == 0
31 |
32 |
33 | def test_kea_dhcp6_cache_get(kea_server: Kea):
34 | response = kea_server.dhcp6.cache_get()
35 | assert len(response) > 0
36 |
37 |
38 | def test_kea_dhcp6_cache_get_by_id(kea_server: Kea):
39 | response = kea_server.dhcp6.cache_get_by_id(
40 | identifier_type="duid", identifier="00:01:00:01:17:96:f9:3a:aa:bb:cc:dd:ee:ff"
41 | )
42 | assert len(response) > 0
43 |
44 |
45 | def test_kea_dhcp6_cache_size(kea_server: Kea):
46 | response = kea_server.dhcp6.cache_size()
47 | assert response.result == 0
48 | assert response.arguments.get("size") > 0
49 |
50 |
51 | def test_kea_dhcp6_cache_remove(kea_server: Kea):
52 | response = kea_server.dhcp6.cache_remove(subnet_id=40123, ip_address="2001:db8::33")
53 | assert response.result == 0
54 |
55 |
56 | def test_kea_dhcp6_cache_get_by_id_non_existent(kea_server: Kea):
57 | response = kea_server.dhcp6.cache_get_by_id(
58 | identifier_type="duid", identifier="00:01:00:01:17:96:f9:3a:aa:bb:cc:dd:ee:ff"
59 | )
60 | assert len(response) == 0
61 |
62 |
63 | def test_kea_dhcp6_cache_clear(kea_server: Kea):
64 | response = kea_server.dhcp6.cache_clear()
65 | assert response.result == 0
66 |
67 |
68 | def test_kea_dhcp6_cache_flush(kea_server: Kea):
69 | response = kea_server.dhcp6.cache_flush(number=123)
70 | assert response.result == 0
71 |
72 |
73 | def test_kea_dhcp6_cache_write(kea_server: Kea):
74 | response = kea_server.dhcp6.cache_write(filepath="/tmp/kea-host-cache.json")
75 | assert response.result == 0
76 |
77 |
78 | def test_kea_dhcp6_cache_load(kea_server: Kea):
79 | response = kea_server.dhcp6.cache_load(filepath="/tmp/kea-host-cache.json")
80 | assert response.result == 0
81 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_class.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaClientClassNotFoundException
5 | from pyisckea.models.dhcp6.client_class import ClientClass6
6 |
7 | """class process:class_del
8 | get (non-existent client-class)
9 | add
10 | add (again to check duplicate client-class)
11 | get
12 | list
13 | update
14 | del
15 | del (non-existent client-class)
16 | """
17 |
18 |
19 | def test_kea_dhcp6_class_get_non_existent(kea_server: Kea):
20 | with pytest.raises(KeaClientClassNotFoundException):
21 | kea_server.dhcp6.class_get(name="ipxe_efi_x64")
22 |
23 |
24 | def test_kea_dhcp6_class_add(kea_server: Kea):
25 | client_class = ClientClass6(
26 | name="ipxe_efi_x64",
27 | test="option[93].hex == 0x0009",
28 | next_server="192.0.2.254",
29 | server_hostname="hal9000",
30 | boot_file_name="/dev/null",
31 | )
32 | response = kea_server.dhcp6.class_add(client_class=client_class)
33 | assert response.result == 0
34 |
35 |
36 | def test_kea_dhcp6_class_get(kea_server: Kea):
37 | response = kea_server.dhcp6.class_get(name="ipxe_efi_x64")
38 | assert response
39 | assert response.name == "ipxe_efi_x64"
40 |
41 |
42 | def test_kea_dhcp6_class_list(kea_server: Kea):
43 | response = kea_server.dhcp6.class_list()
44 | assert len(response) > 0
45 |
46 |
47 | def test_kea_dhcp6_class_del(kea_server: Kea):
48 | response = kea_server.dhcp6.class_del(name="ipxe_efi_x64")
49 | assert response.result == 0
50 |
51 |
52 | def test_kea_dhcp6_class_del_non_existent(kea_server: Kea):
53 | response = kea_server.dhcp6.class_del(name="ipxe_efi_x64")
54 | assert response.result == 3
55 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_ha.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 | """ha process:
4 | status-get (get HA mode)
5 | """
6 |
7 |
8 | def test_kea_dhcp6_ha_check_primary(kea_server: Kea):
9 | status = kea_server.dhcp6.status_get()
10 |
11 | assert status.high_availability
12 | assert len(status.high_availability) > 0
13 |
14 | ha_settings = status.high_availability[0]
15 |
16 | assert ha_settings.ha_mode == "hot-standby"
17 | assert ha_settings.ha_servers.local.role == "primary"
18 | assert ha_settings.ha_servers.local.state in ["hot-standby", "waiting"]
19 | assert ha_settings.ha_servers.remote.last_state in ["hot-standby", "ready", ""]
20 |
21 |
22 | def test_kea_dhcp6_ha_continue_not_paused(kea_server: Kea):
23 | response = kea_server.dhcp6.ha_continue()
24 | assert response.result == 0
25 | assert "not paused" in response.text
26 |
27 |
28 | def test_kea_dhcp6_ha_heartbeat(kea_server: Kea):
29 | response = kea_server.dhcp6.ha_heartbeat()
30 | assert response.result == 0
31 |
32 |
33 | def test_kea_dhcp6_ha_maintenance_cancel_wrong_state(kea_server: Kea):
34 | response = kea_server.dhcp6.ha_maintenance_cancel()
35 | assert response.result == 1
36 | assert "not in the partner-in-maintenance state" in response.text
37 |
38 |
39 | def test_kea_dhcp6_ha_maintenance_notify_not_in_maintenance_mode(kea_server: Kea):
40 | response = kea_server.dhcp6.ha_maintenance_notify(cancel=True)
41 | assert response.result == 1
42 | assert "not in the in-maintenance state" in response.text
43 |
44 |
45 | def test_kea_dhcp6_ha_maintenance_start(kea_server: Kea):
46 | response = kea_server.dhcp6.ha_maintenance_start()
47 | assert response.result == 0
48 |
49 | status = kea_server.dhcp6.status_get()
50 | ha_settings = status.high_availability[0]
51 | assert ha_settings.ha_servers.local.state == "partner-in-maintenance"
52 |
53 |
54 | def test_kea_dhcp6_ha_maintenance_cancel(kea_server: Kea):
55 | response = kea_server.dhcp6.ha_maintenance_cancel()
56 | assert response.result == 0
57 |
58 |
59 | def test_kea_dhcp6_ha_maintenance_reset(kea_server: Kea):
60 | response = kea_server.dhcp6.ha_reset()
61 | assert response.result == 0
62 |
63 |
64 | def test_kea_dhcp6_ha_reset_peer(kea_server: Kea):
65 | kea_server.port = (
66 | 8081 # Need to find a better way to integrate this into pytest as a fixture...
67 | )
68 | response = kea_server.dhcp6.ha_reset()
69 | assert response.result == 0
70 |
71 | kea_server.dhcp6.ha_maintenance_cancel()
72 |
73 |
74 | def test_kea_dhcp6_ha_sync(kea_server: Kea):
75 | response = kea_server.dhcp6.ha_sync(partner_server="server2", max_period=30)
76 | assert response.result == 0
77 |
78 |
79 | def test_kea_dhcp6_ha_synbc_complete_notify(kea_server: Kea):
80 | response = kea_server.dhcp6.ha_sync_complete_notify()
81 | assert response.result == 0
82 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_lease6.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaLeaseNotFoundException
5 | from pyisckea.models.dhcp6.subnet import Subnet6
6 |
7 | """lease6 process:
8 | get (non-existent lease)
9 | add
10 | add (again to check duplicate lease)
11 | get
12 | get-all
13 | get-page
14 | get-by-client-id
15 | get-by-hw-address
16 | update
17 | del
18 | wipe
19 | reclaim
20 | """
21 |
22 |
23 | def test_kea_dhcp6_lease6_get_non_exsistent(kea_server: Kea):
24 | with pytest.raises(KeaLeaseNotFoundException):
25 | kea_server.dhcp6.lease6_get(ip_address="2001:db8::32")
26 |
27 |
28 | def test_kea_dhcp4_lease6_get_all_none(kea_server: Kea):
29 | with pytest.raises(KeaLeaseNotFoundException):
30 | kea_server.dhcp6.lease6_get_all()
31 |
32 |
33 | def test_kea_dhcp6_lease6_add(kea_server: Kea):
34 | # Add Temporary Subnet
35 | data = Subnet6(id=40123, subnet="2001:db8::/64")
36 | subnets = [data]
37 | response = kea_server.dhcp6.subnet6_add(subnets=subnets)
38 | assert response.result == 0
39 |
40 | # Add Lease
41 | lease_response = kea_server.dhcp6.lease6_add(
42 | ip_address="2001:db8::32", duid="1a:1b:1c:1d:1e:1f:20:21:22:23:24", iaid=1234
43 | )
44 | assert lease_response.result == 0
45 |
46 |
47 | def test_kea_dhcp6_lease6_get(kea_server: Kea):
48 | response = kea_server.dhcp6.lease6_get(ip_address="2001:db8::32")
49 | assert response
50 | assert response.ip_address == "2001:db8::32"
51 | assert response.duid == "1a:1b:1c:1d:1e:1f:20:21:22:23:24"
52 | assert response.iaid == 1234
53 |
54 |
55 | def test_kea_dhcp4_lease6_get_all(kea_server: Kea):
56 | response = kea_server.dhcp6.lease6_get_all()
57 | assert len(response) > 0
58 |
59 |
60 | def test_kea_dhcp6_lease6_get_all_subnets(kea_server: Kea):
61 | response = kea_server.dhcp6.lease6_get_all(subnets=[40123])
62 | assert len(response) > 0
63 |
64 |
65 | def test_kea_dhcp6_lease6_get_page(kea_server: Kea):
66 | response = kea_server.dhcp6.lease6_get_page(limit=100, search_from="start")
67 | assert response.count > 0
68 |
69 |
70 | def test_kea_dhcp6_lease6_get_by_duid(kea_server: Kea):
71 | response = kea_server.dhcp6.lease6_get_by_duid(
72 | duid="1a:1b:1c:1d:1e:1f:20:21:22:23:24"
73 | )
74 | assert response
75 | assert response.type == "IA_NA"
76 | assert response.ip_address == "2001:db8::32"
77 | assert response.duid == "1a:1b:1c:1d:1e:1f:20:21:22:23:24"
78 | assert response.iaid == 1234
79 |
80 |
81 | def test_kea_dhcp6_lease6_get_by_hostname_non_existent(kea_server: Kea):
82 | with pytest.raises(KeaLeaseNotFoundException):
83 | kea_server.dhcp6.lease6_get_by_hostname(hostname="bad-hostname")
84 |
85 |
86 | def test_kea_dhcp6_lease6_update(kea_server: Kea):
87 | response = kea_server.dhcp6.lease6_update(
88 | ip_address="2001:db8::32",
89 | duid="1a:1b:1c:1d:1e:1f:20:21:22:23:25",
90 | iaid=1234,
91 | hostname="new-hostname",
92 | )
93 | assert response.result == 0
94 |
95 | updated_lease = kea_server.dhcp6.lease6_get(ip_address="2001:db8::32")
96 | assert updated_lease.hostname == "new-hostname"
97 |
98 |
99 | def test_kea_dhcp6_lease6_del(kea_server: Kea):
100 | response = kea_server.dhcp6.lease6_del(ip_address="2001:db8::32")
101 | assert response.result == 0
102 |
103 |
104 | def test_kea_dhcp6_lease6_del_non_existent(kea_server: Kea):
105 | response = kea_server.dhcp6.lease6_del(ip_address="2001:db8::32")
106 | assert response.result == 3
107 |
108 |
109 | def test_kea_dhcp6_lease6_del_temp_subnet(kea_server: Kea):
110 | delete_response = kea_server.dhcp6.subnet6_del(subnet_id=40123)
111 | assert delete_response.result == 0
112 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_network6.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSharedNetworkNotFoundException
5 | from pyisckea.models.dhcp6.shared_network import SharedNetwork6
6 | from pyisckea.models.dhcp6.subnet import Subnet6
7 |
8 | """network4 process:
9 | get (non-existent network)
10 | add
11 | add (again to check duplicate subnets)
12 | list
13 | get
14 | subnet-add
15 | subnet-del
16 | update (full update without option 3... should disappear)
17 | del
18 | """
19 |
20 |
21 | def test_kea_dhcp6_network6_get_non_existent(kea_server: Kea):
22 | name = "pyisckea-pytest"
23 | with pytest.raises(KeaSharedNetworkNotFoundException):
24 | kea_server.dhcp6.network6_get(name=name)
25 |
26 |
27 | def test_kea_dhcp6_network6_add(kea_server: Kea):
28 | name = "pyisckea-pytest"
29 | data = SharedNetwork6(name=name)
30 | shared_networks = [data]
31 | response = kea_server.dhcp6.network6_add(shared_networks=shared_networks)
32 | assert response.result == 0
33 |
34 |
35 | def test_kea_dhcp6_network6_add_duplicate(kea_server: Kea):
36 | name = "pyisckea-pytest"
37 | data = SharedNetwork6(name=name)
38 | shared_networks = [data]
39 | response = kea_server.dhcp6.network6_add(shared_networks)
40 | assert response.result == 1
41 |
42 |
43 | def test_kea_dhcp6_network6_get(kea_server: Kea):
44 | name = "pyisckea-pytest"
45 | response = kea_server.dhcp6.network6_get(name=name)
46 | assert response
47 | assert response.name == name
48 |
49 |
50 | def test_kea_dhcp6_network6_list(kea_server: Kea):
51 | networks = kea_server.dhcp6.network6_list()
52 | assert networks
53 | assert len(networks) > 0
54 |
55 |
56 | def test_kea_dhcp6_network6_subnet_add(kea_server: Kea):
57 | name = "pyisckea-pytest"
58 |
59 | # Create Temporary Subnet
60 | data = Subnet6(subnet="2001:db8::/64", id=40123)
61 | subnets = [data]
62 | subnet = kea_server.dhcp6.subnet6_add(subnets=subnets)
63 | assert subnet.result == 0
64 |
65 | # Assign Subnet to existing shared network
66 | response = kea_server.dhcp6.network6_subnet_add(name=name, subnet_id=data.id)
67 | assert response.result == 0
68 |
69 | # Confirm shared-network has at least 1 subnet
70 | shared_network = kea_server.dhcp6.network6_get(name=name)
71 | assert shared_network
72 | assert len(shared_network.subnet6) == 1
73 |
74 |
75 | def test_kea_dhcp6_network6_subnet_del(kea_server: Kea):
76 | name = "pyisckea-pytest"
77 |
78 | # Delete temporary subnet assosication
79 | response = kea_server.dhcp6.network6_subnet_del(name=name, subnet_id=40123)
80 | assert response.result == 0
81 |
82 | # Delete Temporary Subnet
83 | deleted_subnet = kea_server.dhcp6.subnet6_del(subnet_id=40123)
84 | assert deleted_subnet.result == 0
85 |
86 | # Confirm Shared Network now has 0 subnets
87 | shared_network = kea_server.dhcp6.network6_get(name=name)
88 | assert shared_network
89 | assert len(shared_network.subnet6) == 0
90 |
91 |
92 | def test_kea_dhcp6_network6_del(kea_server: Kea):
93 | name = "pyisckea-pytest"
94 | response = kea_server.dhcp6.network6_del(name=name)
95 | assert response.result == 0
96 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_parser.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from pyisckea import Kea
4 | from pyisckea.parsers.dhcp6 import Dhcp6Parser
5 |
6 |
7 | def test_kea_dhcp6_parser_parse_config(kea_server: Kea):
8 | cached_config = kea_server.dhcp6.cached_config
9 | parsed = Dhcp6Parser(config=cached_config)
10 |
11 | assert parsed.config.interfaces_config
12 | assert parsed.config.control_socket
13 | assert json.dumps(
14 | parsed.config.model_dump(exclude_none=True, by_alias=True),
15 | indent=4,
16 | sort_keys=True,
17 | )
18 |
19 |
20 | def test_kea_dhcp6_parser_config_test(kea_server: Kea):
21 | cached_config = kea_server.dhcp6.cached_config
22 | parsed = Dhcp6Parser(config=cached_config)
23 | config_to_test = {
24 | "Dhcp6": parsed.config.model_dump(
25 | exclude_none=True, exclude_unset=True, by_alias=True
26 | )
27 | }
28 |
29 | test_results = kea_server.dhcp6.config_test(config=config_to_test)
30 | assert test_results.result == 0
31 |
32 | # Remove hash if exist for now until tests are created to take that into account
33 | if cached_config.get("hash"):
34 | del cached_config["hash"]
35 |
36 | cached_config_json = json.dumps(cached_config, indent=4)
37 | parsed_config_json = json.dumps(config_to_test, indent=4, sort_keys=True)
38 | assert cached_config_json == parsed_config_json
39 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_statistics.py:
--------------------------------------------------------------------------------
1 | from pyisckea import Kea
2 |
3 |
4 | def test_kea_dhcp6_statistic_get_all(kea_server: Kea):
5 | response = kea_server.dhcp6.statistic_get_all()
6 | assert response.result == 0
7 |
8 |
9 | def test_kea_dhcp6_statistic_get(kea_server: Kea):
10 | response = kea_server.dhcp6.statistic_get(name="pkt6-received")
11 | assert response.result == 0
12 | assert response.arguments["pkt6-received"]
13 |
14 |
15 | def test_kea_dhcp6_statistic_get_bad_name(kea_server: Kea):
16 | response = kea_server.dhcp6.statistic_get(name="bad-name-argument")
17 | assert response.result == 0
18 | assert response.arguments == {}
19 |
--------------------------------------------------------------------------------
/tests/dhcp6/test_kea_dhcp6_subnet6.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from pyisckea import Kea
4 | from pyisckea.exceptions import KeaSubnetNotFoundException
5 | from pyisckea.models.dhcp6.subnet import Subnet6
6 |
7 | """subnet4 process:
8 | get (non-existent subnet)
9 | add
10 | add (again to check duplicate subnets)
11 | list
12 | get
13 | delta-add (partial update code 3 option and valid-lifetime)
14 | delta-del (remove valid-lifetime)
15 | update (full update without option 3... should disappear)
16 | del
17 | """
18 |
19 |
20 | def test_kea_dhcp6_subnet6_get_non_existent(kea_server: Kea):
21 | with pytest.raises(KeaSubnetNotFoundException):
22 | kea_server.dhcp6.subnet6_get(subnet_id=40123)
23 |
24 |
25 | def test_kea_dhcp6_subnet6_add(kea_server: Kea):
26 | data = Subnet6(id=40123, subnet="2001:db8::/64")
27 | subnets = [data]
28 | response = kea_server.dhcp6.subnet6_add(subnets=subnets)
29 | assert response.result == 0
30 |
31 |
32 | def test_kea_dhcp6_subnet6_add_existing(kea_server: Kea):
33 | data = Subnet6(id=40123, subnet="2001:db8::/64")
34 | subnets = [data]
35 | response = kea_server.dhcp6.subnet6_add(subnets=subnets)
36 | assert response.result == 1
37 |
38 |
39 | def test_kea_dhcp6_subnet6_list(kea_server: Kea):
40 | response = kea_server.dhcp6.subnet6_list()
41 | assert response
42 |
43 |
44 | def test_kea_dhcp6_subnet6_get(kea_server: Kea):
45 | response = kea_server.dhcp6.subnet6_get(subnet_id=40123)
46 | assert response
47 | assert response.id == 40123
48 |
49 |
50 | def test_kea_dhcp6_subnet6_delta_update(kea_server: Kea):
51 | data = Subnet6(id=40123, subnet="2001:db8::/64", comment="pyisckea-test")
52 | subnets = [data]
53 |
54 | response = kea_server.dhcp6.subnet6_delta_add(subnets=subnets)
55 | assert response.result == 0
56 |
57 |
58 | def test_kea_dhcp6_subnet6_delta_del(kea_server: Kea):
59 | data = Subnet6(id=40123, subnet="2001:db8::/64", comment="pyisckea-test")
60 | subnets = [data]
61 | response = kea_server.dhcp6.subnet6_delta_del(subnets=subnets)
62 | assert response.result == 0
63 |
64 | updated_subnet = kea_server.dhcp6.subnet6_get(subnet_id=40123)
65 | assert updated_subnet
66 | assert updated_subnet.comment == ""
67 |
68 |
69 | def test_kea_dhcp6_subnet6_update(kea_server: Kea):
70 | data = Subnet6(id=40123, subnet="2001:db8::/96")
71 | subnets = [data]
72 | response = kea_server.dhcp6.subnet6_update(subnets=subnets)
73 | assert response.result == 0
74 |
75 | updated_subnet = kea_server.dhcp6.subnet6_get(subnet_id=40123)
76 | assert updated_subnet
77 | assert updated_subnet.subnet == "2001:db8::/96"
78 |
79 |
80 | def test_kea_dhcp6_subnet6_del(kea_server: Kea):
81 | response = kea_server.dhcp6.subnet6_del(subnet_id=40123)
82 | assert response.result == 0
83 |
84 |
85 | def test_kea_dhcp6_subnet6_del_non_existent(kea_server: Kea):
86 | with pytest.raises(KeaSubnetNotFoundException):
87 | kea_server.dhcp6.subnet6_del(subnet_id=40123)
88 |
--------------------------------------------------------------------------------
/tests/test_infrastructure/Dockerfiles/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG KEA_REPO=public/isc/kea-dev
2 | ARG KEA_VERSION=2.7.7-isc20250326114722
3 | # Indicate if the premium packages should be installed.
4 | # Valid values: "premium" or empty.
5 | ARG KEA_PREMIUM=""
6 |
7 |
8 | FROM debian:12.9-slim AS debian-base
9 |
10 | # Container with a modern Supervisord installled.
11 | FROM debian-base AS supervisor-base
12 | RUN apt-get update \
13 | && apt-get install \
14 | -y \
15 | --no-install-recommends \
16 | python3.11=3.11.* \
17 | python3-pip=23.* \
18 | python3-setuptools=66.* \
19 | supervisor \
20 | wget \
21 | && apt-get clean \
22 | && rm -rf /var/lib/apt/lists/* \
23 | && mkdir -p /var/log/supervisor
24 |
25 | # Server containers
26 | FROM supervisor-base AS server
27 | ENTRYPOINT [ "/bin/sh", "-c", \
28 | "supervisord -c /etc/supervisor/supervisord.conf" ]
29 | EXPOSE 8080
30 | HEALTHCHECK CMD [ "supervisorctl", "status " ]
31 |
32 | FROM supervisor-base AS kea-base
33 | # Install Kea dependencies
34 | RUN apt-get update \
35 | && apt-get install \
36 | -y \
37 | --no-install-recommends \
38 | curl=7.88.* \
39 | apt-transport-https=2.6.* \
40 | gnupg=2.2.* \
41 | && apt-get clean \
42 | && rm -rf /var/lib/apt/lists/*
43 |
44 | # Install Kea from Cloudsmith
45 | SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]
46 | ARG KEA_REPO
47 | ARG KEA_VERSION
48 | RUN curl https://dl.cloudsmith.io/${KEA_REPO}/setup.deb.sh | bash \
49 | && apt-get update \
50 | && apt-get install \
51 | --no-install-recommends \
52 | -y \
53 | apt-utils \
54 | isc-kea-ctrl-agent=${KEA_VERSION} \
55 | isc-kea-dhcp4=${KEA_VERSION} \
56 | isc-kea-dhcp6=${KEA_VERSION} \
57 | isc-kea-admin=${KEA_VERSION} \
58 | isc-kea-common=${KEA_VERSION} \
59 | isc-kea-hooks=${KEA_VERSION} \
60 | isc-kea-mysql=${KEA_VERSION} \
61 | && apt-get clean \
62 | && rm -rf /var/lib/apt/lists/* \
63 | && mkdir -p /var/run/kea/
64 |
65 | # Install premium packages. The KEA_REPO variable must
66 | # be set to the private repository and include an access token.
67 | # Docker ignores this section if the KEA_PREMIUM is empty - thanks
68 | # to this, the image builds correctly when the token is unknown.
69 | FROM kea-base AS keapremium-base
70 | ARG KEA_PREMIUM
71 | ARG KEA_VERSION
72 | # Execute only if the premium is enabled
73 | RUN [ "${KEA_PREMIUM}" != "premium" ] || ( \
74 | apt-get update \
75 | && apt-get install \
76 | --no-install-recommends \
77 | -y \
78 | isc-kea-premium-cb-cmds=${KEA_VERSION} \
79 | && apt-get clean \
80 | && rm -rf /var/lib/apt/lists/* \
81 | && mkdir -p /var/run/kea/ \
82 | && ldconfig \
83 | )
84 |
85 | ENTRYPOINT [ "/bin/sh", "-c", \
86 | "supervisord -c /etc/supervisor/supervisord.conf" ]
87 | EXPOSE 8080
88 | HEALTHCHECK CMD [ "supervisorctl", "status " ]
--------------------------------------------------------------------------------
/tests/test_infrastructure/Dockerfiles/Dockerfile.perfdhcp:
--------------------------------------------------------------------------------
1 | FROM ubuntu:22.04
2 |
3 | RUN DEBIAN_FRONTEND=noninteractive \
4 | apt-get update \
5 | && apt-get install \
6 | -y \
7 | --no-install-recommends \
8 | kea-admin
9 |
10 | RUN perfdhcp -4 -R 100 -B -l eth0 -x ael -g multi
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/ha/primary/kea-ctrl-agent.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Control-agent": {
3 | "http-host": "0.0.0.0",
4 | "http-port": 8000,
5 | "authentication": {
6 | "type": "basic",
7 | "clients": [
8 | {
9 | "user": "kea",
10 | "password": "secret123"
11 | }
12 | ]
13 | },
14 | "control-sockets": {
15 | "dhcp4": {
16 | "comment": "socket to DHCP4 server",
17 | "socket-type": "unix",
18 | "socket-name": "/tmp/kea4-ctrl-socket"
19 | },
20 | "dhcp6": {
21 | "socket-type": "unix",
22 | "socket-name": "/tmp/kea6-ctrl-socket"
23 | },
24 | "d2": {
25 | "socket-type": "unix",
26 | "socket-name": "/tmp/kea-ddns-ctrl-socket",
27 | "user-context": {
28 | "in-use": false
29 | }
30 | }
31 | },
32 | "loggers": [
33 | {
34 | "name": "kea-ctrl-agent",
35 | "output_options": [
36 | {
37 | "output": "/var/log/kea-ctrl-agent.log",
38 | "flush": true,
39 | "maxsize": 204800,
40 | "maxver": 4,
41 | "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
42 | }
43 | ],
44 | "severity": "DEBUG",
45 | "debuglevel": 99
46 | }
47 | ]
48 | }
49 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/ha/primary/kea-dhcp4-sql.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp4": {
3 | "multi-threading": {"enable-multi-threading": false},
4 | "server-tag": "pyisckea-1",
5 | "interfaces-config": {
6 | "interfaces": [
7 | "eth0"
8 | ]
9 | },
10 | "control-socket": {
11 | "socket-type": "unix",
12 | "socket-name": "/tmp/kea4-ctrl-socket"
13 | },
14 | "lease-database": {
15 | "type": "mysql",
16 | "name": "kea",
17 | "host": "db",
18 | "connect-timeout": 10,
19 | "max-reconnect-tries": 20,
20 | "on-fail": "stop-retry-exit",
21 | "user": "kea",
22 | "password": "secret123"
23 | },
24 | "hosts-database": {
25 | "type": "mysql",
26 | "name": "kea",
27 | "host": "db",
28 | "connect-timeout": 10,
29 | "max-reconnect-tries": 20,
30 | "on-fail": "stop-retry-exit",
31 | "user": "kea",
32 | "password": "secret123"
33 | },
34 | "config-control": {
35 | "config-databases": [
36 | {
37 | "type": "mysql",
38 | "name": "kea",
39 | "user": "kea",
40 | "password": "secret123",
41 | "host": "db"
42 | }
43 | ],
44 | "config-fetch-wait-time": 20
45 | },
46 | "hooks-libraries": [
47 | {
48 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
49 | },
50 | {
51 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
52 | },
53 | {
54 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_mysql.so"
55 | },
56 | {
57 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
58 | },
59 | {
60 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
61 | },
62 | {
63 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
64 | },
65 | {
66 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
67 | "parameters": {
68 | "high-availability": [
69 | {
70 | "this-server-name": "server1",
71 | "mode": "hot-standby",
72 | "send-lease-updates": true,
73 | "sync-leases": true,
74 | "peers": [
75 | {
76 | "auto-failover": true,
77 | "name": "server1",
78 | "url": "http://192.0.2.150:8000/",
79 | "role": "primary",
80 | "basic-auth-user": "kea",
81 | "basic-auth-password": "secret123"
82 | },
83 | {
84 | "auto-failover": true,
85 | "name": "server2",
86 | "url": "http://192.0.2.151:8000/",
87 | "role": "standby",
88 | "basic-auth-user": "kea",
89 | "basic-auth-password": "secret123"
90 | }
91 | ]
92 | }
93 | ]
94 | }
95 | }
96 | ],
97 | "loggers": [
98 | {
99 | "name": "kea-dhcp4",
100 | "output_options": [
101 | {
102 | "output": "stdout"
103 | }
104 | ],
105 | "severity": "DEBUG",
106 | "debuglevel": 99
107 | }
108 | ]
109 | }
110 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/ha/primary/kea-dhcp6-sql.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp6": {
3 | "multi-threading": {"enable-multi-threading": false},
4 | "server-tag": "pyisckea-1",
5 | "interfaces-config": {
6 | "interfaces": [
7 | "eth0"
8 | ]
9 | },
10 | "control-socket": {
11 | "socket-type": "unix",
12 | "socket-name": "/tmp/kea6-ctrl-socket"
13 | },
14 | "lease-database": {
15 | "type": "mysql",
16 | "name": "kea",
17 | "host": "db",
18 | "connect-timeout": 10,
19 | "max-reconnect-tries": 3,
20 | "on-fail": "stop-retry-exit",
21 | "user": "kea",
22 | "password": "secret123"
23 | },
24 | "hosts-database": {
25 | "type": "mysql",
26 | "name": "kea",
27 | "host": "db",
28 | "connect-timeout": 10,
29 | "max-reconnect-tries": 20,
30 | "on-fail": "stop-retry-exit",
31 | "user": "kea",
32 | "password": "secret123"
33 | },
34 | "config-control": {
35 | "config-databases": [
36 | {
37 | "type": "mysql",
38 | "name": "kea",
39 | "user": "kea",
40 | "password": "secret123",
41 | "host": "db"
42 | }
43 | ],
44 | "config-fetch-wait-time": 20
45 | },
46 | "hooks-libraries": [
47 | {
48 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
49 | },
50 | {
51 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
52 | },
53 | {
54 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_mysql.so"
55 | },
56 | {
57 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
58 | },
59 | {
60 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
61 | },
62 | {
63 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
64 | },
65 | {
66 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
67 | "parameters": {
68 | "high-availability": [
69 | {
70 | "this-server-name": "server1",
71 | "mode": "hot-standby",
72 | "send-lease-updates": true,
73 | "sync-leases": true,
74 | "peers": [
75 | {
76 | "auto-failover": true,
77 | "name": "server1",
78 | "url": "http://192.0.2.150:8000/",
79 | "role": "primary",
80 | "basic-auth-user": "kea",
81 | "basic-auth-password": "secret123"
82 | },
83 | {
84 | "auto-failover": true,
85 | "name": "server2",
86 | "url": "http://192.0.2.151:8000/",
87 | "role": "standby",
88 | "basic-auth-user": "kea",
89 | "basic-auth-password": "secret123"
90 | }
91 | ]
92 | }
93 | ]
94 | }
95 | }
96 | ],
97 | "preferred-lifetime": 3000,
98 | "valid-lifetime": 4000,
99 | "renew-timer": 1000,
100 | "rebind-timer": 2000,
101 | "subnet6": [],
102 | "loggers": [
103 | {
104 | "name": "kea-dhcp6",
105 | "output_options": [
106 | {
107 | "output": "stdout"
108 | }
109 | ],
110 | "debuglevel": 0,
111 | "severity": "INFO"
112 | }
113 | ]
114 | }
115 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/ha/standby/kea-ctrl-agent.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Control-agent": {
3 | "http-host": "0.0.0.0",
4 | "http-port": 8000,
5 | "authentication": {
6 | "type": "basic",
7 | "realm": "kea-control-agent",
8 | "clients": [
9 | {
10 | "user": "kea",
11 | "password": "secret123"
12 | }
13 | ]
14 | },
15 | "control-sockets": {
16 | "dhcp4": {
17 | "comment": "socket to DHCP4 server",
18 | "socket-type": "unix",
19 | "socket-name": "/tmp/kea4-ctrl-socket"
20 | },
21 | "dhcp6": {
22 | "socket-type": "unix",
23 | "socket-name": "/tmp/kea6-ctrl-socket"
24 | },
25 | "d2": {
26 | "socket-type": "unix",
27 | "socket-name": "/tmp/kea-ddns-ctrl-socket",
28 | "user-context": {
29 | "in-use": false
30 | }
31 | }
32 | },
33 | "loggers": [
34 | {
35 | "name": "kea-ctrl-agent",
36 | "output_options": [
37 | {
38 | "output": "/var/log/kea-ctrl-agent.log",
39 | "flush": true,
40 | "maxsize": 204800,
41 | "maxver": 4,
42 | "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
43 | }
44 | ],
45 | "severity": "DEBUG",
46 | "debuglevel": 99
47 | }
48 | ]
49 | }
50 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/ha/standby/kea-dhcp4-sql.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp4": {
3 | "multi-threading": {"enable-multi-threading": false},
4 | "server-tag": "pyisckea-2",
5 | "interfaces-config": {
6 | "interfaces": [
7 | "eth0"
8 | ]
9 | },
10 | "control-socket": {
11 | "socket-type": "unix",
12 | "socket-name": "/tmp/kea4-ctrl-socket"
13 | },
14 | "lease-database": {
15 | "type": "mysql",
16 | "name": "kea",
17 | "host": "db",
18 | "connect-timeout": 10,
19 | "max-reconnect-tries": 3,
20 | "on-fail": "stop-retry-exit",
21 | "user": "kea",
22 | "password": "secret123"
23 | },
24 | "hosts-database": {
25 | "type": "mysql",
26 | "name": "kea",
27 | "host": "db",
28 | "connect-timeout": 10,
29 | "max-reconnect-tries": 20,
30 | "on-fail": "stop-retry-exit",
31 | "user": "kea",
32 | "password": "secret123"
33 | },
34 | "config-control": {
35 | "config-databases": [
36 | {
37 | "type": "mysql",
38 | "name": "kea",
39 | "user": "kea",
40 | "password": "secret123",
41 | "host": "db"
42 | }
43 | ],
44 | "config-fetch-wait-time": 20
45 | },
46 | "hooks-libraries": [
47 | {
48 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
49 | },
50 | {
51 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
52 | },
53 | {
54 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_mysql.so"
55 | },
56 | {
57 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
58 | },
59 | {
60 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
61 | },
62 | {
63 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
64 | },
65 | {
66 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
67 | "parameters": {
68 | "high-availability": [
69 | {
70 | "this-server-name": "server2",
71 | "mode": "hot-standby",
72 | "send-lease-updates": true,
73 | "sync-leases": true,
74 | "peers": [
75 | {
76 | "auto-failover": true,
77 | "name": "server1",
78 | "url": "http://192.0.2.150:8000/",
79 | "role": "primary",
80 | "basic-auth-user": "kea",
81 | "basic-auth-password": "secret123"
82 | },
83 | {
84 | "auto-failover": true,
85 | "name": "server2",
86 | "url": "http://192.0.2.151:8000/",
87 | "role": "standby",
88 | "basic-auth-user": "kea",
89 | "basic-auth-password": "secret123"
90 | }
91 | ]
92 | }
93 | ]
94 | }
95 | }
96 | ],
97 | "loggers": [
98 | {
99 | "name": "kea-dhcp4",
100 | "output_options": [
101 | {
102 | "output": "stdout"
103 | }
104 | ],
105 | "severity": "DEBUG",
106 | "debuglevel": 99
107 | }
108 | ]
109 | }
110 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/ha/standby/kea-dhcp6-sql.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp6": {
3 | "multi-threading": {"enable-multi-threading": false},
4 | "server-tag": "pyisckea-2",
5 | "interfaces-config": {
6 | "interfaces": [
7 | "eth0"
8 | ]
9 | },
10 | "control-socket": {
11 | "socket-type": "unix",
12 | "socket-name": "/tmp/kea6-ctrl-socket"
13 | },
14 | "lease-database": {
15 | "type": "mysql",
16 | "name": "kea",
17 | "host": "db",
18 | "connect-timeout": 10,
19 | "max-reconnect-tries": 3,
20 | "on-fail": "stop-retry-exit",
21 | "user": "kea",
22 | "password": "secret123"
23 | },
24 | "hosts-database": {
25 | "type": "mysql",
26 | "name": "kea",
27 | "host": "db",
28 | "connect-timeout": 10,
29 | "max-reconnect-tries": 20,
30 | "on-fail": "stop-retry-exit",
31 | "user": "kea",
32 | "password": "secret123"
33 | },
34 | "config-control": {
35 | "config-databases": [
36 | {
37 | "type": "mysql",
38 | "name": "kea",
39 | "user": "kea",
40 | "password": "secret123",
41 | "host": "db"
42 | }
43 | ],
44 | "config-fetch-wait-time": 20
45 | },
46 | "hooks-libraries": [
47 | {
48 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
49 | },
50 | {
51 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
52 | },
53 | {
54 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_mysql.so"
55 | },
56 | {
57 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
58 | },
59 | {
60 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
61 | },
62 | {
63 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
64 | },
65 | {
66 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
67 | "parameters": {
68 | "high-availability": [
69 | {
70 | "this-server-name": "server2",
71 | "mode": "hot-standby",
72 | "send-lease-updates": true,
73 | "sync-leases": true,
74 | "peers": [
75 | {
76 | "auto-failover": true,
77 | "name": "server1",
78 | "url": "http://192.0.2.150:8000/",
79 | "role": "primary",
80 | "basic-auth-user": "kea",
81 | "basic-auth-password": "secret123"
82 | },
83 | {
84 | "auto-failover": true,
85 | "name": "server2",
86 | "url": "http://192.0.2.151:8000/",
87 | "role": "standby",
88 | "basic-auth-user": "kea",
89 | "basic-auth-password": "secret123"
90 | }
91 | ]
92 | }
93 | ]
94 | }
95 | }
96 | ],
97 | "preferred-lifetime": 3000,
98 | "valid-lifetime": 4000,
99 | "renew-timer": 1000,
100 | "rebind-timer": 2000,
101 | "subnet6": [],
102 | "loggers": [
103 | {
104 | "name": "kea-dhcp6",
105 | "output_options": [
106 | {
107 | "output": "stdout"
108 | }
109 | ],
110 | "debuglevel": 0,
111 | "severity": "INFO"
112 | }
113 | ]
114 | }
115 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/kea-ctrl-agent.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Control-agent": {
3 | "http-host": "0.0.0.0",
4 | "http-port": 8000,
5 | "authentication": {
6 | "type": "basic",
7 | "realm": "kea-control-agent",
8 | "clients": [
9 | {
10 | "user": "kea",
11 | "password": "secret123"
12 | }
13 | ]
14 | },
15 | "control-sockets": {
16 | "dhcp4": {
17 | "comment": "socket to DHCP4 server",
18 | "socket-type": "unix",
19 | "socket-name": "/tmp/kea4-ctrl-socket"
20 | },
21 | "dhcp6": {
22 | "socket-type": "unix",
23 | "socket-name": "/tmp/kea6-ctrl-socket"
24 | },
25 | "d2": {
26 | "socket-type": "unix",
27 | "socket-name": "/tmp/kea-ddns-ctrl-socket",
28 | "user-context": {
29 | "in-use": false
30 | }
31 | }
32 | },
33 | "loggers": [
34 | {
35 | "name": "kea-ctrl-agent",
36 | "output_options": [
37 | {
38 | "output": "/var/log/kea-ctrl-agent.log",
39 | "flush": true,
40 | "maxsize": 204800,
41 | "maxver": 4,
42 | "pattern": "%d{%y.%m.%d %H:%M:%S.%q} %-5p [%c/%i] %m\n"
43 | }
44 | ],
45 | "severity": "INFO",
46 | "debuglevel": 0
47 | }
48 | ]
49 | }
50 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/kea-ddns.conf:
--------------------------------------------------------------------------------
1 | {
2 | "DhcpDdns" :
3 | {
4 | "ip-address" : "0.0.0.0",
5 | "port" : 53001,
6 | // "dns-server-timeout" : 100,
7 | // "ncr-protocol" : "UDP"
8 | // "ncr-format" : "JSON"
9 | // "control-socket":
10 | // {
11 | // "socket-type": "unix",
12 | // "socket-name": "/tmp/kea-ddns-ctrl-socket"
13 | // },
14 | "forward-ddns" :
15 | {
16 | "ddns-domains" :
17 | [
18 | // {
19 | // "name" : "",
20 | // "key-name" : "",
21 | // "dns-servers" :
22 | // [
23 | // {
24 | // "ip-address" : ""
25 | // ,"port" : 53
26 | // }
27 | // ,
28 | // {
29 | // next DNS server for this DdnsDomain
30 | // }
31 | // :
32 | // ]
33 | // }
34 | // ,
35 | // {
36 | // next Forward DdnsDomain
37 | // }
38 | // :
39 | ]
40 | },
41 | "reverse-ddns" :
42 | {
43 | "ddns-domains" :
44 | [
45 | // {
46 | // "name" : "",
47 | // "key-name" : "",
48 | // "dns-servers" :
49 | // [
50 | // {
51 | // "ip-address" : ""
52 | // ,"port" : 53
53 | // }
54 | // ,
55 | // {
56 | // next DNS server for this DdnsDomain
57 | // }
58 | // :
59 | // ]
60 | // }
61 | // ,
62 | // {
63 | // next Reverse DdnsDomain
64 | // }
65 | // :
66 | ]
67 | },
68 | "tsig-keys" :
69 | [
70 | // {
71 | // "name" : "",
72 | // "algorithm" : "",
73 | // Valid values for algorithm are: HMAC-MD5, HMAC-SHA1,
74 | // HMAC-SHA224, HMAC-SHA256,
75 | // HMAC-SHA384, HMAC-SHA512
76 | // "digest-bits" : 256,
77 | // Minimum truncated length in bits.
78 | // Default 0 (means truncation is forbidden).
79 | // "secret" : ""
80 | // }
81 | // ,
82 | // {
83 | // next TSIG Key
84 | // }
85 | ]
86 | // ,"loggers":
87 | // [
88 | // {
89 | // "name": "kea-dhcp-ddns",
90 | // "severity": "info"
91 | // }
92 | // ]
93 | }
94 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/kea-dhcp4-sql.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp4": {
3 | "server-tag": "pyisckea-1",
4 | "interfaces-config": {
5 | "interfaces": [
6 | "eth0"
7 | ]
8 | },
9 | "control-socket": {
10 | "socket-type": "unix",
11 | "socket-name": "/tmp/kea4-ctrl-socket"
12 | },
13 | "lease-database": {
14 | "type": "mysql",
15 | "name": "kea",
16 | "host": "db",
17 | "connect-timeout": 10,
18 | "max-reconnect-tries": 3,
19 | "on-fail": "stop-retry-exit",
20 | "user": "kea",
21 | "password": "secret123"
22 | },
23 | "hosts-database": {
24 | "type": "mysql",
25 | "name": "kea",
26 | "host": "db",
27 | "connect-timeout": 10,
28 | "max-reconnect-tries": 20,
29 | "on-fail": "stop-retry-exit",
30 | "user": "kea",
31 | "password": "secret123"
32 | },
33 | "config-control": {
34 | "config-databases": [
35 | {
36 | "type": "mysql",
37 | "name": "kea",
38 | "user": "kea",
39 | "password": "secret123",
40 | "host": "db"
41 | }
42 | ],
43 | "config-fetch-wait-time": 20
44 | },
45 | "hooks-libraries": [
46 | {
47 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
48 | },
49 | {
50 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
51 | },
52 | {
53 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_mysql_cb.so"
54 | },
55 | {
56 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_cb_cmds.so"
57 | },
58 | {
59 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
60 | },
61 | {
62 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
63 | },
64 | {
65 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
66 | }
67 | ],
68 | "loggers": [
69 | {
70 | "name": "kea-dhcp4",
71 | "output_options": [
72 | {
73 | "output": "/var/log/kea-dhcp4.log",
74 | "maxsize": 2048000,
75 | "maxver": 4
76 | }
77 | ],
78 | "severity": "INFO",
79 | "debuglevel": 0
80 | }
81 | ]
82 | }
83 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/kea-dhcp4.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp4": {
3 | "interfaces-config": {
4 | "interfaces": [
5 | "eth0"
6 | ]
7 | },
8 | "control-socket": {
9 | "socket-type": "unix",
10 | "socket-name": "/tmp/kea4-ctrl-socket"
11 | },
12 | "lease-database": {
13 | "type": "memfile"
14 | },
15 | "valid-lifetime": 43200,
16 | "renew-timer": 21600,
17 | "rebind-timer": 32400,
18 | "expired-leases-processing": {
19 | "reclaim-timer-wait-time": 3600,
20 | "hold-reclaimed-time": 172800,
21 | "max-reclaim-leases": 0,
22 | "max-reclaim-time": 0
23 | },
24 | "hooks-libraries": [
25 | {
26 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
27 | },
28 | {
29 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
30 | },
31 | {
32 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
33 | },
34 | {
35 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
36 | },
37 | {
38 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
39 | }
40 | ],
41 | "subnet4": [
42 | {
43 | "id": 1,
44 | "subnet": "192.168.1.0/24",
45 | "pools": [
46 | {
47 | "pool": "192.168.1.100-192.168.1.199"
48 | }
49 | ],
50 | "option-data": [
51 | {
52 | "name": "routers",
53 | "data": "192.168.1.1"
54 | },
55 | {
56 | "name": "domain-name-servers",
57 | "data": "1.1.1.1,9.9.9.9"
58 | }
59 | ],
60 | "reservations": [
61 | {
62 | "hw-address": "1a:1b:1c:1d:1e:1f",
63 | "ip-address": "192.168.1.10"
64 | },
65 | {
66 | "client-id": "01:11:22:33:44:55:66",
67 | "ip-address": "192.168.1.11"
68 | }
69 | ]
70 | }
71 | ],
72 | "loggers": [
73 | {
74 | "name": "kea-dhcp4",
75 | "output_options": [
76 | {
77 | "output": "/var/log/kea-dhcp4.log",
78 | "maxsize": 2048000,
79 | "maxver": 4
80 | }
81 | ],
82 | "severity": "INFO",
83 | "debuglevel": 0
84 | }
85 | ]
86 | }
87 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/kea-dhcp6-sql.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp6": {
3 | "server-tag": "pyisckea-1",
4 | "interfaces-config": {
5 | "interfaces": [
6 | "eth0"
7 | ]
8 | },
9 | "control-socket": {
10 | "socket-type": "unix",
11 | "socket-name": "/tmp/kea6-ctrl-socket"
12 | },
13 | "lease-database": {
14 | "type": "mysql",
15 | "name": "kea",
16 | "host": "db",
17 | "connect-timeout": 10,
18 | "max-reconnect-tries": 3,
19 | "on-fail": "stop-retry-exit",
20 | "user": "kea",
21 | "password": "secret123"
22 | },
23 | "hosts-database": {
24 | "type": "mysql",
25 | "name": "kea",
26 | "host": "db",
27 | "connect-timeout": 10,
28 | "max-reconnect-tries": 20,
29 | "on-fail": "stop-retry-exit",
30 | "user": "kea",
31 | "password": "secret123"
32 | },
33 | "config-control": {
34 | "config-databases": [
35 | {
36 | "type": "mysql",
37 | "name": "kea",
38 | "user": "kea",
39 | "password": "secret123",
40 | "host": "db"
41 | }
42 | ],
43 | "config-fetch-wait-time": 20
44 | },
45 | "hooks-libraries": [
46 | {
47 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
48 | },
49 | {
50 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
51 | },
52 | {
53 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_mysql_cb.so"
54 | },
55 | {
56 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_cb_cmds.so"
57 | },
58 | {
59 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
60 | },
61 | {
62 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
63 | },
64 | {
65 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
66 | }
67 | ],
68 | "preferred-lifetime": 3000,
69 | "valid-lifetime": 4000,
70 | "renew-timer": 1000,
71 | "rebind-timer": 2000,
72 | "subnet6": [],
73 | "loggers": [
74 | {
75 | "name": "kea-dhcp6",
76 | "output_options": [
77 | {
78 | "output": "stdout"
79 | }
80 | ],
81 | "debuglevel": 0,
82 | "severity": "INFO"
83 | }
84 | ]
85 | }
86 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/kea-dhcp6.conf:
--------------------------------------------------------------------------------
1 | {
2 | "Dhcp6": {
3 | "interfaces-config": {
4 | "interfaces": [
5 | "eth0"
6 | ]
7 | },
8 | "lease-database": {
9 | "type": "memfile",
10 | "lfc-interval": 3600
11 | },
12 | "control-socket": {
13 | "socket-type": "unix",
14 | "socket-name": "/tmp/kea6-ctrl-socket"
15 | },
16 | "hooks-libraries": [
17 | {
18 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so"
19 | },
20 | {
21 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cmds.so"
22 | },
23 | {
24 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_subnet_cmds.so"
25 | },
26 | {
27 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_class_cmds.so"
28 | },
29 | {
30 | "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_host_cache.so"
31 | }
32 | ],
33 | "preferred-lifetime": 3000,
34 | "valid-lifetime": 4000,
35 | "renew-timer": 1000,
36 | "rebind-timer": 2000,
37 | "subnet6": [
38 | {
39 | "id": 1,
40 | "pools": [
41 | {
42 | "pool": "2001:db8:1::/80"
43 | }
44 | ],
45 | "subnet": "2001:db8:1::/64"
46 | }
47 | ],
48 | "loggers": [
49 | {
50 | "name": "kea-dhcp6",
51 | "output_options": [
52 | {
53 | "output": "stdout"
54 | }
55 | ],
56 | "debuglevel": 0,
57 | "severity": "INFO"
58 | }
59 | ]
60 | }
61 | }
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/mysqld.cnf:
--------------------------------------------------------------------------------
1 | [mysqld]
2 | pid-file = /var/run/mysqld/mysqld.pid
3 | socket = /var/run/mysqld/mysqld.sock
4 | # Where the database files are stored inside the container
5 | datadir = /var/lib/mysql
6 |
7 | # My application special configuration
8 | max_allowed_packet = 32M
9 | sql-mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'
10 |
11 | # Accept connections from any IP address
12 | bind-address = 0.0.0.0
13 | ssl = 0
14 | host_cache_size = 0
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/supervisor/conf.d/kea-agent.conf:
--------------------------------------------------------------------------------
1 | [program:kea-agent]
2 | command=/usr/sbin/kea-ctrl-agent -c /etc/kea/kea-ctrl-agent.conf
3 | autostart = true
4 | autorestart = false
5 | stdout_logfile=/dev/stdout
6 | stdout_logfile_maxbytes=0
7 | stderr_logfile=/dev/stderr
8 | stderr_logfile_maxbytes=0
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/supervisor/conf.d/kea-dhcp4.conf:
--------------------------------------------------------------------------------
1 | [program:kea-dhcp4]
2 | command=/usr/sbin/kea-dhcp4 -c /etc/kea/kea-dhcp4.conf
3 | autostart = true
4 | autorestart = false
5 | stdout_logfile=/dev/stdout
6 | stdout_logfile_maxbytes=0
7 | stderr_logfile=/dev/stderr
8 | stderr_logfile_maxbytes=0
9 |
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/supervisor/conf.d/kea-dhcp6.conf:
--------------------------------------------------------------------------------
1 | [program:kea-dhcp6]
2 | command=/usr/sbin/kea-dhcp6 -c /etc/kea/kea-dhcp6.conf
3 | autostart = true
4 | autorestart = false
5 | stdout_logfile=/dev/stdout
6 | stdout_logfile_maxbytes=0
7 | stderr_logfile=/dev/stderr
8 | stderr_logfile_maxbytes=0
9 |
--------------------------------------------------------------------------------
/tests/test_infrastructure/configs/supervisor/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 | logfile=/var/log/supervisor/supervisord.log
4 |
5 | [inet_http_server]
6 | port = 0.0.0.0:9001
7 |
8 | [rpcinterface:supervisor]
9 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
10 |
11 | [supervisorctl]
12 |
13 | [include]
14 | files = /etc/supervisor/conf.d/*.conf
--------------------------------------------------------------------------------
/tests/test_infrastructure/db-init/02-create-users.sql:
--------------------------------------------------------------------------------
1 | GRANT ALL PRIVILEGES ON kea.* TO 'kea'@'%' WITH GRANT OPTION;
--------------------------------------------------------------------------------
/tests/test_infrastructure/docker-compose-ha-sql.yml:
--------------------------------------------------------------------------------
1 | services:
2 | db:
3 | image: mysql:9.2.0
4 | command:
5 | --bind-address=0.0.0.0
6 | restart: always
7 | environment:
8 | MYSQL_DATABASE: "kea"
9 | MYSQL_ROOT_PASSWORD: "secret123"
10 | MYSQL_ROOT_HOST: "%"
11 | MYSQL_USER: kea
12 | MYSQL_PASSWORD: "secret123"
13 | ports:
14 | - 3306:3306
15 | volumes:
16 | - ./db-init:/docker-entrypoint-initdb.d
17 | networks:
18 | ha_backend:
19 |
20 | keaprimary:
21 | build:
22 | context: .
23 | dockerfile: ./Dockerfiles/Dockerfile
24 | args:
25 | KEA_VERSION: ${KEA_VERSION:-2.7.7-isc20250326114722}
26 | KEA_REPO: ${KEA_REPO:-public/isc/kea-dev}
27 | KEA_PREMIUM: ${KEA_PREMIUM:-""}
28 | ports:
29 | - "8080:8080" # Supervisor API Access
30 | - "8000:8000" # Ctrl Agent Daemon
31 | volumes:
32 | - ./configs/supervisor/supervisord.conf:/etc/supervisor/supervisord.conf:Z
33 | - ./configs/supervisor/conf.d/:/etc/supervisor/conf.d:Z
34 | - ./configs/ha/primary/kea-ctrl-agent.conf:/etc/kea/kea-ctrl-agent.conf:Z
35 | - ./configs/ha/primary/kea-dhcp4-sql.conf:/etc/kea/kea-dhcp4.conf:Z
36 | - ./configs/ha/primary/kea-dhcp6-sql.conf:/etc/kea/kea-dhcp6.conf:Z
37 | tty: true
38 | networks:
39 | ha_backend:
40 | ipv4_address: 192.0.2.150
41 |
42 | keastandby:
43 | build:
44 | context: .
45 | dockerfile: ./Dockerfiles/Dockerfile
46 | args:
47 | KEA_VERSION: ${KEA_VERSION:-2.7.7-isc20250326114722}
48 | KEA_REPO: ${KEA_REPO:-public/isc/kea-dev}
49 | KEA_PREMIUM: ${KEA_PREMIUM:-""}
50 | ports:
51 | - "8081:8080" # Supervisor API Access
52 | - "8001:8000" # Ctrl Agent Daemon
53 | volumes:
54 | - ./configs/supervisor/supervisord.conf:/etc/supervisor/supervisord.conf:Z
55 | - ./configs/supervisor/conf.d/:/etc/supervisor/conf.d:Z
56 | - ./configs/ha/standby/kea-ctrl-agent.conf:/etc/kea/kea-ctrl-agent.conf:Z
57 | - ./configs/ha/standby/kea-dhcp4-sql.conf:/etc/kea/kea-dhcp4.conf:Z
58 | - ./configs/ha/standby/kea-dhcp6-sql.conf:/etc/kea/kea-dhcp6.conf:Z
59 | tty: true
60 | networks:
61 | ha_backend:
62 | ipv4_address: 192.0.2.151
63 |
64 | networks:
65 | ha_backend:
66 | driver: bridge
67 | ipam:
68 | config:
69 | - subnet: 192.0.2.128/25
70 | gateway: 192.0.2.129
--------------------------------------------------------------------------------
/tests/test_infrastructure/docker-compose-sql.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | db:
4 | image: mysql:9.2.0
5 | command:
6 | --bind-address=0.0.0.0
7 | restart: always
8 | environment:
9 | MYSQL_DATABASE: "kea"
10 | MYSQL_ROOT_PASSWORD: "secret123"
11 | MYSQL_ROOT_HOST: "%"
12 | MYSQL_USER: kea
13 | MYSQL_PASSWORD: "secret123"
14 | ports:
15 | - 3306:3306
16 | volumes:
17 | - ./db-init:/docker-entrypoint-initdb.d
18 |
19 | kea:
20 | build:
21 | context: .
22 | dockerfile: ./Dockerfiles/Dockerfile
23 | args:
24 | KEA_VERSION: ${KEA_VERSION}
25 | KEA_REPO: ${KEA_REPO}
26 | KEA_PREMIUM: ${KEA_PREMIUM}
27 | ports:
28 | - "8080:8080" # Supervisor API Access
29 | - "8000:8000" # Ctrl Agent Daemon
30 | - "67:67/udp" # DHCP v4 Daemon
31 | - "547:547/udp" # DHCP v6 Daemon
32 | - "5300:5300/udp" # DDNS Daemon
33 | environment:
34 | KEA_VERSION: "2.6.1-isc20240725093407"
35 | KEA_REPO: "public/isc/kea-2-6"
36 | KEA_PREMIUM: ""
37 | env_file:
38 | - .env
39 | volumes:
40 | - ./configs/supervisor/supervisord.conf:/etc/supervisor/supervisord.conf:Z
41 | - ./configs/supervisor/conf.d/:/etc/supervisor/conf.d:Z
42 | - ./configs/kea-ctrl-agent.conf:/etc/kea/kea-ctrl-agent.conf:Z
43 | - ./configs/kea-dhcp4-sql.conf:/etc/kea/kea-dhcp4.conf:Z
44 | - ./configs/kea-dhcp6-sql.conf:/etc/kea/kea-dhcp6.conf:Z
45 | tty: true
--------------------------------------------------------------------------------
/tests/test_infrastructure/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 | services:
3 | kea:
4 | build:
5 | context: .
6 | dockerfile: ./Dockerfiles/Dockerfile
7 | args:
8 | KEA_VERSION: ${KEA_VERSION:-2.7.7-isc20250326114722}
9 | KEA_REPO: ${KEA_REPO:-public/isc/kea-dev}
10 | KEA_PREMIUM: ${KEA_PREMIUM:-""}
11 | ports:
12 | - "8080:8080" # Supervisor API Access
13 | - "8000:8000" # Ctrl Agent Daemon
14 | - "67:67/udp" # DHCP v4 Daemon
15 | - "547:547/udp" # DHCP v6 Daemon
16 | - "5300:5300/udp" # DDNS Daemon
17 | volumes:
18 | - ./configs/supervisor/supervisord.conf:/etc/supervisor/supervisord.conf:Z
19 | - ./configs/supervisor/conf.d/:/etc/supervisor/conf.d:Z
20 | - ./configs/kea-ctrl-agent.conf:/etc/kea/kea-ctrl-agent.conf:Z
21 | - ./configs/kea-ddns.conf:/etc/kea/kea-ddns.conf:Z
22 | - ./configs/kea-dhcp4.conf:/etc/kea/kea-dhcp4.conf:Z
23 | - ./configs/kea-dhcp6.conf:/etc/kea/kea-dhcp6.conf:Z
24 | tty: true
--------------------------------------------------------------------------------