## -*- coding: utf-8 -*- <%! from jsonxs import jsonxs %> <%! import datetime %> <%! import socket %> <%! import getpass %> <%! from ansiblecmdb.util import to_bool %> ## ## Column definitions ## <%def name="var_cols(cols_visible=None, cols_exclude=None)"> <% cols = [ {"title": "Name", "id": "name", "func": col_name, "sType": "string", "visible": True}, {"title": "Groups", "id": "groups", "func": col_groups, "sType": "string", "visible": False}, {"title": "DTAP", "id": "dtap", "func": col_dtap, "sType": "string", "visible": False}, {"title": "Comment", "id": "comment", "func": col_comment, "sType": "string", "visible": False}, {"title": "Ext ID", "id": "ext_id", "func": col_ext_id, "sType": "string", "visible": False}, {"title": "FQDN", "id": "fqdn", "func": col_fqdn, "sType": "string", "visible": True}, {"title": "Main IP", "id": "main_ip", "func": col_main_ip, "sType": "string", "visible": True}, {"title": "All IPv4", "id": "all_ipv4", "func": col_all_ip4, "sType": "string", "visible": False}, {"title": "All IPv6", "id": "all_ipv6", "func": col_all_ip6, "sType": "string", "visible": False}, {"title": "OS", "id": "os", "func": col_os, "sType": "string", "visible": True}, {"title": "Kernel", "id": "kernel", "func": col_kernel, "sType": "string", "visible": False}, {"title": "Arch", "id": "arch", "func": col_arch, "sType": "string", "visible": False}, {"title": "Virt", "id": "virt", "func": col_virt, "sType": "string", "visible": True}, {"title": "CPU type", "id": "cpu_type", "func": col_cpu_type, "sType": "string", "visible": False}, {"title": "vCPUs", "id": "vcpus", "func": col_vcpus, "sType": "num", "visible": True}, {"title": "RAM [GiB]", "id": "ram", "func": col_ram, "sType": "num", "visible": True}, {"title": "Mem Usage", "id": "mem_usage", "func": col_mem_usage, "sType": "string", "visible": False}, {"title": "Swap Usage", "id": "swap_usage", "func": col_swap_usage, "sType": "string", "visible": False}, {"title": "Disk usage", "id": "disk_usage", "func": col_disk_usage, "sType": "string", "visible": False}, {"title": "PhysDisk size", "id": "physdisk_size", "func": col_physdisk_sizes, "sType": "string", "visible": False}, {"title": "Nr of Ifaces", "id": "nr_of_ifaces", "func": col_nr_of_ifaces, "sType": "num", "visible": False}, {"title": "Timestamp", "id": "timestamp", "func": col_gathered, "sType": "string", "visible": False}, {"title": "Product Name", "id": "prodname", "func": col_prodname, "sType": "string", "visible": False}, {"title": "Product Serial","id": "prodserial", "func": col_prodserial, "sType": "string", "visible": False}, ] # Enable columns specified with '--columns' if cols_visible is not None: for col in cols: if col["id"] in cols_visible: col["visible"] = True else: col["visible"] = False # Remove columns that should be excluded if cols_exclude is not None: cols = filter(lambda col: col["id"] not in cols_exclude, cols) return cols %> ## ## Helper functions for dumping python datastructures ## <%def name="r_list(l)"> % for i in l: % if type(i) == list: ${r_list(i)} % elif type(i) == dict: ${r_dict(i)} % else: ${i} % endif % endfor <%def name="r_dict(d)"> % for k, v in d.items(): % endfor
${k.replace('ansible_', '')} % if type(v) == list: ${r_list(v)} % elif type(v) == dict: ${r_dict(v)} % else: ${v} % endif
## ## HTML fragments ## <%def name="html_header(title, local_js, res_url)"> ${title} % if local_js is False: % else: % endif <%def name="html_header_bar(title)">

${title}

Back to top Clear settings Generated on ${datetime.datetime.now().strftime('%c')} by ${getpass.getuser()} @ ${socket.getfqdn()}
<%def name="html_footer_bar(version)"> <%def name="html_footer()"> <%def name="html_col_toggles(cols)">

Shown columns

% for col in cols: <% visible = "visible" if col['visible'] is False: visible = "invisible" %> ${col['title']} % endfor
<%def name="html_host_overview(cols, hosts, skip_empty=False, **kwargs)">

Host overview

% for col in cols: % endfor % for hostname, host in hosts.items(): <% log.debug(u"Rendering host overview for {0}".format(hostname)) %> % if skip_empty is False or 'ansible_facts' in host: \ % if 'ansible_facts' not in host: \ % for cnt in range(len(cols) - 1): \ % endfor % else: % for col in cols: \ % endfor % endif % endif % endfor
${col['title']}
${col_name(host)} ${col["func"](host, **kwargs)}
<%def name="html_host_details(hosts, collapsed=False, skip_empty=False)">
% for hostname, host in hosts.items(): <% log.debug(u"Rendering host details for {0}".format(hostname)) %> <% html_host_detail(host, collapsed=collapsed, skip_empty=skip_empty) %> % endfor
<%def name="html_host_detail(host, collapsed=False, skip_empty=False)"> <% collapsed_class = "uncollapsed" collapse_toggle_text = "Close all" if collapsed is True: collapsed_class = "collapsed" collapse_toggle_text = "Open all" %> % if skip_empty is False or 'ansible_facts' in host:

${host['name']}

${collapse_toggle_text} % if 'ansible_facts' not in host:

No host information collected

% if 'msg' in host:

${host['msg']}

% endif <% host_groups(host, collapsed_class) %> <% host_custvars(host, collapsed_class) %> % else: <% host_general(host, collapsed_class) %> <% host_groups(host, collapsed_class) %> <% host_custvars(host, collapsed_class) %> <% host_localfacts(host, collapsed_class) %> <% host_factorfacts(host, collapsed_class) %> <% host_customfacts(host, collapsed_class) %> <% host_hardware(host, collapsed_class) %> <% host_os(host, collapsed_class) %> <% host_network(host, collapsed_class) %> <% host_storage(host, collapsed_class) %> % endif
% endif ## ## Javascript fragements ## <%def name="js_init_host_overview(cols)"> function getQueryParams(qs) { qs = qs.split('+').join(' '); var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g; while (tokens = re.exec(qs)) { params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]); } return params; } // Get persisted column visibility from localStorage. var columnVisibility = localStorage.getItem("columnVisibility"); if (columnVisibility == null) { columnVisibility = { % for col in cols: "${col["id"]}": ${str(col["visible"]).lower()}, % endfor }; localStorage.setItem("columnVisibility", JSON.stringify(columnVisibility)); } else { columnVisibility = JSON.parse(columnVisibility); } // Initialize the DataTables jQuery plugin on the host overview table var table = $('#host_overview_tbl').DataTable({ paging: false, columnDefs: [ % for col in cols: { "targets": [${loop.index}], "visible": ${str(col['visible']).lower()}, "sType": "${col['sType']}" }, % endfor ], "fnInitComplete": function() { // Focus the input field $("#host_overview_tbl_filter input").focus(); // Set the search box value to the query string 'search' part var qp = getQueryParams(document.location.search); if ("search" in qp) { $("#host_overview_tbl_filter input").val(qp.search); this.fnFilter(qp.search); } } }); // Display or hide columns based on localStorage preferences. for (var columnId in columnVisibility) { var columnButton = $("a[data-column-id='" + columnId +"']"); var columnNr = columnButton.attr('data-column'); var column = table.column(columnNr); column.visible(columnVisibility[columnId]); var newClass = ['col-invisible','col-visible'][Number(column.visible())]; columnButton.get(0).className = 'col-toggle ' + newClass; } // Show a direct link to the search term table.on( 'search.dt', function () { $('#filter_link').remove(); if (table.search() == "") { } else { $('#host_overview_tbl_filter label').after('   '); } } ); // Show and hide columns on button clicks $('a.col-toggle').on('click', function(e) { e.preventDefault(); var columnId = $(this).attr('data-column-id') var column = table.column( $(this).attr('data-column') ); column.visible( ! column.visible() ); var newClass = ['col-invisible','col-visible'][Number(column.visible())]; e.target.className = 'col-toggle ' + newClass; // Storage column visibility in localStorage. columnVisibility[columnId] = column.visible(); localStorage.setItem("columnVisibility", JSON.stringify(columnVisibility)); }); // Open the Detailed host information when jumping to a host. $('#host_overview td a').on('click', function(e) { var hostId=$(this).attr('href').substr(1); var hostElem = $("h3[data-host-name='"+hostId+"']"); hostElem.addClass('uncollapsed'); hostElem.removeClass('collapsed'); hostElem.next().removeClass('collapsed'); }); <%def name="js_ev_collapse()"> // Open the detailed host information when clicking on the hosts header $('.toggle-collapse').on('click', function(e) { $(this).toggleClass('collapsed'); $(this).toggleClass('uncollapsed'); $(this).next().toggleClass('collapsed'); }); // Toggle opening and closing all information for a host. $('a.toggle-all').on('click', function(e) { e.preventDefault(); if ($(this).text() == "Open all") { $(this).siblings('.collapsed').each(function(item) { $(this).addClass('uncollapsed'); $(this).removeClass('collapsed'); $(this).next().toggleClass('collapsed'); }); $(this).text("Close all"); } else { $(this).text("Open all"); $(this).siblings('.uncollapsed').each(function(item) { $(this).addClass('collapsed'); $(this).removeClass('uncollapsed'); $(this).next().toggleClass('collapsed'); }); } }); ## ## Column functions ## ## Each column function takes variable kwargs. These are not properly unpacked ## in the function scope in Mako for some reason, so if we need to use them, we ## need to do a `kwargs.get()`. I don't like Mako much. <%def name="col_name(host, **kwargs)"> <% link_type = kwargs.get("link_type") %> % if link_type == "anchor": ${jsonxs(host, "name")} % elif link_type == "external": ${jsonxs(host, 'name')} % else: ${jsonxs(host, "name")} % endif <%def name="col_dtap(host, **kwargs)"> ${jsonxs(host, 'hostvars.dtap', default='')} <%def name="col_groups(host, **kwargs)"> ${'
'.join(jsonxs(host, 'groups', default=''))} <%def name="col_fqdn(host, **kwargs)"> ${jsonxs(host, 'ansible_facts.ansible_fqdn', default='')} <%def name="col_main_ip(host, **kwargs)"> <% default_ipv4 = '' if jsonxs(host, 'ansible_facts.ansible_os_family', default='') == 'Windows': ipv4_addresses = [ip for ip in jsonxs(host, 'ansible_facts.ansible_ip_addresses', default=[]) if ':' not in ip] if ipv4_addresses: default_ipv4 = ipv4_addresses[0] else: default_ipv4 = jsonxs(host, 'ansible_facts.ansible_default_ipv4.address', default={}) %> ${default_ipv4} <%def name="col_all_ip4(host, **kwargs)"> <% if jsonxs(host, 'ansible_facts.ansible_os_family', default='') == 'Windows': ipv4_addresses = [ip for ip in jsonxs(host, 'ansible_facts.ansible_ip_addresses', default=[]) if ':' not in ip] else: ipv4_addresses = jsonxs(host, 'ansible_facts.ansible_all_ipv4_addresses', default=[]) %> ${'
'.join(ipv4_addresses)} <%def name="col_all_ip6(host, **kwargs)"> ${'
'.join(jsonxs(host, 'ansible_facts.ansible_all_ipv6_addresses', default=[]))} <%def name="col_os(host, **kwargs)"> % if jsonxs(host, 'ansible_facts.ansible_distribution', default='') in ["OpenBSD"]: ${jsonxs(host, 'ansible_facts.ansible_distribution', default='')} ${jsonxs(host, 'ansible_facts.ansible_distribution_release', default='')} % else: ${jsonxs(host, 'ansible_facts.ansible_distribution', default='')} ${jsonxs(host, 'ansible_facts.ansible_distribution_version', default='')} % endif <%def name="col_kernel(host, **kwargs)"> ${jsonxs(host, 'ansible_facts.ansible_kernel', default='')} <%def name="col_arch(host, **kwargs)"> ${jsonxs(host, 'ansible_facts.ansible_architecture', default='')} / ${jsonxs(host, 'ansible_facts.ansible_userspace_architecture', default='')} <%def name="col_virt(host, **kwargs)"> ${jsonxs(host, 'ansible_facts.ansible_virtualization_type', default='?')} / ${jsonxs(host, 'ansible_facts.ansible_virtualization_role', default='?')} <%def name="col_cpu_type(host, **kwargs)"> <% cpu_type = jsonxs(host, 'ansible_facts.ansible_processor', default=0)%> % if isinstance(cpu_type, list) and len(cpu_type) > 0: ${ cpu_type[-1] } % endif <%def name="col_vcpus(host, **kwargs)"> % if jsonxs(host, 'ansible_facts.ansible_distribution', default='') in ["OpenBSD"]: 0 % else: ${jsonxs(host, 'ansible_facts.ansible_processor_vcpus', default=jsonxs(host, 'ansible_facts.ansible_processor_cores', default=0))} % endif <%def name="col_ram(host, **kwargs)"> ${'%0.1f' % ((int(jsonxs(host, 'ansible_facts.ansible_memtotal_mb', default=0)) / 1024.0))} <%def name="col_mem_usage(host, **kwargs)"> % try: <% i = jsonxs(host, 'ansible_facts.ansible_memory_mb', default=0) sort_used = '%f' % (float(jsonxs(i, "nocache.used", default=0)) / jsonxs(i, "real.total", default=0)) used = float(i["nocache"]["used"]) / i["real"]["total"] * 100 detail_used = round(jsonxs(i, "nocache.used", default=0) / 1024.0, 1) detail_total = round(jsonxs(i, "real.total", default=0) / 1024.0, 1) %>
## hidden sort helper ${sort_used} (${detail_used} / ${detail_total} GiB)
% except: n/a % endtry <%def name="col_swap_usage(host, **kwargs)"> % try: <% i = jsonxs(host, 'ansible_facts.ansible_memory_mb', default=0) sort_used = '%f' % (float(jsonxs(i, "swap.used", default=0)) / jsonxs(i, "swap.total", default=0)) used = float(jsonxs(i, "swap.used", default=0)) / jsonxs(i, "swap.total", default=0) * 100 detail_used = round((jsonxs(i, "swap.used", default=0)) / 1024.0, 1) detail_total = round(jsonxs(i, "swap.total", default=0) / 1024.0, 1) %>
## hidden sort helper ${sort_used} (${detail_used} / ${detail_total} GiB)
% except: n/a % endtry <%def name="col_disk_usage(host, **kwargs)"> % for i in jsonxs(host, 'ansible_facts.ansible_mounts', default=[]): % try: <% try: sort_used = '%f' % (float((i["size_total"] - i["size_available"])) / i["size_total"]) used = float((i["size_total"] - i["size_available"])) / i["size_total"] * 100 detail_used = round((i['size_total'] - i['size_available']) / 1073741824.0, 1) detail_total = round(i['size_total'] / 1073741824.0, 1) except ZeroDivisionError: sort_used = '0' used = 0 detail_used = 0 detail_total = 0 %> ## hidden sort helper ${sort_used}
${i['mount']} (${detail_used} / ${detail_total} GiB)
% except: n/a <% break ## Stop listing disks, since there was an error. %> % endtry % endfor <%def name="col_physdisk_sizes(host, **kwargs)"> % try: % for physdisk_name, physdisk_info in jsonxs(host, 'ansible_facts.ansible_devices', default={}).items(): ${physdisk_name}: ${jsonxs(physdisk_info, 'size', default='')}
% endfor % except AttributeError: % endtry <%def name="col_nr_of_ifaces(host, **kwargs)"> ${len(jsonxs(host, 'ansible_facts.ansible_interfaces', default=[]))} <%def name="col_comment(host, **kwargs)"> ${jsonxs(host, 'hostvars.comment', default='')} <%def name="col_ext_id(host, **kwargs)"> ${jsonxs(host, 'hostvars.ext_id', default='')} <%def name="col_gathered(host, **kwargs)"> % if 'ansible_date_time' in host['ansible_facts']: ${host['ansible_facts']['ansible_date_time'].get('iso8601')} % endif <%def name="col_prodname(host, **kwargs)"> ${jsonxs(host, 'ansible_facts.ansible_product_name', default='')} <%def name="col_prodserial(host, **kwargs)"> ${jsonxs(host, 'ansible_facts.ansible_product_serial', default='')} ## ## Detailed host information blocks ## <%def name="host_general(host, collapsed_class)">

General

Node name${jsonxs(host, 'ansible_facts.ansible_nodename', default='')}
Form factor${jsonxs(host, 'ansible_facts.ansible_form_factor', default='')}
Virtualization role${jsonxs(host, 'ansible_facts.ansible_virtualization_role', default='')}
Virtualization type${jsonxs(host, 'ansible_facts.ansible_virtualization_type', default='')}
<%def name="host_groups(host, collapsed_class)"> % if len(host.get('groups', [])) != 0:

Groups

% endif <%def name="host_custvars(host, collapsed_class)"> % if len(host['hostvars']) != 0:

Custom variables

Careful: these variables may be overridden in various places by Ansible

% for var_name, var_value in host['hostvars'].items():
${var_name} % if type(var_value) == dict: ${r_dict(var_value)} % elif type(var_value) == list: ${r_list(var_value)} % else: ${var_value} % endif % endfor
% endif <%def name="host_localfacts(host, collapsed_class)"> % if len(jsonxs(host, 'ansible_facts.ansible_local', default={}).items()) != 0:

Host local facts

${r_dict(jsonxs(host, 'ansible_facts.ansible_local', default={}))}
% endif <%def name="host_factorfacts(host, collapsed_class)"> <% facter_facts = {} for key, value in jsonxs(host, 'ansible_facts', default={}).items(): if key.startswith('facter_'): facter_facts[key] = value %> % if len(facter_facts) != 0:

Facter facts

${r_dict(facter_facts)}
% endif <%def name="host_customfacts(host, collapsed_class)"> % if len(host.get('custom_facts', {}).items()) != 0:

Custom facts

${r_dict(host.get('custom_facts', {}))}
% endif <%def name="host_hardware(host, collapsed_class)">

Hardware

Vendor${jsonxs(host, 'ansible_facts.ansible_system_vendor', default='')}
Product name${jsonxs(host, 'ansible_facts.ansible_product_name', default='')}
Product serial${jsonxs(host, 'ansible_facts.ansible_product_serial', default='')}
Architecture${jsonxs(host, 'ansible_facts.ansible_architecture', default='')}
Form factor${jsonxs(host, 'ansible_facts.ansible_form_factor', default='')}
Virtualization role${jsonxs(host, 'ansible_facts.ansible_virtualization_role', default='')}
Virtualization type${jsonxs(host, 'ansible_facts.ansible_virtualization_type', default='')}
Machine${jsonxs(host, 'ansible_facts.ansible_machine', default='')}
Processor type${jsonxs(host, 'ansible_facts.ansible_processor[-1]', default='')}
Processor count${jsonxs(host, 'ansible_facts.ansible_processor_count', default='')}
Processor cores${jsonxs(host, 'ansible_facts.ansible_processor_cores', default='')}
Processor threads per core${jsonxs(host, 'ansible_facts.ansible_processor_threads_per_core', default='')}
Processor virtual CPUs${jsonxs(host, 'ansible_facts.ansible_processor_vcpus', default='')}
Mem total mb${jsonxs(host, 'ansible_facts.ansible_memtotal_mb', default='')}
Mem free mb${jsonxs(host, 'ansible_facts.ansible_memfree_mb', default='')}
Swap total mb${jsonxs(host, 'ansible_facts.ansible_swaptotal_mb', default='')}
Swap free mb${jsonxs(host, 'ansible_facts.ansible_swapfree_mb', default='')}
<%def name="host_os(host, collapsed_class)">

Operating System

System${jsonxs(host, 'ansible_facts.ansible_system', default='')}
OS Family${jsonxs(host, 'ansible_facts.ansible_os_family', default='')}
Distribution${jsonxs(host, 'ansible_facts.ansible_distribution', default='')}
Distribution version${jsonxs(host, 'ansible_facts.ansible_distribution_version', default='')}
Distribution release${jsonxs(host, 'ansible_facts.ansible_distribution_release', default='')}
Kernel${jsonxs(host, 'ansible_facts.ansible_kernel', default='')}
Userspace bits${jsonxs(host, 'ansible_facts.ansible_userspace_bits', default='')}
Userspace_architecture${jsonxs(host, 'ansible_facts.ansible_userspace_architecture', default='')}
Date time${jsonxs(host, 'ansible_facts.ansible_date_time.iso8601', default='')}
Locale / Encoding${jsonxs(host, 'ansible_facts.ansible_env.LC_ALL', default='Unknown')}
SELinux?${jsonxs(host, 'ansible_facts.ansible_selinux', default='')}
Package manager${jsonxs(host, 'ansible_facts.ansible_pkg_mgr', default='')}
<%def name="host_network(host, collapsed_class)">

Network

Hostname${jsonxs(host, 'ansible_facts.ansible_hostname', default='')}
Domain${jsonxs(host, 'ansible_facts.ansible_domain', default='')}
FQDN${jsonxs(host, 'ansible_facts.ansible_fqdn', default='')}
All IPv4${'
'.join(jsonxs(host, 'ansible_facts.ansible_all_ipv4_addresses', default=[]))}
All IPv6${'
'.join(jsonxs(host, 'ansible_facts.ansible_all_ipv6_addresses', default=[]))}
% if jsonxs(host, 'ansible_facts.ansible_os_family', default='') != "Windows":
IPv4 Networks % for iface_name in sorted(jsonxs(host, 'ansible_facts.ansible_interfaces', default=[])): <% iface = jsonxs(host, 'ansible_facts.ansible_' + iface_name, default={}) %> % for net in [iface.get('ipv4', {})] + iface.get('ipv4_secondaries', []): % if 'address' in net: % if 'netmask' in net: % else: % endif % endif % endfor % endfor
dev address network netmask
${iface_name} ${net['address']} ${net['network']}${net['netmask']}
% endif
Interface details % for iface in sorted(jsonxs(host, 'ansible_facts.ansible_interfaces', default=[])): % endfor
${iface} % try: ${r_dict(jsonxs(host, 'ansible_facts.ansible_%s' % (iface)))} % except KeyError: No information available % endtry
<%def name="host_storage(host, collapsed_class)">

Storage

Devices % if type(jsonxs(host, 'ansible_facts.ansible_devices', default=[])) == list: ${r_list(jsonxs(host, 'ansible_facts.ansible_devices', default=[]))} % else: ${r_dict(jsonxs(host, 'ansible_facts.ansible_devices', default={}))} % endif
Mounts ${r_list(host['ansible_facts'].get('ansible_mounts', []))}