mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-02-04 07:51:50 +00:00
420 lines
14 KiB
Python
420 lines
14 KiB
Python
# Copyright (c) 2020, FELDSAM s.r.o. - FeldHost™ <support@feldhost.cz>
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
#
|
|
# The API responses used in these tests were recorded from OpenNebula version 5.10.
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from collections import OrderedDict
|
|
|
|
import pytest
|
|
from ansible import constants as C
|
|
from ansible.inventory.data import InventoryData
|
|
from ansible.inventory.manager import InventoryManager
|
|
from ansible.module_utils.common.text.converters import to_native
|
|
from ansible_collections.community.internal_test_tools.tests.unit.mock.loader import DictDataLoader
|
|
from ansible_collections.community.internal_test_tools.tests.unit.mock.path import mock_unfrackpath_noop
|
|
|
|
from ansible_collections.community.general.plugins.inventory.opennebula import InventoryModule
|
|
|
|
original_exists = os.path.exists
|
|
original_access = os.access
|
|
|
|
|
|
def exists_mock(path, exists=True):
|
|
def exists(f):
|
|
if to_native(f) == path:
|
|
return exists
|
|
return original_exists(f)
|
|
|
|
return exists
|
|
|
|
|
|
def access_mock(path, can_access=True):
|
|
def access(f, m, *args, **kwargs):
|
|
if to_native(f) == path:
|
|
return can_access
|
|
return original_access(f, m, *args, **kwargs) # pragma: no cover
|
|
|
|
return access
|
|
|
|
|
|
class HistoryEntry:
|
|
def __init__(self):
|
|
self.SEQ = "384"
|
|
self.HOSTNAME = "sam-691-sam"
|
|
self.HID = "10"
|
|
self.CID = "0"
|
|
self.DS_ID = "100"
|
|
self.VM_MAD = "kvm"
|
|
self.TM_MAD = "3par"
|
|
self.ACTION = "0"
|
|
|
|
|
|
class HistoryRecords:
|
|
def __init__(self):
|
|
self.HISTORY = [HistoryEntry()]
|
|
|
|
|
|
@pytest.fixture
|
|
def inventory():
|
|
r = InventoryModule()
|
|
r.inventory = InventoryData()
|
|
return r
|
|
|
|
|
|
def test_verify_file(tmp_path, inventory):
|
|
file = tmp_path / "foobar.opennebula.yml"
|
|
file.touch()
|
|
assert inventory.verify_file(str(file)) is True
|
|
|
|
|
|
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") 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": []})()
|
|
|
|
vm = type(
|
|
"pyone.bindings.VMType90Sub",
|
|
(object,),
|
|
{
|
|
"DEPLOY_ID": "one-7157",
|
|
"ETIME": 0,
|
|
"GID": 132,
|
|
"GNAME": "CSApparelVDC",
|
|
"HISTORY_RECORDS": HistoryRecords(),
|
|
"ID": 7157,
|
|
"LAST_POLL": 1632762935,
|
|
"LCM_STATE": 3,
|
|
"MONITORING": {},
|
|
"NAME": "sam-691-sam",
|
|
"RESCHED": 0,
|
|
"SNAPSHOTS": [],
|
|
"STATE": 3,
|
|
"STIME": 1632755245,
|
|
"TEMPLATE": OrderedDict(
|
|
{
|
|
"NIC": OrderedDict(
|
|
{
|
|
"AR_ID": "0",
|
|
"BRIDGE": "onebr80",
|
|
"BRIDGE_TYPE": "linux",
|
|
"CLUSTER_ID": "0",
|
|
"IP": "172.22.4.187",
|
|
"MAC": "02:00:ac:16:04:bb",
|
|
"MTU": "8192",
|
|
"NAME": "NIC0",
|
|
"NETWORK": "Private Net CSApparel",
|
|
"NETWORK_ID": "80",
|
|
"NETWORK_UNAME": "CSApparelVDC-admin",
|
|
"NIC_ID": "0",
|
|
"PHYDEV": "team0",
|
|
"SECURITY_GROUPS": "0",
|
|
"TARGET": "one-7157-0",
|
|
"VLAN_ID": "480",
|
|
"VN_MAD": "802.1Q",
|
|
}
|
|
)
|
|
}
|
|
),
|
|
"USER_TEMPLATE": OrderedDict(
|
|
{
|
|
"HYPERVISOR": "kvm",
|
|
"INPUTS_ORDER": "",
|
|
"LOGO": "images/logos/centos.png",
|
|
"MEMORY_UNIT_COST": "MB",
|
|
"SCHED_REQUIREMENTS": 'CLUSTER_ID="0"',
|
|
}
|
|
),
|
|
},
|
|
)()
|
|
data.VM.append(vm)
|
|
|
|
vm = type(
|
|
"pyone.bindings.VMType90Sub",
|
|
(object,),
|
|
{
|
|
"DEPLOY_ID": "one-327",
|
|
"ETIME": 0,
|
|
"GID": 0,
|
|
"GNAME": "oneadmin",
|
|
"HISTORY_RECORDS": [],
|
|
"ID": 327,
|
|
"LAST_POLL": 1632763543,
|
|
"LCM_STATE": 3,
|
|
"MONITORING": {},
|
|
"NAME": "zabbix-327",
|
|
"RESCHED": 0,
|
|
"SNAPSHOTS": [],
|
|
"STATE": 3,
|
|
"STIME": 1575410106,
|
|
"TEMPLATE": OrderedDict(
|
|
{
|
|
"NIC": [
|
|
OrderedDict(
|
|
{
|
|
"AR_ID": "0",
|
|
"BRIDGE": "onerb.103",
|
|
"BRIDGE_TYPE": "linux",
|
|
"IP": "185.165.1.1",
|
|
"IP6_GLOBAL": "2000:a001::b9ff:feae:aa0d",
|
|
"IP6_LINK": "fe80::b9ff:feae:aa0d",
|
|
"MAC": "02:00:b9:ae:aa:0d",
|
|
"NAME": "NIC0",
|
|
"NETWORK": "Public",
|
|
"NETWORK_ID": "7",
|
|
"NIC_ID": "0",
|
|
"PHYDEV": "team0",
|
|
"SECURITY_GROUPS": "0",
|
|
"TARGET": "one-327-0",
|
|
"VLAN_ID": "100",
|
|
"VN_MAD": "802.1Q",
|
|
}
|
|
),
|
|
OrderedDict(
|
|
{
|
|
"AR_ID": "0",
|
|
"BRIDGE": "br0",
|
|
"BRIDGE_TYPE": "linux",
|
|
"CLUSTER_ID": "0",
|
|
"IP": "192.168.1.1",
|
|
"MAC": "02:00:c0:a8:3b:01",
|
|
"NAME": "NIC1",
|
|
"NETWORK": "Management",
|
|
"NETWORK_ID": "11",
|
|
"NIC_ID": "1",
|
|
"SECURITY_GROUPS": "0",
|
|
"TARGET": "one-327-1",
|
|
"VN_MAD": "bridge",
|
|
}
|
|
),
|
|
]
|
|
}
|
|
),
|
|
"USER_TEMPLATE": OrderedDict(
|
|
{
|
|
"HYPERVISOR": "kvm",
|
|
"INPUTS_ORDER": "",
|
|
"LABELS": "Oracle Linux",
|
|
"LOGO": "images/logos/centos.png",
|
|
"MEMORY_UNIT_COST": "MB",
|
|
"SAVED_TEMPLATE_ID": "29",
|
|
}
|
|
),
|
|
},
|
|
)()
|
|
data.VM.append(vm)
|
|
|
|
vm = type(
|
|
"pyone.bindings.VMType90Sub",
|
|
(object,),
|
|
{
|
|
"DEPLOY_ID": "one-107",
|
|
"ETIME": 0,
|
|
"GID": 0,
|
|
"GNAME": "oneadmin",
|
|
"HISTORY_RECORDS": [],
|
|
"ID": 107,
|
|
"LAST_POLL": 1632764186,
|
|
"LCM_STATE": 3,
|
|
"MONITORING": {},
|
|
"NAME": "gitlab-107",
|
|
"RESCHED": 0,
|
|
"SNAPSHOTS": [],
|
|
"STATE": 3,
|
|
"STIME": 1572485522,
|
|
"TEMPLATE": OrderedDict(
|
|
{
|
|
"NIC": OrderedDict(
|
|
{
|
|
"AR_ID": "0",
|
|
"BRIDGE": "onerb.103",
|
|
"BRIDGE_TYPE": "linux",
|
|
"IP": "185.165.1.3",
|
|
"IP6_GLOBAL": "2000:a001::b9ff:feae:aa03",
|
|
"IP6_LINK": "fe80::b9ff:feae:aa03",
|
|
"MAC": "02:00:b9:ae:aa:03",
|
|
"NAME": "NIC0",
|
|
"NETWORK": "Public",
|
|
"NETWORK_ID": "7",
|
|
"NIC_ID": "0",
|
|
"PHYDEV": "team0",
|
|
"SECURITY_GROUPS": "0",
|
|
"TARGET": "one-107-0",
|
|
"VLAN_ID": "100",
|
|
"VN_MAD": "802.1Q",
|
|
}
|
|
)
|
|
}
|
|
),
|
|
"USER_TEMPLATE": OrderedDict(
|
|
{
|
|
"HYPERVISOR": "kvm",
|
|
"INPUTS_ORDER": "",
|
|
"LABELS": "Gitlab,Centos",
|
|
"LOGO": "images/logos/centos.png",
|
|
"MEMORY_UNIT_COST": "MB",
|
|
"SCHED_REQUIREMENTS": 'ID="0" | ID="1" | ID="2"',
|
|
"SSH_PORT": "8822",
|
|
}
|
|
),
|
|
},
|
|
)()
|
|
data.VM.append(vm)
|
|
|
|
return data
|
|
|
|
|
|
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,
|
|
}
|
|
|
|
|
|
# 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=mk_get_options(options_base_test))
|
|
|
|
auth = inventory._get_connection_info()
|
|
assert auth.username and auth.password
|
|
|
|
|
|
def test_populate_constructable_templating(mocker):
|
|
inventory_filename = "/fake/opennebula.yml"
|
|
|
|
mocker.patch.object(InventoryModule, "_get_vm_pool", side_effect=get_vm_pool_json)
|
|
mocker.patch("ansible_collections.community.general.plugins.inventory.opennebula.HAS_PYONE", True)
|
|
mocker.patch("ansible.inventory.manager.unfrackpath", mock_unfrackpath_noop)
|
|
mocker.patch("os.path.exists", exists_mock(inventory_filename))
|
|
mocker.patch("os.access", access_mock(inventory_filename))
|
|
|
|
# the templating engine is needed for the constructable groups/vars
|
|
# so give that some fake data and instantiate it.
|
|
C.INVENTORY_ENABLED = ["community.general.opennebula"]
|
|
inventory_file = {
|
|
inventory_filename: r"""
|
|
---
|
|
plugin: community.general.opennebula
|
|
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
|
|
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
|
|
"""
|
|
}
|
|
im = InventoryManager(loader=DictDataLoader(inventory_file), sources=inventory_filename)
|
|
|
|
# note the vm_pool (and json data file) has four hosts,
|
|
# but the options above asks ansible to filter one out
|
|
assert len(get_vm_pool_json().VM) == 4
|
|
assert {vm.NAME for vm in get_vm_pool_json().VM} == {
|
|
"terraform_demo_00",
|
|
"terraform_demo_01",
|
|
"terraform_demo_srv_00",
|
|
"bs-windows",
|
|
}
|
|
assert set(im._inventory.hosts) == {"terraform_demo_00", "terraform_demo_01", "terraform_demo_srv_00"}
|
|
|
|
host_demo00 = im._inventory.get_host("terraform_demo_00")
|
|
host_demo01 = im._inventory.get_host("terraform_demo_01")
|
|
host_demosrv = im._inventory.get_host("terraform_demo_srv_00")
|
|
|
|
assert "benchmark_clients" in im._inventory.groups
|
|
assert "lin" in im._inventory.groups
|
|
assert im._inventory.groups["benchmark_clients"].hosts == [host_demo00, host_demo01]
|
|
assert im._inventory.groups["lin"].hosts == [host_demo00, host_demo01, host_demosrv]
|
|
|
|
# test group by label:
|
|
assert "bench" in im._inventory.groups
|
|
assert "foo" in im._inventory.groups
|
|
assert im._inventory.groups["bench"].hosts == [host_demo00, host_demo01, host_demosrv]
|
|
assert im._inventory.groups["serv"].hosts == [host_demosrv]
|
|
assert im._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 im._inventory.groups["tgroup_bench_clients"].hosts == [host_demo00, host_demo01]
|
|
assert im._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=mk_get_options(options_base_test))
|
|
inventory._populate()
|
|
|
|
# get different hosts
|
|
host_sam = inventory.inventory.get_host("sam-691-sam")
|
|
host_zabbix = inventory.inventory.get_host("zabbix-327")
|
|
host_gitlab = inventory.inventory.get_host("gitlab-107")
|
|
|
|
# test if groups exists
|
|
assert "Gitlab" in inventory.inventory.groups
|
|
assert "Centos" in inventory.inventory.groups
|
|
assert "Oracle_Linux" in inventory.inventory.groups
|
|
|
|
# check if host_zabbix is in Oracle_Linux group
|
|
group_oracle_linux = inventory.inventory.groups["Oracle_Linux"]
|
|
assert group_oracle_linux.hosts == [host_zabbix]
|
|
|
|
# check if host_gitlab is in Gitlab and Centos group
|
|
group_gitlab = inventory.inventory.groups["Gitlab"]
|
|
group_centos = inventory.inventory.groups["Centos"]
|
|
assert group_gitlab.hosts == [host_gitlab]
|
|
assert group_centos.hosts == [host_gitlab]
|
|
|
|
# check IPv4 address
|
|
assert host_sam.get_vars()["v4_first_ip"] == "172.22.4.187"
|
|
|
|
# check IPv6 address
|
|
assert host_zabbix.get_vars()["v6_first_ip"] == "2000:a001::b9ff:feae:aa0d"
|
|
|
|
# check ansible_hosts
|
|
assert host_sam.get_vars()["ansible_host"] == "172.22.4.187"
|
|
assert host_zabbix.get_vars()["ansible_host"] == "185.165.1.1"
|
|
assert host_gitlab.get_vars()["ansible_host"] == "185.165.1.3"
|
|
|
|
# check for custom ssh port
|
|
assert host_gitlab.get_vars()["ansible_port"] == "8822"
|