--- - name: build inventory hosts: localhost # user: ansible vars: ## default inventory file specified in the ansible.cfg # _inventory_file_name: "{{ ansible_inventory_sources[0] }}" # ## custom inventory file _inventory_file_name: "{{ ansible_inventory_sources[0].split('/')[:-1] | join('/') }}/xcat-inventory.yml" _overwrite_inventory_file: False # ## enable dynamic inventory, disables writing inventory file _dynamic_inventory: True ## xcat API connection _xcat_api: xcat_user: "ansible" xcat_password: "0cf5t33lAcc355" xcat_http_scheme: "http" xcat_api_endpoint: "127.0.0.1" xcat_api_endpoint_port: "80" # - This playbook can be called as the first task in the runner/site task to act as a purely dynamic inventory sourced from xcat, use the _dynamic_inventory variable to enable # - it will populate an in-memory inventory from xcat, meaning other inventory files do not need to be written # - when present, existing inventory file or group_vars/ + host_vars/ inventory sources will also be used as per ansibles inventory sourcing precedence (this will merge hostvars/groups, so watch out) tasks: ###### getting the xcat web service replying to API calls without ssl, purely for dev # sudo nano -cw /etc/httpd/conf.d/xcat-ws.conf # ORIGIONAL # ScriptAlias /xcatrhevh /opt/xcat/ws/xcatrhevh.cgi # ScriptAlias /xcatws /opt/xcat/ws/xcatws.cgi # LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so # RewriteEngine On # RewriteCond %{SERVER_PORT} 80 # RewriteCond %{HTTPS} !=on # RewriteRule ^/?xcatws/(.*) https://%{SERVER_NAME}/xcatws/$1 [R,L] # RewriteRule ^/?xcatwsv2/(.*) https://%{SERVER_NAME}/xcatwsv2/$1 [R,L] # # Require all granted # # DISABLED SSL AND CHANGE REDIRECT TO CGI SCRIPT @ HTTP # ScriptAlias /xcatrhevh /opt/xcat/ws/xcatrhevh.cgi # ScriptAlias /xcatws /opt/xcat/ws/xcatws.cgi # LoadModule rewrite_module /usr/lib64/apache2-prefork/mod_rewrite.so # RewriteEngine On # RewriteCond %{SERVER_PORT} 80 # RewriteCond %{HTTPS} !=off # RewriteRule ^/?xcatws/(.*) http://%{SERVER_NAME}/xcatws/$1 [R,L] # RewriteRule ^/?xcatwsv2/(.*) http://%{SERVER_NAME}/xcatwsv2/$1 [R,L] # # Require all granted # ###### finding xcat service account password # [root@xcat01(ansible-service) ~]# /opt/xcat/bin/gettab key=xcat passwd.username passwd.password # passwd.username: ansible # passwd.password: 0cf5t33lAcc355 ###### testing curl # curl -X GET 'http://127.0.0.1/xcatws/nodes?userName=ansible&userPW=0cf5t33lAcc355' ###### query the xcat API - name: set runtime facts set_fact: _xcat_api_token: "{{ _xcat_api['xcat_http_scheme'] }}://{{ _xcat_api['xcat_api_endpoint'] }}:{{ _xcat_api['xcat_api_endpoint_port'] }}/xcatws/tokens" _xcat_api_nodes: "{{ _xcat_api['xcat_http_scheme'] }}://{{ _xcat_api['xcat_api_endpoint'] }}:{{ _xcat_api['xcat_api_endpoint_port'] }}/xcatws/nodes" - name: get API auth cookie uri: url: "{{ _xcat_api_token }}?pretty=1" validate_certs: no method: POST headers: Content-Type: application/json body: '{"userName":"{{ xcat_user }}","userPW":"{{ xcat_password }}"}' body_format: json status_code: 201 vars: xcat_user: "{{ _xcat_api['xcat_user'] }}" xcat_password: "{{ _xcat_api['xcat_password'] }}" register: request - name: set API token set_fact: _xcat_api_token: "{{ request['json']['token']['id'] }}" - name: get nodes list uri: url: "{{ _xcat_api_nodes }}" validate_certs: no method: GET headers: X-Auth-Token: "{{ _xcat_api_token }}" status_code: 200 register: request - name: set node list set_fact: _node_list: "{{ request['json'] }}" - name: get nodes attributes uri: url: "{{ _xcat_api_nodes }}/{{ entry }}?pretty=1" validate_certs: no method: GET headers: X-Auth-Token: "{{ _xcat_api_token }}" status_code: 200 loop: "{{ _node_list }}" loop_control: loop_var: entry register: request # - debug: # msg: # - "{{ request }}" # - "{{ request['results'][1] }}" # - "{{ _node_list }}" ###### sort the API request into inventory ingestable format - name: create list of hosts and interfaces set_fact: _host_interfaces: "{{ _host_interfaces | default([]) +[ {'host': host, 'interfaces': interfaces} ] }}" loop: "{{ request['results'] }}" loop_control: loop_var: entry vars: host: "{{ entry['entry'] }}" host_record: "{{ entry['json'] }}" interfaces: "{{ host_record[host] | dict2items | selectattr('key', 'search', '^nicips.' ) | map(attribute='key') | map('split', '.') | map('last') }}" when: - interfaces | length >0 - name: build inventory artefacts set_fact: _xcat_nics: "{{ _xcat_nics | default([]) +[{ 'host': host, 'intspec': [{ 'device': interface, 'ip': nicip, 'network': nicnetwork, 'type': nictype }] }] }}" _host_groups: "{{ _host_groups | default([]) +[{ 'host': host, 'groups': remove_all_group }] }}" # build a dict to the ansible yaml inventory spec, include placeholder fields for xcat_nics/ipmi_nic, these fields will be populated with selectattr based on host name from xcat_nics dict _all_host_group: "{{ _all_host_group | default({}) | combine({ 'all': { 'hosts': { host: { 'ansible_ssh_host': primary_ip, 'xcat_nics': 'placeholder', 'ipmi_nic': 'placeholder' } } } }, recursive=True) }}" # unused - groups are created from host entries # _xcat_groups: "{{ _xcat_groups | default([]) +[remove_all_group] }}" with_subelements: - "{{ _host_interfaces }}" - "interfaces" loop_control: loop_var: entry vars: host: "{{ entry.0['host'] }}" interface: "{{ entry.1 }}" request_host_record: "{{ request['results'] | selectattr('entry', '==', host) | map(attribute='json') }}" nicips_key: "nicips.{{ interface }}" nicnetworks_key: "nicnetworks.{{ interface }}" nictype_key: "nictypes.{{ interface }}" nicip: "{{ request_host_record[0][host][nicips_key] | default('incomplete_record') }}" nicnetwork: "{{ request_host_record[0][host][nicnetworks_key] | default('incomplete_record') }}" nictype: "{{ request_host_record[0][host][nictype_key] | default('incomplete_record') }}" host_groups: "{{ request_host_record[0][host]['groups'].split(',') | default([]) }}" # remove 'all' group if added by xcat, this is a special group that will be used to store inventory hostvars and merged into the inventory dict remove_all_group: "{{ host_groups | difference(['all']) }}" primary_ip: "{{ request_host_record[0][host]['ip'] }}" when: - not nicip == 'incomplete_record' - not nicnetwork == 'incomplete_record' - not nictype == 'incomplete_record' - host_groups | length >0 # unused - groups are automatically created from host entries # - name: set runtime facts # set_fact: # _xcat_groups: "{{ _xcat_groups | flatten | unique | sort }}" - name: build 'all' group inventory dict set_fact: _all_host_group: "{{ _all_host_group | default({}) | combine({ 'all': { 'hosts': { host: all_host_group_entry } } }, recursive=True) }}" loop: "{{ _all_host_group['all']['hosts'] | list }}" loop_control: loop_var: entry vars: xcat_nics: "{{ _xcat_nics | selectattr('host', '==', entry) | map(attribute='intspec') | flatten | rejectattr('network', '==', 'ipmi') }}" ipmi_nic: "{{ _xcat_nics | selectattr('host', '==', entry) | map(attribute='intspec') | flatten | selectattr('network', '==', 'ipmi') }}" host: "{{ entry }}" all_host_group_entry: "{{ _all_host_group['all']['hosts'][entry] | combine({'xcat_nics': xcat_nics, 'ipmi_nic': ipmi_nic }, recursive=True) }}" - name: append groups to inventory dict set_fact: _other_host_groups: "{{ _other_host_groups | default({}) | combine({ group: { 'hosts': { host: none } } }, recursive=True) }}" with_subelements: - "{{ _host_groups }}" - "groups" loop_control: loop_var: entry vars: host: "{{ entry.0['host'] }}" group: "{{ entry.1 }}" - name: combine _all_host_group with _other_host_groups, inventory complete set_fact: _all_host_group: "{{ _all_host_group | default({}) | combine(_other_host_groups, recursive=True) }}" ###### write the inventory dict to flat file - name: write flat file inventory block: - name: find current user uid facts ansible.builtin.getent: database: passwd key: "{{ current_user }}" vars: current_user: "{{ lookup('env', 'USER') }}" - name: get current uid:gid ansible.builtin.set_fact: _current_uid: "{{ ansible_facts['getent_passwd'][current_user][1] }}" _current_gid: "{{ ansible_facts['getent_passwd'][current_user][2] }}" vars: current_user: "{{ lookup('env', 'USER') }}" - name: find current user primary group gid facts getent: database: group key: "{{ _current_gid }}" - name: get user/group names set_fact: _current_user: "{{ current_user }}" _current_group: "{{ primary_group[0] }}" vars: current_user: "{{ lookup('env', 'USER') }}" primary_group: "{{ ansible_facts['getent_group'] | dict2items | map(attribute='key') }}" - name: check for existing inventory file ansible.builtin.stat: path: "{{ _inventory_file_name }}" register: _inventory_present # note: the regex removal of 'null' (sourced from _other_host_groups {host: none}), this tidies the inventory file to look like the ansible spec examples although it is invalid yaml # ansible doesnt mind the 'null' host value in an inventory file but doesnt like strings - name: write to file copy: content: | #jinja2: lstrip_blocks: True {{ _all_host_group | to_nice_yaml(indent=2,sort_keys=False) | regex_replace('null', '') }} dest: "{{ _inventory_file_name }}" force: true owner: "{{ _current_user }}" group: "{{ _current_group }}" mode: 0640 when: - not _inventory_present['stat']['exists'] | bool or _overwrite_inventory_file | bool # test inventory # ansible -i output.yml -a "uname -a" when: - not _dynamic_inventory ###### write the networks dict to flat file # this same logic should be used to get all unique networks - these will be used in an api query # need to also get some other info from xcat such as the cluster name? ###### add hosts to in-memory inventory - name: add hosts to in-memory inventory add_host: > name={{ host }} groups={{ host_groups }} ansible_ssh_host={{ ansible_ssh_host }} xcat_nics={{ xcat_nics }} ipmi_nic={{ ipmi_nic }} dynamic_inventory=True loop: "{{ _all_host_group['all']['hosts'] | list }}" loop_control: loop_var: entry vars: host: "{{ entry }}" ansible_ssh_host: "{{ _all_host_group['all']['hosts'][entry]['ansible_ssh_host'] }}" xcat_nics: "{{ _all_host_group['all']['hosts'][entry]['xcat_nics'] }}" ipmi_nic: "{{ _all_host_group['all']['hosts'][entry]['ipmi_nic'] }}" host_groups: "{{ _host_groups | selectattr('host', '==', entry) | map(attribute='groups') | flatten | unique }}" when: - _dynamic_inventory # - debug: # msg: # - "{{ hostvars['compute001'] }}" # - "{{ groups }}" # - "{{ hostvars['compute001']['dynamic_inventory'] }}"