'
98 | def __init__(self, value, plain, start_mark, end_mark, style=None):
99 | self.value = value
100 | self.plain = plain
101 | self.start_mark = start_mark
102 | self.end_mark = end_mark
103 | self.style = style
104 |
105 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Ansible-CMDB
2 | theme: readthedocs
3 | markdown_extensions:
4 | - admonition
5 | pages:
6 | - About: index.md
7 | - Installation: installation.md
8 | - Usage: usage.md
9 | - FAQ: faq.md
10 | - Development: dev.md
11 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | mako
2 | pyyaml
3 | ushlex
4 | jsonxs
5 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 | import re
5 | from distutils.core import setup
6 | from setuptools import find_packages
7 |
8 | def get_long_description():
9 | path = os.path.join(os.path.dirname(__file__), 'README.md')
10 | with open(path) as f:
11 | return f.read()
12 |
13 | def get_version():
14 | return open('src/ansiblecmdb/data/VERSION', 'r').read().strip()
15 |
16 | def get_data_files(path, strip='', prefix=''):
17 | data_files = []
18 | for dirpath, dirnames, filenames in os.walk(path):
19 | files = [os.path.join(dirpath, filename) for filename in filenames]
20 | data_files.append( [prefix + dirpath[len(strip):], files] )
21 | return data_files
22 |
23 |
24 | if sys.argv[-1] == 'publish':
25 | os.system('python setup.py sdist upload')
26 | print('You should also add a git tag for this version:')
27 | print(' git tag {0}'.format(get_version()))
28 | print(' git push --tags')
29 | sys.exit()
30 |
31 | setup(
32 | name='ansible-cmdb',
33 | version=get_version(),
34 | license='GPLv3',
35 | description='Generate host overview from ansible fact gathering output',
36 | long_description=get_long_description(),
37 | url='https://github.com/fboender/ansible-cmdb',
38 |
39 | author='Ferry Boender',
40 | author_email='ferry.boender@electricmonk.nl',
41 |
42 | package_dir={'': 'src'},
43 | packages=find_packages('src'),
44 | include_package_data=True,
45 | data_files=\
46 | get_data_files(
47 | 'src/ansiblecmdb/data',
48 | strip='src',
49 | prefix='lib'
50 | ) +
51 | [['lib/ansiblecmdb/', ['src/ansible-cmdb.py']]],
52 | zip_safe=False,
53 | install_requires=['mako', 'pyyaml', 'ushlex', 'jsonxs'],
54 | scripts=[
55 | 'src/ansible-cmdb',
56 | ],
57 |
58 | classifiers=[
59 | 'Development Status :: 5 - Production/Stable',
60 | 'Environment :: Console',
61 | 'Intended Audience :: Developers',
62 | 'Intended Audience :: Information Technology',
63 | 'Intended Audience :: System Administrators',
64 | 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
65 | 'Natural Language :: English',
66 | 'Operating System :: POSIX',
67 | 'Programming Language :: Python',
68 | 'Programming Language :: Python :: 2.6',
69 | 'Programming Language :: Python :: 2.7',
70 | 'Programming Language :: Python :: 3',
71 | 'Topic :: System :: Installation/Setup',
72 | 'Topic :: System :: Systems Administration',
73 | 'Topic :: Utilities',
74 | ],
75 | )
76 |
--------------------------------------------------------------------------------
/src/ansible-cmdb:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Wrapper script to find python version to use.
5 | #
6 |
7 | # Debug message helper
8 | dbg () {
9 | [ "$DEBUG" -eq 1 ] && echo "$*" >&2
10 | }
11 |
12 | # Find suitable python binary
13 | find_py_bin () {
14 | which -a python | while read -r TRY_PY_BIN
15 | do
16 | dbg "Trying python bin: $TRY_PY_BIN"
17 |
18 | PY_VMAJOR=$($TRY_PY_BIN -c "import sys; print(sys.version_info[0])")
19 | PY_VMINOR=$($TRY_PY_BIN -c "import sys; print(sys.version_info[1])")
20 |
21 | if [ "$PY_VMAJOR" -eq 3 ]; then
22 | echo "$TRY_PY_BIN"
23 | exit 0
24 | elif [ "$PY_VMAJOR" -eq 2 ] && [ "$PY_VMINOR" -gt "6" ]; then
25 | echo "$TRY_PY_BIN"
26 | exit 0
27 | fi
28 | done
29 | }
30 |
31 | # Find path to the real ansible-cmdb python script
32 | find_cmdb_bin () {
33 | BIN_DIR=$(dirname "$0")
34 | if [ -f "$BIN_DIR/ansible-cmdb.py" ]; then
35 | dbg "Trying ansible-cmdb bin: $BIN_DIR/ansible-cmdb.py"
36 | echo "$BIN_DIR/ansible-cmdb.py"
37 | elif [ -f "$BIN_DIR/../lib/ansible-cmdb/ansible-cmdb.py" ]; then
38 | dbg "Trying ansible-cmdb bin: $BIN_DIR/../lib/ansible-cmdb/ansible-cmdb.py"
39 | echo "$BIN_DIR/../lib/ansible-cmdb/ansible-cmdb.py"
40 | elif [ -f "$BIN_DIR/../lib/ansiblecmdb/ansible-cmdb.py" ]; then
41 | dbg "Trying ansible-cmdb bin: $BIN_DIR/../lib/ansiblecmdb/ansible-cmdb.py"
42 | echo "$BIN_DIR/../lib/ansiblecmdb/ansible-cmdb.py"
43 | else
44 | echo "Couldn't find $BIN_DIR/ansible-cmdb.py in . or $BIN_DIR/../lib/ansible-cmdb/ or $BIN_DIR/../lib/ansiblecmdb/ (cwd=$PWD)" >&2
45 | exit 2
46 | fi
47 | }
48 |
49 | DEBUG=0
50 | if [ "$1" = "-d" ] || [ "$1" = "--debug" ]; then
51 | DEBUG=1
52 | fi
53 |
54 | PY_BIN="$(find_py_bin)"
55 | if [ -z "$PY_BIN" ]; then
56 | echo "No suitable python version found (v2.7 or higher required). Aborting" >&2
57 | exit 1
58 | fi
59 |
60 | CMDB_BIN="$(find_cmdb_bin)"
61 | if [ -z "$CMDB_BIN" ]; then
62 | echo "Couldn't find ansible-cmdb.py. Aborting" >&2
63 | exit 2
64 | fi
65 |
66 | # Run it
67 | dbg "Using python bin $PY_BIN"
68 | dbg "Using ansible-cmdb bin $CMDB_BIN"
69 | "$PY_BIN" "$CMDB_BIN" "$@"
70 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/__init__.py:
--------------------------------------------------------------------------------
1 | from . import ihateyaml
2 | from .parser import HostsParser, DynInvParser
3 | from .ansible import Ansible
4 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/VERSION:
--------------------------------------------------------------------------------
1 | MASTER
2 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/static/images/sort_asc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fboender/ansible-cmdb/3f3e412d2a7be91c97c5a1842f4e57cc85b06961/src/ansiblecmdb/data/static/images/sort_asc.png
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/static/images/sort_both.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fboender/ansible-cmdb/3f3e412d2a7be91c97c5a1842f4e57cc85b06961/src/ansiblecmdb/data/static/images/sort_both.png
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/static/images/sort_desc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fboender/ansible-cmdb/3f3e412d2a7be91c97c5a1842f4e57cc85b06961/src/ansiblecmdb/data/static/images/sort_desc.png
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/csv.tpl:
--------------------------------------------------------------------------------
1 | <%
2 |
3 | import sys
4 | import csv
5 | import logging
6 |
7 | log = logging.getLogger(__name__)
8 |
9 | cols = [
10 | {"title": "Name", "id": "name", "visible": True, "field": lambda h: h.get('name', '')},
11 | {"title": "OS", "id": "os", "visible": True, "field": lambda h: h['ansible_facts'].get('ansible_distribution', '') + ' ' + h['ansible_facts'].get('ansible_distribution_version', '')},
12 | {"title": "IP", "id": "ip", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_default_ipv4', {}).get('address', '')},
13 | {"title": "Arch", "id": "arch", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_architecture', 'Unk') + '/' + host['ansible_facts'].get('ansible_userspace_architecture', 'Unk')},
14 | {"title": "Mem", "id": "mem", "visible": True, "field": lambda h: '%0.0fg' % (int(host['ansible_facts'].get('ansible_memtotal_mb', 0)) / 1000.0)},
15 | {"title": "MemFree", "id": "memfree", "visible": True, "field": lambda h: '%0.0fg' % (int(host['ansible_facts'].get('ansible_memfree_mb', 0)) / 1000.0)},
16 | {"title": "MemUsed", "id": "memused", "visible": True, "field": lambda h: '%0.0fg' % (int(host['ansible_facts'].get('ansible_memory_mb', {}).get('real', {}).get('used',0)) / 1000.0)},
17 | {"title": "CPUs", "id": "cpus", "visible": True, "field": lambda h: str(host['ansible_facts'].get('ansible_processor_count', 0))},
18 | {"title": "Virt", "id": "virt", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_virtualization_type', 'Unk') + '/' + host['ansible_facts'].get('ansible_virtualization_role', 'Unk')},
19 | {"title": "Disk avail", "id": "disk_avail", "visible": True, "field": lambda h: ', '.join(['{0:0.1f}g'.format(i['size_available']/1048576000) for i in host['ansible_facts'].get('ansible_mounts', []) if 'size_available' in i and i['size_available'] > 1])},
20 | ]
21 |
22 | # Enable columns specified with '--columns'
23 | if columns is not None:
24 | for col in cols:
25 | if col["id"] in columns:
26 | col["visible"] = True
27 | else:
28 | col["visible"] = False
29 |
30 | def get_cols():
31 | return [col for col in cols if col['visible'] is True]
32 |
33 | fieldnames = []
34 | for col in get_cols():
35 | fieldnames.append(col['title'])
36 |
37 | writer = csv.writer(sys.stdout, delimiter=',', quotechar='"', quoting=csv.QUOTE_ALL)
38 | writer.writerow(fieldnames)
39 | for hostname, host in sorted(hosts.items()):
40 | if 'ansible_facts' not in host:
41 | log.warning(u'{0}: No info collected.'.format(hostname))
42 | else:
43 | out_cols = []
44 | for col in get_cols():
45 | out_cols.append(col['field'](host))
46 | writer.writerow(out_cols)
47 | %>
48 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/html_fancy.tpl:
--------------------------------------------------------------------------------
1 | ## -*- coding: utf-8 -*-
2 | <%! from ansiblecmdb.util import to_bool %>
3 |
4 | <%namespace name="defs" file="/html_fancy_defs.html" import="*" />
5 |
6 | <%
7 | # Default parameter values
8 | local_js = to_bool(context.get('local_js', '0'))
9 | collapsed = to_bool(context.get('collapsed', '0'))
10 | host_details = to_bool(context.get('host_details', '1'))
11 | skip_empty = to_bool(context.get('skip_empty', '0'))
12 |
13 | # Get column definitions from html_fancy_defs.html
14 | cols = var_cols(columns, exclude_columns)
15 |
16 | # Extend default columns with custom columns
17 | cols.extend(cust_cols)
18 |
19 | # Set the Javascript resource URL (local disk or CDN)
20 | if local_js is False:
21 | res_url = "https://cdn.datatables.net/1.10.2/"
22 | else:
23 | res_url = "file://" + data_dir + "/static/"
24 |
25 | # Set the link type for the host overview table's 'host' column (the link that
26 | # takes you to the host details).
27 | link_type = "anchor"
28 | if host_details is False:
29 | link_type = "none"
30 | %>
31 |
32 | <% html_header("Ansible Overview", local_js, res_url) %>
33 | <% html_header_bar("Host overview") %>
34 | <% html_col_toggles(cols) %>
35 | <% html_host_overview(cols, hosts, skip_empty=skip_empty, link_type=link_type) %>
36 | % if host_details is True:
37 | <% html_host_details(hosts, collapsed=collapsed, skip_empty=skip_empty) %>
38 | % endif
39 | <% html_footer_bar(version) %>
40 |
41 |
68 |
69 | <% html_footer() %>
70 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/html_fancy_split.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys
4 | import os
5 | import codecs
6 | from mako.template import Template
7 | from mako.lookup import TemplateLookup
8 |
9 | def render(hosts, vars={}, tpl_dirs=[]):
10 | if not os.path.isdir('cmdb'):
11 | os.mkdir('cmdb')
12 |
13 | lookup = TemplateLookup(directories=tpl_dirs,
14 | default_filters=['decode.utf8'],
15 | input_encoding='utf-8',
16 | output_encoding='utf-8',
17 | encoding_errors='replace')
18 |
19 | # Render host overview
20 | template = lookup.get_template('html_fancy_split_overview.tpl')
21 | out_file = os.path.join('cmdb', 'index.html')
22 | output = template.render(hosts=hosts, **vars).lstrip().decode('utf8')
23 | with codecs.open(out_file, 'w', encoding='utf8') as f:
24 | f.write(output)
25 |
26 | # Render host details
27 | template = lookup.get_template('html_fancy_split_detail.tpl')
28 | for hostname, host in hosts.items():
29 | out_file = os.path.join('cmdb', u'{0}.html'.format(hostname))
30 | output = template.render(host=host, **vars).lstrip().decode('utf8')
31 | with codecs.open(out_file, 'w', encoding='utf8') as f:
32 | f.write(output)
33 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/html_fancy_split_detail.tpl:
--------------------------------------------------------------------------------
1 | ## -*- coding: utf-8 -*-
2 | <%! from ansiblecmdb.util import to_bool %>
3 |
4 | <%namespace name="defs" file="/html_fancy_defs.html" import="*" />
5 |
6 | <%
7 | # Default parameter values
8 | local_js = to_bool(context.get('local_js', '0'))
9 | collapsed = to_bool(context.get('collapsed', '0'))
10 |
11 | # Set the Javascript resource URL (local disk or CDN)
12 | if local_js is False:
13 | res_url = "https://cdn.datatables.net/1.10.2/"
14 | else:
15 | res_url = "file://" + data_dir + "/static/"
16 | %>
17 |
18 | <% html_header(host['name'], local_js, res_url) %>
19 | <% html_header_bar(host['name']) %>
20 |
21 | <% html_host_detail(host, collapsed=collapsed, skip_empty=skip_empty, is_split=True) %>
22 |
23 | <% html_footer_bar(version) %>
24 |
25 |
30 |
31 | <% html_footer() %>
32 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/html_fancy_split_overview.tpl:
--------------------------------------------------------------------------------
1 | ## -*- coding: utf-8 -*-
2 | <%! from ansiblecmdb.util import to_bool %>
3 |
4 | <%namespace name="defs" file="/html_fancy_defs.html" import="*" />
5 |
6 | <%
7 | # Default parameter values
8 | local_js = to_bool(context.get('local_js', '0'))
9 | collapsed = to_bool(context.get('collapsed', '0'))
10 | host_details = to_bool(context.get('host_details', '1'))
11 | skip_empty = to_bool(context.get('skip_empty', '0'))
12 |
13 | # Get column definitions from html_fancy_defs.html
14 | cols = var_cols(columns, exclude_columns)
15 |
16 | # Extend default columns with custom columns
17 | cols.extend(cust_cols)
18 |
19 | # Set the Javascript resource URL (local disk or CDN)
20 | if local_js is False:
21 | res_url = "https://cdn.datatables.net/1.10.2/"
22 | else:
23 | res_url = "file://" + data_dir + "/static/"
24 |
25 | # Set the link type for the host overview table's 'host' column (the link that
26 | # takes you to the host details).
27 | link_type = "external"
28 | if host_details is False:
29 | link_type = "none"
30 | %>
31 |
32 | <% html_header("Ansible Overview", local_js, res_url) %>
33 | <% html_header_bar("Host overview") %>
34 | <% html_col_toggles(cols) %>
35 | <% html_host_overview(cols, hosts, skip_empty=skip_empty, link_type=link_type) %>
36 |
42 | <% html_footer() %>
43 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/json.tpl:
--------------------------------------------------------------------------------
1 | <%
2 | import json
3 |
4 | class CustEncoder(json.JSONEncoder):
5 | def default(self, obj):
6 | if isinstance(obj, set):
7 | return list(obj)
8 | return json.JSONEncoder.default(self, obj)
9 |
10 | print(json.dumps(hosts, indent=2, cls=CustEncoder))
11 | %>
12 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/markdown_split.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys
4 | import os
5 | import codecs
6 | from mako.template import Template
7 | from mako.lookup import TemplateLookup
8 |
9 | def render(hosts, vars={}, tpl_dirs=[]):
10 | if not os.path.isdir('cmdb'):
11 | os.mkdir('cmdb')
12 |
13 | lookup = TemplateLookup(directories=tpl_dirs,
14 | default_filters=['decode.utf8'],
15 | input_encoding='utf-8',
16 | output_encoding='utf-8',
17 | encoding_errors='replace')
18 |
19 | # Render host overview
20 | template = lookup.get_template('markdown_split_overview.tpl')
21 | out_file = os.path.join('cmdb', 'overview.md')
22 | output = template.render(hosts=hosts, **vars).lstrip().decode('utf8')
23 | with codecs.open(out_file, 'w', encoding='utf8') as f:
24 | f.write(output)
25 |
26 | # Render host details
27 | template = lookup.get_template('markdown_split_detail.tpl')
28 | for hostname, host in sorted(hosts.items()):
29 | out_file = os.path.join('cmdb', '{0}.md'.format(hostname))
30 | output = template.render(hostname=hostname, host=host, **vars).lstrip().decode('utf8')
31 | with codecs.open(out_file, 'w', encoding='utf8') as f:
32 | f.write(output)
33 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/markdown_split_overview.tpl:
--------------------------------------------------------------------------------
1 | <%
2 | import datetime
3 | %>\
4 | ${"#"} Hosts
5 |
6 | % for hostname, host in sorted(hosts.items()):
7 | * [${hostname}](${hostname}.md)
8 | % endfor
9 |
10 | Generated by [ansible-cmdb](https://github.com/fboender/ansible-cmdb) vMASTER on ${datetime.datetime.now().strftime('%c')}. © Ferry Boender
11 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/sql.tpl:
--------------------------------------------------------------------------------
1 | <%
2 | from jsonxs import jsonxs
3 | %>
4 | <%def name="col_fqdn(host)"><%
5 | return jsonxs(host, 'ansible_facts.ansible_fqdn', default='')
6 | %>%def>
7 | <%def name="col_main_ip(host)"><%
8 | default_ipv4 = ''
9 | if jsonxs(host, 'ansible_facts.ansible_os_family', default='') == 'Windows':
10 | ipv4_addresses = [ip for ip in jsonxs(host, 'ansible_facts.ansible_ip_addresses', default=[]) if ':' not in ip]
11 | if ipv4_addresses:
12 | default_ipv4 = ipv4_addresses[0]
13 | else:
14 | default_ipv4 = jsonxs(host, 'ansible_facts.ansible_default_ipv4.address', default='')
15 |
16 | return default_ipv4.strip()
17 | %>%def>
18 | <%def name="col_os_name(host)"><%
19 | return jsonxs(host, 'ansible_facts.ansible_distribution', default='')
20 | %>%def>
21 | <%def name="col_os_version(host)"><%
22 | if jsonxs(host, 'ansible_facts.ansible_distribution', default='') in ["OpenBSD"]:
23 | return jsonxs(host, 'ansible_facts.ansible_distribution_release', default='')
24 | else:
25 | return jsonxs(host, 'ansible_facts.ansible_distribution_version', default='')
26 | endif
27 | %>%def>
28 | <%def name="col_system_type(host)"><%
29 | return jsonxs(host, 'ansible_facts.ansible_system', default='')
30 | %>%def>
31 | <%def name="col_kernel(host)"><%
32 | return jsonxs(host, 'ansible_facts.ansible_kernel', default='')
33 | %>%def>
34 | <%def name="col_arch_hardware(host)"><%
35 | return jsonxs(host, 'ansible_facts.ansible_architecture', default='')
36 | %>%def>
37 | <%def name="col_arch_userspace(host)"><%
38 | return jsonxs(host, 'ansible_facts.ansible_userspace_architecture', default='')
39 | %>%def>
40 | <%def name="col_virt_type(host)"><%
41 | return jsonxs(host, 'ansible_facts.ansible_virtualization_type', default='?')
42 | %>%def>
43 | <%def name="col_virt_role(host)"><%
44 | return jsonxs(host, 'ansible_facts.ansible_virtualization_role', default='?')
45 | %>%def>
46 | <%def name="col_cpu_type(host)"><%
47 | cpu_type = jsonxs(host, 'ansible_facts.ansible_processor', default=0)
48 | if isinstance(cpu_type, list) and len(cpu_type) > 0:
49 | return cpu_type[-1]
50 | else:
51 | return ''
52 | %>%def>
53 | <%def name="col_vcpus(host)"><%
54 | if jsonxs(host, 'ansible_facts.ansible_distribution', default='') in ["OpenBSD"]:
55 | return jsonxs(host, 'ansible_facts.ansible_processor_count', default=0)
56 | else:
57 | return jsonxs(host, 'ansible_facts.ansible_processor_vcpus', default=jsonxs(host, 'ansible_facts.ansible_processor_cores', default=0))
58 | endif
59 | %>%def>
60 | <%def name="col_ram(host)"><%
61 | return '%0.1f' % ((int(jsonxs(host, 'ansible_facts.ansible_memtotal_mb', default=0)) / 1024.0))
62 | %>%def>
63 | <%def name="col_disk_total(host)"><%
64 | for i in jsonxs(host, 'ansible_facts.ansible_mounts', default=[]):
65 | if i["mount"] == '/':
66 | return round(i.get('size_total', 0) / 1073741824.0, 1)
67 | endif
68 | endfor
69 | return 0
70 | %>%def>
71 | <%def name="col_disk_free(host)"><%
72 | for i in jsonxs(host, 'ansible_facts.ansible_mounts', default=[]):
73 | if i["mount"] == '/':
74 | try:
75 | return round(i["size_available"] / 1073741824.0, 1)
76 | except:
77 | return 0
78 | endtry
79 | endif
80 | endfor
81 | return 0
82 | %>%def>
83 | DROP TABLE IF EXISTS hosts;
84 | CREATE TABLE hosts (
85 | name VARCHAR(255),
86 | fqdn VARCHAR(255),
87 | main_ip VARCHAR(15),
88 | os_name VARCHAR(80),
89 | os_version VARCHAR(40),
90 | system_type VARCHAR(40),
91 | kernel VARCHAR(40),
92 | arch_hardware VARCHAR(12),
93 | arch_userspace VARCHAR(12),
94 | virt_type VARCHAR(20),
95 | virt_role VARCHAR(20),
96 | cpu_type VARCHAR(60),
97 | vcpus INT,
98 | ram FLOAT,
99 | disk_total FLOAT,
100 | disk_free FLOAT
101 | );
102 |
103 | % for hostname, host in sorted(hosts.items()):
104 | INSERT INTO hosts (
105 | name,
106 | fqdn,
107 | main_ip,
108 | os_name,
109 | os_version,
110 | system_type,
111 | kernel,
112 | arch_hardware,
113 | arch_userspace,
114 | virt_type,
115 | virt_role,
116 | cpu_type,
117 | vcpus,
118 | ram,
119 | disk_total,
120 | disk_free
121 | ) VALUES (
122 | "${jsonxs(host, 'name', default='Unknown')}",
123 | "${col_fqdn(host)}",
124 | "${col_main_ip(host)}",
125 | "${col_os_name(host)}",
126 | "${col_os_version(host)}",
127 | "${col_system_type(host)}",
128 | "${col_kernel(host)}",
129 | "${col_arch_hardware(host)}",
130 | "${col_arch_userspace(host)}",
131 | "${col_virt_type(host)}",
132 | "${col_virt_role(host)}",
133 | "${col_cpu_type(host)}",
134 | ${col_vcpus(host)},
135 | ${col_ram(host)},
136 | ${col_disk_total(host)},
137 | ${col_disk_free(host)}
138 | );
139 | %endfor
140 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/data/tpl/txt_table.tpl:
--------------------------------------------------------------------------------
1 | <%
2 | import sys
3 | import logging
4 |
5 | log = logging.getLogger(__name__)
6 |
7 | col_space = 2
8 |
9 | cols = [
10 | {"title": "Name", "id": "name", "visible": True, "field": lambda h: h.get('name', '')},
11 | {"title": "OS", "id": "os", "visible": True, "field": lambda h: h['ansible_facts'].get('ansible_distribution', '') + ' ' + h['ansible_facts'].get('ansible_distribution_version', '')},
12 | {"title": "IP", "id": "ip", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_default_ipv4', {}).get('address', '')},
13 | {"title": "Mac", "id": "mac", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_default_ipv4', {}).get('macaddress', '')},
14 | {"title": "Arch", "id": "arch", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_architecture', 'Unk') + '/' + host['ansible_facts'].get('ansible_userspace_architecture', 'Unk')},
15 | {"title": "Mem", "id": "mem", "visible": True, "field": lambda h: '%0.0fg' % (int(host['ansible_facts'].get('ansible_memtotal_mb', 0)) / 1000.0)},
16 | {"title": "MemFree", "id": "memfree", "visible": True, "field": lambda h: '%0.0fg' % (int(host['ansible_facts'].get('ansible_memfree_mb', 0)) / 1000.0)},
17 | {"title": "MemUsed", "id": "memused", "visible": True, "field": lambda h: '%0.0fg' % (int(host['ansible_facts'].get('ansible_memory_mb', {}).get('real', {}).get('used',0)) / 1000.0)},
18 | {"title": "CPUs", "id": "cpus", "visible": True, "field": lambda h: str(host['ansible_facts'].get('ansible_processor_count', 0))},
19 | {"title": "Virt", "id": "virt", "visible": True, "field": lambda h: host['ansible_facts'].get('ansible_virtualization_type', 'Unk') + '/' + host['ansible_facts'].get('ansible_virtualization_role', 'Unk')},
20 | {"title": "Disk avail", "id": "disk_avail", "visible": True, "field": lambda h: ', '.join(['{0:0.1f}g'.format(i['size_available']/1048576000) for i in host['ansible_facts'].get('ansible_mounts', []) if 'size_available' in i and i['size_available'] > 1])},
21 | ]
22 |
23 | # Enable columns specified with '--columns'
24 | if columns is not None:
25 | for col in cols:
26 | if col["id"] in columns:
27 | col["visible"] = True
28 | else:
29 | col["visible"] = False
30 |
31 | def get_cols():
32 | return [col for col in cols if col['visible'] is True]
33 |
34 | # Find longest value in a column
35 | col_longest = {}
36 |
37 | # Init col width to titles' len
38 | for col in get_cols():
39 | col_longest[col['title']] = len(col['title'])
40 |
41 | for hostname, host in hosts.items():
42 | for col in get_cols():
43 | try:
44 | field_value = col['field'](host)
45 | if len(field_value) > col_longest.get(col['title'], 0):
46 | col_longest[col['title']] = len(field_value)
47 | except KeyError:
48 | pass
49 |
50 | # Print out headers
51 | for col in get_cols():
52 | sys.stdout.write(col['title'].ljust(col_longest[col['title']] + col_space))
53 | sys.stdout.write('\n')
54 |
55 | for col in get_cols():
56 | sys.stdout.write(u'-' * col_longest[col['title']] + (u' ' * col_space))
57 | sys.stdout.write('\n')
58 |
59 | # Print out columns
60 | for hostname, host in sorted(hosts.items()):
61 | if 'ansible_facts' not in host:
62 | log.warning(u'{0}: No info collected.'.format(hostname))
63 | else:
64 | for col in get_cols():
65 | sys.stdout.write(col['field'](host).ljust(col_longest[col['title']]) + (' ' * col_space))
66 | sys.stdout.write('\n')
67 | %>
68 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/ihateyaml.py:
--------------------------------------------------------------------------------
1 | """
2 | Custom Yaml library wrapper, because Yaml is a bag of shit.
3 | """
4 |
5 | try:
6 | import yaml
7 | except ImportError:
8 | import yaml3 as yaml
9 |
10 | class StupidYAMLShit(yaml.SafeLoader):
11 | """
12 | FUCK PYYAML. This class overrides some insanely deep shit which took at
13 | least two hours to get working. This class overrides SafeLoader and handles
14 | tags (e.g. '!bullshit') nonsense in PyYAML, because obviously there is no
15 | ignore_tags option or a simple callback that actually works. That would be
16 | user-friendly, and user-friendliness is insanity, amirite?!
17 |
18 | Also, there is no single entry point to hook into this, so we need to
19 | specifically inherit from *SafeLoader* and not from *Loader*. Thanks for
20 | wasting some more of my fucking time PyYAML, you turd.
21 |
22 | On a pyyaml-rage-induced side note: How many apps are vulnerable because
23 | all the PyYaml docs mention 'load' and not 'safe_load'? Was this diseased
24 | pile of gunk written by a PHP programmer?!
25 |
26 | """
27 | def handle_tag(self, node_name, node):
28 | # I just *know* there are gonna be problems with simply returning a
29 | # Scalar, but I don't give a fuck at this point.
30 | if node_name == "vault":
31 | new_node = yaml.ScalarNode(node_name, 'ENCRYPTED CONTENTS REDACTED')
32 | else:
33 | new_node = yaml.ScalarNode(node_name, node.value)
34 |
35 | return self.construct_scalar(new_node)
36 |
37 |
38 | # Fugly!
39 | StupidYAMLShit.add_multi_constructor(
40 | u'!',
41 | StupidYAMLShit.handle_tag)
42 |
43 |
44 | def safe_load(contents):
45 | return yaml.load(contents, Loader=StupidYAMLShit)
46 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/render.py:
--------------------------------------------------------------------------------
1 | import os
2 | import imp
3 | from mako.template import Template
4 | from mako.lookup import TemplateLookup
5 |
6 |
7 | class Render:
8 | """
9 | Wrapper class to facilitate rendering.
10 |
11 | This is mostly a helper class for finding template locations and
12 | initializing Mako properly. It can also call executable "templates" (python
13 | scripts) for rendering.
14 |
15 | """
16 | def __init__(self, tpl, tpl_dirs):
17 | self.tpl = tpl
18 | self.tpl_dirs = tpl_dirs
19 | self.tpl_possibilities = self._tpl_possibilities()
20 | self.tpl_file = self._find_tpl()
21 |
22 | def _tpl_possibilities(self):
23 | """
24 | Construct a list of possible paths to templates.
25 | """
26 | tpl_possibilities = [
27 | os.path.realpath(self.tpl)
28 | ]
29 | for tpl_dir in self.tpl_dirs:
30 | tpl_possibilities.append(os.path.realpath(os.path.join(tpl_dir, "{0}.tpl".format(self.tpl))))
31 | tpl_possibilities.append(os.path.realpath(os.path.join(tpl_dir, "{0}.py".format(self.tpl))))
32 |
33 | return tpl_possibilities
34 |
35 | def _find_tpl(self):
36 | """
37 | Find a template in the list of possible paths.
38 | """
39 | for tpl_possibility in self.tpl_possibilities:
40 | if os.path.isfile(tpl_possibility):
41 | return tpl_possibility
42 |
43 | return None
44 |
45 | def render(self, hosts, vars={}):
46 | """
47 | Render a mako or .py file.
48 | """
49 | if self.tpl_file.endswith(".tpl"):
50 | return self._render_mako(hosts, vars)
51 | elif self.tpl_file.endswith(".py"):
52 | return self._render_py(hosts, vars)
53 | else:
54 | raise ValueError("Don't know how to handle '{0}'".format(self.tpl_file))
55 |
56 | def _render_mako(self, hosts, vars={}):
57 | lookup = TemplateLookup(directories=self.tpl_dirs,
58 | default_filters=['decode.utf8'],
59 | input_encoding='utf-8',
60 | output_encoding='utf-8',
61 | encoding_errors='replace')
62 | template = Template(filename=self.tpl_file,
63 | lookup=lookup,
64 | default_filters=['decode.utf8'],
65 | input_encoding='utf-8',
66 | output_encoding='utf-8')
67 | return template.render(hosts=hosts, **vars)
68 |
69 | def _render_py(self, hosts, vars={}):
70 | module = imp.load_source('r', self.tpl_file)
71 | return module.render(hosts, vars=vars, tpl_dirs=self.tpl_dirs)
72 |
--------------------------------------------------------------------------------
/src/ansiblecmdb/util.py:
--------------------------------------------------------------------------------
1 | import copy
2 | import os
3 | import stat
4 |
5 |
6 | def is_executable(path):
7 | """
8 | Determine whether `path` points to an executable file.
9 | """
10 | return stat.S_IXUSR & os.stat(path)[stat.ST_MODE]
11 |
12 |
13 | def deepupdate(target, src, overwrite=True):
14 | """Deep update target list, dict or set or other iterable with src
15 | For each k,v in src: if k doesn't exist in target, it is deep copied from
16 | src to target. Otherwise, if v is a list, target[k] is extended with
17 | src[k]. If v is a set, target[k] is updated with v, If v is a dict,
18 | recursively deep-update it. If `overwrite` is False, existing values in
19 | target will not be overwritten.
20 |
21 | Examples:
22 | >>> t = {'name': 'Ferry', 'hobbies': ['programming', 'sci-fi']}
23 | >>> deepupdate(t, {'hobbies': ['gaming']})
24 | >>> print t
25 | {'name': 'Ferry', 'hobbies': ['programming', 'sci-fi', 'gaming']}
26 | """
27 | for k, v in src.items():
28 | if type(v) == list:
29 | if not k in target:
30 | target[k] = copy.deepcopy(v)
31 | elif overwrite is True:
32 | target[k].extend(v)
33 | elif type(v) == dict:
34 | if not k in target:
35 | target[k] = copy.deepcopy(v)
36 | else:
37 | deepupdate(target[k], v, overwrite=overwrite)
38 | elif type(v) == set:
39 | if not k in target:
40 | target[k] = v.copy()
41 | elif overwrite is True:
42 | if type(target[k]) == list:
43 | target[k].extend(v)
44 | elif type(target[k]) == set:
45 | target[k].update(v)
46 | else:
47 | raise TypeError("Cannot update {} with {}".format(type(target[k]), type(v)))
48 | else:
49 | if k not in target or overwrite is True:
50 | target[k] = copy.copy(v)
51 |
52 |
53 | def find_path(dirs, path_to_find):
54 | """
55 | Go through a bunch of dirs and see if dir+path_to_find exists there.
56 | Returns the first dir that matches. Otherwise, return None.
57 | """
58 | for dir in dirs:
59 | if os.path.exists(os.path.join(dir, path_to_find)):
60 | return dir
61 | return None
62 |
63 |
64 | def to_bool(s):
65 | """
66 | Convert string `s` into a boolean. `s` can be 'true', 'True', 1, 'false',
67 | 'False', 0.
68 |
69 | Examples:
70 |
71 | >>> to_bool("true")
72 | True
73 | >>> to_bool("0")
74 | False
75 | >>> to_bool(True)
76 | True
77 | """
78 | if isinstance(s, bool):
79 | return s
80 | elif s.lower() in ['true', '1']:
81 | return True
82 | elif s.lower() in ['false', '0']:
83 | return False
84 | else:
85 | raise ValueError("Can't cast '%s' to bool" % (s))
86 |
--------------------------------------------------------------------------------
/test/f_extend/extend/debian.dev.local:
--------------------------------------------------------------------------------
1 | {
2 | "ansible_facts": {
3 | "ansible_env": {
4 | "EDITOR": "nano"
5 | }
6 | },
7 | "software": [
8 | "Apache2",
9 | "MySQL5.5"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/test/f_extend/out_setup/debian.dev.local:
--------------------------------------------------------------------------------
1 | {
2 | "ansible_facts": {
3 | "ansible_env": {
4 | "EDITOR": "vim"
5 | }
6 | },
7 | "changed": false
8 | }
9 |
--------------------------------------------------------------------------------
/test/f_factcache/hosts:
--------------------------------------------------------------------------------
1 | [dev]
2 | debian.dev.local dtap=dev
3 |
--------------------------------------------------------------------------------
/test/f_factcache/out/debian.dev.local:
--------------------------------------------------------------------------------
1 | {"ansible_all_ipv4_addresses": ["192.168.56.2"], "ansible_all_ipv6_addresses": ["fe80::a00:27ff:fef9:98a7"], "ansible_architecture": "x86_64", "ansible_bios_date": "12/01/2006", "ansible_bios_version": "VirtualBox", "ansible_cmdline": {"BOOT_IMAGE": "/vmlinuz-2.6.32-5-amd64", "quiet": true, "ro": true, "root": "/dev/mapper/debian-root"}, "ansible_date_time": {"date": "2015-08-30", "day": "30", "epoch": "1440923780", "hour": "10", "iso8601": "2015-08-30T08:36:20Z", "iso8601_micro": "2015-08-30T08:36:20.457036Z", "minute": "36", "month": "08", "second": "20", "time": "10:36:20", "tz": "CEST", "tz_offset": "+0200", "weekday": "Sunday", "year": "2015"}, "ansible_default_ipv4": {"address": "192.168.56.2", "alias": "eth0", "gateway": "192.168.56.1", "interface": "eth0", "macaddress": "08:00:27:f9:98:a7", "mtu": 1500, "netmask": "255.255.255.0", "network": "192.168.56.0", "type": "ether"}, "ansible_default_ipv6": {}, "ansible_devices": {"sda": {"holders": [], "host": "SATA controller: Intel Corporation 82801HBM/HEM (ICH8M/ICH8M-E) SATA AHCI Controller (rev 02)", "model": "VBOX HARDDISK", "partitions": {"sda1": {"sectors": "497664", "sectorsize": 512, "size": "243.00 MB", "start": "2048"}, "sda2": {"sectors": "2", "sectorsize": 512, "size": "1.00 KB", "start": "501758"}, "sda5": {"sectors": "209211392", "sectorsize": 512, "size": "99.76 GB", "start": "501760"}}, "removable": "0", "rotational": "1", "scheduler_mode": "cfq", "sectors": "209715200", "sectorsize": "512", "size": "100.00 GB", "support_discard": null, "vendor": "ATA"}, "sr0": {"holders": [], "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)", "model": "CD-ROM", "partitions": {}, "removable": "1", "rotational": "1", "scheduler_mode": "cfq", "sectors": "2097151", "sectorsize": "512", "size": "1024.00 MB", "support_discard": null, "vendor": "VBOX"}}, "ansible_distribution": "Debian", "ansible_distribution_major_version": "6", "ansible_distribution_release": "NA", "ansible_distribution_version": "6.0.10", "ansible_domain": "", "ansible_env": {"EDITOR": "vim", "GDK_USE_XFT": "1", "HOME": "/home/fboender", "LANG": "en_US.UTF-8", "LANGUAGE": "en_US.UTF-8", "LC_ALL": "en_US.UTF-8", "LC_CTYPE": "en_US.UTF-8", "LESS": "-RgiMSx4 -FX", "LOGNAME": "fboender", "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36::ow=103;30;01:", "PAGER": "less", "PATH": "~/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/usr/local/sbin:/usr/sbin:/sbin:~/bin/:", "PWD": "/home/fboender", "SHELL": "/bin/bash", "SHLVL": "1", "SSH_CLIENT": "192.168.56.1 60106 22", "SSH_CONNECTION": "192.168.56.1 60106 192.168.56.2 22", "SSH_TTY": "/dev/pts/1", "TERM": "xterm-256color", "TNS_ADMIN": "/usr/local/lib/instantclient_10_2/network/admin/", "USER": "fboender", "_": "/bin/sh"}, "ansible_eth0": {"active": true, "device": "eth0", "ipv4": {"address": "192.168.56.2", "netmask": "255.255.255.0", "network": "192.168.56.0"}, "ipv6": [{"address": "fe80::a00:27ff:fef9:98a7", "prefix": "64", "scope": "link"}], "macaddress": "08:00:27:f9:98:a7", "module": "pcnet32", "mtu": 1500, "promisc": false, "type": "ether"}, "ansible_fips": false, "ansible_form_factor": "Other", "ansible_fqdn": "localhost", "ansible_hostname": "dev", "ansible_interfaces": ["lo", "eth0"], "ansible_kernel": "2.6.32-5-amd64", "ansible_lo": {"active": true, "device": "lo", "ipv4": {"address": "127.0.0.1", "netmask": "255.0.0.0", "network": "127.0.0.0"}, "ipv6": [{"address": "::1", "prefix": "128", "scope": "host"}], "mtu": 16436, "promisc": false, "type": "loopback"}, "ansible_lsb": {"codename": "squeeze", "description": "Debian GNU/Linux 6.0.10 (squeeze)", "id": "Debian", "major_release": "6", "release": "6.0.10"}, "ansible_machine": "x86_64", "ansible_machine_id": "00a3ac55878f7a9340c879050000036c", "ansible_memfree_mb": 217, "ansible_memory_mb": {"nocache": {"free": 395, "used": 101}, "real": {"free": 217, "total": 496, "used": 279}, "swap": {"cached": 0, "free": 727, "total": 727, "used": 0}}, "ansible_memtotal_mb": 496, "ansible_mounts": [{"device": "/dev/mapper/debian-root", "fstype": "ext3", "mount": "/", "options": "rw,errors=remount-ro", "size_available": 92955344896, "size_total": 104680742912, "uuid": "NA"}, {"device": "/dev/sda1", "fstype": "ext2", "mount": "/boot", "options": "rw", "size_available": 209807360, "size_total": 238787584, "uuid": "NA"}], "ansible_nodename": "dev.local", "ansible_os_family": "Debian", "ansible_pkg_mgr": "apt", "ansible_processor": ["GenuineIntel", "Intel(R) Core(TM) i7-4712HQ CPU @ 2.30GHz"], "ansible_processor_cores": 1, "ansible_processor_count": 1, "ansible_processor_threads_per_core": 1, "ansible_processor_vcpus": 1, "ansible_product_name": "VirtualBox", "ansible_product_serial": "NA", "ansible_product_uuid": "NA", "ansible_product_version": "1.2", "ansible_python_version": "2.6.6", "ansible_selinux": false, "ansible_ssh_host_key_dsa_public": "AAAAB3NzaC1kc3MAAACBAOJWOpQVltXw3wNsRq20+r37aOHiD11hNvNttywbVkNPLo+s7Q0Y0lctaOWl9WR4b3EK55t+a7/sCqS7Qy5CGwtMmsg3ayUUNZLSwwAkAZ2UISyYRbb3AJwMb3HqBXu6P/lm5GRDEycU+bQUUdVOsBe6kwMEKUdBtsa++ipCCCcNAAAAFQCUI0c8CwmSvtwcuj7JTjEJnhBKiQAAAIEA4Mhgav6N18adoQ6xvgHNVrdf/ilNOv1tFUpL2pFlH21zrONj19/hT/HSyj7CeDV0Hpfwg1gGYI12TNgf+9NDOfz2ceXef6QVfG1Nf8j7HAp9KoSU50MCM9la3oTUnN4AwwPGp8ItuHwzmGubt1UaVaBPpeeNrMCWqewHF8bgZmAAAACAB68uE+BWPsGpKqdXeaohvinF296nWc0urbXQ6yPVaATT96UP+vT2QToZY+4Zkcs6l3gL0kS7s8Y/50AxbvO1yKFhIqBnH/p4tV21jdTnXL066bbU60f5tjC5/ty+zYQREKAm3XiLxOSRyyC0M34bFVIqCtZ5tMax2xtaRndDlys=", "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDFwue0q1kD5CgZczAKg10/DFyKWgxoSK1J/r/Tk9PqvckNjwVx7Yn78rElXgo4SCMceWPIucb8Yl8FpmdnuXH8/yn5i+snOpBQddoFun/CiB3HUw28T2M7Y9q4QtEcMiULBq1oiCoYJfNU9o3aD2caxk8OhcrF5k7Ec5DIyAGN8doYxey6icl6ohUJR6x6jnZO+6uoSKyHwxS3HBZ+6RrVY7ckCuRk/w24P7YM5sEnHZ9dnS4uTVCYKrJpygYUbN/HrSNuIIAQpvitZWua6t7mFy1zugCc0Lj8QbPStnsntIVWoIwWY+iFnFrS6N3IiGHAyOv6Jla0P3HEFmrhoVIH", "ansible_swapfree_mb": 727, "ansible_swaptotal_mb": 727, "ansible_system": "Linux", "ansible_system_vendor": "innotek GmbH", "ansible_user_dir": "/home/fboender", "ansible_user_gecos": "fboender,,,", "ansible_user_gid": 1000, "ansible_user_id": "fboender", "ansible_user_shell": "/bin/bash", "ansible_user_uid": 1000, "ansible_userspace_architecture": "x86_64", "ansible_userspace_bits": "64", "ansible_virtualization_role": "guest", "ansible_virtualization_type": "virtualbox", "module_setup": true}
--------------------------------------------------------------------------------
/test/f_hostparse/hosts:
--------------------------------------------------------------------------------
1 |
2 | [db]
3 | db.dev.local
4 |
5 | [db:vars]
6 | function=db
7 |
8 | [dev_local]
9 | db.dev.local
10 |
11 | [dev:children]
12 | dev_local
13 |
14 | [dev:vars]
15 | dtap=dev
16 |
17 | [web]
18 | web[01:03].dev.local
19 |
20 | [frontend]
21 | fe[01:03].dev[01:02].local
22 |
--------------------------------------------------------------------------------
/test/f_hostparse/out/db.dev.local:
--------------------------------------------------------------------------------
1 | {
2 | "ansible_facts": {
3 | "ansible_fqdn": "localhost",
4 | "ansible_hostname": "dev",
5 | "ansible_nodename": "db.dev.local",
6 | "module_setup": true
7 | },
8 | "changed": false
9 | }
10 |
--------------------------------------------------------------------------------
/test/f_inventory/dyninv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import sys
4 |
5 | inv = """\
6 | {
7 | "databases": {
8 | "hosts": ["host1.example.com", "host2.example.com"],
9 | "vars": {
10 | "a": true
11 | }
12 | },
13 | "webservers": [ "host2.example.com", "host3.example.com" ],
14 | "atlanta": {
15 | "hosts": [ "host1.example.com", "host4.example.com", "host5.example.com" ],
16 | "vars": {
17 | "b": false
18 | },
19 | "children": [ "marietta", "5points" ]
20 | },
21 | "marietta": [ "host6.example.com" ],
22 | "5points": [ "host7.example.com" ],
23 | "_meta" : {
24 | "hostvars": {
25 | "moocow.example.com": {
26 | "asdf" : 1234
27 | },
28 | "llama.example.com": {
29 | "asdf" : 5678
30 | }
31 | }
32 | }
33 | }
34 | """
35 |
36 | sys.stdout.write(inv)
37 |
--------------------------------------------------------------------------------
/test/f_inventory/hostsdir/hosts_db:
--------------------------------------------------------------------------------
1 | [db]
2 | db.dev.local
3 |
4 | [db:vars]
5 | function=db
6 |
--------------------------------------------------------------------------------
/test/f_inventory/hostsdir/hosts_dev:
--------------------------------------------------------------------------------
1 | [dev_local]
2 | db.dev.local
3 |
4 | [dev:children]
5 | dev_local
6 |
7 | [dev:vars]
8 | dtap=dev
9 |
--------------------------------------------------------------------------------
/test/f_inventory/mixeddir/config.ini:
--------------------------------------------------------------------------------
1 | # Just empty ini file
2 | [empty]
3 |
4 | ini_setting = 1
5 |
--------------------------------------------------------------------------------
/test/f_inventory/mixeddir/dyninv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import sys
4 |
5 | inv = """\
6 | {
7 | "databases": {
8 | "hosts": ["host1.example.com", "host2.example.com"],
9 | "vars": {
10 | "a": true
11 | }
12 | },
13 | "webservers": [ "host2.example.com", "host3.example.com" ],
14 | "atlanta": {
15 | "hosts": [ "host1.example.com", "host4.example.com", "host5.example.com" ],
16 | "vars": {
17 | "b": false
18 | },
19 | "children": [ "marietta", "5points" ]
20 | },
21 | "marietta": [ "host6.example.com" ],
22 | "5points": [ "host7.example.com" ],
23 | "_meta" : {
24 | "hostvars": {
25 | "moocow.example.com": {
26 | "asdf" : 1234
27 | },
28 | "llama.example.com": {
29 | "asdf" : 5678
30 | }
31 | }
32 | }
33 | }
34 | """
35 |
36 | sys.stdout.write(inv)
37 |
--------------------------------------------------------------------------------
/test/f_inventory/mixeddir/hosts:
--------------------------------------------------------------------------------
1 |
2 | [db]
3 | db.dev.local
4 |
5 | [db:vars]
6 | function=db
7 |
8 | [dev_local]
9 | db.dev.local
10 |
11 | [dev:children]
12 | dev_local
13 |
14 | [dev:vars]
15 | dtap=dev
16 |
17 | [web]
18 | web[01:03].dev.local
19 |
20 | [frontend]
21 | fe[01:03].dev[01:02].local
22 |
--------------------------------------------------------------------------------
/test/f_inventory/out/db.dev.local:
--------------------------------------------------------------------------------
1 | {
2 | "ansible_facts": {
3 | "ansible_fqdn": "localhost",
4 | "ansible_hostname": "dev",
5 | "ansible_nodename": "db.dev.local",
6 | "module_setup": true
7 | },
8 | "changed": false
9 | }
10 |
--------------------------------------------------------------------------------
/test/test.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import unittest
3 | import imp
4 | import os
5 |
6 | sys.path.insert(0, os.path.realpath('../lib'))
7 | sys.path.insert(0, os.path.realpath('../src'))
8 | import ansiblecmdb
9 |
10 |
11 | class ExtendTestCase(unittest.TestCase):
12 | """
13 | Test the extending of facts.
14 | """
15 | def testExtendOverrideParams(self):
16 | """
17 | Test that we can override a native fact
18 | """
19 | fact_dirs = ['f_extend/out_setup', 'f_extend/extend']
20 | ansible = ansiblecmdb.Ansible(fact_dirs)
21 | env_editor = ansible.hosts['debian.dev.local']['ansible_facts']['ansible_env']['EDITOR']
22 | self.assertEqual(env_editor, 'nano')
23 |
24 | def testExtendAddParams(self):
25 | """
26 | Test that we can add new facts
27 | """
28 | fact_dirs = ['f_extend/out_setup', 'f_extend/extend']
29 | ansible = ansiblecmdb.Ansible(fact_dirs)
30 | software = ansible.hosts['debian.dev.local']['software']
31 | self.assertIn('Apache2', software)
32 |
33 |
34 | class HostParseTestCase(unittest.TestCase):
35 | """
36 | Test specifics of the hosts inventory parser
37 | """
38 | def testChildGroupHosts(self):
39 | """
40 | Test that children groups contain all hosts they should.
41 | """
42 | fact_dirs = ['f_hostparse/out']
43 | inventories = ['f_hostparse/hosts']
44 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories)
45 | groups = ansible.hosts['db.dev.local']['groups']
46 | self.assertIn('db', groups)
47 | self.assertIn('dev', groups)
48 | self.assertIn('dev_local', groups)
49 |
50 | def testChildGroupVars(self):
51 | """
52 | Test that all vars applied against a child group are set on the hosts.
53 | """
54 | fact_dirs = ['f_hostparse/out']
55 | inventories = ['f_hostparse/hosts']
56 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories)
57 | host_vars = ansible.hosts['db.dev.local']['hostvars']
58 | self.assertEqual(host_vars['function'], 'db')
59 | self.assertEqual(host_vars['dtap'], 'dev')
60 |
61 | def testExpandHostDef(self):
62 | """
63 | Verify that host ranges are properly expanded. E.g. db[01-03].local ->
64 | db01.local, db02.local, db03.local.
65 | """
66 | fact_dirs = ['f_hostparse/out']
67 | inventories = ['f_hostparse/hosts']
68 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories)
69 | self.assertIn('web02.dev.local', ansible.hosts)
70 | self.assertIn('fe03.dev02.local', ansible.hosts)
71 |
72 |
73 | class InventoryTestCase(unittest.TestCase):
74 | def testHostsDir(self):
75 | """
76 | Verify that we can specify a directory as the hosts inventory file and
77 | that all files are parsed.
78 | """
79 | fact_dirs = ['f_inventory/out']
80 | inventories = ['f_inventory/hostsdir']
81 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories)
82 | host_vars = ansible.hosts['db.dev.local']['hostvars']
83 | groups = ansible.hosts['db.dev.local']['groups']
84 | self.assertEqual(host_vars['function'], 'db')
85 | self.assertIn('db', groups)
86 |
87 | def testDynInv(self):
88 | """
89 | Verify that we can specify a path to a dynamic inventory as the
90 | inventory file, and it will be executed, it's output parsed and added
91 | as available hosts.
92 | """
93 | fact_dirs = ['f_inventory/out'] # Reuse f_hostparse
94 | inventories = ['f_inventory/dyninv.py']
95 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories)
96 | self.assertIn('host5.example.com', ansible.hosts)
97 | host_vars = ansible.hosts['host5.example.com']['hostvars']
98 | groups = ansible.hosts['host5.example.com']['groups']
99 | self.assertEqual(host_vars['b'], False)
100 | self.assertIn("atlanta", groups)
101 |
102 | def testMixedDir(self):
103 | """
104 | Verify that a mixed dir of hosts files and dynamic inventory scripts is
105 | parsed correctly.
106 | """
107 | fact_dirs = ['f_inventory/out']
108 | inventories = ['f_inventory/mixeddir']
109 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories)
110 | # results from dynamic inventory
111 | self.assertIn("host4.example.com", ansible.hosts)
112 | self.assertIn("moocow.example.com", ansible.hosts)
113 | # results from normal hosts file.
114 | self.assertIn("web03.dev.local", ansible.hosts)
115 | # INI file ignored.
116 | self.assertNotIn("ini_setting", ansible.hosts)
117 |
118 |
119 | class FactCacheTestCase(unittest.TestCase):
120 | """
121 | Test that we properly read fact-cached output dirs.
122 | """
123 | def testFactCache(self):
124 | fact_dirs = ['f_factcache/out']
125 | inventories = ['f_factcache/hosts']
126 | ansible = ansiblecmdb.Ansible(fact_dirs, inventories, fact_cache=True)
127 | host_vars = ansible.hosts['debian.dev.local']['hostvars']
128 | groups = ansible.hosts['debian.dev.local']['groups']
129 | ansible_facts = ansible.hosts['debian.dev.local']['ansible_facts']
130 | self.assertIn('dev', groups)
131 | self.assertEqual(host_vars['dtap'], 'dev')
132 | self.assertIn('ansible_env', ansible_facts)
133 |
134 |
135 | if __name__ == '__main__':
136 | unittest.main(exit=True)
137 |
138 | try:
139 | os.unlink('../src/ansible-cmdbc') # FIXME: Where is this coming from? Our weird import I assume.
140 | except Exception:
141 | pass
142 |
--------------------------------------------------------------------------------
/test/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Python v2"
4 | python2 test.py
5 |
6 | echo "Python v3"
7 | python3 test.py
8 |
--------------------------------------------------------------------------------