update dev setup
check code / check-code (push) Successful in 31s
Details
check code / check-code (push) Successful in 31s
Details
This commit is contained in:
parent
c5f3fa9e00
commit
ba9d301dac
|
@ -1,5 +1,5 @@
|
||||||
manager:
|
manager:
|
||||||
max_workers: 2
|
max_workers: 1
|
||||||
plan_outputs:
|
plan_outputs:
|
||||||
html:
|
html:
|
||||||
class: octodns.provider.plan.PlanMarkdown
|
class: octodns.provider.plan.PlanMarkdown
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
values:
|
values:
|
||||||
- ns1.example.com.
|
- ns1.example.com.
|
||||||
- ns2.example.com.
|
- ns2.example.com.
|
||||||
|
ns1:
|
||||||
|
type: A
|
||||||
|
value: 192.168.1.1
|
||||||
|
ns2:
|
||||||
|
type: A
|
||||||
|
value: 192.168.1.2
|
||||||
record1:
|
record1:
|
||||||
type: CNAME
|
type: CNAME
|
||||||
value: record11.example.com.
|
value: record11.example.com.
|
||||||
|
|
16
justfile
16
justfile
|
@ -46,9 +46,9 @@ lint:
|
||||||
hatch run lint:style
|
hatch run lint:style
|
||||||
hatch run lint:typing
|
hatch run lint:typing
|
||||||
|
|
||||||
format:
|
format *args:
|
||||||
just show_system_info
|
just show_system_info
|
||||||
hatch run lint:fmt
|
hatch run lint:fmt {{ args }}
|
||||||
|
|
||||||
check:
|
check:
|
||||||
just lint
|
just lint
|
||||||
|
@ -71,11 +71,11 @@ clean:
|
||||||
rm -rf dev/redis-data/*
|
rm -rf dev/redis-data/*
|
||||||
rm -rf dev/netbox-data/*
|
rm -rf dev/netbox-data/*
|
||||||
|
|
||||||
sync *flags:
|
sync *args:
|
||||||
cd dev && octodns-sync --debug --config-file sync.yml --force {{ flags }}
|
hatch -v run default:sync {{ args }}
|
||||||
|
|
||||||
dump *flags:
|
dump *args:
|
||||||
cd dev && octodns-dump --debug --config-file sync.yml --output-dir output {{ flags }} '*' netbox
|
hatch -v run default:dump {{ args }}
|
||||||
|
|
||||||
validate *flags:
|
validate *args:
|
||||||
cd dev && octodns-validate --debug --config-file sync.yml {{ flags }}
|
hatch -v run default:validate {{ args }}
|
||||||
|
|
|
@ -60,6 +60,10 @@ test-cov = ["coverage erase", "coverage run -m pytest {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"]
|
||||||
|
|
||||||
|
sync = ["cd dev && octodns-sync --debug --config-file sync.yml --force {args}"]
|
||||||
|
dump = ["cd dev && octodns-dump --debug --config-file sync.yml --output-dir output {args} '*' netbox"]
|
||||||
|
validate = ["cd dev && octodns-validate --debug --config-file sync.yml {args}"]
|
||||||
|
|
||||||
[tool.hatch.envs.lint]
|
[tool.hatch.envs.lint]
|
||||||
python = "3.11"
|
python = "3.11"
|
||||||
detached = true
|
detached = true
|
||||||
|
@ -108,6 +112,7 @@ exclude = [
|
||||||
"dist",
|
"dist",
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"venv",
|
"venv",
|
||||||
|
"dev"
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
|
|
|
@ -2,20 +2,23 @@ import logging
|
||||||
from typing import Any, Literal
|
from typing import Any, 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
|
import pynetbox.core.response
|
||||||
|
|
||||||
|
|
||||||
class NetBoxDNSSource(octodns.source.base.BaseSource):
|
class NetBoxDNSSource(octodns.provider.base.BaseProvider):
|
||||||
"""
|
"""
|
||||||
OctoDNS provider for NetboxDNS
|
OctoDNS provider for NetboxDNS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SUPPORTS_GEO = False
|
SUPPORTS_GEO = False
|
||||||
SUPPORTS_DYNAMIC = False
|
SUPPORTS_DYNAMIC = False
|
||||||
|
SUPPORTS_ROOT_NS = True
|
||||||
|
SUPPORTS_MULTIVALUE_PTR = True
|
||||||
SUPPORTS: set[str] = { # noqa
|
SUPPORTS: set[str] = { # noqa
|
||||||
"A",
|
"A",
|
||||||
"AAAA",
|
"AAAA",
|
||||||
|
@ -56,13 +59,15 @@ class NetBoxDNSSource(octodns.source.base.BaseSource):
|
||||||
ttl=3600,
|
ttl=3600,
|
||||||
replace_duplicates=False,
|
replace_duplicates=False,
|
||||||
make_absolute=False,
|
make_absolute=False,
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Initialize the NetboxDNSSource
|
Initialize the NetboxDNSSource
|
||||||
"""
|
"""
|
||||||
self.log = logging.getLogger(f"NetboxDNSSource[{id}]")
|
self.log = logging.getLogger(f"NetboxDNSSource[{id}]")
|
||||||
self.log.debug(f"__init__: {id=}, {url=}, {view=}, {replace_duplicates=}, {make_absolute=}")
|
self.log.debug(f"__init__: {id=}, {url=}, {view=}, {replace_duplicates=}, {make_absolute=}")
|
||||||
super().__init__(id)
|
super().__init__(id, *args, **kwargs)
|
||||||
|
|
||||||
self.api = pynetbox.core.api.Api(url, token)
|
self.api = pynetbox.core.api.Api(url, token)
|
||||||
self.nb_view = self._get_nb_view(view)
|
self.nb_view = self._get_nb_view(view)
|
||||||
|
@ -193,7 +198,7 @@ class NetBoxDNSSource(octodns.source.base.BaseSource):
|
||||||
}
|
}
|
||||||
|
|
||||||
case "SPF" | "TXT":
|
case "SPF" | "TXT":
|
||||||
value = raw_value.replace(";", r"\;")
|
value = raw_value.replace(";", "\\;")
|
||||||
|
|
||||||
case "SRV":
|
case "SRV":
|
||||||
value = {
|
value = {
|
||||||
|
@ -267,7 +272,7 @@ class NetBoxDNSSource(octodns.source.base.BaseSource):
|
||||||
|
|
||||||
def populate(
|
def populate(
|
||||||
self, zone: octodns.zone.Zone, target: bool = False, lenient: bool = False
|
self, zone: octodns.zone.Zone, target: bool = False, lenient: bool = False
|
||||||
) -> None:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
|
||||||
|
@ -277,10 +282,17 @@ class NetBoxDNSSource(octodns.source.base.BaseSource):
|
||||||
"""
|
"""
|
||||||
self.log.info(f"populate -> '{zone.name}', target={target}, lenient={lenient}")
|
self.log.info(f"populate -> '{zone.name}', target={target}, lenient={lenient}")
|
||||||
|
|
||||||
records = self._format_nb_records(zone)
|
try:
|
||||||
|
records = self._format_nb_records(zone)
|
||||||
|
except LookupError:
|
||||||
|
return False
|
||||||
|
|
||||||
for data in records:
|
for data in records:
|
||||||
if len(data["values"]) == 1:
|
if len(data["values"]) == 1:
|
||||||
data["value"] = data.pop("values")[0]
|
data["value"] = data.pop("values")[0]
|
||||||
|
if target and data["type"] in ["NS", "SOA", "PTR"]:
|
||||||
|
self.log.debug(f"{data['type']} type not supported in target mode")
|
||||||
|
continue
|
||||||
record = octodns.record.Record.new(
|
record = octodns.record.Record.new(
|
||||||
zone=zone,
|
zone=zone,
|
||||||
name=data["name"],
|
name=data["name"],
|
||||||
|
@ -291,3 +303,121 @@ class NetBoxDNSSource(octodns.source.base.BaseSource):
|
||||||
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}'")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _include_change(self, change: octodns.record.change.Change) -> bool:
|
||||||
|
"""Filter out record types which the provider can't create in netbox"""
|
||||||
|
if change.new._type in ["SOA", "PTR", "NS"]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _apply(self, plan: octodns.provider.plan.Plan) -> None:
|
||||||
|
"""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 = {repr(v)[1:-1] for v in 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.replace("\\\\", "\\").replace("\\;", ";"),
|
||||||
|
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 = {repr(v)[1:-1] for v in 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 = {repr(v)[1:-1] for v in 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 = {repr(v)[1:-1] for v in 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.replace("\\\\", "\\").replace("\\;", ";"),
|
||||||
|
disable_ptr=True,
|
||||||
|
)
|
||||||
|
self.log.debug(f"{nb_record!r}")
|
||||||
|
|
Loading…
Reference in New Issue