Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle URL record type #23

Merged
merged 4 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Note: This is no official software project by INWX, it just kindly uses their pu
- [Debug and dry-run](#debug-and-dry-run)
- [I deleted all my productive records!](#i-deleted-all-my-productive-records)
- [Simulate API response](#simulate-api-response)
- [URL records](#url-records)
- [License](#license)


Expand Down Expand Up @@ -216,6 +217,11 @@ This could look like the following:
In order to get this output from an existing domain, you can run the program with the `--debug` flag and search for the line starting with `Response (nameserver.info):`.


### URL Records

INWX has [URL records](https://kb.inwx.com/en-us/3-nameserver/106-how-can-i-forward-a-domain-to-an-internet-address) that allow for redirections of a domain to another domain. These records are somewhat supported by this tool, but there are [several issues and bugs](https://github.com/mxmehl/inwx-dns-recordmaster/pull/23). It's recommended to add and edit these records in the web interface and not via this tool as the INWX API doesn't seem to be reliable, but you can still store these configurations locally.


## License

The main license of this project is the GNU General Public License 3.0, no later version (`GPL-3.0-only`), Copyright Max Mehl.
Expand Down
16 changes: 15 additions & 1 deletion recordmaster/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,21 @@

__version__ = version("inwx-dns-recordmaster")

RECORD_KEYS = ("id", "name", "type", "content", "ttl", "prio")
# All record keys
RECORD_KEYS = (
"id",
"name",
"type",
"content",
"ttl",
"prio",
"urlRedirectType",
"urlRedirectTitle",
"urlRedirectDescription",
"urlRedirectFavIcon",
"urlRedirectKeywords",
"urlAppend",
)

DEFAULT_APP_CONFIG = """# App configuration for INWX DNS Recordmaster.
# This is not the place for domain records, these can be anywhere and used with the -c flag
Expand Down
5 changes: 5 additions & 0 deletions recordmaster/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ def inwx_api(
logging.info("API call for '%s' has not been executed in dry-run mode", method)
return {}

# Convert boolean values to 0/1 as this is what the INWX seems to expect
for key, value in params.items():
if isinstance(value, bool):
params[key] = 1 if value else 0

api_result = api.call_api(api_method=method, method_params=params)

# Handle return codes
Expand Down
20 changes: 14 additions & 6 deletions recordmaster/_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


@dataclass
class Record:
class Record: # pylint: disable=too-many-instance-attributes
"""Dataclass holding a nameserver record, be it remote or local"""

# nameserver details
Expand All @@ -28,6 +28,13 @@ class Record:
content: str = ""
ttl: int = 3600
prio: int = 0
# pylint: disable=invalid-name
urlRedirectType: str = ""
urlRedirectTitle: str = ""
urlRedirectDescription: str = ""
urlRedirectFavIcon: str = ""
urlRedirectKeywords: str = ""
urlAppend: bool = False

def import_records(self, data: dict, domain: str = "", root: str = ""):
"""Update records by providing a dict"""
Expand Down Expand Up @@ -85,11 +92,12 @@ def to_local_conf_format(self, records: list[Record], ignore_types: list) -> dic

# Type and content are straightforward, we don't need to convert it
rec_yaml: dict[str, str | int] = {"type": rec.type, "content": rec.content}
# TTL and prio will be set unless it's the default value
if rec.ttl != Record.ttl:
rec_yaml["ttl"] = rec.ttl
if rec.prio != Record.prio:
rec_yaml["prio"] = rec.prio

# All the other attributes unless they have the default value
# This is, in RECORD_KEYS, all from the 5th element, ttl
for attr in RECORD_KEYS[4:]:
if getattr(rec, attr) != getattr(Record, attr):
rec_yaml[attr] = getattr(rec, attr)

data[name].append(rec_yaml)

Expand Down
30 changes: 17 additions & 13 deletions recordmaster/_sync_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ def sync_existing_local_to_remote(
api: ApiClient, domain: Domain, dry: bool, interactive: bool
) -> None:
"""Compare previously matched local records to remote ones. If differences, update remote"""
# pylint: disable=too-many-nested-blocks
# Loop over local records which have an ID, so matched to a remote entry
for loc_rec in [loc_rec for loc_rec in domain.local_records if loc_rec.id]:
# For each ID, compare content, ttl and prio
for key in ("content", "ttl", "prio"):
# For each ID, compare content, ttl, prio etc, collect changes, and make API call
changes = {}
for key in RECORD_KEYS[3:]:
# Get local and corresponding remote attribute
loc_val = getattr(loc_rec, key)
rem_val = next(
Expand All @@ -30,10 +30,10 @@ def sync_existing_local_to_remote(
if rem_rec.id == loc_rec.id
)
# Update attribute at remote if values differ
if loc_val and (loc_val != rem_val):
if loc_val != rem_val:
# Log and update record
logging.info(
"[%s] Updating '%s' record of '%s': '%s' from '%s' to '%s'",
"[%s] Update '%s' record of '%s': '%s' from '%s' to '%s'",
domain.name,
loc_rec.type,
loc_rec.name,
Expand All @@ -43,20 +43,24 @@ def sync_existing_local_to_remote(
)

# Update record via API call
inwx_api(
api,
"nameserver.updateRecord",
interactive=interactive,
dry=dry,
id=loc_rec.id,
**{key: loc_val},
)
changes[key] = loc_val
else:
# No action needed as records are equal or undefined
logging.debug(
"[%s] (%s) %s equal: %s = %s", loc_rec.name, loc_rec.id, key, rem_val, loc_val
)

# Execute collected changes for this ID, if they exist
if changes:
inwx_api(
api,
"nameserver.updateRecord",
interactive=interactive,
dry=dry,
id=loc_rec.id,
**changes,
)


def create_missing_at_remote(
api: ApiClient, domain: Domain, records: list[Record], dry: bool, interactive: bool
Expand Down