Zone

exception octodns.zone.SubzoneRecordException(msg, record)[source]

Bases: Exception

Exception raised when a record belongs in a sub-zone but is added to the parent.

This exception is raised when attempting to add a record to a zone that should actually be managed in a configured sub-zone. Only NS and DS records are allowed at the sub-zone boundary.

Parameters:

record (octodns.record.base.Record) – The record that caused the exception.

__init__(msg, record)[source]
exception octodns.zone.DuplicateRecordException(msg, existing, new)[source]

Bases: Exception

Exception raised when attempting to add a duplicate record to a zone.

A duplicate is defined as a record with the same name and type as an existing record in the zone. The exception includes references to both the existing and new records for debugging.

Parameters:
__init__(msg, existing, new)[source]
exception octodns.zone.InvalidNodeException(msg, record)[source]

Bases: Exception

Exception raised when CNAME records coexist with other records at a node.

Per DNS standards, CNAME records cannot coexist with other record types at the same node. This exception is raised when such an invalid configuration is detected.

Parameters:

record (octodns.record.base.Record) – The record that caused the exception.

__init__(msg, record)[source]
exception octodns.zone.InvalidNameError[source]

Bases: Exception

Exception raised when a zone name is invalid.

Zone names must: - End with a dot (.) - Not contain double dots (..) - Not contain whitespace

class octodns.zone.Zone(name, sub_zones, update_pcent_threshold=None, delete_pcent_threshold=None)[source]

Bases: object

Container for DNS records belonging to a single DNS zone.

A Zone represents a DNS zone and manages all the records within it. It provides methods for adding, removing, and querying records, as well as computing changes between zones and applying those changes.

Zones support copy-on-write semantics via the copy() method, which creates shallow copies that are hydrated on first modification. This allows for efficient processing of zones through multiple stages without unnecessary copying.

Key features:

  • Record management: Add, remove, and query DNS records

  • Validation: Enforce DNS standards (CNAME restrictions, sub-zone rules)

  • IDNA support: Handle internationalized domain names

  • Sub-zone awareness: Respect configured sub-zone boundaries

  • Change tracking: Compute differences between desired and existing state

  • Copy-on-write: Efficient shallow copying with lazy hydration

Example usage:

from octodns.zone import Zone
from octodns.record import Record

zone = Zone('example.com.', [])
record = Record.new(zone, 'www', {'type': 'A', 'ttl': 300, 'value': '1.2.3.4'})
zone.add_record(record)

# Create a shallow copy
copy = zone.copy()
# Modifications to copy don't affect the original until hydrated
log = <Logger Zone (WARNING)>
__init__(name, sub_zones, update_pcent_threshold=None, delete_pcent_threshold=None)[source]

Initialize a DNS zone.

Parameters:
  • name (str) – The zone name (must end with a dot). Internationalized domain names (IDN) are automatically encoded to IDNA format.

  • sub_zones (list[str]) – List of sub-zone names managed separately. Records belonging to sub-zones will be rejected (except NS/DS at the boundary).

  • update_pcent_threshold (float or None) – Override for maximum update percentage threshold. If None, uses provider default.

  • delete_pcent_threshold (float or None) – Override for maximum delete percentage threshold. If None, uses provider default.

Raises:

InvalidNameError – If the zone name is invalid (missing trailing dot, contains double dots, or has whitespace).

Important

  • Zone names must end with a dot (.)

  • Zone names are automatically encoded to IDNA format internally

  • Sub-zones prevent records from being added to the parent zone

property records

Get all records in this zone.

Returns a set of all DNS records in the zone. If this is a shallow copy (not yet hydrated), returns records from the origin zone.

Returns:

Set of all records in the zone.

Return type:

set[octodns.record.base.Record]

property root_ns

Get the root NS record for this zone.

The root NS record is the NS record at the zone apex (empty hostname). Returns None if no root NS record exists.

Returns:

The root NS record, or None if not present.

Return type:

octodns.record.ns.NsRecord or None

hostname_from_fqdn(fqdn)[source]

Extract the hostname portion from a fully qualified domain name.

Strips the zone name from the FQDN to get just the hostname portion. Handles both IDNA-encoded and UTF-8 domain names correctly.

Parameters:

fqdn (str) – Fully qualified domain name.

Returns:

The hostname portion (without the zone name).

Return type:

str

Example:

zone = Zone('example.com.', [])
zone.hostname_from_fqdn('www.example.com.')  # Returns 'www'
zone.hostname_from_fqdn('example.com.')      # Returns ''
owns(_type, fqdn)[source]

Determine if this zone owns a given FQDN for a specific record type.

Checks whether a record with the given FQDN and type should be managed by this zone, taking into account sub-zone boundaries. Records under sub-zones are not owned by the parent (except NS records at the exact sub-zone boundary).

Parameters:
  • _type (str) – The DNS record type (e.g., ‘A’, ‘CNAME’, ‘NS’).

  • fqdn (str) – Fully qualified domain name to check.

Returns:

True if this zone owns the FQDN for this type, False otherwise.

Return type:

bool

Important

  • NS records at sub-zone boundaries are owned by the parent zone

  • All other records under sub-zones are not owned by the parent

  • FQDNs are automatically normalized (trailing dot added if missing)

add_record(record, replace=False, lenient=False)[source]

Add a DNS record to this zone.

Adds the provided record to the zone with validation. If this is a shallow copy (has an origin), it will be hydrated before adding.

Parameters:
  • record (octodns.record.base.Record) – The DNS record to add to the zone.

  • replace (bool) – If True, replace any existing record with the same name and type. If False, raise an exception if a duplicate exists.

  • lenient (bool) – If True, skip some validation checks (sub-zone checks, CNAME coexistence checks). Useful when loading existing data that may not be standards-compliant.

Raises:

Important

  • Automatically hydrates shallow copies on first modification

  • NS/DS records are allowed at sub-zone boundaries

  • CNAME records cannot coexist with other records at the same node

  • Use replace=True to update existing records

  • Use lenient=True when loading potentially non-compliant data

remove_record(record)[source]

Remove a DNS record from this zone.

Removes the provided record from the zone. If this is a shallow copy (has an origin), it will be hydrated before removing.

Parameters:

record (octodns.record.base.Record) – The DNS record to remove from the zone.

Important

  • Automatically hydrates shallow copies on first modification

  • Clearing the root NS record (empty name) also clears the cached root_ns property

  • Silently succeeds if the record doesn’t exist in the zone

_remove_record(record)[source]
changes(desired, target)[source]

Compute the changes needed to transform this zone into the desired state.

Compares this zone (existing state) with the desired zone and returns a list of changes (Creates, Updates, Deletes) required to make this zone match the desired state. Respects record-level include/exclude filtering and provider support.

Parameters:
  • desired (Zone) – The desired zone state to compare against.

  • target (octodns.provider.base.BaseProvider) – The target provider that will apply these changes. Used to check record support and apply include/exclude rules.

Returns:

List of changes needed to transform this zone to the desired state.

Return type:

list[octodns.record.change.Change]

Important

  • Skips records marked as ignored

  • Respects record-level included and excluded lists

  • Only includes changes for record types the target supports

  • Returns Creates, Updates (via record.changes), and Deletes

apply(changes)[source]

Apply a list of changes to this zone.

Applies the provided changes by adding new/updated records and removing deleted records. Uses replace=True and lenient=True to handle updates and non-standard records gracefully.

Parameters:

changes (list[octodns.record.change.Change]) – List of changes to apply to the zone.

Important

  • Delete changes remove the existing record

  • Create and Update changes add the new record with replace=True

  • All adds use lenient=True to skip validation

  • Changes are applied in the order provided

hydrate()[source]

Convert a shallow copy into a hydrated copy with its own record references.

Hydration copies all records from the origin zone into this zone, making it independent. The records themselves are still the original objects and should not be modified directly. Use add_record() with replace=True or remove_record() to make changes.

Returns:

True if hydration occurred, False if already hydrated.

Return type:

bool

Note

This method is automatically called by add_record() and remove_record() when needed, so manual calls are rarely necessary.

Important

  • Only hydrates if this is a shallow copy (has an _origin)

  • Clears the _origin reference after hydration

  • Uses lenient=True when adding records from origin

  • Records are still shared with the origin (not deep copied)

copy()[source]

Create a shallow copy of this zone using copy-on-write semantics.

Creates a new zone that shares records with this zone until the copy is modified. When add_record() or remove_record() is called on the copy, it will be automatically hydrated with its own record references.

Returns:

A shallow copy of this zone.

Return type:

Zone

Important

  • The copy shares records with the original until hydrated

  • Hydration happens automatically on first modification

  • Records in the hydrated copy are still the same objects (not deep copied)

  • Modifying records directly affects both zones; use record.copy() and add_record(..., replace=True) instead

Example:

original = Zone('example.com.', [])
# ... add records to original ...

copy = original.copy()  # Shallow copy, shares records
# No copying has occurred yet

copy.add_record(new_record)  # Triggers hydration, copies record refs
# Now copy has its own record references
__repr__()[source]

Return a string representation of this zone.

Returns:

String in the format Zone<zone_name> using the decoded name.

Return type:

str