|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +# |
| 3 | +# Copyright (c) 2025, Jana Hoch <[email protected]> |
| 4 | +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) |
| 5 | +# SPDX-License-Identifier: GPL-3.0-or-later |
| 6 | + |
| 7 | +from __future__ import absolute_import, division, print_function |
| 8 | + |
| 9 | +__metaclass__ = type |
| 10 | + |
| 11 | +from unittest.mock import patch, Mock |
| 12 | + |
| 13 | +import pytest |
| 14 | + |
| 15 | +proxmoxer = pytest.importorskip("proxmoxer") |
| 16 | + |
| 17 | +from ansible_collections.community.proxmox.plugins.modules import proxmox_subnet |
| 18 | +from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import ( |
| 19 | + ModuleTestCase, |
| 20 | + set_module_args, |
| 21 | +) |
| 22 | +import ansible_collections.community.proxmox.plugins.module_utils.proxmox as proxmox_utils |
| 23 | + |
| 24 | +RAW_SUBNETS = [ |
| 25 | + { |
| 26 | + "type": "subnet", |
| 27 | + "subnet": "ans1-10.10.2.0-24", |
| 28 | + "zone": "ans1", |
| 29 | + "snat": 0, |
| 30 | + "cidr": "10.10.2.0/24", |
| 31 | + "id": "ans1-10.10.2.0-24", |
| 32 | + "mask": "24", |
| 33 | + "dhcp-range": [ |
| 34 | + { |
| 35 | + "start-address": "10.10.2.5", |
| 36 | + "end-address": "10.10.2.25" |
| 37 | + }, |
| 38 | + { |
| 39 | + "start-address": "10.10.2.50", |
| 40 | + "end-address": "10.10.2.100" |
| 41 | + } |
| 42 | + ], |
| 43 | + "digest": "c870dc42a3b5356b6037590e9552cbd5d2334963", |
| 44 | + "vnet": "test", |
| 45 | + "network": "10.10.2.0" |
| 46 | + } |
| 47 | +] |
| 48 | + |
| 49 | + |
| 50 | +def exit_json(*args, **kwargs): |
| 51 | + """function to patch over exit_json; package return data into an exception""" |
| 52 | + if 'changed' not in kwargs: |
| 53 | + kwargs['changed'] = False |
| 54 | + raise SystemExit(kwargs) |
| 55 | + |
| 56 | + |
| 57 | +def fail_json(*args, **kwargs): |
| 58 | + """function to patch over fail_json; package return data into an exception""" |
| 59 | + kwargs['failed'] = True |
| 60 | + raise SystemExit(kwargs) |
| 61 | + |
| 62 | + |
| 63 | +def get_module_args(vnet, subnet, zone, state='present', dhcp_range=None, snat=0, dhcp_range_update_mode='append'): |
| 64 | + return { |
| 65 | + 'api_host': 'host', |
| 66 | + 'api_user': 'user', |
| 67 | + 'api_password': 'password', |
| 68 | + 'vnet': vnet, |
| 69 | + 'subnet': subnet, |
| 70 | + 'zone': zone, |
| 71 | + 'state': state, |
| 72 | + 'dhcp_range': dhcp_range, |
| 73 | + 'snat': snat, |
| 74 | + 'dhcp_range_update_mode': dhcp_range_update_mode |
| 75 | + } |
| 76 | + |
| 77 | + |
| 78 | +class TestProxmoxSubnetModule(ModuleTestCase): |
| 79 | + def setUp(self): |
| 80 | + super(TestProxmoxSubnetModule, self).setUp() |
| 81 | + proxmox_utils.HAS_PROXMOXER = True |
| 82 | + self.module = proxmox_subnet |
| 83 | + self.fail_json_patcher = patch('ansible.module_utils.basic.AnsibleModule.fail_json', |
| 84 | + new=Mock(side_effect=fail_json)) |
| 85 | + self.exit_json_patcher = patch('ansible.module_utils.basic.AnsibleModule.exit_json', new=exit_json) |
| 86 | + |
| 87 | + self.fail_json_mock = self.fail_json_patcher.start() |
| 88 | + self.exit_json_patcher.start() |
| 89 | + self.connect_mock = patch( |
| 90 | + "ansible_collections.community.proxmox.plugins.module_utils.proxmox.ProxmoxAnsible._connect", |
| 91 | + ).start() |
| 92 | + self.connect_mock.return_value.cluster.return_value.sdn.return_value.vnets.return_value.subnets.return_value.get.return_value = RAW_SUBNETS |
| 93 | + |
| 94 | + def tearDown(self): |
| 95 | + self.connect_mock.stop() |
| 96 | + self.exit_json_patcher.stop() |
| 97 | + self.fail_json_patcher.stop() |
| 98 | + super(TestProxmoxSubnetModule, self).tearDown() |
| 99 | + |
| 100 | + def test_subnet_create(self): |
| 101 | + # Create new Zone |
| 102 | + with pytest.raises(SystemExit) as exc_info: |
| 103 | + with set_module_args(get_module_args(vnet='new_vnet', |
| 104 | + subnet='10.10.10.0/24', |
| 105 | + zone='test_zone')): |
| 106 | + self.module.main() |
| 107 | + result = exc_info.value.args[0] |
| 108 | + assert result["changed"] is True |
| 109 | + assert result["msg"] == "Created new subnet 10.10.10.0/24" |
| 110 | + assert result['subnet'] == 'test_zone-10.10.10.0-24' |
| 111 | + |
| 112 | + def test_subnet_update(self): |
| 113 | + # Normal subnet param (snat) differ |
| 114 | + with pytest.raises(SystemExit) as exc_info: |
| 115 | + with set_module_args(get_module_args(vnet='test', |
| 116 | + subnet='10.10.2.0/24', |
| 117 | + zone='ans1', |
| 118 | + snat=1)): |
| 119 | + self.module.main() |
| 120 | + result = exc_info.value.args[0] |
| 121 | + assert result["changed"] is True |
| 122 | + assert result["msg"] == "Updated subnet ans1-10.10.2.0-24" |
| 123 | + assert result['subnet'] == 'ans1-10.10.2.0-24' |
| 124 | + |
| 125 | + # No update needed |
| 126 | + with pytest.raises(SystemExit) as exc_info: |
| 127 | + with set_module_args(get_module_args(vnet='test', |
| 128 | + subnet='10.10.2.0/24', |
| 129 | + zone='ans1')): |
| 130 | + self.module.main() |
| 131 | + result = exc_info.value.args[0] |
| 132 | + assert result["changed"] is False |
| 133 | + assert result["msg"] == "subnet ans1-10.10.2.0-24 is already present with correct parameters." |
| 134 | + assert result['subnet'] == 'ans1-10.10.2.0-24' |
| 135 | + |
| 136 | + # New dhcp_range |
| 137 | + with pytest.raises(SystemExit) as exc_info: |
| 138 | + with set_module_args(get_module_args(vnet='test', |
| 139 | + subnet='10.10.2.0/24', |
| 140 | + zone='ans1', |
| 141 | + dhcp_range=[{'start': '10.10.2.150', 'end': '10.10.2.200'}])): |
| 142 | + self.module.main() |
| 143 | + result = exc_info.value.args[0] |
| 144 | + assert result["changed"] is True |
| 145 | + assert result["msg"] == "Updated subnet ans1-10.10.2.0-24" |
| 146 | + assert result['subnet'] == 'ans1-10.10.2.0-24' |
| 147 | + |
| 148 | + # dhcp_range is partially overlapping and mode is append |
| 149 | + with pytest.raises(SystemExit) as exc_info: |
| 150 | + with set_module_args(get_module_args(vnet='test', |
| 151 | + subnet='10.10.2.0/24', |
| 152 | + zone='ans1', |
| 153 | + dhcp_range=[{'start': '10.10.2.10', 'end': '10.10.2.20'}])): |
| 154 | + self.module.main() |
| 155 | + result = exc_info.value.args[0] |
| 156 | + assert self.fail_json_mock.called |
| 157 | + assert result["failed"] is True |
| 158 | + assert result["msg"] == "There are partially overlapping DHCP ranges. this is not allowed." |
| 159 | + |
| 160 | + def test_subnet_absent(self): |
| 161 | + with pytest.raises(SystemExit) as exc_info: |
| 162 | + with set_module_args(get_module_args(vnet='test', |
| 163 | + subnet='10.10.2.0/24', |
| 164 | + zone='ans1', state='absent')): |
| 165 | + self.module.main() |
| 166 | + result = exc_info.value.args[0] |
| 167 | + assert result["changed"] is True |
| 168 | + assert result["msg"] == "Deleted subnet ans1-10.10.2.0-24" |
| 169 | + assert result['subnet'] == 'ans1-10.10.2.0-24' |
0 commit comments