1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00
Files

151 lines
4.9 KiB
Python

#
#
#
from datetime import datetime
from logging import getLogger
from uuid import uuid4
from .. import __version__
from ..record import Record
from .base import BaseProcessor
# TODO: remove once we require python >= 3.11
try: # pragma: no cover
from datetime import UTC
except ImportError: # pragma: no cover
from datetime import timedelta, timezone
UTC = timezone(timedelta())
def _keys(values):
return set(v.split('=', 1)[0] for v in values)
class MetaProcessor(BaseProcessor):
'''
Add a special metadata record with timestamps, UUIDs, versions, and/or
provider name. Will only be updated when there are other changes being made.
A useful tool to aid in debugging and monitoring of DNS infrastructure.
Timestamps or UUIDs can be useful in checking whether changes are
propagating, either from a provider's backend to their servers or via AXFRs.
Provider can be utilized to determine which DNS system responded to a query
when things are operating in dual authority or split horizon setups.
Creates a TXT record with the name configured with values based on processor
settings. Values are in the form `key=<value>`, e.g.
`time=2023-09-10T05:49:04.246953`
processors:
meta:
class: octodns.processor.meta.MetaProcessor
# The name to use for the meta record.
# (default: meta)
record_name: meta
# Include a timestamp with a UTC value indicating the timeframe when the
# last change was made.
# (default: true)
include_time: true
# Include a UUID that can be utilized to uniquely identify the run
# pushing data
# (default: false)
include_uuid: false
# Include the provider id for the target where data is being pushed
# (default: false)
include_provider: false
# Include the octoDNS version being used
# (default: false)
include_version: false
'''
@classmethod
def get_time(cls):
return datetime.now(UTC).isoformat()
@classmethod
def get_uuid(cls):
return str(uuid4())
def __init__(
self,
id,
record_name='meta',
include_time=True,
include_uuid=False,
include_version=False,
include_provider=False,
ttl=60,
):
self.log = getLogger(f'MetaSource[{id}]')
super().__init__(id)
self.log.info(
'__init__: record_name=%s, include_time=%s, include_uuid=%s, include_version=%s, include_provider=%s, ttl=%d',
record_name,
include_time,
include_uuid,
include_version,
include_provider,
ttl,
)
self.record_name = record_name
self.time = self.get_time() if include_time else None
self.uuid = self.get_uuid() if include_uuid else None
self.include_version = include_version
self.include_provider = include_provider
self.ttl = ttl
def values(self, target_id):
ret = []
if self.include_version:
ret.append(f'octodns-version={__version__}')
if self.include_provider:
ret.append(f'provider={target_id}')
if self.time:
ret.append(f'time={self.time}')
if self.uuid:
ret.append(f'uuid={self.uuid}')
return ret
def process_source_and_target_zones(self, desired, existing, target):
meta = Record.new(
desired,
self.record_name,
{'ttl': self.ttl, 'type': 'TXT', 'values': self.values(target.id)},
# we may be passing in empty values here to be filled out later in
# process_source_and_target_zones
lenient=True,
)
desired.add_record(meta)
return desired, existing
def _is_up_to_date_meta(self, change, target_id):
# always something so we can see if its type and name
record = change.record
# existing state, if there is one
existing = getattr(change, 'existing', None)
return (
record._type == 'TXT'
and record.name == self.record_name
and existing is not None
# don't care about the values here, just the fields/keys
and _keys(self.values(target_id)) == _keys(existing.values)
)
def process_plan(self, plan, sources, target):
if (
plan
and len(plan.changes) == 1
and self._is_up_to_date_meta(plan.changes[0], target.id)
):
# the only change is the meta record, and it's not meaningfully
# changing so we don't actually want to make the update, meta should
# only be enough to cause a plan on its own if the fields changed
return None
# There's more than one thing changing so meta should update and/or meta
# is meaningfully changing or being created...
return plan