mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Implement contructable support for opennebula inventory plugin: keyed… (#4524)
* Implement contructable support for opennebula inventory plugin: keyed_groups, compose, groups * Fixed templating mock issues in unit tests, corrected some linting errors * trying to make the linter happy * Now trying to make python2.7 happy * Added changelog fragment * changelog fragment needs pluralization * Update changelogs/fragments/4524-update-opennebula-inventory-plugin-to-match-documentation.yaml Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
d9ba598938
commit
8e72e98adb
4 changed files with 342 additions and 26 deletions
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
bugfixes:
|
||||||
|
- opennebula inventory plugin - complete the implementation of ``constructable`` for opennebula inventory plugin. Now ``keyed_groups``, ``compose``, ``groups`` actually work (https://github.com/ansible-collections/community.general/issues/4497).
|
|
@ -206,28 +206,40 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
|
||||||
def _populate(self):
|
def _populate(self):
|
||||||
hostname_preference = self.get_option('hostname')
|
hostname_preference = self.get_option('hostname')
|
||||||
group_by_labels = self.get_option('group_by_labels')
|
group_by_labels = self.get_option('group_by_labels')
|
||||||
|
strict = self.get_option('strict')
|
||||||
|
|
||||||
# Add a top group 'one'
|
# Add a top group 'one'
|
||||||
self.inventory.add_group(group='all')
|
self.inventory.add_group(group='all')
|
||||||
|
|
||||||
filter_by_label = self.get_option('filter_by_label')
|
filter_by_label = self.get_option('filter_by_label')
|
||||||
for server in self._retrieve_servers(filter_by_label):
|
servers = self._retrieve_servers(filter_by_label)
|
||||||
|
for server in servers:
|
||||||
|
hostname = server['name']
|
||||||
# check for labels
|
# check for labels
|
||||||
if group_by_labels and server['LABELS']:
|
if group_by_labels and server['LABELS']:
|
||||||
for label in server['LABELS']:
|
for label in server['LABELS']:
|
||||||
self.inventory.add_group(group=label)
|
self.inventory.add_group(group=label)
|
||||||
self.inventory.add_host(host=server['name'], group=label)
|
self.inventory.add_host(host=hostname, group=label)
|
||||||
|
|
||||||
self.inventory.add_host(host=server['name'], group='all')
|
self.inventory.add_host(host=hostname, group='all')
|
||||||
|
|
||||||
for attribute, value in server.items():
|
for attribute, value in server.items():
|
||||||
self.inventory.set_variable(server['name'], attribute, value)
|
self.inventory.set_variable(hostname, attribute, value)
|
||||||
|
|
||||||
if hostname_preference != 'name':
|
if hostname_preference != 'name':
|
||||||
self.inventory.set_variable(server['name'], 'ansible_host', server[hostname_preference])
|
self.inventory.set_variable(hostname, 'ansible_host', server[hostname_preference])
|
||||||
|
|
||||||
if server.get('SSH_PORT'):
|
if server.get('SSH_PORT'):
|
||||||
self.inventory.set_variable(server['name'], 'ansible_port', server['SSH_PORT'])
|
self.inventory.set_variable(hostname, 'ansible_port', server['SSH_PORT'])
|
||||||
|
|
||||||
|
# handle construcable implementation: get composed variables if any
|
||||||
|
self._set_composite_vars(self.get_option('compose'), server, hostname, strict=strict)
|
||||||
|
|
||||||
|
# groups based on jinja conditionals get added to specific groups
|
||||||
|
self._add_host_to_composed_groups(self.get_option('groups'), server, hostname, strict=strict)
|
||||||
|
|
||||||
|
# groups based on variables associated with them in the inventory
|
||||||
|
self._add_host_to_keyed_groups(self.get_option('keyed_groups'), server, hostname, strict=strict)
|
||||||
|
|
||||||
def parse(self, inventory, loader, path, cache=True):
|
def parse(self, inventory, loader, path, cache=True):
|
||||||
if not HAS_PYONE:
|
if not HAS_PYONE:
|
||||||
|
|
222
tests/unit/plugins/inventory/fixtures/opennebula_inventory.json
Normal file
222
tests/unit/plugins/inventory/fixtures/opennebula_inventory.json
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"DEPLOY_ID": "bcfec9d9-c0d0-4523-b5e7-62993947e94c",
|
||||||
|
"ETIME": 0,
|
||||||
|
"GID": 105,
|
||||||
|
"GNAME": "SW",
|
||||||
|
"HISTORY_RECORDS": {},
|
||||||
|
"ID": 451,
|
||||||
|
"LAST_POLL": 0,
|
||||||
|
"LCM_STATE": 3,
|
||||||
|
"MONITORING": {},
|
||||||
|
"NAME": "terraform_demo_00",
|
||||||
|
"RESCHED": 0,
|
||||||
|
"STATE": 3,
|
||||||
|
"STIME": 1649886492,
|
||||||
|
"TEMPLATE": {
|
||||||
|
"NIC": [
|
||||||
|
{
|
||||||
|
"AR_ID": "0",
|
||||||
|
"BRIDGE": "mgmt0",
|
||||||
|
"BRIDGE_TYPE": "linux",
|
||||||
|
"CLUSTER_ID": "0",
|
||||||
|
"IP": "192.168.11.248",
|
||||||
|
"MAC": "02:00:c0:a8:2b:bb",
|
||||||
|
"MODEL": "virtio",
|
||||||
|
"NAME": "NIC0",
|
||||||
|
"NETWORK": "Infrastructure",
|
||||||
|
"NETWORK_ID": "0",
|
||||||
|
"NIC_ID": "0",
|
||||||
|
"SECURITY_GROUPS": "0,101",
|
||||||
|
"TARGET": "one-453-0",
|
||||||
|
"VLAN_ID": "12",
|
||||||
|
"VN_MAD": "802.1Q"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"NIC_DEFAULT": {
|
||||||
|
"MODEL": "virtio"
|
||||||
|
},
|
||||||
|
"TEMPLATE_ID": "28",
|
||||||
|
"TM_MAD_SYSTEM": "shared",
|
||||||
|
"VCPU": "4",
|
||||||
|
"VMID": "453"
|
||||||
|
},
|
||||||
|
"USER_TEMPLATE": {
|
||||||
|
"GUEST_OS": "linux",
|
||||||
|
"INPUTS_ORDER": "",
|
||||||
|
"LABELS": "foo,bench",
|
||||||
|
"LOGO": "images/logos/linux.png",
|
||||||
|
"MEMORY_UNIT_COST": "MB",
|
||||||
|
"SCHED_REQUIREMENTS": "ARCH=\"x86_64\"",
|
||||||
|
"TGROUP": "bench_clients"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DEPLOY_ID": "25895435-5e3a-4d50-a025-e03a7a463abd",
|
||||||
|
"ETIME": 0,
|
||||||
|
"GID": 105,
|
||||||
|
"GNAME": "SW",
|
||||||
|
"HISTORY_RECORDS": {},
|
||||||
|
"ID": 451,
|
||||||
|
"LAST_POLL": 0,
|
||||||
|
"LCM_STATE": 3,
|
||||||
|
"MONITORING": {},
|
||||||
|
"NAME": "terraform_demo_01",
|
||||||
|
"RESCHED": 0,
|
||||||
|
"STATE": 3,
|
||||||
|
"STIME": 1649886492,
|
||||||
|
"TEMPLATE": {
|
||||||
|
"NIC": [
|
||||||
|
{
|
||||||
|
"AR_ID": "0",
|
||||||
|
"BRIDGE": "mgmt0",
|
||||||
|
"BRIDGE_TYPE": "linux",
|
||||||
|
"CLUSTER_ID": "0",
|
||||||
|
"IP": "192.168.11.241",
|
||||||
|
"MAC": "02:00:c0:a8:4b:bb",
|
||||||
|
"MODEL": "virtio",
|
||||||
|
"NAME": "NIC0",
|
||||||
|
"NETWORK": "Infrastructure",
|
||||||
|
"NETWORK_ID": "0",
|
||||||
|
"NIC_ID": "0",
|
||||||
|
"SECURITY_GROUPS": "0,101",
|
||||||
|
"TARGET": "one-451-0",
|
||||||
|
"VLAN_ID": "12",
|
||||||
|
"VN_MAD": "802.1Q"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"NIC_DEFAULT": {
|
||||||
|
"MODEL": "virtio"
|
||||||
|
},
|
||||||
|
"TEMPLATE_ID": "28",
|
||||||
|
"TM_MAD_SYSTEM": "shared",
|
||||||
|
"VCPU": "4",
|
||||||
|
"VMID": "451"
|
||||||
|
},
|
||||||
|
"USER_TEMPLATE": {
|
||||||
|
"GUEST_OS": "linux",
|
||||||
|
"INPUTS_ORDER": "",
|
||||||
|
"LABELS": "foo,bench",
|
||||||
|
"LOGO": "images/logos/linux.png",
|
||||||
|
"MEMORY_UNIT_COST": "MB",
|
||||||
|
"SCHED_REQUIREMENTS": "ARCH=\"x86_64\"",
|
||||||
|
"TESTATTR": "testvar",
|
||||||
|
"TGROUP": "bench_clients"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DEPLOY_ID": "2b00c379-3601-45ee-acf5-e7b3ff2b7bca",
|
||||||
|
"ETIME": 0,
|
||||||
|
"GID": 105,
|
||||||
|
"GNAME": "SW",
|
||||||
|
"HISTORY_RECORDS": {},
|
||||||
|
"ID": 451,
|
||||||
|
"LAST_POLL": 0,
|
||||||
|
"LCM_STATE": 3,
|
||||||
|
"MONITORING": {},
|
||||||
|
"NAME": "terraform_demo_srv_00",
|
||||||
|
"RESCHED": 0,
|
||||||
|
"STATE": 3,
|
||||||
|
"STIME": 1649886492,
|
||||||
|
"TEMPLATE": {
|
||||||
|
"NIC": [
|
||||||
|
{
|
||||||
|
"AR_ID": "0",
|
||||||
|
"BRIDGE": "mgmt0",
|
||||||
|
"BRIDGE_TYPE": "linux",
|
||||||
|
"CLUSTER_ID": "0",
|
||||||
|
"IP": "192.168.11.247",
|
||||||
|
"MAC": "02:00:c0:a8:0b:cc",
|
||||||
|
"MODEL": "virtio",
|
||||||
|
"NAME": "NIC0",
|
||||||
|
"NETWORK": "Infrastructure",
|
||||||
|
"NETWORK_ID": "0",
|
||||||
|
"NIC_ID": "0",
|
||||||
|
"SECURITY_GROUPS": "0,101",
|
||||||
|
"TARGET": "one-452-0",
|
||||||
|
"VLAN_ID": "12",
|
||||||
|
"VN_MAD": "802.1Q"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"NIC_DEFAULT": {
|
||||||
|
"MODEL": "virtio"
|
||||||
|
},
|
||||||
|
"TEMPLATE_ID": "28",
|
||||||
|
"TM_MAD_SYSTEM": "shared",
|
||||||
|
"VCPU": "4",
|
||||||
|
"VMID": "452"
|
||||||
|
},
|
||||||
|
"USER_TEMPLATE": {
|
||||||
|
"GUEST_OS": "linux",
|
||||||
|
"INPUTS_ORDER": "",
|
||||||
|
"LABELS": "serv,bench",
|
||||||
|
"LOGO": "images/logos/linux.png",
|
||||||
|
"MEMORY_UNIT_COST": "MB",
|
||||||
|
"SCHED_REQUIREMENTS": "ARCH=\"x86_64\"",
|
||||||
|
"TGROUP": "bench_server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DEPLOY_ID": "97037f55-dd2c-4549-8d24-561a6569e870",
|
||||||
|
"ETIME": 0,
|
||||||
|
"GID": 105,
|
||||||
|
"GNAME": "SW",
|
||||||
|
"HISTORY_RECORDS": {},
|
||||||
|
"ID": 311,
|
||||||
|
"LAST_POLL": 0,
|
||||||
|
"LCM_STATE": 3,
|
||||||
|
"MONITORING": {},
|
||||||
|
"NAME": "bs-windows",
|
||||||
|
"RESCHED": 0,
|
||||||
|
"STATE": 3,
|
||||||
|
"STIME": 1648076254,
|
||||||
|
"TEMPLATE": {
|
||||||
|
"NIC": [
|
||||||
|
{
|
||||||
|
"AR_ID": "0",
|
||||||
|
"BRIDGE": "mgmt0",
|
||||||
|
"BRIDGE_TYPE": "linux",
|
||||||
|
"CLUSTER_ID": "0",
|
||||||
|
"IP": "192.168.11.209",
|
||||||
|
"MAC": "02:00:c0:a8:0b:dd",
|
||||||
|
"MODEL": "virtio",
|
||||||
|
"NAME": "NIC0",
|
||||||
|
"NETWORK": "Infrastructure",
|
||||||
|
"NETWORK_ID": "0",
|
||||||
|
"NETWORK_UNAME": "admin",
|
||||||
|
"NIC_ID": "0",
|
||||||
|
"SECURITY_GROUPS": "0,101",
|
||||||
|
"TARGET": "one-311-0",
|
||||||
|
"VLAN_ID": "12",
|
||||||
|
"VN_MAD": "802.1Q"
|
||||||
|
},
|
||||||
|
[
|
||||||
|
"TEMPLATE_ID",
|
||||||
|
"23"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"TM_MAD_SYSTEM",
|
||||||
|
"shared"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"VCPU",
|
||||||
|
"4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"VMID",
|
||||||
|
"311"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"UID": 22,
|
||||||
|
"UNAME": "bsanders",
|
||||||
|
"USER_TEMPLATE": {
|
||||||
|
"GUEST_OS": "windows",
|
||||||
|
"INPUTS_ORDER": "",
|
||||||
|
"LABELS": "serv",
|
||||||
|
"HYPERVISOR": "kvm",
|
||||||
|
"SCHED_REQUIREMENTS": "ARCH=\"x86_64\"",
|
||||||
|
"SET_HOSTNAME": "windows"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -9,14 +9,18 @@ from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ansible.inventory.data import InventoryData
|
from ansible.inventory.data import InventoryData
|
||||||
|
from ansible.parsing.dataloader import DataLoader
|
||||||
|
from ansible.template import Templar
|
||||||
from ansible_collections.community.general.plugins.inventory.opennebula import InventoryModule
|
from ansible_collections.community.general.plugins.inventory.opennebula import InventoryModule
|
||||||
|
from ansible_collections.community.general.tests.unit.compat.mock import create_autospec
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture
|
||||||
def inventory():
|
def inventory():
|
||||||
r = InventoryModule()
|
r = InventoryModule()
|
||||||
r.inventory = InventoryData()
|
r.inventory = InventoryData()
|
||||||
|
@ -33,6 +37,18 @@ def test_verify_file_bad_config(inventory):
|
||||||
assert inventory.verify_file('foobar.opennebula.yml') is False
|
assert inventory.verify_file('foobar.opennebula.yml') is False
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_pool_json():
|
||||||
|
with open('tests/unit/plugins/inventory/fixtures/opennebula_inventory.json', 'r') as json_file:
|
||||||
|
jsondata = json.load(json_file)
|
||||||
|
|
||||||
|
data = type('pyone.bindings.VM_POOLSub', (object,), {'VM': []})()
|
||||||
|
|
||||||
|
for fake_server in jsondata:
|
||||||
|
data.VM.append(type('pyone.bindings.VMType90Sub', (object,), fake_server)())
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_vm_pool():
|
def get_vm_pool():
|
||||||
data = type('pyone.bindings.VM_POOLSub', (object,), {'VM': []})()
|
data = type('pyone.bindings.VM_POOLSub', (object,), {'VM': []})()
|
||||||
|
|
||||||
|
@ -195,36 +211,99 @@ def get_vm_pool():
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_option(option):
|
options_base_test = {
|
||||||
if option == 'api_url':
|
'api_url': 'https://opennebula:2633/RPC2',
|
||||||
return 'https://opennebula:2633/RPC2'
|
'api_username': 'username',
|
||||||
if option == 'api_username':
|
'api_password': 'password',
|
||||||
return 'username'
|
'api_authfile': '~/.one/one_auth',
|
||||||
elif option == 'api_password':
|
'hostname': 'v4_first_ip',
|
||||||
return 'password'
|
'group_by_labels': True,
|
||||||
elif option == 'api_authfile':
|
'filter_by_label': None,
|
||||||
return '~/.one/one_auth'
|
}
|
||||||
elif option == 'hostname':
|
|
||||||
return 'v4_first_ip'
|
options_constructable_test = options_base_test.copy()
|
||||||
elif option == 'group_by_labels':
|
options_constructable_test.update({
|
||||||
return True
|
'compose': {'is_linux': "GUEST_OS == 'linux'"},
|
||||||
elif option == 'filter_by_label':
|
'filter_by_label': 'bench',
|
||||||
return None
|
'groups': {
|
||||||
else:
|
'benchmark_clients': "TGROUP.endswith('clients')",
|
||||||
return False
|
'lin': 'is_linux == True'
|
||||||
|
},
|
||||||
|
'keyed_groups': [{'key': 'TGROUP', 'prefix': 'tgroup'}],
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# given a dictionary `opts_dict`, return a function that behaves like ansible's inventory get_options
|
||||||
|
def mk_get_options(opts_dict):
|
||||||
|
def inner(opt):
|
||||||
|
return opts_dict.get(opt, False)
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def test_get_connection_info(inventory, mocker):
|
def test_get_connection_info(inventory, mocker):
|
||||||
inventory.get_option = mocker.MagicMock(side_effect=get_option)
|
inventory.get_option = mocker.MagicMock(side_effect=mk_get_options(options_base_test))
|
||||||
|
|
||||||
auth = inventory._get_connection_info()
|
auth = inventory._get_connection_info()
|
||||||
assert (auth.username and auth.password)
|
assert (auth.username and auth.password)
|
||||||
|
|
||||||
|
|
||||||
|
def test_populate_constructable_templating(inventory, mocker):
|
||||||
|
# bypass API fetch call
|
||||||
|
inventory._get_vm_pool = mocker.MagicMock(side_effect=get_vm_pool_json)
|
||||||
|
inventory.get_option = mocker.MagicMock(side_effect=mk_get_options(options_constructable_test))
|
||||||
|
|
||||||
|
# the templating engine is needed for the constructable groups/vars
|
||||||
|
# so give that some fake data and instantiate it.
|
||||||
|
fake_config_filepath = '/fake/opennebula.yml'
|
||||||
|
fake_cache = {fake_config_filepath: options_constructable_test.copy()}
|
||||||
|
fake_cache[fake_config_filepath]['plugin'] = 'community.general.opennebula'
|
||||||
|
dataloader = create_autospec(DataLoader, instance=True)
|
||||||
|
dataloader._FILE_CACHE = fake_cache
|
||||||
|
inventory.templar = Templar(loader=dataloader)
|
||||||
|
|
||||||
|
inventory._populate()
|
||||||
|
|
||||||
|
# note the vm_pool (and json data file) has four hosts,
|
||||||
|
# but options_constructable_test asks ansible to filter it out
|
||||||
|
assert len(get_vm_pool_json().VM) == 4
|
||||||
|
assert set([vm.NAME for vm in get_vm_pool_json().VM]) == set([
|
||||||
|
'terraform_demo_00',
|
||||||
|
'terraform_demo_01',
|
||||||
|
'terraform_demo_srv_00',
|
||||||
|
'bs-windows',
|
||||||
|
])
|
||||||
|
assert set(inventory.inventory.hosts) == set(['terraform_demo_00', 'terraform_demo_01', 'terraform_demo_srv_00'])
|
||||||
|
|
||||||
|
host_demo00 = inventory.inventory.get_host('terraform_demo_00')
|
||||||
|
host_demo01 = inventory.inventory.get_host('terraform_demo_01')
|
||||||
|
host_demosrv = inventory.inventory.get_host('terraform_demo_srv_00')
|
||||||
|
|
||||||
|
assert 'benchmark_clients' in inventory.inventory.groups
|
||||||
|
assert 'lin' in inventory.inventory.groups
|
||||||
|
assert inventory.inventory.groups['benchmark_clients'].hosts == [host_demo00, host_demo01]
|
||||||
|
assert inventory.inventory.groups['lin'].hosts == [host_demo00, host_demo01, host_demosrv]
|
||||||
|
|
||||||
|
# test group by label:
|
||||||
|
assert 'bench' in inventory.inventory.groups
|
||||||
|
assert 'foo' in inventory.inventory.groups
|
||||||
|
assert inventory.inventory.groups['bench'].hosts == [host_demo00, host_demo01, host_demosrv]
|
||||||
|
assert inventory.inventory.groups['serv'].hosts == [host_demosrv]
|
||||||
|
assert inventory.inventory.groups['foo'].hosts == [host_demo00, host_demo01]
|
||||||
|
|
||||||
|
# test `compose` transforms GUEST_OS=Linux to is_linux == True
|
||||||
|
assert host_demo00.get_vars()['GUEST_OS'] == 'linux'
|
||||||
|
assert host_demo00.get_vars()['is_linux'] is True
|
||||||
|
|
||||||
|
# test `keyed_groups`
|
||||||
|
assert inventory.inventory.groups['tgroup_bench_clients'].hosts == [host_demo00, host_demo01]
|
||||||
|
assert inventory.inventory.groups['tgroup_bench_server'].hosts == [host_demosrv]
|
||||||
|
|
||||||
|
|
||||||
def test_populate(inventory, mocker):
|
def test_populate(inventory, mocker):
|
||||||
# bypass API fetch call
|
# bypass API fetch call
|
||||||
inventory._get_vm_pool = mocker.MagicMock(side_effect=get_vm_pool)
|
inventory._get_vm_pool = mocker.MagicMock(side_effect=get_vm_pool)
|
||||||
inventory.get_option = mocker.MagicMock(side_effect=get_option)
|
inventory.get_option = mocker.MagicMock(side_effect=mk_get_options(options_base_test))
|
||||||
inventory._populate()
|
inventory._populate()
|
||||||
|
|
||||||
# get different hosts
|
# get different hosts
|
||||||
|
|
Loading…
Reference in a new issue