
Deficiencies in the older maas API client that is used to manage DNS records for DNS HA prevent DNS records being created. This change creates the DNS records by directly interacting with the REST maas api (v2). If the old approach of pre-creating the DNS records is followed then those are used rather than creating new ones. Change-Id: I6e1d7e5f0a7d813c78dfc1e78743ae7b218fbd01 Closes-Bug: 1764832
188 lines
6.8 KiB
Python
Executable File
188 lines
6.8 KiB
Python
Executable File
#!/usr/bin/python3
|
|
#
|
|
# Copyright 2016 Canonical Ltd
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import argparse
|
|
import requests_oauthlib
|
|
import logging
|
|
import sys
|
|
|
|
import maasclient
|
|
|
|
|
|
class MAASDNS(object):
|
|
def __init__(self, options):
|
|
self.maas = maasclient.MAASClient(options.maas_server,
|
|
options.maas_credentials)
|
|
# String representation of the fqdn
|
|
self.fqdn = options.fqdn
|
|
# Dictionary representation of MAAS dnsresource object
|
|
# TODO: Do this as a property
|
|
self.dnsresource = self.get_dnsresource()
|
|
# String representation of the time to live
|
|
self.ttl = str(options.ttl)
|
|
# String representation of the ip
|
|
self.ip = options.ip_address
|
|
self.maas_server = options.maas_server
|
|
self.maas_creds = options.maas_credentials
|
|
|
|
def get_dnsresource(self):
|
|
""" Get a dnsresource object """
|
|
dnsresources = self.maas.get_dnsresources()
|
|
self.dnsresource = None
|
|
for dnsresource in dnsresources:
|
|
if dnsresource['fqdn'] == self.fqdn:
|
|
self.dnsresource = dnsresource
|
|
return self.dnsresource
|
|
|
|
def get_dnsresource_id(self):
|
|
""" Get a dnsresource ID """
|
|
return self.dnsresource['id']
|
|
|
|
def update_resource(self):
|
|
""" Update a dnsresource record with an IP """
|
|
return self.maas.update_dnsresource(self.dnsresource['id'],
|
|
self.dnsresource['fqdn'],
|
|
self.ip)
|
|
|
|
def create_dnsresource(self):
|
|
""" Create a DNS resource object
|
|
Due to https://bugs.launchpad.net/maas/+bug/1555393
|
|
this is implemented outside of the maas lib.
|
|
"""
|
|
dns_url = '{}/api/2.0/dnsresources/?format=json'.format(
|
|
self.maas_server)
|
|
(consumer_key, access_token, token_secret) = self.maas_creds.split(':')
|
|
|
|
# The use of PLAINTEXT signature is inline with libmaas
|
|
# https://goo.gl/EJPrM7 but as noted there should be switched
|
|
# to HMAC once it works server-side.
|
|
maas_session = requests_oauthlib.OAuth1Session(
|
|
consumer_key,
|
|
signature_method='PLAINTEXT',
|
|
resource_owner_key=access_token,
|
|
resource_owner_secret=token_secret)
|
|
fqdn_list = self.fqdn.split('.')
|
|
payload = {
|
|
'fqdn': self.fqdn,
|
|
'name': fqdn_list[0],
|
|
'domain': '.'.join(fqdn_list[1:]),
|
|
'address_ttl': self.ttl,
|
|
'ip_addresses': self.ip,
|
|
}
|
|
return maas_session.post(dns_url, data=payload)
|
|
|
|
|
|
class MAASIP(object):
|
|
def __init__(self, options):
|
|
self.maas = maasclient.MAASClient(options.maas_server,
|
|
options.maas_credentials)
|
|
# String representation of the IP
|
|
self.ip = options.ip_address
|
|
# Dictionary representation of MAAS ipaddresss object
|
|
# TODO: Do this as a property
|
|
self.ipaddress = self.get_ipaddress()
|
|
|
|
def get_ipaddress(self):
|
|
""" Get an ipaddresses object """
|
|
ipaddresses = self.maas.get_ipaddresses()
|
|
self.ipaddress = None
|
|
for ipaddress in ipaddresses:
|
|
if ipaddress['ip'] == self.ip:
|
|
self.ipaddress = ipaddress
|
|
return self.ipaddress
|
|
|
|
def create_ipaddress(self, hostname=None):
|
|
""" Create an ipaddresses object
|
|
Due to https://bugs.launchpad.net/maas/+bug/1555393
|
|
This is currently unused
|
|
"""
|
|
return self.maas.create_ipaddress(self.ip, hostname)
|
|
|
|
|
|
def setup_logging(logfile, log_level='INFO'):
|
|
logFormatter = logging.Formatter(
|
|
fmt="%(asctime)s [%(levelname)s] %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S")
|
|
rootLogger = logging.getLogger()
|
|
rootLogger.setLevel(log_level)
|
|
|
|
consoleHandler = logging.StreamHandler()
|
|
consoleHandler.setFormatter(logFormatter)
|
|
rootLogger.addHandler(consoleHandler)
|
|
|
|
try:
|
|
fileLogger = logging.getLogger('file')
|
|
fileLogger.propagate = False
|
|
|
|
fileHandler = logging.FileHandler(logfile)
|
|
fileHandler.setFormatter(logFormatter)
|
|
rootLogger.addHandler(fileHandler)
|
|
fileLogger.addHandler(fileHandler)
|
|
except IOError:
|
|
logging.error('Unable to write to logfile: {}'.format(logfile))
|
|
|
|
|
|
def dns_ha():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--maas_server', '-s',
|
|
help='URL to mangage the MAAS server',
|
|
required=True)
|
|
parser.add_argument('--maas_credentials', '-c',
|
|
help='MAAS OAUTH credentials',
|
|
required=True)
|
|
parser.add_argument('--fqdn', '-d',
|
|
help='Fully Qualified Domain Name',
|
|
required=True)
|
|
parser.add_argument('--ip_address', '-i',
|
|
help='IP Address, target of the A record',
|
|
required=True)
|
|
parser.add_argument('--ttl', '-t',
|
|
help='DNS Time To Live in seconds',
|
|
default='')
|
|
parser.add_argument('--logfile', '-l',
|
|
help='Path to logfile',
|
|
default='/var/log/{}.log'
|
|
''.format(sys.argv[0]
|
|
.split('/')[-1]
|
|
.split('.')[0]))
|
|
options = parser.parse_args()
|
|
|
|
setup_logging(options.logfile)
|
|
logging.info("Starting maas_dns")
|
|
|
|
dns_obj = MAASDNS(options)
|
|
if not dns_obj.dnsresource:
|
|
dns_obj.create_dnsresource()
|
|
elif dns_obj.dnsresource.get('ip_addresses'):
|
|
# TODO: Handle multiple IPs returned for ip_addresses
|
|
for ip in dns_obj.dnsresource['ip_addresses']:
|
|
if ip.get('ip') != options.ip_address:
|
|
logging.info('Update the dnsresource with IP: {}'
|
|
''.format(options.ip_address))
|
|
dns_obj.update_resource()
|
|
else:
|
|
logging.info('IP is the SAME {}, no update required'
|
|
''.format(options.ip_address))
|
|
else:
|
|
logging.info('Update the dnsresource with IP: {}'
|
|
''.format(options.ip_address))
|
|
dns_obj.update_resource()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
dns_ha()
|