charm-hacluster/ocf/maas/maas_dns.py
Liam Young 526ffd7587 Create DNS records when using DNS HA
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
2018-04-23 17:26:47 +00:00

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()