mirror of
				https://github.com/github/octodns.git
				synced 2024-05-11 05:55:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			117 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#
 | 
						|
#
 | 
						|
#
 | 
						|
 | 
						|
from __future__ import absolute_import, division, print_function, \
 | 
						|
    unicode_literals
 | 
						|
 | 
						|
from os import makedirs, path
 | 
						|
from os.path import isdir
 | 
						|
import logging
 | 
						|
 | 
						|
from .base import BaseProvider
 | 
						|
 | 
						|
 | 
						|
class EtcHostsProvider(BaseProvider):
 | 
						|
    '''
 | 
						|
    Provider that creates a "best effort" static/emergency content that can be
 | 
						|
    used in /etc/hosts to resolve things. A, AAAA records are supported and
 | 
						|
    ALIAS and CNAME records will be included when they can be mapped within the
 | 
						|
    zone.
 | 
						|
 | 
						|
    config:
 | 
						|
        class: octodns.provider.etc_hosts.EtcHostsProvider
 | 
						|
        # The output director for the hosts file <zone>.hosts
 | 
						|
        directory: ./hosts
 | 
						|
    '''
 | 
						|
    SUPPORTS_GEO = False
 | 
						|
    SUPPORTS_DYNAMIC = False
 | 
						|
    SUPPORTS = set(('A', 'AAAA', 'ALIAS', 'CNAME'))
 | 
						|
 | 
						|
    def __init__(self, id, directory, *args, **kwargs):
 | 
						|
        self.log = logging.getLogger('EtcHostsProvider[{}]'.format(id))
 | 
						|
        self.log.debug('__init__: id=%s, directory=%s', id, directory)
 | 
						|
        super(EtcHostsProvider, self).__init__(id, *args, **kwargs)
 | 
						|
        self.directory = directory
 | 
						|
 | 
						|
    def populate(self, zone, target=False, lenient=False):
 | 
						|
        self.log.debug('populate: name=%s, target=%s, lenient=%s', zone.name,
 | 
						|
                       target, lenient)
 | 
						|
 | 
						|
        # We never act as a source, at least for now, if/when we do we still
 | 
						|
        # need to noop `if target`
 | 
						|
        return False
 | 
						|
 | 
						|
    def _apply(self, plan):
 | 
						|
        desired = plan.desired
 | 
						|
        changes = plan.changes
 | 
						|
        self.log.debug('_apply: zone=%s, len(changes)=%d', desired.name,
 | 
						|
                       len(changes))
 | 
						|
        cnames = {}
 | 
						|
        values = {}
 | 
						|
        for record in sorted([c.new for c in changes]):
 | 
						|
            # Since we don't have existing we'll only see creates
 | 
						|
            fqdn = record.fqdn[:-1]
 | 
						|
            if record._type in ('ALIAS', 'CNAME'):
 | 
						|
                # Store cnames so we can try and look them up in a minute
 | 
						|
                cnames[fqdn] = record.value[:-1]
 | 
						|
            elif record._type == 'AAAA' and fqdn in values:
 | 
						|
                # We'll prefer A over AAAA, skipping rather than replacing an
 | 
						|
                # existing A
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                # If we're here it's and A or AAAA and we want to record it's
 | 
						|
                # value (maybe replacing if it's an A and we have a AAAA
 | 
						|
                values[fqdn] = record.values[0]
 | 
						|
 | 
						|
        if not isdir(self.directory):
 | 
						|
            makedirs(self.directory)
 | 
						|
 | 
						|
        filename = '{}hosts'.format(path.join(self.directory, desired.name))
 | 
						|
        self.log.info('_apply: filename=%s', filename)
 | 
						|
        with open(filename, 'w') as fh:
 | 
						|
            fh.write('##################################################\n')
 | 
						|
            fh.write('# octoDNS {} {}\n'.format(self.id, desired.name))
 | 
						|
            fh.write('##################################################\n\n')
 | 
						|
            if values:
 | 
						|
                fh.write('## A & AAAA\n\n')
 | 
						|
                for fqdn, value in sorted(values.items()):
 | 
						|
                    if fqdn[0] == '*':
 | 
						|
                        fh.write('# ')
 | 
						|
                    fh.write('{}\t{}\n\n'.format(value, fqdn))
 | 
						|
 | 
						|
            if cnames:
 | 
						|
                fh.write('\n## CNAME (mapped)\n\n')
 | 
						|
                for fqdn, value in sorted(cnames.items()):
 | 
						|
                    # Print out a comment of the first level
 | 
						|
                    fh.write('# {} -> {}\n'.format(fqdn, value))
 | 
						|
                    seen = set()
 | 
						|
                    while True:
 | 
						|
                        seen.add(value)
 | 
						|
                        try:
 | 
						|
                            value = values[value]
 | 
						|
                            # If we're here we've found the target, print it
 | 
						|
                            # and break the loop
 | 
						|
                            fh.write('{}\t{}\n'.format(value, fqdn))
 | 
						|
                            break
 | 
						|
                        except KeyError:
 | 
						|
                            # Try and step down one level
 | 
						|
                            orig = value
 | 
						|
                            value = cnames.get(value, None)
 | 
						|
                            # Print out this step
 | 
						|
                            if value:
 | 
						|
                                if value in seen:
 | 
						|
                                    # We'd loop here, break it
 | 
						|
                                    fh.write('# {} -> {} **loop**\n'
 | 
						|
                                             .format(orig, value))
 | 
						|
                                    break
 | 
						|
                                else:
 | 
						|
                                    fh.write('# {} -> {}\n'
 | 
						|
                                             .format(orig, value))
 | 
						|
                            else:
 | 
						|
                                # Don't have anywhere else to go
 | 
						|
                                fh.write('# {} -> **unknown**\n'.format(orig))
 | 
						|
                                break
 | 
						|
 | 
						|
                    fh.write('\n')
 |