diff --git a/src/main.py b/src/main.py
index 958a77c..06b5c0f 100755
--- a/src/main.py
+++ b/src/main.py
@@ -10,12 +10,13 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
+# along with this program. If not, see .
+import os
import sys
import requests
import json
@@ -27,189 +28,189 @@ from collections import OrderedDict
def get_cf_dns_domain(cf_zone_id):
- return "https://api.cloudflare.com/client/v4/zones/{}/dns_records".format(
- cf_zone_id
- )
+ return "https://api.cloudflare.com/client/v4/zones/{}/dns_records".format(
+ cf_zone_id
+ )
def get_cf_dns_domain_record_id(cf_zone_id, cf_record_id):
- return "/".join([
- get_cf_dns_domain(cf_zone_id),
- cf_record_id
- ])
+ return "/".join([
+ get_cf_dns_domain(cf_zone_id),
+ cf_record_id
+ ])
# !! Do not use this !! The global Cloudflair API is dangerous. Keeping this here for posterity and info
# def get_headers_global_api(cf_api_key, cf_email):
-# return {
-# "X-Auth-Key": cf_api_key,
-# "X-Auth-Email": cf_email
-# }
+# return {
+# "X-Auth-Key": cf_api_key,
+# "X-Auth-Email": cf_email
+# }
def get_headers(cf_api_key):
- return {
- "Authorization": "Bearer {}".format(cf_api_key)
- }
+ return {
+ "Authorization": "Bearer {}".format(cf_api_key)
+ }
def get_record_id(cf_zone_id, cf_api_key):
- resp = requests.get(
- url=get_cf_dns_domain(cf_zone_id),
- headers=get_headers(cf_api_key)
- )
- failed = resp.status_code != 200
- if not failed:
- print(json.dumps(resp.json(), indent=4, sort_keys=True))
- return failed
+ resp = requests.get(
+ url=get_cf_dns_domain(cf_zone_id),
+ headers=get_headers(cf_api_key)
+ )
+ failed = resp.status_code != 200
+ if not failed:
+ print(json.dumps(resp.json(), indent=4, sort_keys=True))
+ return failed
# A hack to prevent pycharm from bitching
def dictionary_split(s, delim='='):
- k, v = s.split(delim, 1)
- return k, v
+ k, v = s.split(delim, 1)
+ return k, v
# Get the current ip address of the user
def get_new_ip():
- with contextlib.suppress(Exception):
- a = requests.get("https://1.1.1.1/cdn-cgi/trace").text.split("\n")
- ip = dict(dictionary_split(item) for item in a if item != "")["ip"]
- return ip
- return None
+ with contextlib.suppress(Exception):
+ a = requests.get("https://1.1.1.1/cdn-cgi/trace").text.split("\n")
+ ip = dict(dictionary_split(item) for item in a if item != "")["ip"]
+ return ip
+ return None
def get_old_ip(cf_zone_id, cf_record_id, cf_api_key):
- resp = requests.get(
- url=get_cf_dns_domain_record_id(cf_zone_id, cf_record_id),
- headers=get_headers(cf_api_key)
- )
- if resp.status_code == 200:
- return resp.json()["result"]["content"]
- return None
+ resp = requests.get(
+ url=get_cf_dns_domain_record_id(cf_zone_id, cf_record_id),
+ headers=get_headers(cf_api_key)
+ )
+ if resp.status_code == 200:
+ return resp.json()["result"]["content"]
+ return None
def put_new_ip(domain_name, ip, cf_zone_id, cf_record_id, cf_api_key):
- json_payload = {
- "type": 'A',
- "name": domain_name,
- "content": ip,
- "proxied": True
- }
+ json_payload = {
+ "type": 'A',
+ "name": domain_name,
+ "content": ip,
+ "proxied": True
+ }
- resp = requests.put(
- url=get_cf_dns_domain_record_id(cf_zone_id, cf_record_id),
- json=json_payload,
- headers=get_headers(cf_api_key)
- )
- success = resp.status_code == 200
- return success
+ resp = requests.put(
+ url=get_cf_dns_domain_record_id(cf_zone_id, cf_record_id),
+ json=json_payload,
+ headers=get_headers(cf_api_key)
+ )
+ success = resp.status_code == 200
+ return success
def config_parser_has_option(config, profile, option):
- # 'DEFAULT' is a special value in ConfigParser
- config_name = "" if profile == "DEFAULT" else profile
- if config.has_option(config_name, option):
- return config[profile][option]
- return None
+ # 'DEFAULT' is a special value in ConfigParser
+ config_name = "" if profile == "DEFAULT" else profile
+ if config.has_option(config_name, option):
+ return config[profile][option]
+ return None
def get_default_path(filename):
paths = (".", os.environ["HOME"], "/root", "/")
- for path in path:
+ for path in paths:
full_path = os.path.join(path, filename)
- if os.file.exists(full_path):
+ if os.path.isfile(full_path):
return full_path
return filename
def main():
- # Logger - set to logging.INFO for production
- logging.basicConfig(level=logging.INFO)
+ # Logger - set to logging.INFO for production
+ logging.basicConfig(level=logging.INFO)
- # String constants
- default_secret_file = ".credentials.secret"
- default_profile = "CLOUDFLARE"
- default_fullpath = get_defualt_path(default_secret_file)
+ # String constants
+ default_secret_file = ".credentials.secret"
+ default_profile = "CLOUDFLARE"
+ default_fullpath = get_default_path(default_secret_file)
# Find the default path to crdentials file
- # Argument Parser
- parser = argparse.ArgumentParser(
- description="A utility for updating DNS records for dynamic IP addresses",
- epilog="Configuration files should be owned by root with an octal permission of 600 (read/write by root only). Run this program as sudo.",
- add_help=True
- )
- parser.add_argument("-i", "--get-record-ids", help="Print out (in JSON format) all records for your Cloudflair account", action="store_true")
- parser.add_argument("-c", "--config", help="Specify configuration file (DEFAULT: '{}')".format(default_fullpath), default=default_fullpath)
- parser.add_argument("-p", "--profile", help="Specify profile in configuration file (DEFAULT: '{}')".format(default_profile), default=default_profile)
- parser.add_argument("-s", "--set", help="Set ip address to ip specified")
- parser.add_argument("-g", "--get", help="Get old (from Cloudflare DNS records) and current ip address", action="store_true")
- arguments = parser.parse_args()
+ # Argument Parser
+ parser = argparse.ArgumentParser(
+ description="A utility for updating DNS records for dynamic IP addresses",
+ epilog="Configuration files should be owned by root with an octal permission of 600 (read/write by root only). Run this program as sudo.",
+ add_help=True
+ )
+ parser.add_argument("-i", "--get-record-ids", help="Print out (in JSON format) all records for your Cloudflair account", action="store_true")
+ parser.add_argument("-c", "--config", help="Specify configuration file (DEFAULT: '{}')".format(default_fullpath), default=default_fullpath)
+ parser.add_argument("-p", "--profile", help="Specify profile in configuration file (DEFAULT: '{}')".format(default_profile), default=default_profile)
+ parser.add_argument("-s", "--set", help="Set ip address to ip specified")
+ parser.add_argument("-g", "--get", help="Get old (from Cloudflare DNS records) and current ip address", action="store_true")
+ arguments = parser.parse_args()
- # Configuration file
- config = configparser.ConfigParser()
- config.read(arguments.config)
+ # Configuration file
+ config = configparser.ConfigParser()
+ config.read(arguments.config)
- options = OrderedDict([
- ("domain_name", config_parser_has_option(config, arguments.profile, "domain_name")),
- ("cf_api_key", config_parser_has_option(config, arguments.profile, "cf_api_key")),
- ("cf_zone_id", config_parser_has_option(config, arguments.profile, "cf_zone_id")),
- ("cf_record_id", config_parser_has_option(config, arguments.profile, "cf_record_id"))
- ])
+ options = OrderedDict([
+ ("domain_name", config_parser_has_option(config, arguments.profile, "domain_name")),
+ ("cf_api_key", config_parser_has_option(config, arguments.profile, "cf_api_key")),
+ ("cf_zone_id", config_parser_has_option(config, arguments.profile, "cf_zone_id")),
+ ("cf_record_id", config_parser_has_option(config, arguments.profile, "cf_record_id"))
+ ])
- # Validate configuration file
- missing_parameter = next((i for i in options if options[i] is None and i != "cf_record_id"), None)
- if missing_parameter is not None:
- logging.error("Configuration: {} | Profile: {} | Option: {}".format(
- arguments.config, arguments.profile, missing_parameter
- ))
- return 1
+ # Validate configuration file
+ missing_parameter = next((i for i in options if options[i] is None and i != "cf_record_id"), None)
+ if missing_parameter is not None:
+ logging.error("Configuration: {} | Profile: {} | Option: {}".format(
+ arguments.config, arguments.profile, missing_parameter
+ ))
+ return 1
- # If cf_zone_id is None, print out all record ids. The user will have to update this setting in their config file
- # This will have to be done at least once
- if arguments.get_record_ids or options["cf_record_id"] is None:
- return int(get_record_id(options["cf_zone_id"], options["cf_api_key"]))
+ # If cf_zone_id is None, print out all record ids. The user will have to update this setting in their config file
+ # This will have to be done at least once
+ if arguments.get_record_ids or options["cf_record_id"] is None:
+ return int(get_record_id(options["cf_zone_id"], options["cf_api_key"]))
- # Get the current dns record from cloudflair (old)
- old_ip = get_old_ip(options["cf_zone_id"], options["cf_record_id"], options["cf_api_key"])
+ # Get the current dns record from cloudflair (old)
+ old_ip = get_old_ip(options["cf_zone_id"], options["cf_record_id"], options["cf_api_key"])
- # Get the current ip address of the server (new)
- new_ip = None
+ # Get the current ip address of the server (new)
+ new_ip = None
- if arguments.set:
- new_ip = arguments.set
- else:
- new_ip = get_new_ip()
-
- if new_ip is None:
- logging.error("Could not determine new IP")
- return 1
+ if arguments.set:
+ new_ip = arguments.set
+ else:
+ new_ip = get_new_ip()
+
+ if new_ip is None:
+ logging.error("Could not determine new IP")
+ return 1
- # Print beginning of status line
- if old_ip is None:
- logging.warn("Could not determine old IP")
-
- # Update cloudflair's dns record if necessary and print out the end of the status line
- if arguments.get:
- logging.info("Current IP address: {}".format(new_ip))
- elif old_ip == new_ip:
- logging.info("New IP matches old IP: {}".format(new_ip))
- else:
- if put_new_ip(options["domain_name"], new_ip, options["cf_zone_id"], options["cf_record_id"], options["cf_api_key"]):
- logging.info("IP updated to: {} from: {}".format(old_ip, new_ip))
- else:
- logging.error("Failed to update to: {} from: {}".format(old_ip, new_ip))
- return 1
- return 0
+ # Print beginning of status line
+ if old_ip is None:
+ logging.warn("Could not determine old IP")
+
+ # Update cloudflair's dns record if necessary and print out the end of the status line
+ if arguments.get:
+ logging.info("Current IP address: {}".format(new_ip))
+ elif old_ip == new_ip:
+ logging.info("New IP matches old IP: {}".format(new_ip))
+ else:
+ if put_new_ip(options["domain_name"], new_ip, options["cf_zone_id"], options["cf_record_id"], options["cf_api_key"]):
+ logging.info("IP updated to: {} from: {}".format(old_ip, new_ip))
+ else:
+ logging.error("Failed to update to: {} from: {}".format(old_ip, new_ip))
+ return 1
+ return 0
# Exit codes:
# 0 - Success
# 1 - An error occurred
if __name__ == "__main__":
- try:
- sys.exit(main())
- except Exception as e:
- print("Exception!!", e)
- sys.exit(1)
+ try:
+ sys.exit(main())
+ except Exception as e:
+ print("Exception!!", e)
+ sys.exit(1)