From 1bfbb2b756da3b0fd2ec4207d928907623805abd Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 16 Jul 2025 09:34:54 +0000 Subject: [PATCH] Add v2 of cloud facts playbook --- etc/kayobe/ansible.cfg | 2 + etc/kayobe/ansible/filter_plugins/filters.py | 87 +++++++++++++++ etc/kayobe/ansible/get-cloud-facts.yml | 105 +++++++++++++------ 3 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 etc/kayobe/ansible/filter_plugins/filters.py diff --git a/etc/kayobe/ansible.cfg b/etc/kayobe/ansible.cfg index e6c3e9c12d..c4bf7a0dda 100644 --- a/etc/kayobe/ansible.cfg +++ b/etc/kayobe/ansible.cfg @@ -10,6 +10,8 @@ inject_facts_as_vars = False callbacks_enabled = ansible.posix.profile_tasks # Silence warning about invalid characters found in group names force_valid_group_names = ignore +# Default value plus custom filter plugins path +filter_plugins = $ANSIBLE_HOME/plugins/filter:/usr/share/ansible/plugins/filter:$KAYOBE_CONFIG_PATH/ansible/filter_plugins/ [inventory] # Fail when any inventory source cannot be parsed. diff --git a/etc/kayobe/ansible/filter_plugins/filters.py b/etc/kayobe/ansible/filter_plugins/filters.py new file mode 100644 index 0000000000..4c39139688 --- /dev/null +++ b/etc/kayobe/ansible/filter_plugins/filters.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +from ansible.errors import AnsibleFilterError + +class FilterModule(object): + def filters(self): + return { + 'group_hostvars_by_var': + self.group_hostvars_by_var, + 'get_hostvars_by_host': + self.get_hostvars_by_host + } + + def group_hostvars_by_var(self, hostvars, var, subkey=None): + """ + Returns a dictionary where the keys are values for the + specified var in hostvars, and the values are the hosts + that match that value. + + For example, a grouping of hosts by OS release might look like: + distribution_release: + noble: + - node1 + - node2 + jammy: + - node3 + - node4 + - node5 + + Some Ansible commands, such as ansible.builtin.command, return a + dict rather than a single value. So 'subkey' is used for these cases + to access the desired value. + """ + result = {} + + for host in hostvars.keys(): + try: + indiv_host_var = hostvars[host][var] + if subkey is not None: + indiv_host_var = indiv_host_var[subkey] + result.setdefault(indiv_host_var, []).append(host) + except KeyError as e: + raise AnsibleFilterError(f"Variable {var} not found for host {host} in hostvars: {e}") + + return result + + def get_hostvars_by_host(self, hostvars, var, subkey=None): + """ + Returns a dictionary where the keys are hosts and the values + are the values for the specified var in hostvars. + + For example, the deployed containers by host might look like: + deployed_containers: + node1: + - grafana + - glance + - nova + - prometheus + node2: + - designate + - neutron + - nova + + Some Ansible commands, such as ansible.builtin.command, return a + dict rather than a single value. So 'subkey' is used for these cases + to access the desired value. + """ + result = {} + for host in hostvars.keys(): + try: + indiv_host_var = hostvars[host][var] + for key in indiv_host_var.keys(): + # Check if task to assign value was skipped + if key == "skipped": + result[host] = "No data" + continue + if subkey is not None: + indiv_host_var = indiv_host_var[subkey] + if indiv_host_var: + result[host] = indiv_host_var + else: + result[host] = [] + + except KeyError as e: + raise AnsibleFilterError(f"Variable {var} not found for host {host} in hostvars: {e}") + + return result diff --git a/etc/kayobe/ansible/get-cloud-facts.yml b/etc/kayobe/ansible/get-cloud-facts.yml index e966f8acce..62f5856aad 100644 --- a/etc/kayobe/ansible/get-cloud-facts.yml +++ b/etc/kayobe/ansible/get-cloud-facts.yml @@ -1,4 +1,65 @@ --- +- name: Get facts for control host + hosts: localhost + gather_facts: true + tasks: + - name: Check if ngs enabled + ansible.builtin.set_fact: + ngs_enabled: >- + {{ 'genericswitch' in kolla_neutron_ml2_mechanism_drivers | default(false) }} + when: kolla_neutron_ml2_mechanism_drivers + +- name: Get facts for seed node + hosts: seed + gather_facts: true + tasks: + - name: Is there a dedicated seed node? + ansible.builtin.set_fact: + seed_node_is_vm: "{{ ansible_facts.virtualization_role == 'guest' }}" + +- name: Get facts + hosts: all + gather_facts: true + tasks: + - name: Pinging stackhpc.com to check internet connectivity + ansible.builtin.command: "ping stackhpc.com -c 3" + register: successful_ping + timeout: 5 + ignore_errors: true + + - name: Set internet connectivity fact + ansible.builtin.set_fact: + internet_connectivity: "{{ not successful_ping.failed }}" + - name: Get OS distribution + ansible.builtin.set_fact: + distribution: "{{ ansible_facts.distribution }}" + + + - name: Get OS distribution release + ansible.builtin.set_fact: + distribution_release: "{{ ansible_facts.distribution_release }}" + + + - name: Get kernel version + ansible.builtin.command: "uname -r" + register: kernel_version + + - name: Check if docker is installed + ansible.builtin.command: "which docker" + register: docker_check + failed_when: docker_check.rc not in [0] + ignore_errors: true + + - name: Get running contianers + ansible.builtin.command: "docker ps --format {% raw %}'{{.Names}}'{% endraw %}" + become: true + register: containers_list + when: not docker_check.failed + + - name: Check if hugepages enabled + ansible.builtin.set_fact: + hugepages_enabled: "{{ 'hugepages' in ansible_facts.cmdline }}" + - name: Gather Cloud Facts hosts: localhost gather_facts: true @@ -6,8 +67,12 @@ - name: Write facts to file vars: cloud_facts: - ansible_control_host_distribution: "{{ ansible_facts.distribution }}" - ansible_control_host_distribution_release: "{{ ansible_facts.distribution_release }}" + internet_connectivity: "{{ hostvars | group_hostvars_by_var('internet_connectivity') }}" + seed_node_is_vm: "{{ hostvars[groups['seed'][0]]['seed_node_is_vm'] | default('N/A') if groups ['seed'] else 'N/A' }}" + distributions: "{{ hostvars | group_hostvars_by_var('distribution') }}" + dist_releases: "{{ hostvars | group_hostvars_by_var('distribution_release') }}" + kernel_versions: "{{ hostvars | group_hostvars_by_var('kernel_version','stdout') }}" + deployed_containers: "{{ hostvars | get_hostvars_by_host('containers_list', 'stdout_lines') | default({}) }}" openstack_release: "{{ openstack_release }}" openstack_release_name: "{{ openstack_release_codename }}" ansible_control_host_is_vm: "{{ ansible_facts.virtualization_role == 'guest' }}" @@ -22,41 +87,10 @@ ceph_release: "{{ cephadm_ceph_release }}" storage_hyperconverged: "{{ groups['controllers'] | intersect(groups['osds']) | length > 0 | bool }}" wazuh_enabled: "{{ groups['wazuh-agent'] | length > 0 | bool }}" + ngs_enabled: "{{ ngs_enabled | default(false) }}" kayobe_managed_switches: "{{ groups['switches'] | length > 0 | bool }}" proxy_configured: "{{ http_proxy | bool or https_proxy | bool }}" bifrost_version: "{{ kolla_bifrost_source_version }}" - barbican_enabled: "{{ kolla_enable_barbican }}" - nova_enabled: "{{ kolla_enable_nova }}" - neutron_enabled: "{{ kolla_enable_neutron }}" - ovs_enabled: "{{ kolla_enable_openvswitch }}" - ovn_enabled: "{{ kolla_enable_ovn }}" - glance_enabled: "{{ kolla_enable_glance }}" - cinder_enabled: "{{ kolla_enable_cinder }}" - keystone_enabled: "{{ kolla_enable_keystone }}" - horizon_enabled: "{{ kolla_enable_horizon }}" - fluentd_enabled: "{{ kolla_enable_fluentd }}" - rabbitmq_enabled: "{{ kolla_enable_rabbitmq }}" - mariadb_enabled: "{{ kolla_enable_mariadb }}" - mariabackup_enabled: "{{ kolla_enable_mariabackup }}" - memcached_enabled: "{{ kolla_enable_memcached }}" - haproxy_enabled: "{{ kolla_enable_haproxy }}" - keepalived_enabled: "{{ kolla_enable_keepalived }}" - octavia_enabled: "{{ kolla_enable_octavia }}" - designate_enabled: "{{ kolla_enable_designate }}" - manila_enabled: "{{ kolla_enable_manila }}" - magnum_enabled: "{{ kolla_enable_magnum }}" - heat_enabled: "{{ kolla_enable_heat }}" - ironic_enabled: "{{ kolla_enable_ironic }}" - skyline_enabled: "{{ kolla_enable_skyline }}" - blazar_enabled: "{{ kolla_enable_blazar }}" - pulp_enabled: "{{ seed_pulp_container_enabled }}" - opensearch_enabled: "{{ kolla_enable_opensearch }}" - opensearch_dashboards_enabled: "{{ kolla_enable_opensearch_dashboards }}" - influxdb_enabled: "{{ kolla_enable_influxdb }}" - grafana_enabled: "{{ kolla_enable_grafana }}" - prometheus_enabled: "{{ kolla_enable_prometheus }}" - cloudkitty_enabled: "{{ kolla_enable_cloudkitty }}" - telegraf_enabled: "{{ kolla_enable_telegraf }}" internal_tls_enabled: "{{ kolla_enable_tls_internal }}" external_tls_enabled: "{{ kolla_enable_tls_external }}" firewalld_enabled_all: >- @@ -82,6 +116,9 @@ stackhpc_package_repos_enabled: "{{ stackhpc_repos_enabled }}" pulp_tls_enabled: "{{ pulp_enable_tls }}" kolla_image_tags: "{{ kolla_image_tags }}" + gpu_passthrough_enabled: "{{ gpu_group_map | length > 0 | bool }}" + vGPU_enabled: "{{ groups['vgpu'] | length > 0 | bool }}" + hugepages_enabled: "{{ hostvars | group_hostvars_by_var('hugepages_enabled') }}" ansible.builtin.copy: content: "{{ cloud_facts | to_nice_json(sort_keys=false) }}" dest: ~/cloud-facts.json