Skip to content

Conversation

GavinHeff
Copy link
Contributor

Adds playbook to gather more advanced facts about a multinode cloud

Copy link
Member

Choose a reason for hiding this comment

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

It appears to have picked up some stray changes from another PR. You might need another rebase

#!usr/bin/env python3

from ansible.errors import AnsibleFilterError
# from ansible.plugins.filter.core imprt FilterModule
Copy link
Member

Choose a reason for hiding this comment

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

Is this required?

Comment on lines +80 to 82
# ngs_enabled_: "{{ 'genericswitch' in (kolla_neutron_ml2_mechanism_drivers | default([])) | bool }}"
# # ABOVE LEADS TO ERROR: argument of type ''NoneType'' is not iterable. argument of type ''NoneType'' is not iterable'
kayobe_managed_switches: "{{ groups['switches'] | 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.

I'd guess it's only defined if it's used, so could you add a check to first ensure kolla_neutron_ml2_mechanism_drivers is defined, and if it is, check the values it contains?

Comment on lines +2 to +14
- name: Can the control host reach the internet?
hosts: localhost
gather_facts: true
tasks:
- name: Check internet connectivity
ansible.builtin.command: "ping stackhpc.com -c 3"
register: internet_connectivity
timeout: 5
ignore_errors: true

- name: Set internet connectivity fact
ansible.builtin.set_fact:
internet_connectivity: "{{ not internet_connectivity.failed }}"
Copy link
Member

Choose a reason for hiding this comment

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

Two things to note here:

  1. it's probably useful to know this fact for all the hosts, not just localhost, so I'd move it to the "All" section later.
  2. It's not just checking internet connectivity, it's also resolving the DNS entry for stackhpc.com, and it's ICMP (ping) in particular. Could it be rephrased, so it's more specific about what it's doing? i.e. "pinging stackhpc.com"

Copy link
Member

Choose a reason for hiding this comment

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

If you want to extend it further, you could also add a curl command in, to check other protocols, ping an IP address (to sidestep DNS), or ping something that isn't stackhpc.com

register: kernel_version

- name: Get running contianers
ansible.builtin.command: "docker ps --format {% raw %}'{{.Names}}'{% endraw %}"
Copy link
Member

Choose a reason for hiding this comment

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

This could fail if it runs on a host that doesn't have docker installed, so needs to account for that

ansible.builtin.copy:
content: "{{ cloud_facts | to_nice_json(sort_keys=false) }}"
dest: ~/cloud-facts.json
dest: ~/cloud-facts_2.json
Copy link
Member

Choose a reason for hiding this comment

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

Lets just keep this as cloud-facts.json

self.group_hostvars_by_host
}

def group_hostvars_by_var(self, hostvars, var, stdout=None):
Copy link
Member

Choose a reason for hiding this comment

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

Could you add docstrings for these filters?

@Alex-Welsh
Copy link
Member

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new playbook to gather more detailed facts about the cloud environment, supported by a new custom Ansible filter. While the overall direction is good, the new filter plugin in filters.py contains several issues, including a critical bug that silences errors, leftover debugging comments, and inconsistencies. The get-cloud-facts.yml playbook also has some potential problems, such as a misleading fact name and unsafe variable access that could lead to playbook failures. I have provided specific comments and code suggestions to address these issues and improve the robustness and clarity of the new code.

result[host] = []

except KeyError as e:
AnsibleFilterError(f"Host {host} not found in hostvars.keys(): {e}")

Choose a reason for hiding this comment

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

critical

The AnsibleFilterError is instantiated but not raised, which means any KeyError is silently ignored. This is a critical bug and is likely the cause of the problem mentioned in the comment on line 36. The exception must be raised to signal an error. I've also improved the error message to be more specific.

                raise AnsibleFilterError(f"Variable '{var}' not found for host '{host}': {e}")

try:
indiv_host_vars = hostvars[host][var]
if stdout is not None:
indiv_host_vars = indiv_host_vars['stdout_lines']

Choose a reason for hiding this comment

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

high

The stdout parameter is used as a flag to access the hardcoded key 'stdout_lines'. This is inconsistent with group_hostvars_by_var, where stdout is used as the key itself. To improve consistency and flexibility, this function should also use the value of the stdout parameter as the key. You will need to update the call in get-cloud-facts.yml to pass 'stdout_lines' instead of 'stdout'.

                    indiv_host_vars = indiv_host_vars[stdout]

tasks:
- name: Check if hugepages enabled
ansible.builtin.set_fact:
hugepages_enabled: "{{ ansible_facts.cmdline }}"

Choose a reason for hiding this comment

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

high

The fact hugepages_enabled is set to the value of ansible_facts.cmdline, which is a dictionary of all kernel command-line parameters. This is misleading, as the name suggests a boolean value. It would be clearer and less error-prone to set this fact to a boolean indicating whether hugepages are actually configured.

        hugepages_enabled: "{{ 'hugepages' in ansible_facts.cmdline }}"

ansible_control_host_distribution: "{{ ansible_facts.distribution }}"
ansible_control_host_distribution_release: "{{ ansible_facts.distribution_release }}"
internet_connectivity: "{{ internet_connectivity }}"
seed_node_is_vm: "{{ hostvars[groups['seed'][0]]['seed_node_is_vm'] | default('N/A') }}"

Choose a reason for hiding this comment

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

high

Accessing groups['seed'][0] directly will cause the playbook to fail if the seed group is empty. It's safer to check for the group's existence before trying to access an element from it.

          seed_node_is_vm: "{{ hostvars[groups['seed'][0]]['seed_node_is_vm'] | default('N/A') if groups['seed'] else 'N/A' }}"

@@ -0,0 +1,57 @@
#!usr/bin/env python3

Choose a reason for hiding this comment

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

medium

The shebang is missing a leading /. While this file is a filter plugin and not directly executed, it's a best practice to have a correct shebang for consistency and to prevent issues if it's ever executed directly.

Suggested change
#!usr/bin/env python3
#!/usr/bin/env python3

#!usr/bin/env python3

from ansible.errors import AnsibleFilterError
# from ansible.plugins.filter.core imprt FilterModule

Choose a reason for hiding this comment

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

medium

This commented-out line appears to be a leftover from development. It includes a typo (imprt) and should be removed to keep the code clean.

Comment on lines +24 to +27
if indiv_host_var not in result.keys():
result[indiv_host_var] = [host]
else:
result[indiv_host_var].append(host)

Choose a reason for hiding this comment

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

medium

The logic for adding hosts to the result dictionary can be simplified using dict.setdefault(). This makes the code more concise and is considered more Pythonic.

                result.setdefault(indiv_host_var, []).append(host)

def group_hostvars_by_host(self, hostvars, var, stdout=None, hosts=None):
result = {}

### Currently returns blank dict, try block doesn't appear to be working

Choose a reason for hiding this comment

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

medium

This comment indicates that the code was not working during development. It should be removed before merging. The underlying issue seems to be in the except block below, which I've commented on separately.

Comment on lines +80 to +81
# ngs_enabled_: "{{ 'genericswitch' in (kolla_neutron_ml2_mechanism_drivers | default([])) | bool }}"
# # ABOVE LEADS TO ERROR: argument of type ''NoneType'' is not iterable. argument of type ''NoneType'' is not iterable'

Choose a reason for hiding this comment

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

medium

These commented-out lines and the associated error message appear to be leftovers from development and should be removed to keep the playbook clean.

ansible.builtin.copy:
content: "{{ cloud_facts | to_nice_json(sort_keys=false) }}"
dest: ~/cloud-facts.json
dest: ~/cloud-facts_2.json

Choose a reason for hiding this comment

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

medium

The destination filename ~/cloud-facts_2.json seems like a temporary name used for development or testing. Consider changing it back to the original ~/cloud-facts.json or a more suitable permanent name.

        dest: ~/cloud-facts.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants