Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions playbooks/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
- name: Gather System Facts
hosts: all
gather_facts: true

vars:
# These are production specs for Itential P6
hardware_specs:
mongodb:
cpu_count: 16
ram_size: 128
disk_size: 1000
platform:
cpu_count: 16
ram_size: 64
disk_size: 250
redis:
cpu_count: 16
ram_size: 32
disk_size: 100

tasks:

- name: Gather host information
itential.deployer.gather_host_information:
register: host_info

- name: Extract OS information
ansible.builtin.set_fact:
os: "{{ host_info.os }}"

# OS and Architecture validation
- name: Check OS compatibility
ansible.builtin.set_fact:
os_valid: >-
{{
(os.distribution == 'RedHat' and ansible_distribution_major_version in ['8', '9']) or
(os.distribution == 'Rocky' and ansible_distribution_major_version in ['8', '9']) or
(os.distribution == 'OracleLinux' and ansible_distribution_major_version in ['8', '9']) or

Check warning on line 39 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (102 > 100 characters)
(os.distribution == 'Amazon' and ansible_distribution_major_version == '2023')
}}

- name: Assert that this is a supported OS
ansible.builtin.assert:
that: "{{ os_valid }} == true"
fail_msg: "{{ os.distribution }} {{ os.distribution_version }} is not a supported OS!"
success_msg: "OS validation passed!"
quiet: true

- name: Check architecture compatibility
ansible.builtin.set_fact:
arch_valid: "{{ os.architecture in ['x86_64', 'aarch64'] }}"

- name: Assert that this is a supported Architecture
ansible.builtin.assert:
that: "{{ arch_valid }} == true"
fail_msg: "{{ os.architecture }} is not a supported architecture!"
success_msg: "Architecture validation passed!"
quiet: true

# Hardware spec validation
- name: Determine which hardware spec applies to this host
ansible.builtin.set_fact:
applicable_spec: >-
{%- if 'mongodb' in group_names -%}
mongodb
{%- elif 'platform' in group_names -%}
platform
{%- elif 'redis' in group_names -%}
redis
{%- else -%}
none
{%- endif -%}

- name: Get root partition size
ansible.builtin.set_fact:
root_disk_size_gb: "{{ (ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_total') | first / 1024 / 1024 / 1024) | round(2) }}"

Check warning on line 77 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (159 > 100 characters)
when: ansible_mounts | selectattr('mount', 'equalto', '/') | list | length > 0

- name: Validate hardware specs against requirements
ansible.builtin.set_fact:
hardware_validation:
applicable_spec: "{{ applicable_spec }}"
required:
cpu_count: "{{ hardware_specs[applicable_spec].cpu_count if applicable_spec != 'none' else 'N/A' }}"

Check warning on line 85 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (112 > 100 characters)
ram_size_gb: "{{ hardware_specs[applicable_spec].ram_size if applicable_spec != 'none' else 'N/A' }}"

Check warning on line 86 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (113 > 100 characters)
disk_size_gb: "{{ hardware_specs[applicable_spec].disk_size if applicable_spec != 'none' else 'N/A' }}"

Check warning on line 87 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (115 > 100 characters)
actual:
cpu_count: "{{ ansible_processor_vcpus }}"
ram_size_gb: "{{ (ansible_memtotal_mb / 1024) | round(2) }}"
disk_size_gb: "{{ root_disk_size_gb | default('N/A') }}"
validation:
cpu_valid: "{{ (applicable_spec == 'none') or (ansible_processor_vcpus >= hardware_specs[applicable_spec].cpu_count) }}"

Check warning on line 93 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (132 > 100 characters)
ram_valid: "{{ (applicable_spec == 'none') or ((ansible_memtotal_mb / 1024) >= hardware_specs[applicable_spec].ram_size) }}"

Check warning on line 94 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (136 > 100 characters)
disk_valid: "{{ (applicable_spec == 'none') or ((root_disk_size_gb | default(0) | float) >= hardware_specs[applicable_spec].disk_size) }}"

Check warning on line 95 in playbooks/verify.yml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[line-length]

Line too long (150 > 100 characters)
all_valid: "{{ (applicable_spec == 'none') or ((ansible_processor_vcpus >= hardware_specs[applicable_spec].cpu_count) and ((ansible_memtotal_mb / 1024) >= hardware_specs[applicable_spec].ram_size) and ((root_disk_size_gb | default(0) | float) >= hardware_specs[applicable_spec].disk_size)) }}"

- name: Validate CPU Count
ansible.builtin.assert:
that: hardware_validation.validation.cpu_valid | bool
fail_msg: "CPU validation failed"
quiet: true
ignore_errors: true
register: cpu_validation

- name: Validate Memory Amount
ansible.builtin.assert:
that: hardware_validation.validation.memory_valid | bool
fail_msg: "Memory validation failed"
quiet: true
ignore_errors: true
register: memory_validation

- name: Validate Disk Size
ansible.builtin.assert:
that: hardware_validation.validation.disk_valid | bool
fail_msg: "Disk validation failed"
quiet: true
ignore_errors: true
register: disk_validation

# Fail at the end if any validation failed
- name: Check if any validations failed
ansible.builtin.fail:
msg: |
Hardware validation failures detected:
{% if cpu_validation is failed %}
- CPU: {{ hardware_validation.required.cpu_count }} required, {{ hardware_validation.actual.cpu_count }} found
{% endif %}
{% if memory_validation is failed %}
- Memory: {{ hardware_validation.required.ram_size_gb }}GB required, {{ hardware_validation.actual.ram_size_gb }}GB found
{% endif %}
{% if disk_validation is failed %}
- Disk: {{ hardware_validation.required.disk_size_gb }}GB required, {{ hardware_validation.actual.disk_size_gb }}GB found
{% endif %}
when: cpu_validation is failed or memory_validation is failed or disk_validation is failed

- name: Aggregate Results
hosts: localhost
gather_facts: false

tasks:
- name: Collect all host information
ansible.builtin.set_fact:
all_systems_info: "{{ all_systems_info | default([]) + [hostvars[item].host_info] }}"
loop: "{{ groups['all'] }}"

- name: Display aggregated information
ansible.builtin.debug:
var: all_systems_info
154 changes: 154 additions & 0 deletions plugins/modules/gather_host_information.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#!/usr/bin/python

# Copyright (c) 2026, Itential, Inc
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = r'''
---
module: gather_host_information

short_description: Inspect facts and gather interesting data

version_added: "3.0.0"

description: This module will inspect the host facts and gather interesting data to be used in the
verification and certification of environments.

author:
- Steven Schattenberg (@steven-schattenberg-itential)
'''

EXAMPLES = r'''
- name: Gather standard facts
itential.deployer.gather_host_information:
'''

RETURN = r'''
details:
description: Details from the host
type: object
returned: always
sample: false
'''

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.facts.compat import ansible_facts

def build_disk_list(ansible_mounts):
"""Build simplified disk list from ansible_mounts data"""
disk_list = []

for item in ansible_mounts:
if 'size_total' in item:
disk_list.append({
'mount': item['mount'],
'size_gb': round(item['size_total'] / 1024 / 1024 / 1024, 2)
})

return disk_list

def build_interface_list(facts):
"""Build simplified interface information"""
interfaces = []

# Get list of all interfaces
interface_names = facts.get('interfaces', [])

for iface_name in interface_names:
# Skip loopback
if iface_name == 'lo':
continue

# Get the interface details
iface_data = facts.get(iface_name, {})

if not iface_data or not isinstance(iface_data, dict):
continue

interface_info = {
'name': iface_name,
'active': iface_data.get('active', False),
'type': iface_data.get('type', 'unknown'),
'ipv4': iface_data.get('ipv4', {}),
'ipv6': iface_data.get('ipv6', [])
}

interfaces.append(interface_info)

return interfaces

def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict()

# seed the result dict in the object
result = dict(
changed=False,
details=False,
)

# the AnsibleModule object will be our abstraction working with Ansible
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)

# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
if module.check_mode:
module.exit_json(**result)

# Get the facts from the host
facts = ansible_facts(module)

# Gather OS information...
result["os"] = {}
result["os"]["distribution"] = facts.get("distribution", "unknown")
result["os"]["distribution_version"] = facts.get("distribution_version", "unknown")
result["os"]["os_family"] = facts.get("os_family", "unknown")
result["os"]["kernel"] = facts.get("kernel", "unknown")
result["os"]["architecture"] = facts.get("architecture", "unknown")
result["os"]["hostname"] = facts.get("hostname", "unknown")
result["os"]["fqdn"] = facts.get("fqdn", "unknown")

# Gather hardware information...
result["hardware"] = {}
result["hardware"]["cpu"] = {}
result["hardware"]["cpu"]["processor_count"] = facts.get("processor_count", 0)
result["hardware"]["cpu"]["processor_cores"] = facts.get("processor_cores", 0)
result["hardware"]["cpu"]["processor_vcpus"] = facts.get("processor_vcpus", 0)
result["hardware"]["cpu"]["processor_threads_per_core"] = facts.get("processor_threads_per_core", 0)
result["hardware"]["cpu"]["processor"] = facts.get("processor", [])
result["hardware"]["memory"] = {}
result["hardware"]["memory"]["memtotal_mb"] = facts.get("memtotal_mb", 0)
result["hardware"]["memory"]["memfree_mb"] = facts.get("memfree_mb", 0)
result["hardware"]["memory"]["swaptotal_mb"] = facts.get("swaptotal_mb", 0)
result["hardware"]["disk"] = build_disk_list(facts.get("mounts", []))

# Gather security information...
result["security"] = {}
result["security"]["selinux"] = facts.get("selinux", {"status": "not available"})

# Is firewalld running?
firewalld = facts.get('services', {}).get('firewalld.service')
if firewalld:
result["security"]["firewalld"] = firewalld

# Gather networking information...
result["networking"] = {}
result["networking"]["interfaces"] = build_interface_list(facts)
result["networking"]["default_ipv4"] = facts.get("default_ipv4", {})
result["networking"]["default_ipv6"] = facts.get("default_ipv6", {})

# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
module.exit_json(**result)

def main():
run_module()

if __name__ == '__main__':
main()
4 changes: 4 additions & 0 deletions roles/mongodb/defaults/main/install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ mongodb_mongod_service_delay: 10
# MongoDB status settings
mongodb_status_poll: 3
mongodb_status_interval: 10

# The name and location of the certification report
mongodb_report_dir: "/tmp/itential-reports"
mongodb_report_file: "{{ mongodb_report_dir }}/mongodb_report_{{ inventory_hostname }}.md"
Loading
Loading