"""Nagios Inventory
    A sphinx directive, that is meant to be used in the index of the domains dir.
    It pulls a lists of hosts and their host_groups(roles) from nagios. Using this info and any
    manual docs about the host it builds pages for each host. It then add links to those hosts
    back into the doc it was called from.
"""

import docutils
from glob import glob
import os
import string

import sphinx

from pynag import Model
Model.cfg_file = '/etc/nagios3/nagios.cfg'

class RGDomains(docutils.parsers.rst.Directive):
    """For each domain specified with this directive return a RGDomain object."""
    has_content = True
    required_arguments = 0
    optional_arguments = 0
    final_argument_whitespace = False
    option_spec = {}

    def run(self):
        #Putting these in __init__ is complicated because the base class has 10 arguments to its init
        self.env = self.state.document.settings.env
        self.outdir = os.path.join(os.path.dirname(self.env.doctreedir), 'autogenerated')
        self.link_base = '/doc/autogenerated'
        self.template_dir = os.path.join(self.env.srcdir, self.env.config.templates_path[0])
        self.roles_dir = os.path.join(self.template_dir, 'roles')
        self.domains_dir = os.path.join(self.template_dir, 'domains')

        ret = []

        for domain in self.content:
            #Create a new domain node and corresponding title
            subsection = docutils.nodes.section(ids=[domain, ])
            titlenode = docutils.nodes.title(domain, domain)
            name = docutils.nodes.fully_normalize_name(titlenode.astext())
            subsection['names'].append(name)
            subsection += titlenode
            domain_outdir = os.path.join(self.outdir, domain)
            if not os.path.exists(domain_outdir):
                os.makedirs(domain_outdir)
            #Setup the host nodes
            self.create_host_nodes(domain, subsection)
            #Add the subsection to the directive output
            ret.append(subsection)

        return ret

    def create_host_nodes(self, domain, parent):
        """Finds the hosts in nagios and the filesystem then creates files and nodes for them.
        """
        fs_hosts = self.find_fs_hosts(domain)
        nag_hosts = self.find_nagios_hosts(domain)

        #Run through nagios list creating pages
        rendered = []
        for host in nag_hosts.iterkeys():
            if fs_hosts.has_key(host): #In both lists combine rendered page
                self.write_page(host, nag_hosts[host] + fs_hosts[host])
                #Remove from the fs_hosts dictionary
                del fs_hosts[host]
            else: #Only in nagios render page
                self.write_page(host, nag_hosts[host])
            rendered.append(host)

        #Process any hosts only on the filesystem, (duplicates have been removed)
        for host in fs_hosts.iterkeys(): 
            self.write_page(host, fs_hosts[host])
            rendered.append(host)

        #Create a list_node to hold them all and attach parent
        list_node = docutils.nodes.bullet_list()
        parent += list_node
        #Create links on the page where this directive is found.
        rendered.sort()
        for host in rendered:
            self.link_host(host, list_node)

    def find_fs_hosts(self, domain):
        """Get hosts from the filesystem, only looks in the dir for self.domain.
            { fqdn:[hostgroup, ] }
        """
        hosts = {}
        domain_dir = os.path.join(self.domains_dir, domain)
        for path in glob(domain_dir + '/*.rst'):
            hosts[os.path.basename(path).split('.')[0] + '.' + domain] = [] #No roles known for fs based hosts
        return hosts

    def find_nagios_hosts(self, domain):
        """Get hosts from nagios. { fqdn:[hostgroup, ] }
        """
        hosts = {}
        nag_hosts = Model.Host.objects.all
        nag_hosts.sort(key=lambda host: host.host_name)
        for host in nag_hosts:
            #Drop the template hosts
            if host.register != '1':
                continue
            #skip other domains
            split = host.host_name.split('.', 1)
            if len(split) < 2 or domain != split[1]:
                continue
            hostgroups = []
            for hostgroup in host.hostgroups.split(','): #seperate and strip whitespace and any +
                hostgroups.append(hostgroup.strip().strip('+'))

            hosts[host.host_name] = hostgroups

        return hosts

    def link_host(self, fqdn, parent):
        """Creates the list_item node and a link to it the host.
        """
        #create list_item and paragraph
        list_item = docutils.nodes.list_item()
        parent += list_item
        paragraph_node = docutils.nodes.paragraph()
        list_item += paragraph_node

        #Create the link and add it to the paragraph
        hostname, domain = fqdn.split('.', 1)
        target = '%s/%s/%s.html' % (self.link_base, domain, hostname)
        link = docutils.nodes.reference(fqdn, fqdn, internal=False, refuri=target)
        paragraph_node += link

    def write_page(self, fqdn, roles):
        """Writes out a generated file to the filesystem for a host.
            Combines the info in a manual file with all info from any role templates.
        """
        hostname, domain = fqdn.split('.', 1)
        html_file = os.path.join(self.outdir, domain, hostname + '.html')
        #html body of the file, utilizes
        base_path = os.path.join(self.domains_dir, domain, hostname + '.rst')
        if os.path.exists(base_path):
            base_file = open(base_path, 'r')
            body = docutils.core.publish_string(source=base_file.read(), writer_name='html')
            base_file.close()
        else:
            body = "<h1>%s</h1><br>" % fqdn

        for role in roles:
            role_path = os.path.join(self.roles_dir, role + '.rst')
            if os.path.exists(role_path):
                role_file = open(role_path, 'r')
                role_template = string.Template(role_file.read())
                role_file.close()
                role_body = role_template.safe_substitute(fqdn=fqdn, hostname=hostname, domain=domain, \
                    gfqdn=fqdn.replace('.', '_')) #gfqdn is for graphite, time for better templates?
                body += docutils.core.publish_string(source=role_body, writer_name='html')
            else:
                body += '<h1 class="title"><a href="/doc/roles/%s.html">%s role</a></h1><br>' % (role, role)

        html_builder = sphinx.builders.html.StandaloneHTMLBuilder(self.env.app)
        html_builder.prepare_writing([fqdn])
        relative_page = '%s/%s/%s.html' % (self.link_base.split('/')[-1], domain, hostname)
        html_builder.handle_page(relative_page, {'body': body}, outfilename=html_file)

def setup(app):
    """This function is called by Sphinx to integrate into its run."""
    app.add_directive('rg_domains', RGDomains)
