Skip to content

Instantly share code, notes, and snippets.

@gtmanfred
Last active April 10, 2018 15:54
Show Gist options
  • Save gtmanfred/b2691c1d53a61b374f0a5d4cefa10e65 to your computer and use it in GitHub Desktop.
Save gtmanfred/b2691c1d53a61b374f0a5d4cefa10e65 to your computer and use it in GitHub Desktop.
save ip of server to DO api if dhcp changes it for some reason
#!/usr/bin/env python3
import aiocron
import aiohttp
import asyncio
import fcntl
import json
import logging
import os
import socket
import struct
ENDPOINT = 'https://api.digitalocean.com/v2/'
TOKEN = '<DO token>'
INTERFACE = '<interface with ip>'
NAME = '<name for record>'
DOMAIN = '<domain for record>'
# Public ip or Private ip
# Public ip queries icanhazip.com
# Private ip looks for the first ip address on the INTERFACE
NETWORK_TYPE = '<public/private'
logging.basicConfig(level=logging.INFO)
log = logging.getLogger(__name__)
async def get_private_address():
loop = asyncio.get_event_loop()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fn_ = await loop.run_in_executor(
None,
fcntl.ioctl,
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', INTERFACE[:15].encode())
)
return await loop.run_in_executor(None, socket.inet_ntoa, fn_[20:24])
async def get_public_address():
async with aiohttp.ClientSession() as session:
async with session.get('http://icanhazip.com') as resp:
data = await resp.text()
return data.strip()
get_ip_address = globals()[f'get_{NETWORK_TYPE}_address']
async def api_call(path, payload=None, method='get'):
headers = {'Authorization': 'Bearer {0}'.format(TOKEN),
'Content-Type': 'application/json'}
kwargs = {'url': path}
if method in ('post', 'put'):
kwargs['json'] = payload
async with aiohttp.ClientSession(headers=headers) as session:
async with getattr(session, method)(**kwargs) as response:
response.raise_for_status()
return await response.json()
async def update(record_id):
payload = {'data': await get_ip_address()}
await api_call(os.path.join(ENDPOINT, 'domains', DOMAIN, 'records', f'{record_id}'), payload=payload, method='put')
async def create():
payload = {
"type": "A",
"name": NAME,
"data": await get_ip_address(),
"priority": None,
"port": None,
"weight": None,
}
await api_call(os.path.join(ENDPOINT, 'domains', DOMAIN, 'records'), payload=payload, method='post')
@aiocron.crontab('* * * * *')
async def check():
records = await api_call(os.path.join(ENDPOINT, 'domains', DOMAIN, 'records'))
for record in records['domain_records']:
if record['name'] == NAME:
if record['data'] == await get_ip_address():
log.info('Current Record is correct.')
return
else:
log.info('Updating Record.')
await update(record['id'])
break
else:
log.info('Creating Record.')
await create()
if __name__ == '__main__':
log.info('Starting IO Loop')
asyncio.get_event_loop().run_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment