1
0
Fork 0
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:
Bill Sanders 2022-04-21 04:16:15 -07:00 committed by GitHub
parent d9ba598938
commit 8e72e98adb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 342 additions and 26 deletions

View file

@ -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).

View file

@ -206,28 +206,40 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
def _populate(self):
hostname_preference = self.get_option('hostname')
group_by_labels = self.get_option('group_by_labels')
strict = self.get_option('strict')
# Add a top group 'one'
self.inventory.add_group(group='all')
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
if group_by_labels and server['LABELS']:
for label in server['LABELS']:
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():
self.inventory.set_variable(server['name'], attribute, value)
self.inventory.set_variable(hostname, attribute, value)
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'):
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):
if not HAS_PYONE:

View 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"
}
}
]

View file

@ -9,14 +9,18 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from collections import OrderedDict
import json
import pytest
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.tests.unit.compat.mock import create_autospec
@pytest.fixture(scope="module")
@pytest.fixture
def inventory():
r = InventoryModule()
r.inventory = InventoryData()
@ -33,6 +37,18 @@ def test_verify_file_bad_config(inventory):
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():
data = type('pyone.bindings.VM_POOLSub', (object,), {'VM': []})()
@ -195,36 +211,99 @@ def get_vm_pool():
return data
def get_option(option):
if option == 'api_url':
return 'https://opennebula:2633/RPC2'
if option == 'api_username':
return 'username'
elif option == 'api_password':
return 'password'
elif option == 'api_authfile':
return '~/.one/one_auth'
elif option == 'hostname':
return 'v4_first_ip'
elif option == 'group_by_labels':
return True
elif option == 'filter_by_label':
return None
else:
return False
options_base_test = {
'api_url': 'https://opennebula:2633/RPC2',
'api_username': 'username',
'api_password': 'password',
'api_authfile': '~/.one/one_auth',
'hostname': 'v4_first_ip',
'group_by_labels': True,
'filter_by_label': None,
}
options_constructable_test = options_base_test.copy()
options_constructable_test.update({
'compose': {'is_linux': "GUEST_OS == 'linux'"},
'filter_by_label': 'bench',
'groups': {
'benchmark_clients': "TGROUP.endswith('clients')",
'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):
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()
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):
# bypass API fetch call
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()
# get different hosts