TripleO Ansible project repository. Contains playbooks for use with TripleO OpenStack deployments.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
10 KiB

# Copyright 2020 Red Hat, Inc.
# All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
module: tripleo_all_nodes_data
- James Slagle (@slagle) <>
version_added: '2.8'
short_description: Renders the all_nodes data for TripleO as group_vars
notes: []
- This module renders the all_nodes data for TripleO as group_vars which are
then available on overcloud nodes.
- The number of forks to spawn in parallel to compute the data for each
service. Defaults to the forks set for ansible.
required: False
- name: Render all_nodes data
import json
from multiprocessing import Manager, Process
import os
import traceback
from ansible.errors import AnsibleError
from ansible.plugins.action import ActionBase
from ansible.plugins.filter import ipaddr
from ansible.utils.display import Display
DISPLAY = Display()
class ActionModule(ActionBase):
"""Renders the all_nodes data for TripleO as group_vars"""
def compute_service(self, service, all_nodes):
DISPLAY.vv("Processing {}".format(service))
# <service>_enabled: true
all_nodes[service + '_enabled'] = True
# <service>_node_ips: <list of ips>
DISPLAY.vv(" Computing data for {}_node_ips".format(service))
service_network = self.service_net_map.get(
service + '_network', 'ctlplane')
service_hosts = self.groups.get(service, [])
service_node_ips = list(
map(lambda host: self.h_vars[host][service_network + '_ip'],
for extra_node_ip in self.all_nodes_extra_map_data.get(
service + '_node_ips', []):
if extra_node_ip not in service_node_ips:
all_nodes[service + '_node_ips'] = service_node_ips
if self.nova_additional_cell:
# <service>_cell_node_names: <list of hostnames>
v = service_network + '_hostname'
service_cell_node_names = \
list(map(lambda host: self.h_vars[host][v],
all_nodes[service + '_cell_node_names'] = \
# <service>_node_names: <list of hostnames>
DISPLAY.vv(" Computing data for {}_node_names".format(service))
v = service_network + '_hostname'
service_node_names = \
list(map(lambda host: self.h_vars[host][v],
for extra_node_name in self.all_nodes_extra_map_data.get(
service + '_node_names', []):
if extra_node_name not in service_node_names:
all_nodes[service + '_node_names'] = service_node_names
# <service>_short_node_names: <list of hostnames>
DISPLAY.vv(" Computing data for {}_short_node_names".format(service))
service_short_node_names = \
list(map(lambda host: self.h_vars[host]['inventory_hostname'],
for extra_short_node_name in self.all_nodes_extra_map_data.get(
service + '_short_node_names', []):
if extra_short_node_name not in service_node_names:
all_nodes[service + '_short_node_names'] = \
# <service>_short_bootstrap_node_name: hostname
DISPLAY.vv(" Computing data for {}_short_bootstrap_node_name".format(service))
if self.all_nodes_extra_map_data.get(
service + '_short_bootstrap_node_name', None):
v = service + '_short_bootstrap_node_name'
service_hosts += self.all_nodes_extra_map_data[v]
if service_hosts:
all_nodes[service + '_short_bootstrap_node_name'] = \
# <service>_bootstrap_node_ip: hostname
DISPLAY.vv(" Computing data for {}_short_bootstrap_node_ip".format(service))
if self.all_nodes_extra_map_data.get(
service + '_bootstrap_node_ip', None):
v = service + '_bootstrap_node_ip'
service_bootstrap_node_ips = \
service_node_ips + self.all_nodes_extra_map_data[v]
service_bootstrap_node_ips = service_node_ips
if service_bootstrap_node_ips:
all_nodes[service + '_bootstrap_node_ip'] = \
def process_services(self, enabled_services, all_nodes, forks):
# This breaks up the enabled_services list into smaller lists with
# length equal to the number of forks.
enabled_services_length = len(enabled_services)
for i in range(0, enabled_services_length, forks):
# It would be nice to be able to use multiprocessing.Pool here,
# however, that resulted in many pickle errors.
# For each smaller list, spawn a process to compute each service in
# that chunk.
end = i + forks
if end > enabled_services_length:
end = enabled_services_length
processes = [Process(target=self.compute_service,
args=(enabled_services[x], all_nodes))
for x in range(i, end)]
[p.start() for p in processes]
[p.join() for p in processes]
[p.terminate() for p in processes]
def compute_all_nodes(self, all_nodes, task_vars):
DISPLAY.vv("Starting compute and render for all_nodes data")
# Internal Ansible objects for inventory and variables
inventory = self._task.get_variable_manager()._inventory
self.groups = inventory.get_groups_dict()
# host_vars
self.h_vars = self._task.get_variable_manager().get_vars()['hostvars']
# Needed tripleo variables for convenience
self.service_net_map = task_vars['service_net_map']
self.nova_additional_cell = task_vars['nova_additional_cell']
self.all_nodes_extra_map_data = task_vars['all_nodes_extra_map_data']
net_vip_map = task_vars['net_vip_map']
enabled_services = task_vars['enabled_services']
primary_role_name = task_vars['primary_role_name']
enabled_services += self.all_nodes_extra_map_data.get(
'enabled_services', [])
# make enabled_services unique
enabled_services = list(set(enabled_services))
all_nodes['enabled_services'] = enabled_services
forks = self._task.args.get('forks', task_vars['ansible_forks'])
DISPLAY.vv("forks set to {}".format(forks))
self.process_services(enabled_services, all_nodes, forks)
# <service>: service_network
DISPLAY.vv("Computing data for service_net_map")
for key, value in self.service_net_map.items():
all_nodes[key] = value
# all values from all_nodes_extra_map_data when nova_additional_cell
if self.nova_additional_cell:
for key, value in self.all_nodes_extra_map_data.items():
all_nodes[key] = value
# redis_vip: ip
DISPLAY.vv("Computing data for redis_vip")
if 'redis' in enabled_services or self.nova_additional_cell:
if 'redis_vip' in self.all_nodes_extra_map_data:
all_nodes['redis_vip'] = self.all_nodes_extra_map_data['redis_vip']
elif 'redis' in net_vip_map:
all_nodes['redis_vip'] = net_vip_map['redis']
# ovn_dbs_vip: ip
DISPLAY.vv("Computing data for ovn_dbs_vip")
if 'ovn_dbs' in enabled_services or self.nova_additional_cell:
if 'ovn_dbs_vip' in self.all_nodes_extra_map_data:
all_nodes['ovn_dbs_vip'] = \
elif 'ovn_dbs' in net_vip_map:
all_nodes['ovn_dbs_vip'] = net_vip_map['ovn_dbs']
DISPLAY.vv("Computing data for top level vars")
all_nodes['deploy_identifier'] = task_vars['deploy_identifier']
all_nodes['stack_action'] = task_vars['stack_action']
all_nodes['stack_update_type'] = task_vars['stack_update_type']
all_nodes['container_cli'] = task_vars['container_cli']
# controller_node_<ips/names>
# note that these are supposed to be strings, not lists
DISPLAY.vv("Computing data for controller node ips/names")
primary_hosts = self.groups.get(primary_role_name, [])
all_nodes['controller_node_ips'] = \
','.join(list(map(lambda host: self.h_vars[host]['ctlplane_ip'],
all_nodes['controller_node_names'] = \
','.join(list(map(lambda host: self.h_vars[host]['inventory_hostname'],
def run(self, tmp=None, task_vars=None):
"""Renders the all_nodes data for TripleO as group_vars"""
manager = Manager()
all_nodes = manager.dict()
self.compute_all_nodes(all_nodes, task_vars)
all_nodes = dict(all_nodes)
all_nodes_path = os.path.join(task_vars['playbook_dir'],
'group_vars', 'overcloud.json')
with open(all_nodes_path, 'w') as f:
DISPLAY.vv("Rendering all_nodes to {}".format(all_nodes_path))
json.dump(all_nodes, f, sort_keys=True, indent=4)
except Exception as e:
raise AnsibleError(str(e))
# multiprocessing can hang the plugin exit if there are still
# references to the Manager() object. Even though we have called
# .shutdown(), clean up all_nodes just to be safe.
all_nodes = None
return dict(all_nodes=all_nodes)