Compare commits
2 commits
353791797d
...
5714ce5af9
Author | SHA1 | Date | |
---|---|---|---|
5714ce5af9 | |||
4a2ba28aaa |
11 changed files with 317 additions and 180 deletions
|
@ -11,7 +11,6 @@ charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
|
|
||||||
[*.{yml,yaml,xml,hcl,tf}]
|
[*.{yml,yaml,xml,hcl,tf}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
|
24
.gitea/workflows/check_code.yml
Normal file
24
.gitea/workflows/check_code.yml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
name: check/lint python code with hatch
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches: [main, master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-python-hatch:
|
||||||
|
runs-on: python311
|
||||||
|
steps:
|
||||||
|
- name: checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: install hatch
|
||||||
|
run: pip install -U hatch
|
||||||
|
|
||||||
|
- name: test codestyle
|
||||||
|
run: hatch run lint:style
|
||||||
|
|
||||||
|
- name: test typing
|
||||||
|
run: hatch run lint:typing
|
153
.gitignore
vendored
153
.gitignore
vendored
|
@ -1,7 +1,152 @@
|
||||||
.idea/
|
.idea/
|
||||||
.vscode/
|
.vscode/
|
||||||
__pycache__
|
test.ipynb
|
||||||
.env
|
test.py
|
||||||
.python-version
|
test.sh
|
||||||
.ruff_cache/
|
downloads/
|
||||||
|
venv
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
__pycache__/
|
||||||
|
.pytest_cache/
|
||||||
|
.pytest_cache&
|
||||||
|
|
||||||
|
### Python template
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
5
.lefthook/pre-commit/format.sh
Normal file
5
.lefthook/pre-commit/format.sh
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/env bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
just format
|
2
.tool-versions
Normal file
2
.tool-versions
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
just 1.16.0
|
||||||
|
lefthook 1.4.6
|
11
README.md
11
README.md
|
@ -1,6 +1,6 @@
|
||||||
# netbox-plugin-dns source for octodns
|
# netbox-plugin-dns source for octodns
|
||||||
|
|
||||||
> Works with https://github.com/peteeckel/netbox-plugin-dns
|
> works with https://github.com/peteeckel/netbox-plugin-dns
|
||||||
|
|
||||||
## config
|
## config
|
||||||
|
|
||||||
|
@ -11,11 +11,12 @@ providers:
|
||||||
# Netbox url
|
# Netbox url
|
||||||
# [mandatory, default=null]
|
# [mandatory, default=null]
|
||||||
url: "https://some-url"
|
url: "https://some-url"
|
||||||
# Netbox api token
|
# Netbox API token
|
||||||
# [mandatory, default=null]
|
# [mandatory, default=null]
|
||||||
token: env/NETBOX_API_KEY
|
token: env/NETBOX_API_KEY
|
||||||
# View of the zone. Can be either a string (the view name) or "null"
|
# View of the zone. Can be either a string -> the view name
|
||||||
# to only query zones without a view. Set to false to ignore views
|
# "null" -> to only query zones without a view
|
||||||
|
# false -> to ignore views
|
||||||
# [optional, default=false]
|
# [optional, default=false]
|
||||||
view: false
|
view: false
|
||||||
# When records sourced from multiple providers, allows provider
|
# When records sourced from multiple providers, allows provider
|
||||||
|
@ -30,7 +31,7 @@ providers:
|
||||||
|
|
||||||
## install
|
## install
|
||||||
|
|
||||||
### via pip
|
### via pip + git
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install octodns-netbox-dns@git+https://github.com/olofvndrhr/octodns-netbox-dns.git@main
|
pip install octodns-netbox-dns@git+https://github.com/olofvndrhr/octodns-netbox-dns.git@main
|
||||||
|
|
54
justfile
Normal file
54
justfile
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env just --justfile
|
||||||
|
|
||||||
|
default: show_receipts
|
||||||
|
|
||||||
|
set shell := ["bash", "-uc"]
|
||||||
|
set dotenv-load
|
||||||
|
|
||||||
|
show_receipts:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
show_system_info:
|
||||||
|
@echo "=================================="
|
||||||
|
@echo "os : {{os()}}"
|
||||||
|
@echo "arch: {{arch()}}"
|
||||||
|
@echo "justfile dir: {{justfile_directory()}}"
|
||||||
|
@echo "invocation dir: {{invocation_directory()}}"
|
||||||
|
@echo "running dir: `pwd -P`"
|
||||||
|
@echo "=================================="
|
||||||
|
|
||||||
|
setup:
|
||||||
|
@asdf install
|
||||||
|
@lefthook install
|
||||||
|
|
||||||
|
create_venv:
|
||||||
|
@echo "creating venv"
|
||||||
|
@python3 -m pip install --upgrade pip setuptools wheel
|
||||||
|
@python3 -m venv venv
|
||||||
|
|
||||||
|
install_deps:
|
||||||
|
@echo "installing dependencies"
|
||||||
|
@python3 -m hatch dep show requirements --project-only > /tmp/requirements.txt
|
||||||
|
@pip3 install -r /tmp/requirements.txt
|
||||||
|
|
||||||
|
install_deps_dev:
|
||||||
|
@echo "installing dev dependencies"
|
||||||
|
@python3 -m hatch dep show requirements --project-only > /tmp/requirements.txt
|
||||||
|
@python3 -m hatch dep show requirements --env-only >> /tmp/requirements.txt
|
||||||
|
@pip3 install -r /tmp/requirements.txt
|
||||||
|
|
||||||
|
create_reqs:
|
||||||
|
@echo "creating requirements"
|
||||||
|
@pipreqs --force --savepath requirements.txt src/octodns_netbox_dns
|
||||||
|
|
||||||
|
lint:
|
||||||
|
just show_system_info
|
||||||
|
just test_shfmt
|
||||||
|
just test_shfmt
|
||||||
|
@hatch run lint:style
|
||||||
|
@hatch run lint:typing
|
||||||
|
|
||||||
|
format:
|
||||||
|
just show_system_info
|
||||||
|
just format_shfmt
|
||||||
|
@hatch run lint:fmt
|
8
lefthook.yml
Normal file
8
lefthook.yml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
colors: true
|
||||||
|
no_tty: false
|
||||||
|
|
||||||
|
pre-commit:
|
||||||
|
scripts:
|
||||||
|
"format.sh":
|
||||||
|
stage_fixed: true
|
||||||
|
runner: bash
|
|
@ -44,17 +44,15 @@ packages = ["src/octodns_netbox_dns"]
|
||||||
### envs
|
### envs
|
||||||
|
|
||||||
[tool.hatch.envs.default]
|
[tool.hatch.envs.default]
|
||||||
|
python = "3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pytest>=7.0.0",
|
"pytest==7.4.3",
|
||||||
"coverage>=6.3.1",
|
"coverage==7.3.2",
|
||||||
"mypy>=1.5.1",
|
|
||||||
"ruff>=0.1.3",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.hatch.envs.default.scripts]
|
[tool.hatch.envs.default.scripts]
|
||||||
python = "3.11"
|
test = "pytest {args:tests}"
|
||||||
test = "pytest --verbose {args:tests}"
|
test-cov = ["coverage erase", "coverage run -m pytest {args:tests}"]
|
||||||
test-cov = ["coverage erase", "coverage run -m pytest --verbose {args:tests}"]
|
|
||||||
cov-report = ["- coverage combine", "coverage report", "coverage xml"]
|
cov-report = ["- coverage combine", "coverage report", "coverage xml"]
|
||||||
cov = ["test-cov", "cov-report"]
|
cov = ["test-cov", "cov-report"]
|
||||||
|
|
||||||
|
@ -62,34 +60,16 @@ cov = ["test-cov", "cov-report"]
|
||||||
python = "3.11"
|
python = "3.11"
|
||||||
detached = true
|
detached = true
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mypy>=1.5.1",
|
"mypy==1.7.1",
|
||||||
"ruff>=0.1.3",
|
"ruff==0.1.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.hatch.envs.lint.scripts]
|
[tool.hatch.envs.lint.scripts]
|
||||||
typing = "mypy --non-interactive --install-types {args:src/octodns_netbox_dns}"
|
typing = "mypy --non-interactive --install-types {args:src/octodns_netbox_dns}"
|
||||||
style = ["ruff check --diff {args:.}", "ruff format --check --diff {args:.}"]
|
style = ["ruff check --diff {args:.}", "ruff format --check --diff {args:.}"]
|
||||||
fmt = ["ruff format {args:.}", "ruff --fix {args:.}", "style"]
|
fmt = ["ruff format {args:.}", "ruff check --fix {args:.}", "style"]
|
||||||
all = ["style", "typing"]
|
all = ["style", "typing"]
|
||||||
|
|
||||||
### black
|
|
||||||
|
|
||||||
[tool.black]
|
|
||||||
line-length = 100
|
|
||||||
target-version = ["py311"]
|
|
||||||
|
|
||||||
### pyright
|
|
||||||
|
|
||||||
[tool.pyright]
|
|
||||||
typeCheckingMode = "basic"
|
|
||||||
pythonVersion = "3.11"
|
|
||||||
reportUnnecessaryTypeIgnoreComment = true
|
|
||||||
reportShadowedImports = true
|
|
||||||
reportUnusedExpression = true
|
|
||||||
reportMatchNotExhaustive = true
|
|
||||||
# venvPath = "."
|
|
||||||
# venv = "venv"
|
|
||||||
|
|
||||||
### ruff
|
### ruff
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
|
@ -146,7 +126,7 @@ select = [
|
||||||
"W",
|
"W",
|
||||||
"YTT",
|
"YTT",
|
||||||
]
|
]
|
||||||
ignore = ["E501", "D103", "D100", "D102", "PLR2004", "D403", "FBT003", "ISC001"]
|
ignore = ["E501", "D103", "D100", "D102", "PLR2004", "D403", "ISC001", "FBT001", "FBT002", "FBT003"]
|
||||||
unfixable = ["F401"]
|
unfixable = ["F401"]
|
||||||
|
|
||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
|
@ -189,15 +169,33 @@ max-doc-length = 100
|
||||||
### mypy
|
### mypy
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
disallow_untyped_defs = false
|
#plugins = ["pydantic.mypy"]
|
||||||
disallow_incomplete_defs = false
|
follow_imports = "silent"
|
||||||
follow_imports = "normal"
|
warn_redundant_casts = true
|
||||||
|
warn_unused_ignores = true
|
||||||
|
disallow_any_generics = true
|
||||||
|
check_untyped_defs = true
|
||||||
|
no_implicit_reexport = true
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
warn_return_any = true
|
||||||
pretty = true
|
pretty = true
|
||||||
show_column_numbers = true
|
show_column_numbers = true
|
||||||
show_error_codes = true
|
show_error_codes = true
|
||||||
warn_no_return = false
|
show_error_context = true
|
||||||
warn_unused_ignores = true
|
|
||||||
|
#[tool.pydantic-mypy]
|
||||||
|
#init_forbid_extra = true
|
||||||
|
#init_typed = true
|
||||||
|
#warn_required_dynamic_aliases = true
|
||||||
|
|
||||||
|
### pytest
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
pythonpath = ["src"]
|
||||||
|
addopts = "--color=yes --exitfirst --verbose -ra"
|
||||||
|
#addopts = "--color=yes --exitfirst --verbose -ra --capture=tee-sys"
|
||||||
|
filterwarnings = [
|
||||||
|
'ignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning',
|
||||||
|
]
|
||||||
|
|
||||||
### coverage
|
### coverage
|
||||||
|
|
||||||
|
@ -208,8 +206,8 @@ parallel = true
|
||||||
omit = ["src/octodns_netbox_dns/__about__.py"]
|
omit = ["src/octodns_netbox_dns/__about__.py"]
|
||||||
|
|
||||||
[tool.coverage.paths]
|
[tool.coverage.paths]
|
||||||
testproj = ["src/octodns_netbox_dns", "*/src/octodns_netbox_dns"]
|
testproj = ["src/octodns_netbox_dns", "*/octodns_netbox_dns/src/octodns_netbox_dns"]
|
||||||
tests = ["tests", "*/tests"]
|
tests = ["tests", "*/octodns_netbox_dns/tests"]
|
||||||
|
|
||||||
[tool.coverage.report]
|
[tool.coverage.report]
|
||||||
# Regexes for lines to exclude from consideration
|
# Regexes for lines to exclude from consideration
|
||||||
|
|
4
renovate.json
Normal file
4
renovate.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": ["local>44net/renovate"]
|
||||||
|
}
|
|
@ -1,20 +1,22 @@
|
||||||
"""OctoDNS provider for NetboxDNS."""
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
import dns.rdata
|
import dns.rdata
|
||||||
import octodns.provider.base
|
|
||||||
import octodns.provider.plan
|
|
||||||
import octodns.record
|
import octodns.record
|
||||||
|
import octodns.source.base
|
||||||
import octodns.zone
|
import octodns.zone
|
||||||
import pynetbox.core.api
|
import pynetbox.core.api
|
||||||
import pynetbox.core.response as pynb_resp
|
import pynetbox.core.response
|
||||||
|
|
||||||
|
|
||||||
class NetBoxDNSSource(octodns.provider.base.BaseProvider):
|
class NetBoxDNSSource(octodns.source.base.BaseSource):
|
||||||
SUPPORTS_GEO: bool = False
|
"""
|
||||||
SUPPORTS_DYNAMIC: bool = False
|
OctoDNS provider for NetboxDNS
|
||||||
SUPPORTS: set[str] = {
|
"""
|
||||||
|
|
||||||
|
SUPPORTS_GEO = False
|
||||||
|
SUPPORTS_DYNAMIC = False
|
||||||
|
SUPPORTS: frozenset = {
|
||||||
"A",
|
"A",
|
||||||
"AAAA",
|
"AAAA",
|
||||||
"AFSDB",
|
"AFSDB",
|
||||||
|
@ -45,10 +47,6 @@ class NetBoxDNSSource(octodns.provider.base.BaseProvider):
|
||||||
"TXT",
|
"TXT",
|
||||||
}
|
}
|
||||||
|
|
||||||
_api: pynetbox.core.api.Api
|
|
||||||
# log: logging.Logger
|
|
||||||
_ttl: int
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
id: int,
|
id: int,
|
||||||
|
@ -56,15 +54,15 @@ class NetBoxDNSSource(octodns.provider.base.BaseProvider):
|
||||||
token: str,
|
token: str,
|
||||||
view: str | None | Literal[False] = False,
|
view: str | None | Literal[False] = False,
|
||||||
ttl=3600,
|
ttl=3600,
|
||||||
replace_duplicates: bool = False,
|
replace_duplicates=False,
|
||||||
make_absolute: bool = False,
|
make_absolute=False,
|
||||||
):
|
):
|
||||||
"""Initialize the NetboxDNSSource."""
|
"""
|
||||||
|
Initialize the NetboxDNSSource
|
||||||
|
"""
|
||||||
self.log = logging.getLogger(f"NetboxDNSSource[{id}]")
|
self.log = logging.getLogger(f"NetboxDNSSource[{id}]")
|
||||||
self.log.debug(
|
self.log.debug(f"__init__: {id=}, {url=}, {view=}, {replace_duplicates=}, {make_absolute=}")
|
||||||
f"__init__: id={id}, url={url}, view={view}, replace_duplicates={replace_duplicates}, make_absolute={make_absolute}"
|
super().__init__(id)
|
||||||
)
|
|
||||||
super(NetBoxDNSSource, self).__init__(id)
|
|
||||||
self._api = pynetbox.core.api.Api(url, token)
|
self._api = pynetbox.core.api.Api(url, token)
|
||||||
self._nb_view = {} if view is False else self._get_view(view)
|
self._nb_view = {} if view is False else self._get_view(view)
|
||||||
self._ttl = ttl
|
self._ttl = ttl
|
||||||
|
@ -76,27 +74,34 @@ class NetBoxDNSSource(octodns.provider.base.BaseProvider):
|
||||||
return value
|
return value
|
||||||
return value + "."
|
return value + "."
|
||||||
|
|
||||||
def _get_view(self, view: str | None) -> dict[str, int | str]:
|
def _get_view(self, view: str | Literal[False]) -> dict[str, int | str]:
|
||||||
if view is None:
|
if view is None:
|
||||||
return {"view": "null"}
|
return {"view": "null"}
|
||||||
|
|
||||||
nb_view: pynb_resp.Record = self._api.plugins.netbox_dns.views.get(name=view)
|
nb_view: pynetbox.core.response.Record = self._api.plugins.netbox_dns.views.get(name=view)
|
||||||
if nb_view is None:
|
if nb_view is None:
|
||||||
raise ValueError(f"dns view: '{view}' has not been found")
|
msg = f"dns view: '{view}' has not been found"
|
||||||
|
self.log.error(msg)
|
||||||
|
raise ValueError(msg)
|
||||||
self.log.debug(f"found {nb_view.name} {nb_view.id}")
|
self.log.debug(f"found {nb_view.name} {nb_view.id}")
|
||||||
|
|
||||||
return {"view_id": nb_view.id}
|
return {"view_id": nb_view.id}
|
||||||
|
|
||||||
def _get_nb_zone(self, name: str, view: dict[str, str | int]) -> pynb_resp.Record:
|
def _get_nb_zone(self, name: str, view: dict[str, str | int]) -> pynetbox.core.response.Record:
|
||||||
"""Given a zone name and a view name, look it up in NetBox.
|
"""
|
||||||
Raises: pynetbox.RequestError if declared view is not existant"""
|
Given a zone name and a view name, look it up in NetBox.
|
||||||
|
|
||||||
|
@raise pynetbox.RequestError: if declared view is not existent
|
||||||
|
"""
|
||||||
query_params = {"name": name[:-1], **view}
|
query_params = {"name": name[:-1], **view}
|
||||||
nb_zone = self._api.plugins.netbox_dns.zones.get(**query_params)
|
nb_zone = self._api.plugins.netbox_dns.zones.get(**query_params)
|
||||||
|
|
||||||
return nb_zone
|
return nb_zone
|
||||||
|
|
||||||
def populate(self, zone: octodns.zone.Zone, target: bool = False, lenient: bool = False):
|
def populate(self, zone: octodns.zone.Zone, target: bool = False, lenient: bool = False):
|
||||||
"""Get all the records of a zone from NetBox and add them to the OctoDNS zone."""
|
"""
|
||||||
|
Get all the records of a zone from NetBox and add them to the OctoDNS zone.
|
||||||
|
"""
|
||||||
self.log.debug(f"populate: name={zone.name}, target={target}, lenient={lenient}")
|
self.log.debug(f"populate: name={zone.name}, target={target}, lenient={lenient}")
|
||||||
|
|
||||||
records = {}
|
records = {}
|
||||||
|
@ -218,111 +223,3 @@ class NetBoxDNSSource(octodns.provider.base.BaseProvider):
|
||||||
zone.add_record(record, lenient=lenient, replace=self.replace_duplicates)
|
zone.add_record(record, lenient=lenient, replace=self.replace_duplicates)
|
||||||
|
|
||||||
self.log.info(f"populate: found {len(zone.records)} records for zone {zone.name}")
|
self.log.info(f"populate: found {len(zone.records)} records for zone {zone.name}")
|
||||||
|
|
||||||
def _apply(self, plan: octodns.provider.plan.Plan):
|
|
||||||
"""Apply the changes to the NetBox DNS zone."""
|
|
||||||
self.log.debug(f"_apply: zone={plan.desired.name}, len(changes)={len(plan.changes)}")
|
|
||||||
|
|
||||||
nb_zone = self._get_nb_zone(plan.desired.name, view=self._nb_view)
|
|
||||||
|
|
||||||
for change in plan.changes:
|
|
||||||
match change:
|
|
||||||
case octodns.record.Create():
|
|
||||||
name = change.new.name
|
|
||||||
if name == "":
|
|
||||||
name = "@"
|
|
||||||
|
|
||||||
match change.new:
|
|
||||||
case octodns.record.ValueMixin():
|
|
||||||
new = {repr(change.new.value)[1:-1]}
|
|
||||||
case octodns.record.ValuesMixin():
|
|
||||||
new = set(map(lambda v: repr(v)[1:-1], change.new.values))
|
|
||||||
case _:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
for value in new:
|
|
||||||
nb_record = self._api.plugins.netbox_dns.records.create(
|
|
||||||
zone=nb_zone.id,
|
|
||||||
name=name,
|
|
||||||
type=change.new._type,
|
|
||||||
ttl=change.new.ttl,
|
|
||||||
value=value,
|
|
||||||
disable_ptr=True,
|
|
||||||
)
|
|
||||||
self.log.debug(f"{nb_record!r}")
|
|
||||||
|
|
||||||
case octodns.record.Delete():
|
|
||||||
name = change.existing.name
|
|
||||||
if name == "":
|
|
||||||
name = "@"
|
|
||||||
|
|
||||||
nb_records = self._api.plugins.netbox_dns.records.filter(
|
|
||||||
zone_id=nb_zone.id,
|
|
||||||
name=change.existing.name,
|
|
||||||
type=change.existing._type,
|
|
||||||
)
|
|
||||||
|
|
||||||
match change.existing:
|
|
||||||
case octodns.record.ValueMixin():
|
|
||||||
existing = {repr(change.existing.value)[1:-1]}
|
|
||||||
case octodns.record.ValuesMixin():
|
|
||||||
existing = set(map(lambda v: repr(v)[1:-1], change.existing.values))
|
|
||||||
case _:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
for nb_record in nb_records:
|
|
||||||
for value in existing:
|
|
||||||
if nb_record.value == value:
|
|
||||||
self.log.debug(
|
|
||||||
f"{nb_record.id} {nb_record.name} {nb_record.type} {nb_record.value} {value}"
|
|
||||||
)
|
|
||||||
self.log.debug(f"{nb_record.url} {nb_record.endpoint.url}")
|
|
||||||
nb_record.delete()
|
|
||||||
|
|
||||||
case octodns.record.Update():
|
|
||||||
name = change.existing.name
|
|
||||||
if name == "":
|
|
||||||
name = "@"
|
|
||||||
|
|
||||||
nb_records = self._api.plugins.netbox_dns.records.filter(
|
|
||||||
zone_id=nb_zone.id,
|
|
||||||
name=name,
|
|
||||||
type=change.existing._type,
|
|
||||||
)
|
|
||||||
|
|
||||||
match change.existing:
|
|
||||||
case octodns.record.ValueMixin():
|
|
||||||
existing = {repr(change.existing.value)[1:-1]}
|
|
||||||
case octodns.record.ValuesMixin():
|
|
||||||
existing = set(map(lambda v: repr(v)[1:-1], change.existing.values))
|
|
||||||
case _:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
match change.new:
|
|
||||||
case octodns.record.ValueMixin():
|
|
||||||
new = {repr(change.new.value)[1:-1]}
|
|
||||||
case octodns.record.ValuesMixin():
|
|
||||||
new = set(map(lambda v: repr(v)[1:-1], change.new.values))
|
|
||||||
case _:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
delete = existing.difference(new)
|
|
||||||
update = existing.intersection(new)
|
|
||||||
create = new.difference(existing)
|
|
||||||
|
|
||||||
for nb_record in nb_records:
|
|
||||||
if nb_record.value in delete:
|
|
||||||
nb_record.delete()
|
|
||||||
if nb_record.value in update:
|
|
||||||
nb_record.ttl = change.new.ttl
|
|
||||||
nb_record.save()
|
|
||||||
|
|
||||||
for value in create:
|
|
||||||
nb_record = self._api.plugins.netbox_dns.records.create(
|
|
||||||
zone=nb_zone.id,
|
|
||||||
name=name,
|
|
||||||
type=change.new._type,
|
|
||||||
ttl=change.new.ttl,
|
|
||||||
value=value,
|
|
||||||
disable_ptr=True,
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in a new issue