Skip to content

Commit

Permalink
seperated json and bind import export methods refs #29
Browse files Browse the repository at this point in the history
  • Loading branch information
infinityofspace committed Sep 15, 2024
1 parent 8338119 commit ccdc595
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 46 deletions.
27 changes: 20 additions & 7 deletions pkb_client/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from datetime import datetime

from pkb_client.client import PKBClient, API_ENDPOINT
from pkb_client.client.dns import DNSRecordType, DNSFileFormat, DNSRestoreMode
from pkb_client.client.dns import DNSRecordType, DNSRestoreMode
from pkb_client.client.forwarding import URLForwardingType


Expand Down Expand Up @@ -85,15 +85,19 @@ def main():
parser_dns_receive.set_defaults(func=PKBClient.dns_retrieve)
parser_dns_receive.add_argument("domain", help="The domain for which the DNS record should be retrieved.")

parser_dns_export = subparsers.add_parser("dns-export", help="Save all DNS records to a local file.")
parser_dns_export = subparsers.add_parser("dns-export", help="Save all DNS records to a local json file.")
parser_dns_export.set_defaults(func=PKBClient.dns_export)
parser_dns_export.add_argument("domain",
help="The domain for which the DNS record should be retrieved and saved.")
parser_dns_export.add_argument("filename", help="The filename where to save the exported DNS records.")
parser_dns_export.add_argument("--type", help="The file format in which the DNS records should be saved.",
choices=list(DNSFileFormat), default=DNSFileFormat.JSON)

parser_dns_import = subparsers.add_parser("dns-import", help="Restore all DNS records from a local file.",
parser_dns_export_bind = subparsers.add_parser("dns-export-bind", help="Save all DNS records to a local BIND file.")
parser_dns_export_bind.set_defaults(func=PKBClient.dns_export_bind)
parser_dns_export_bind.add_argument("domain",
help="The domain for which the DNS record should be retrieved and saved.")
parser_dns_export_bind.add_argument("filename", help="The filename where to save the exported DNS records.")

parser_dns_import = subparsers.add_parser("dns-import", help="Restore all DNS records from a local json file.",
formatter_class=argparse.RawTextHelpFormatter)
parser_dns_import.set_defaults(func=PKBClient.dns_import)
parser_dns_import.add_argument("domain", help="The domain for which the DNS record should be restored.")
Expand All @@ -103,8 +107,17 @@ def main():
replace: replace only existing DNS records with the DNS records from the provided file, but do not create any new DNS records
keep: keep the existing DNS records and only create new ones for all DNS records from the specified file if they do not exist
""", type=DNSRestoreMode.from_string, choices=list(DNSRestoreMode))
parser_dns_import.add_argument("--type", help="The file format from which the DNS records should be restored.",
choices=list(DNSFileFormat), default=DNSFileFormat.JSON)

parser_dns_import_bind = subparsers.add_parser("dns-import-bind",
help="Restore all DNS records from a local BIND file.",
formatter_class=argparse.RawTextHelpFormatter)
parser_dns_import_bind.set_defaults(func=PKBClient.dns_import_bind)
parser_dns_import_bind.add_argument("filename", help="The filename from which the DNS records are to be restored.")
parser_dns_import_bind.add_argument("restore_mode", help="""The restore mode (DNS records are identified by the record id):
clean: remove all existing DNS records and restore all DNS records from the provided file
replace: replace only existing DNS records with the DNS records from the provided file, but do not create any new DNS records
keep: keep the existing DNS records and only create new ones for all DNS records from the specified file if they do not exist
""", type=DNSRestoreMode.from_string, choices=list(DNSRestoreMode))

parser_domain_pricing = subparsers.add_parser("domain-pricing", help="Get the pricing for Porkbun domains.")
parser_domain_pricing.set_defaults(func=PKBClient.get_domain_pricing)
Expand Down
2 changes: 1 addition & 1 deletion pkb_client/client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .bind_file import BindFile, BindRecord, RecordClass
from .client import PKBClient, PKBClientException, API_ENDPOINT
from .dns import DNSRecord, DNSRestoreMode, DNSRecordType, DNSFileFormat
from .dns import DNSRecord, DNSRestoreMode, DNSRecordType
from .domain import DomainInfo
from .forwarding import URLForwarding, URLForwardingType
from .ssl_cert import SSLCertBundle
96 changes: 63 additions & 33 deletions pkb_client/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import dns.resolver
import requests

from pkb_client.client.bind_file import BindFile
from pkb_client.client.dns import DNSRecord, DNSRestoreMode, DNSRecordType, DNSFileFormat, DNS_RECORDS_WITH_PRIORITY
from pkb_client.client import BindFile
from pkb_client.client.dns import DNSRecord, DNSRestoreMode, DNSRecordType, DNS_RECORDS_WITH_PRIORITY
from pkb_client.client.domain import DomainInfo
from pkb_client.client.forwarding import URLForwarding, URLForwardingType
from pkb_client.client.ssl_cert import SSLCertBundle
Expand Down Expand Up @@ -327,16 +327,14 @@ def dns_retrieve_all(self,
def dns_export(self,
domain: str,
filename: str,
type: DNSFileFormat = DNSFileFormat.JSON,
**kwargs) -> bool:
"""
Export all DNS record from the given domain to a file. This method does not represent a Porkbun API method.
If the file format is JSON, the DNS records with all custom fields like notes are exported.
In case the file format is BIND, a BIND zone file is created, which only contains the essential information.
Export all DNS record from the given domain to a json file.
This method does not represent a Porkbun API method.
DNS records with all custom fields like notes are exported.
:param domain: the domain for which the DNS record should be retrieved and saved
:param filename: the filename where to save the exported DNS records
:param type: the file format in which the DNS records should be saved
:return: True if everything went well
"""
Expand All @@ -354,32 +352,64 @@ def dns_export(self,
if filepath.exists():
logging.warning("file already exists, overwriting...")

if type == DNSFileFormat.JSON:
with open(filepath, "w") as f:
json.dump(dns_records_dict, f, default=lambda o: o.__dict__, indent=4)
elif type == DNSFileFormat.BIND:
# domain header
bind_file_content = f"$ORIGIN {domain}"

# SOA record
soa_records = dns.resolver.resolve(domain, "SOA")
if soa_records:
soa_record = soa_records[0]
bind_file_content += f"\n@ IN SOA {soa_record.mname} {soa_record.rname} ({soa_record.serial} {soa_record.refresh} {soa_record.retry} {soa_record.expire} {soa_record.minimum})"

# records
for record in dns_records:
# name record class ttl record type record data
if record.prio:
record_content = f"{record.prio} {record.content}"
else:
record_content = record.content
bind_file_content += f"\n{record.name} IN {record.ttl} {record.type} {record_content}"

with open(filepath, "w") as f:
f.write(bind_file_content)
else:
raise ValueError("Unknown file type")
with open(filepath, "w") as f:
json.dump(dns_records_dict, f, default=lambda o: o.__dict__, indent=4)

logging.info("export finished")

return True

def dns_export_bind(self,
domain: str,
filename: str,
**kwargs) -> bool:
"""
Export all DNS record from the given domain to a BIND file.
This method does not represent a Porkbun API method.
Porkbun DNS record notes are exported as comments.
:param domain: the domain for which the DNS record should be retrieved and saved
:param filename: the filename where to save the exported DNS records
:return: True if everything went well
"""

logging.info("retrieve current DNS records...")
dns_records = self.dns_retrieve(domain)

logging.info("save DNS records to {} ...".format(filename))
# merge the single DNS records into one single dict with the record id as key
dns_records_dict = dict()
for record in dns_records:
dns_records_dict[record.id] = record

filepath = Path(filename)
if filepath.exists():
logging.warning("file already exists, overwriting...")

# domain header
bind_file_content = f"$ORIGIN {domain}"

# SOA record
soa_records = dns.resolver.resolve(domain, "SOA")
if soa_records:
soa_record = soa_records[0]
bind_file_content += f"\n@ IN SOA {soa_record.mname} {soa_record.rname} ({soa_record.serial} {soa_record.refresh} {soa_record.retry} {soa_record.expire} {soa_record.minimum})"

# records
for record in dns_records:
# name record class ttl record type record data
if record.prio:
record_content = f"{record.prio} {record.content}"
else:
record_content = record.content
bind_file_content += f"\n{record.name} IN {record.ttl} {record.type} {record_content}"

if record.notes:
bind_file_content += f" ; {record.notes}"

with open(filepath, "w") as f:
f.write(bind_file_content)

logging.info("export finished")

Expand Down
5 changes: 0 additions & 5 deletions pkb_client/client/dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,3 @@ def from_string(a):
return DNSRestoreMode[a]
except KeyError:
return a


class DNSFileFormat(str, Enum):
BIND = "BIND"
JSON = "JSON"

0 comments on commit ccdc595

Please sign in to comment.