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
2 changes: 2 additions & 0 deletions etc/kayobe/ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
87 changes: 87 additions & 0 deletions etc/kayobe/ansible/filter_plugins/filters.py
Original file line number Diff line number Diff line change
@@ -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
105 changes: 71 additions & 34 deletions etc/kayobe/ansible/get-cloud-facts.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,78 @@
---
- name: Get facts for control host
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Set facts rather than Get facts

Suggested change
- name: Get facts for control host
- name: Set facts for control host

Applies to subsequent plays as well

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be extra clear

Suggested change
- name: Get facts
- name: Set facts for all hosts

hosts: all
gather_facts: true
tasks:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a pass on spacing between these tasks. Sometimes there's no gap, sometimes there's two lines.
Should be one empty line between each task (no gap between the first task and the tasks: line)

- 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 }}"
Comment on lines +30 to +40
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went looking for ways to deduplicate this and it turns out you can specify multiple facts in one task, see docs

So you can do:

Suggested change
- 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: Set facts
ansible.builtin.set_fact:
internet_connectivity: "{{ not successful_ping.failed }}"
distribution: "{{ ansible_facts.distribution }}"
distribution_release: "{{ ansible_facts.distribution_release }}"
hugepages_enabled: "{{ 'hugepages' in ansible_facts.cmdline }}"

(note I've also added hugepages_enabled to my example)



- name: Get kernel version
ansible.builtin.command: "uname -r"
register: kernel_version

- name: Check if docker is installed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: capitalisation

Suggested change
- name: Check if docker is installed
- 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this play is now a bit similar to the previous "gather facts" plays. I'd rename it to be a bit more clear

Suggested change
- name: Gather Cloud Facts
- name: Write cloud facts file

hosts: localhost
gather_facts: true
tasks:
- 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' }}"
Expand All @@ -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: >-
Expand 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 }}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just by convention, lets keep all vars lowercase

Suggested change
vGPU_enabled: "{{ groups['vgpu'] | 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