Works with digital ocean api. Need to work out the env stuff
This commit is contained in:
parent
31566bf459
commit
2d7414f02b
98
bifrost/digital_ocean.py
Normal file
98
bifrost/digital_ocean.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
def create_domain_a_record(domain, name, ip_address, api_url_base, api_token, logger):
|
||||||
|
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {0}'.format(api_token)}
|
||||||
|
api_url = f"{api_url_base}/domains/{domain}/records"
|
||||||
|
|
||||||
|
data = {"type": "A", "name": name, "data": ip_address, "priority": None, "port": None, "ttl": 1800, "weight": None,
|
||||||
|
"flags": None, "tag": None}
|
||||||
|
response = requests.post(api_url, headers=headers, json=data)
|
||||||
|
|
||||||
|
if response.status_code > 499:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Server Error")
|
||||||
|
return None
|
||||||
|
elif response.status_code == 404:
|
||||||
|
logger.error(f"[!] [{response.status_code}] URL not found: [{api_url}]")
|
||||||
|
return None
|
||||||
|
elif response.status_code == 401:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Authentication Failed")
|
||||||
|
return None
|
||||||
|
elif response.status_code > 399:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Bad Request")
|
||||||
|
logger.error(f"Domain: {domain}, Name: {name}, New IP Address: {ip_address}")
|
||||||
|
logger.error(response.content)
|
||||||
|
return None
|
||||||
|
elif response.status_code > 299:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Unexpected redirect")
|
||||||
|
return None
|
||||||
|
elif response.status_code == 201:
|
||||||
|
logger.info(f'{domain} updated to {ip_address}')
|
||||||
|
return json.loads(response.content)
|
||||||
|
else:
|
||||||
|
logger.error(f"[?] Unexpected Error: [HTTP {response.status_code}]: Content: {response.content}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def update_domain_a_record(domain, record_id, new_ip_address, api_url_base, api_token, logger):
|
||||||
|
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {0}'.format(api_token)}
|
||||||
|
api_url = f"{api_url_base}/domains/{domain}/records/{record_id}"
|
||||||
|
|
||||||
|
data = {"data": new_ip_address}
|
||||||
|
response = requests.put(api_url, headers=headers, json=data)
|
||||||
|
|
||||||
|
if response.status_code > 499:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Server Error")
|
||||||
|
return None
|
||||||
|
elif response.status_code == 404:
|
||||||
|
logger.error(f"[!] [{response.status_code}] URL not found: [{api_url}]")
|
||||||
|
return None
|
||||||
|
elif response.status_code == 401:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Authentication Failed")
|
||||||
|
return None
|
||||||
|
elif response.status_code > 399:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Bad Request")
|
||||||
|
logger.error(f"Domain: {domain}, Record ID: {record_id}, New IP Address: {new_ip_address}")
|
||||||
|
logger.error(response.content)
|
||||||
|
return None
|
||||||
|
elif response.status_code > 299:
|
||||||
|
logger.error(f"[!] [{response.status_code}] Unexpected redirect")
|
||||||
|
return None
|
||||||
|
elif response.status_code == 201:
|
||||||
|
logger.info(f'{domain} updated to {new_ip_address}')
|
||||||
|
return json.loads(response.content)
|
||||||
|
else:
|
||||||
|
logger.error(f"[?] Unexpected Error: [HTTP {response.status_code}]: Content: {response.content}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def list_all_domain_records(domain, api_url_base, api_token, logger):
|
||||||
|
headers = {'Content-Type': 'application/json', 'Authorization': 'Bearer {0}'.format(api_token)}
|
||||||
|
api_url = f"{api_url_base}/domains/{domain}/records"
|
||||||
|
response = requests.get(api_url, headers=headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
return json.loads(response.content.decode('utf-8'))["domain_records"]
|
||||||
|
else:
|
||||||
|
logger.error(f"[?] Unexpected Error: [HTTP {response.status_code}]: Content: {response.content}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_domain_a_record(domain, name, api_url_base, api_token, logger):
|
||||||
|
for item in list_all_domain_records(domain, api_url_base, api_token, logger):
|
||||||
|
if item["name"] == name and item["type"] == "A":
|
||||||
|
return item
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def update_domain(domain, name, new_ip_address, api_url_base, api_token, logger):
|
||||||
|
record = get_domain_a_record(domain, name, api_url_base, api_token, logger)
|
||||||
|
if record is None:
|
||||||
|
logger.info(f'Creating domain a record for {domain} with ip address {new_ip_address}')
|
||||||
|
create_domain_a_record(domain, name, new_ip_address, api_url_base, api_token, logger)
|
||||||
|
elif record["data"] != new_ip_address:
|
||||||
|
logger.info(f'Updating domain a record for {domain}. Old ip address {record["data"]}, new ip address {new_ip_address}')
|
||||||
|
update_domain_a_record(domain, record["id"], new_ip_address, api_url_base, api_token, logger)
|
||||||
|
else:
|
||||||
|
logger.info(f'Not updating domain a record for {domain}. IP address {record["data"]} is current')
|
@ -1,20 +1,17 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import xmlrpc.client
|
|
||||||
|
|
||||||
from pyramid.httpexceptions import HTTPUnauthorized
|
from pyramid.httpexceptions import HTTPUnauthorized
|
||||||
from pyramid.security import forget, Authenticated
|
from pyramid.security import forget, Authenticated
|
||||||
from pyramid.view import view_config, forbidden_view_config
|
from pyramid.view import view_config, forbidden_view_config
|
||||||
|
from bifrost.digital_ocean import update_domain
|
||||||
|
|
||||||
log = logging.getLogger('bifrost.updater')
|
log = logging.getLogger('bifrost.updater')
|
||||||
|
api_url_base = 'https://api.digitalocean.com/v2'
|
||||||
|
api_token = None
|
||||||
|
|
||||||
|
|
||||||
@view_config(route_name='update', request_param='domain', renderer='json', permission=Authenticated)
|
@view_config(route_name='update', request_param='domain', renderer='json', permission=Authenticated)
|
||||||
def update_view(request):
|
def update_view(request):
|
||||||
file = request.registry.settings['biforst.file']
|
|
||||||
username = request.registry.settings['webfaction.username']
|
|
||||||
password = request.registry.settings['webfaction.password']
|
|
||||||
current_ip = request.GET.get('ip', None)
|
current_ip = request.GET.get('ip', None)
|
||||||
if current_ip is None:
|
if current_ip is None:
|
||||||
if 'X-Forwarded-For' in request.headers:
|
if 'X-Forwarded-For' in request.headers:
|
||||||
@ -22,43 +19,11 @@ def update_view(request):
|
|||||||
else:
|
else:
|
||||||
current_ip = request.remote_addr
|
current_ip = request.remote_addr
|
||||||
|
|
||||||
domain = request.GET['domain']
|
name, domain = request.GET['domain'].split('.', maxsplit=1)
|
||||||
db = load(file)
|
update_domain(domain, name, current_ip, api_url_base, api_token, log)
|
||||||
|
|
||||||
if domain in db and db[domain] != current_ip:
|
|
||||||
server = xmlrpc.client.ServerProxy('https://api.webfaction.com/')
|
|
||||||
session_id, account = server.login(username, password)
|
|
||||||
server.delete_dns_override(session_id, domain)
|
|
||||||
server.create_dns_override(session_id, domain, current_ip, '', '', '', '')
|
|
||||||
db[domain] = current_ip
|
|
||||||
update(file, db)
|
|
||||||
|
|
||||||
log.info('{0} updated to {1}'.format(domain, current_ip))
|
|
||||||
else:
|
|
||||||
log.info('{0} not updated'.format(domain))
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def load(file):
|
|
||||||
exp = re.compile(r"^([a-z1-9.]+):([0-9]{1,3}(?:\.[0-9]{1,3}){3})$", re.I)
|
|
||||||
if not os.path.isfile(file):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
current = {}
|
|
||||||
with open(file) as f:
|
|
||||||
for line in f:
|
|
||||||
match = exp.match(line)
|
|
||||||
if match:
|
|
||||||
domain, ip = match.groups()
|
|
||||||
current[domain] = ip
|
|
||||||
return current
|
|
||||||
|
|
||||||
|
|
||||||
def update(file, db):
|
|
||||||
with open(file, 'w') as f:
|
|
||||||
f.writelines(['{0}:{1}\n'.format(key, value) for key, value in db.items()])
|
|
||||||
|
|
||||||
|
|
||||||
@forbidden_view_config()
|
@forbidden_view_config()
|
||||||
def basic_challenge(request):
|
def basic_challenge(request):
|
||||||
response = HTTPUnauthorized()
|
response = HTTPUnauthorized()
|
||||||
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pyramid
|
||||||
|
waitress
|
||||||
|
requests
|
9
setup.py
9
setup.py
@ -9,10 +9,11 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
|
|||||||
requires = [
|
requires = [
|
||||||
'pyramid',
|
'pyramid',
|
||||||
'waitress',
|
'waitress',
|
||||||
|
'requests'
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(name='bifrost',
|
setup(name='bifrost',
|
||||||
version='0.0',
|
version='1.0',
|
||||||
description='bifrost',
|
description='bifrost',
|
||||||
long_description=README + '\n\n' + CHANGES,
|
long_description=README + '\n\n' + CHANGES,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
@ -21,9 +22,9 @@ setup(name='bifrost',
|
|||||||
"Topic :: Internet :: WWW/HTTP",
|
"Topic :: Internet :: WWW/HTTP",
|
||||||
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
||||||
],
|
],
|
||||||
author='',
|
author='tanshu',
|
||||||
author_email='',
|
author_email='programming@tanshu.com',
|
||||||
url='',
|
url='https://git.tanshu.com/tanshu',
|
||||||
keywords='web pyramid pylons',
|
keywords='web pyramid pylons',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
Loading…
Reference in New Issue
Block a user