Add keyed_groups feature (#52045)

This implements:
- Allow creating keyed group parents
pull/53353/head
Alan Rominger 6 years ago committed by Sloane Hertel
parent 646a8586a5
commit 56e3597856

@ -0,0 +1,2 @@
minor_changes:
- keyed_groups now has a 'parent_group' keyword that allows assigning all generated groups to the same parent group

@ -335,7 +335,6 @@ class Constructable(object):
def _add_host_to_keyed_groups(self, keys, variables, host, strict=False): def _add_host_to_keyed_groups(self, keys, variables, host, strict=False):
''' helper to create groups for plugins based on variable values and add the corresponding hosts to it''' ''' helper to create groups for plugins based on variable values and add the corresponding hosts to it'''
if keys and isinstance(keys, list): if keys and isinstance(keys, list):
groups = []
for keyed in keys: for keyed in keys:
if keyed and isinstance(keyed, dict): if keyed and isinstance(keyed, dict):
@ -349,26 +348,33 @@ class Constructable(object):
if key: if key:
prefix = keyed.get('prefix', '') prefix = keyed.get('prefix', '')
sep = keyed.get('separator', '_') sep = keyed.get('separator', '_')
raw_parent_name = keyed.get('parent_group', None)
new_raw_group_names = []
if isinstance(key, string_types): if isinstance(key, string_types):
groups.append('%s%s%s' % (prefix, sep, key)) new_raw_group_names.append(key)
elif isinstance(key, list): elif isinstance(key, list):
for name in key: for name in key:
groups.append('%s%s%s' % (prefix, sep, name)) new_raw_group_names.append(name)
elif isinstance(key, Mapping): elif isinstance(key, Mapping):
for (gname, gval) in key.items(): for (gname, gval) in key.items():
name = '%s%s%s' % (gname, sep, gval) name = '%s%s%s' % (gname, sep, gval)
groups.append('%s%s%s' % (prefix, sep, name)) new_raw_group_names.append(name)
else: else:
raise AnsibleParserError("Invalid group name format, expected a string or a list of them or dictionary, got: %s" % type(key)) raise AnsibleParserError("Invalid group name format, expected a string or a list of them or dictionary, got: %s" % type(key))
for bare_name in new_raw_group_names:
gname = to_safe_group_name('%s%s%s' % (prefix, sep, bare_name))
self.inventory.add_group(gname)
self.inventory.add_child(gname, host)
if raw_parent_name:
parent_name = to_safe_group_name(raw_parent_name)
self.inventory.add_group(parent_name)
self.inventory.add_child(parent_name, gname)
else: else:
if strict: if strict:
raise AnsibleParserError("No key or key resulted empty, invalid entry") raise AnsibleParserError("No key or key resulted empty, invalid entry")
else: else:
raise AnsibleParserError("Invalid keyed group entry, it must be a dictionary: %s " % keyed) raise AnsibleParserError("Invalid keyed group entry, it must be a dictionary: %s " % keyed)
# now actually add any groups
for group_name in groups:
gname = to_safe_group_name(group_name)
self.inventory.add_group(gname)
self.inventory.add_child(gname, host)

@ -53,9 +53,19 @@ EXAMPLES = r'''
- prefix: distro - prefix: distro
key: ansible_distribution key: ansible_distribution
# the following examples assume the first inventory is from contrib/inventory/ec2.py
# this creates a group per ec2 architecture and assign hosts to the matching ones (arch_x86_64, arch_sparc, etc) # this creates a group per ec2 architecture and assign hosts to the matching ones (arch_x86_64, arch_sparc, etc)
- prefix: arch - prefix: arch
key: ec2_architecture key: ec2_architecture
# this creates a group per ec2 region like "us_west_1"
- prefix: ""
separator: ""
key: ec2_region
# this creates a common parent group for all ec2 availability zones
- key: ec2_placement
parent_group: all_ec2_zones
''' '''
import os import os

@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
# Copyright 2019 Alan Rominger <arominge@redhat.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import pytest
from ansible.plugins.inventory.constructed import InventoryModule
from ansible.inventory.data import InventoryData
from ansible.template import Templar
@pytest.fixture(scope="module")
def inventory_module():
r = InventoryModule()
r.inventory = InventoryData()
r.templar = Templar(None)
return r
def test_group_by_value_only(inventory_module):
inventory_module.inventory.add_host('foohost')
inventory_module.inventory.set_variable('foohost', 'bar', 'my_group_name')
host = inventory_module.inventory.get_host('foohost')
keyed_groups = [
{
'prefix': '',
'separator': '',
'key': 'bar'
}
]
inventory_module._add_host_to_keyed_groups(
keyed_groups, host.vars, host.name, strict=False
)
assert 'my_group_name' in inventory_module.inventory.groups
group = inventory_module.inventory.groups['my_group_name']
assert group.hosts == [host]
def test_keyed_group_separator(inventory_module):
inventory_module.inventory.add_host('farm')
inventory_module.inventory.set_variable('farm', 'farmer', 'mcdonald')
inventory_module.inventory.set_variable('farm', 'barn', {'cow': 'betsy'})
host = inventory_module.inventory.get_host('farm')
keyed_groups = [
{
'prefix': 'farmer',
'separator': '_old_',
'key': 'farmer',
'unsafe': True
},
{
'separator': 'mmmmmmmmmm',
'key': 'barn',
'unsafe': True
}
]
inventory_module._add_host_to_keyed_groups(
keyed_groups, host.vars, host.name, strict=False
)
for group_name in ('farmer_old_mcdonald', 'mmmmmmmmmmcowmmmmmmmmmmbetsy'):
assert group_name in inventory_module.inventory.groups
group = inventory_module.inventory.groups[group_name]
assert group.hosts == [host]
def test_keyed_parent_groups(inventory_module):
inventory_module.inventory.add_host('web1')
inventory_module.inventory.add_host('web2')
inventory_module.inventory.set_variable('web1', 'region', 'japan')
inventory_module.inventory.set_variable('web2', 'region', 'japan')
host1 = inventory_module.inventory.get_host('web1')
host2 = inventory_module.inventory.get_host('web2')
keyed_groups = [
{
'prefix': 'region',
'key': 'region',
'parent_group': 'region_list'
}
]
for host in [host1, host2]:
inventory_module._add_host_to_keyed_groups(
keyed_groups, host.vars, host.name, strict=False
)
assert 'region_japan' in inventory_module.inventory.groups
assert 'region_list' in inventory_module.inventory.groups
region_group = inventory_module.inventory.groups['region_japan']
all_regions = inventory_module.inventory.groups['region_list']
assert all_regions.child_groups == [region_group]
assert region_group.hosts == [host1, host2]
Loading…
Cancel
Save