From 0090cc933cf0033fa3283057434b794fa8925dbf Mon Sep 17 00:00:00 2001 From: ybhalchim Date: Sun, 8 Dec 2024 14:20:26 +0530 Subject: [PATCH 1/6] Ipam_address_Integration test cases --- meta/runtime.yml | 2 + plugins/modules/ipam_address.py | 446 ++++++++++++++++++ plugins/modules/ipam_address_info.py | 361 ++++++++++++++ .../targets/ipam_address/meta/main.yml | 2 + .../targets/ipam_address/tasks/main.yml | 165 +++++++ .../targets/setup_address/tasks/main.yml | 28 ++ 6 files changed, 1004 insertions(+) create mode 100644 plugins/modules/ipam_address.py create mode 100644 plugins/modules/ipam_address_info.py create mode 100644 tests/integration/targets/ipam_address/meta/main.yml create mode 100644 tests/integration/targets/ipam_address/tasks/main.yml create mode 100644 tests/integration/targets/setup_address/tasks/main.yml diff --git a/meta/runtime.yml b/meta/runtime.yml index 55f479e..dfaa35f 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -18,6 +18,8 @@ action_groups: - ipam_subnet_info - ipam_address_block - ipam_address_block_info + - ipam_address + - ipam_address_info plugin_routing: modules: diff --git a/plugins/modules/ipam_address.py b/plugins/modules/ipam_address.py new file mode 100644 index 0000000..85211b4 --- /dev/null +++ b/plugins/modules/ipam_address.py @@ -0,0 +1,446 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Infoblox Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: ipam_address +short_description: Manage Address +description: + - Manage Address +version_added: 2.0.0 +author: Infoblox Inc. (@infobloxopen) +options: + id: + description: + - ID of the object + type: str + required: false + state: + description: + - Indicate desired state of the object + type: str + required: false + choices: + - present + - absent + default: present + address: + description: + - "The address in form \"a.b.c.d\"." + type: str + comment: + description: + - "The description for the address object. May contain 0 to 1024 characters. Can include UTF-8." + type: str + external_keys: + description: + - "The external keys (source key) for this address in JSON format." + type: dict + host: + description: + - "The resource identifier." + type: str + hwaddr: + description: + - "The hardware address associated with this IP address." + type: str + interface: + description: + - "The name of the network interface card (NIC) associated with the address, if any." + type: str + names: + description: + - "The list of all names associated with this address." + type: list + elements: dict + suboptions: + name: + description: + - "The name expressed as a single label or FQDN." + type: str + type: + description: + - "The origin of the name." + type: str + parent: + description: + - "The resource identifier." + type: str + range: + description: + - "The resource identifier." + type: str + space: + description: + - "The resource identifier." + type: str + tags: + description: + - "The tags for this address in JSON format." + type: dict + +extends_documentation_fragment: + - infoblox.bloxone.common +""" # noqa: E501 + +RETURN = r""" +id: + description: + - ID of the Address object + type: str + returned: Always +item: + description: + - Address object + type: complex + returned: Always + contains: + address: + description: + - "The address in form \"a.b.c.d\"." + type: str + returned: Always + comment: + description: + - "The description for the address object. May contain 0 to 1024 characters. Can include UTF-8." + type: str + returned: Always + compartment_id: + description: + - "The compartment associated with the object. If no compartment is associated with the object, the value defaults to empty." + type: str + returned: Always + created_at: + description: + - "Time when the object has been created." + type: str + returned: Always + dhcp_info: + description: + - "The DHCP information associated with this object." + type: dict + returned: Always + contains: + client_hostname: + description: + - "The DHCP host name associated with this client." + type: str + returned: Always + client_hwaddr: + description: + - "The hardware address associated with this client." + type: str + returned: Always + client_id: + description: + - "The ID associated with this client." + type: str + returned: Always + end: + description: + - "The timestamp at which the I(state), when set to I(leased), will be changed to I(free)." + type: str + returned: Always + fingerprint: + description: + - "The DHCP fingerprint for the associated lease." + type: str + returned: Always + iaid: + description: + - "Identity Association Identifier (IAID) of the lease. Applicable only for DHCPv6." + type: int + returned: Always + lease_type: + description: + - "Lease type. Applicable only for address under DHCP control. The value can be empty for address not under DHCP control." + - "Valid values are:" + - "* I(DHCPv6NonTemporaryAddress): DHCPv6 non-temporary address (NA)" + - "* I(DHCPv6TemporaryAddress): DHCPv6 temporary address (TA)" + - "* I(DHCPv6PrefixDelegation): DHCPv6 prefix delegation (PD)" + - "* I(DHCPv4): DHCPv4 lease" + type: str + returned: Always + preferred_lifetime: + description: + - "The length of time that a valid address is preferred (i.e., the time until deprecation). When the preferred lifetime expires, the address becomes deprecated on the client. It is still considered leased on the server. Applicable only for DHCPv6." + type: str + returned: Always + remain: + description: + - "The remaining time, in seconds, until which the I(state), when set to I(leased), will remain in that state." + type: int + returned: Always + start: + description: + - "The timestamp at which I(state) was first set to I(leased)." + type: str + returned: Always + state: + description: + - "Indicates the status of this IP address from a DHCP protocol standpoint as:" + - "* I(none): Address is not under DHCP control." + - "* I(free): Address is under DHCP control but has no lease currently assigned." + - "* I(leased): Address is under DHCP control and has a lease currently assigned. The lease details are contained in the matching I(dhcp/lease) resource." + type: str + returned: Always + state_ts: + description: + - "The timestamp at which the I(state) was last reported." + type: str + returned: Always + disable_dhcp: + description: + - "Read only. Represent the value of the same field in the associated I(dhcp/fixed_address) object." + type: bool + returned: Always + discovery_attrs: + description: + - "The discovery attributes for this address in JSON format." + type: dict + returned: Always + discovery_metadata: + description: + - "The discovery metadata for this address in JSON format." + type: dict + returned: Always + external_keys: + description: + - "The external keys (source key) for this address in JSON format." + type: dict + returned: Always + host: + description: + - "The resource identifier." + type: str + returned: Always + hwaddr: + description: + - "The hardware address associated with this IP address." + type: str + returned: Always + id: + description: + - "The resource identifier." + type: str + returned: Always + interface: + description: + - "The name of the network interface card (NIC) associated with the address, if any." + type: str + returned: Always + names: + description: + - "The list of all names associated with this address." + type: list + returned: Always + elements: dict + contains: + name: + description: + - "The name expressed as a single label or FQDN." + type: str + returned: Always + type: + description: + - "The origin of the name." + type: str + returned: Always + parent: + description: + - "The resource identifier." + type: str + returned: Always + protocol: + description: + - "The type of protocol (I(ip4) or I(ip6))." + type: str + returned: Always + range: + description: + - "The resource identifier." + type: str + returned: Always + space: + description: + - "The resource identifier." + type: str + returned: Always + state: + description: + - "The state of the address (I(used) or I(free))." + type: str + returned: Always + tags: + description: + - "The tags for this address in JSON format." + type: dict + returned: Always + updated_at: + description: + - "Time when the object has been updated. Equals to I(created_at) if not updated after creation." + type: str + returned: Always + usage: + description: + - "The usage is a combination of indicators, each tracking a specific associated use. Listed below are usage indicators with their meaning: usage indicator | description ---------------------- | -------------------------------- I(IPAM) | Address was created by the IPAM component. I(IPAM), I(RESERVED) | Address was created by the API call I(ipam/address) or I(ipam/host). I(IPAM), I(NETWORK) | Address was automatically created by the IPAM component and is the network address of the parent subnet. I(IPAM), I(BROADCAST) | Address was automatically created by the IPAM component and is the broadcast address of the parent subnet. I(DHCP) | Address was created by the DHCP component. I(DHCP), I(FIXEDADDRESS) | Address was created by the API call I(dhcp/fixed_address). I(DHCP), I(LEASED) | An active lease for that address was issued by a DHCP server. I(DHCP), I(DISABLED) | Address is disabled. I(DNS) | Address is used by one or more DNS records. I(DISCOVERED) | Address is discovered by some network discovery probe like Network Insight or NetMRI in NIOS." + type: list + returned: Always +""" # noqa: E501 + +from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule + +try: + from bloxone_client import ApiException, NotFoundException + from ipam import Address, AddressApi +except ImportError: + pass # Handled by BloxoneAnsibleModule + + +class AddressModule(BloxoneAnsibleModule): + def __init__(self, *args, **kwargs): + super(AddressModule, self).__init__(*args, **kwargs) + + exclude = ["state", "csp_url", "api_key", "id"] + self._payload_params = {k: v for k, v in self.params.items() if v is not None and k not in exclude} + self._payload = Address.from_dict(self._payload_params) + self._existing = None + + @property + def existing(self): + return self._existing + + @existing.setter + def existing(self, value): + self._existing = value + + @property + def payload_params(self): + return self._payload_params + + @property + def payload(self): + return self._payload + + def payload_changed(self): + if self.existing is None: + # if existing is None, then it is a create operation + return True + + return self.is_changed(self.existing.model_dump(by_alias=True, exclude_none=True), self.payload_params) + + def find(self): + if self.params["id"] is not None: + try: + resp = AddressApi(self.client).read(self.params["id"], inherit="full") + return resp.result + except NotFoundException as e: + if self.params["state"] == "absent": + return None + raise e + else: + filter = f"address=='{self.params['address']}' and space=='{self.params['space']}'" + resp = AddressApi(self.client).list(filter=filter) + if len(resp.results) == 1: + return resp.results[0] + if len(resp.results) > 1: + self.fail_json(msg=f"Found multiple Address: {resp.results}") + if len(resp.results) == 0: + return None + + def create(self): + if self.check_mode: + return None + + resp = AddressApi(self.client).create(body=self.payload) + return resp.result.model_dump(by_alias=True, exclude_none=True) + + def update(self): + if self.check_mode: + return None + + resp = AddressApi(self.client).update(id=self.existing.id, body=self.payload) + return resp.result.model_dump(by_alias=True, exclude_none=True) + + def delete(self): + if self.check_mode: + return + + AddressApi(self.client).delete(self.existing.id) + + def run_command(self): + result = dict(changed=False, object={}, id=None) + + # based on the state that is passed in, we will execute the appropriate + # functions + try: + self.existing = self.find() + item = {} + if self.params["state"] == "present" and self.existing is None: + item = self.create() + result["changed"] = True + result["msg"] = "Address created" + elif self.params["state"] == "present" and self.existing is not None: + if self.payload_changed(): + item = self.update() + result["changed"] = True + result["msg"] = "Address updated" + elif self.params["state"] == "absent" and self.existing is not None: + self.delete() + result["changed"] = True + result["msg"] = "Address deleted" + + if self.check_mode: + # if in check mode, do not update the result or the diff, just return the changed state + self.exit_json(**result) + + result["diff"] = dict( + before=self.existing.model_dump(by_alias=True, exclude_none=True) if self.existing is not None else {}, + after=item, + ) + result["object"] = item + result["id"] = self.existing.id if self.existing is not None else item["id"] if (item and "id" in item) else None + except ApiException as e: + self.fail_json(msg=f"Failed to execute command: {e.status} {e.reason} {e.body}") + + self.exit_json(**result) + + +def main(): + module_args = dict( + id=dict(type="str", required=False), + state=dict(type="str", required=False, choices=["present", "absent"], default="present"), + address=dict(type="str"), + comment=dict(type="str"), + external_keys=dict(type="dict"), + host=dict(type="str"), + hwaddr=dict(type="str"), + interface=dict(type="str"), + names=dict(type="list", elements="dict", options=dict( + name=dict(type="str"), + type=dict(type="str"), + )), + parent=dict(type="str"), + range=dict(type="str"), + space=dict(type="str"), + tags=dict(type="dict"), + + ) + + module = AddressModule( + argument_spec=module_args, + supports_check_mode=True, + required_if=[("state", "present", ["address", "space"])], + ) + + module.run_command() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/ipam_address_info.py b/plugins/modules/ipam_address_info.py new file mode 100644 index 0000000..e973f60 --- /dev/null +++ b/plugins/modules/ipam_address_info.py @@ -0,0 +1,361 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: Infoblox Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: ipam_address_info +short_description: Manage Address +description: + - Manage Address +version_added: 2.0.0 +author: Infoblox Inc. (@infobloxopen) +options: + id: + description: + - ID of the object + type: str + required: false + filters: + description: + - Filter dict to filter objects + type: dict + required: false + filter_query: + description: + - Filter query to filter objects + type: str + required: false + inherit: + description: + - Return inheritance information + type: str + required: false + choices: + - full + - partial + - none + default: full + tag_filters: + description: + - Filter dict to filter objects by tags + type: dict + required: false + tag_filter_query: + description: + - Filter query to filter objects by tags + type: str + required: false + +extends_documentation_fragment: + - infoblox.bloxone.common +""" # noqa: E501 + +RETURN = r""" +id: + description: + - ID of the Address object + type: str + returned: Always +objects: + description: + - Address object + type: list + elements: dict + returned: Always + contains: + address: + description: + - "The address in form \"a.b.c.d\"." + type: str + returned: Always + comment: + description: + - "The description for the address object. May contain 0 to 1024 characters. Can include UTF-8." + type: str + returned: Always + compartment_id: + description: + - "The compartment associated with the object. If no compartment is associated with the object, the value defaults to empty." + type: str + returned: Always + created_at: + description: + - "Time when the object has been created." + type: str + returned: Always + dhcp_info: + description: + - "The DHCP information associated with this object." + type: dict + returned: Always + contains: + client_hostname: + description: + - "The DHCP host name associated with this client." + type: str + returned: Always + client_hwaddr: + description: + - "The hardware address associated with this client." + type: str + returned: Always + client_id: + description: + - "The ID associated with this client." + type: str + returned: Always + end: + description: + - "The timestamp at which the I(state), when set to I(leased), will be changed to I(free)." + type: str + returned: Always + fingerprint: + description: + - "The DHCP fingerprint for the associated lease." + type: str + returned: Always + iaid: + description: + - "Identity Association Identifier (IAID) of the lease. Applicable only for DHCPv6." + type: int + returned: Always + lease_type: + description: + - "Lease type. Applicable only for address under DHCP control. The value can be empty for address not under DHCP control." + - "Valid values are:" + - "* I(DHCPv6NonTemporaryAddress): DHCPv6 non-temporary address (NA)" + - "* I(DHCPv6TemporaryAddress): DHCPv6 temporary address (TA)" + - "* I(DHCPv6PrefixDelegation): DHCPv6 prefix delegation (PD)" + - "* I(DHCPv4): DHCPv4 lease" + type: str + returned: Always + preferred_lifetime: + description: + - "The length of time that a valid address is preferred (i.e., the time until deprecation). When the preferred lifetime expires, the address becomes deprecated on the client. It is still considered leased on the server. Applicable only for DHCPv6." + type: str + returned: Always + remain: + description: + - "The remaining time, in seconds, until which the I(state), when set to I(leased), will remain in that state." + type: int + returned: Always + start: + description: + - "The timestamp at which I(state) was first set to I(leased)." + type: str + returned: Always + state: + description: + - "Indicates the status of this IP address from a DHCP protocol standpoint as:" + - "* I(none): Address is not under DHCP control." + - "* I(free): Address is under DHCP control but has no lease currently assigned." + - "* I(leased): Address is under DHCP control and has a lease currently assigned. The lease details are contained in the matching I(dhcp/lease) resource." + type: str + returned: Always + state_ts: + description: + - "The timestamp at which the I(state) was last reported." + type: str + returned: Always + disable_dhcp: + description: + - "Read only. Represent the value of the same field in the associated I(dhcp/fixed_address) object." + type: bool + returned: Always + discovery_attrs: + description: + - "The discovery attributes for this address in JSON format." + type: dict + returned: Always + discovery_metadata: + description: + - "The discovery metadata for this address in JSON format." + type: dict + returned: Always + external_keys: + description: + - "The external keys (source key) for this address in JSON format." + type: dict + returned: Always + host: + description: + - "The resource identifier." + type: str + returned: Always + hwaddr: + description: + - "The hardware address associated with this IP address." + type: str + returned: Always + id: + description: + - "The resource identifier." + type: str + returned: Always + interface: + description: + - "The name of the network interface card (NIC) associated with the address, if any." + type: str + returned: Always + names: + description: + - "The list of all names associated with this address." + type: list + returned: Always + elements: dict + contains: + name: + description: + - "The name expressed as a single label or FQDN." + type: str + returned: Always + type: + description: + - "The origin of the name." + type: str + returned: Always + parent: + description: + - "The resource identifier." + type: str + returned: Always + protocol: + description: + - "The type of protocol (I(ip4) or I(ip6))." + type: str + returned: Always + range: + description: + - "The resource identifier." + type: str + returned: Always + space: + description: + - "The resource identifier." + type: str + returned: Always + state: + description: + - "The state of the address (I(used) or I(free))." + type: str + returned: Always + tags: + description: + - "The tags for this address in JSON format." + type: dict + returned: Always + updated_at: + description: + - "Time when the object has been updated. Equals to I(created_at) if not updated after creation." + type: str + returned: Always + usage: + description: + - "The usage is a combination of indicators, each tracking a specific associated use. Listed below are usage indicators with their meaning: usage indicator | description ---------------------- | -------------------------------- I(IPAM) | Address was created by the IPAM component. I(IPAM), I(RESERVED) | Address was created by the API call I(ipam/address) or I(ipam/host). I(IPAM), I(NETWORK) | Address was automatically created by the IPAM component and is the network address of the parent subnet. I(IPAM), I(BROADCAST) | Address was automatically created by the IPAM component and is the broadcast address of the parent subnet. I(DHCP) | Address was created by the DHCP component. I(DHCP), I(FIXEDADDRESS) | Address was created by the API call I(dhcp/fixed_address). I(DHCP), I(LEASED) | An active lease for that address was issued by a DHCP server. I(DHCP), I(DISABLED) | Address is disabled. I(DNS) | Address is used by one or more DNS records. I(DISCOVERED) | Address is discovered by some network discovery probe like Network Insight or NetMRI in NIOS." + type: list + returned: Always +""" # noqa: E501 + +from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule + +try: + from bloxone_client import ApiException, NotFoundException + from ipam import AddressApi +except ImportError: + pass # Handled by BloxoneAnsibleModule + + +class AddressInfoModule(BloxoneAnsibleModule): + def __init__(self, *args, **kwargs): + super(AddressInfoModule, self).__init__(*args, **kwargs) + self._existing = None + self._limit = 1000 + + def find_by_id(self): + try: + resp = AddressApi(self.client).read(self.params["id"], inherit="full") + return [resp.result] + except NotFoundException as e: + return None + + def find(self): + if self.params["id"] is not None: + return self.find_by_id() + + filter_str = None + if self.params["filters"] is not None: + filter_str = " and ".join([f"{k}=='{v}'" for k, v in self.params["filters"].items()]) + elif self.params["filter_query"] is not None: + filter_str = self.params["filter_query"] + + tag_filter_str = None + if self.params["tag_filters"] is not None: + tag_filter_str = " and ".join([f"{k}=='{v}'" for k, v in self.params["tag_filters"].items()]) + elif self.params["tag_filter_query"] is not None: + tag_filter_str = self.params["tag_filter_query"] + + all_results = [] + offset = 0 + + while True: + try: + resp = AddressApi(self.client).list( + offset=offset, limit=self._limit, filter=filter_str, tfilter=tag_filter_str + ) + all_results.extend(resp.results) + + if len(resp.results) < self._limit: + break + offset += self._limit + + except ApiException as e: + self.fail_json(msg=f"Failed to execute command: {e.status} {e.reason} {e.body}") + + return all_results + + def run_command(self): + result = dict(objects=[]) + + if self.check_mode: + self.exit_json(**result) + + find_results = self.find() + + all_results = [] + for r in find_results: + all_results.append(r.model_dump(by_alias=True, exclude_none=True)) + + result["objects"] = all_results + self.exit_json(**result) + + +def main(): + # define available arguments/parameters a user can pass to the module + module_args = dict( + id=dict(type="str", required=False), + filters=dict(type="dict", required=False), + filter_query=dict(type="str", required=False), + inherit=dict(type="str", required=False, choices=["full", "partial", "none"], default="full"), + tag_filters=dict(type="dict", required=False), + tag_filter_query=dict(type="str", required=False), + ) + + module = AddressInfoModule( + argument_spec=module_args, + supports_check_mode=True, + mutually_exclusive=[ + ["id", "filters", "filter_query"], + ["id", "tag_filters", "tag_filter_query"], + ], + ) + module.run_command() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/ipam_address/meta/main.yml b/tests/integration/targets/ipam_address/meta/main.yml new file mode 100644 index 0000000..f6d2fce --- /dev/null +++ b/tests/integration/targets/ipam_address/meta/main.yml @@ -0,0 +1,2 @@ +--- +dependencies: [setup_address] \ No newline at end of file diff --git a/tests/integration/targets/ipam_address/tasks/main.yml b/tests/integration/targets/ipam_address/tasks/main.yml new file mode 100644 index 0000000..d6786ab --- /dev/null +++ b/tests/integration/targets/ipam_address/tasks/main.yml @@ -0,0 +1,165 @@ +--- +#TODO: add tests +# The following require additional plugins to be supported. +# - Address with NextAvailable ID count +# - Address with NextAvailable Subnet +# - Address with NextAvailable AddressBlock +# - Address with NextAvailable Range +- module_defaults: + group/infoblox.bloxone.all: + csp_url: "{{ csp_url }}" + api_key: "{{ api_key }}" + block: + # Create a random IP space name to avoid conflicts + - ansible.builtin.set_fact: + tag_value: "site-{{ 999999 | random | string }}" + +# - name: Debug the IP space ID being used for address reservation +# debug: +# var: ip_space + + # Basic tests for Address Block + - name: "create a address" + infoblox.bloxone.ipam_address: + address: "10.1.0.3" + space: "{{ ip_space.id }}" + state: "present" + register: address + - name: "Get information about the address" + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.1.0.3", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length != 0 + + - name: "create a address(idempotent)" + infoblox.bloxone.ipam_address: + address: "10.1.0.3" + space: "{{ ip_space.id }}" + state: "present" + register: address + - assert: + that: + - address is not changed + - address is not failed + + - name: "create the address with names" + infoblox.bloxone.ipam_address: + address: "10.1.0.8" + names: [{"name": "test-1","type": "user"}] + space: "{{ ip_space.id }}" + state: "present" + register: address + - name: "Get information about the address" + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.1.0.8", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length > 0 + + - name: "create the address with names (check mode)" + infoblox.bloxone.ipam_address: + address: "10.1.0.8" + names: [{"name": "test-1","type": "user"}] + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + + - name: "create the address with comment" + infoblox.bloxone.ipam_address: + address: "10.1.0.5" + comment: "some comment" + space: "{{ ip_space.id }}" + state: "present" + register: address + - name: "Get information about the address" + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.1.0.5", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects[0].comment == "some comment" + + - name: "create the address with hwaddr" + infoblox.bloxone.ipam_address: + address: "10.1.0.6" + hwaddr : "00:11:22:33:44:55" + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + + - name: "create the address with interface" + infoblox.bloxone.ipam_address: + address: "10.1.0.7" + interface: "eth0" + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + + - name: "create the address with tags" + infoblox.bloxone.ipam_address: + address: "10.1.0.9" + tags: { "tag1": "value1","tag2": "value2",} + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + +# - name: "create the address with next available id count" +# infoblox.bloxone.ipam_address: +# next_available_id : "{{ next_available_id }}" +# space: "{{ ip_space.id }}" +# count: 5 +# state: "present" +# check_mode: true + + - name: "Delete a address" + infoblox.bloxone.ipam_address: + address: "10.1.0.3" + space: "{{ ip_space.id }}" + state: "absent" + register: ip_space + - name: Get information about the address + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.1.0.3", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length == 0 + + + - name: "Delete a Subnet" + infoblox.bloxone.ipam_subnet: + address: "10.1.0.0/16" + space: "{{ ip_space.id }}" + state: "absent" + register: subnet + + - name: "Delete IP Space" + infoblox.bloxone.ipam_ip_space: + # name: ip_space.object.name + name: "{{ ip_name }}" + state: "absent" + #check_mode: true + register: ip_space \ No newline at end of file diff --git a/tests/integration/targets/setup_address/tasks/main.yml b/tests/integration/targets/setup_address/tasks/main.yml new file mode 100644 index 0000000..aa0d95f --- /dev/null +++ b/tests/integration/targets/setup_address/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- module_defaults: + group/infoblox.bloxone.all: + csp_url: "{{ csp_url }}" + api_key: "{{ api_key }}" + block: + # Create a random IP space name to avoid conflicts + - ansible.builtin.set_fact: + ip_name: "test-ip-space1.-{{ 999999 | random | string }}" + tag_value: "site-{{ 999999 | random | string }}" + + # Basic tests for Address Block + - name: "Create an IP space" + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "present" + register: ip_space + + - name: Debug the IP space name created + debug: + var: ip_space.object.name + + - name: "Create a Subnet" + infoblox.bloxone.ipam_subnet: + address: "10.1.0.0/16" + space: "{{ ip_space.id }}" + state: "present" + register: subnet From d3418b0f535c726eed2f2f0a15b9266a04f757bf Mon Sep 17 00:00:00 2001 From: ybhalchim Date: Wed, 11 Dec 2024 14:29:41 +0530 Subject: [PATCH 2/6] Added address info --- plugins/modules/ipam_address.py | 127 +++++++++++++++++- plugins/modules/ipam_address_info.py | 77 ++++++++++- .../targets/ipam_address/tasks/main.yml | 94 ++++++------- .../targets/ipam_address_info/meta/main.yml | 2 + .../targets/ipam_address_info/tasks/main.yml | 69 ++++++++++ .../targets/setup_address/tasks/main.yml | 9 +- 6 files changed, 318 insertions(+), 60 deletions(-) create mode 100644 tests/integration/targets/ipam_address_info/meta/main.yml create mode 100644 tests/integration/targets/ipam_address_info/tasks/main.yml diff --git a/plugins/modules/ipam_address.py b/plugins/modules/ipam_address.py index 85211b4..c9a370a 100644 --- a/plugins/modules/ipam_address.py +++ b/plugins/modules/ipam_address.py @@ -7,6 +7,8 @@ __metaclass__ = type +from plugins.lookup.bloxone import EXAMPLES + DOCUMENTATION = r""" --- module: ipam_address @@ -89,6 +91,129 @@ - infoblox.bloxone.common """ # noqa: E501 +EXAMPLES = r""" + - name: "Create an IP space" + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "present" + register: ip_space + + - name: "Create a Subnet" + infoblox.bloxone.ipam_subnet: + address: "10.0.0.0/16" + space: "{{ ip_space.id }}" + state: "present" + register: subnet + + - name: Create a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + state: "present" + register: address + - name: Get information about the Address + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.0.0.3", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length != 0 + + - name: Create the Address with Name + infoblox.bloxone.ipam_address: + address: "10.0.0.8" + names: [{"name": "test-1","type": "user"}] + space: "{{ ip_space.id }}" + state: "present" + register: address + - name: Get information about the Address + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.0.0.8", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length > 0 + + - name: Create the Address with hwaddr (check mode) + infoblox.bloxone.ipam_address: + address: "10.0.0.8" + hwaddr : "00:11:22:33:44:55" + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + + - name: Create the Address with Interface (check mode) + infoblox.bloxone.ipam_address: + address: "10.0.0.8" + interface: "eth0" + space: "{{ ip_space.id }}" + state: "present" + register: address + check_mode: true + + - name: Create the Address with Tags (check mode) + infoblox.bloxone.ipam_address: + address: "10.0.0.8" + tags: { "tag1": "value1","tag2": "value2",} + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + + - name: Delete a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + state: "absent" + + - name: Delete a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.5" + space: "{{ ip_space.id }}" + state: "absent" + + - name: Delete a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.8" + space: "{{ ip_space.id }}" + state: "absent" + + - name: Get information about the address + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.0.0.3", + space: "{{ ip_space.id }}" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length == 0 + + - name: Delete a Subnet + infoblox.bloxone.ipam_subnet: + address: "10.0.0.0/16" + space: "{{ ip_space.id }}" + state: "absent" + register: subnet + + - name: Delete IP Space + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "absent" + register: ip_space +""" # noqa: E501 + RETURN = r""" id: description: @@ -338,7 +463,7 @@ def payload_changed(self): def find(self): if self.params["id"] is not None: try: - resp = AddressApi(self.client).read(self.params["id"], inherit="full") + resp = AddressApi(self.client).read(self.params["id"]) return resp.result except NotFoundException as e: if self.params["state"] == "absent": diff --git a/plugins/modules/ipam_address_info.py b/plugins/modules/ipam_address_info.py index e973f60..d5d8b08 100644 --- a/plugins/modules/ipam_address_info.py +++ b/plugins/modules/ipam_address_info.py @@ -7,6 +7,8 @@ __metaclass__ = type +from plugins.lookup.bloxone import EXAMPLES + DOCUMENTATION = r""" --- module: ipam_address_info @@ -56,6 +58,79 @@ - infoblox.bloxone.common """ # noqa: E501 +EXAMPLES = r""" + - name: "Create an IP space" + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "present" + register: ip_space + + - name: "Create a Subnet" + infoblox.bloxone.ipam_subnet: + address: "10.0.0.0/16" + space: "{{ ip_space.id }}" + state: "present" + register: subnet + + - name: Create A Address + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + tags: + region: "eu" + state: "present" + register: address + + - name: Get information about the Address by ID + infoblox.bloxone.ipam_address_info: + id: "{{ address.id }}" + register: address_info + - assert: + that: + - address_info.objects | length != 0 + + - name: Get Address Block information by filters + infoblox.bloxone.ipam_address_info: + filters: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + register: address_info + - assert: + that: + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id + + - name: Get information about the Address by tag + infoblox.bloxone.ipam_address_info: + tag_filters: + region : "eu" + register: address_info + - assert: + that: + - address_info.objects | length != 0 + + - name: Get Address information by filter query + infoblox.bloxone.ipam_address_info: + filter_query: "address=='10.0.0.3' and space=='{{ ip_space.id }}'" + register: address_info + - assert: + that: + - address_info.objects | length != 0 + + - name: "Delete a Subnet" + infoblox.bloxone.ipam_subnet: + address: "10.0.0.0/16" + space: "{{ ip_space.id }}" + state: "absent" + register: subnet + + - name: "Delete IP Space" + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "absent" + register: ip_space +"""# noqa: E501 + RETURN = r""" id: description: @@ -279,7 +354,7 @@ def __init__(self, *args, **kwargs): def find_by_id(self): try: - resp = AddressApi(self.client).read(self.params["id"], inherit="full") + resp = AddressApi(self.client).read(self.params["id"]) return [resp.result] except NotFoundException as e: return None diff --git a/tests/integration/targets/ipam_address/tasks/main.yml b/tests/integration/targets/ipam_address/tasks/main.yml index d6786ab..c3294cc 100644 --- a/tests/integration/targets/ipam_address/tasks/main.yml +++ b/tests/integration/targets/ipam_address/tasks/main.yml @@ -5,30 +5,24 @@ # - Address with NextAvailable Subnet # - Address with NextAvailable AddressBlock # - Address with NextAvailable Range +- - module_defaults: group/infoblox.bloxone.all: csp_url: "{{ csp_url }}" api_key: "{{ api_key }}" block: - # Create a random IP space name to avoid conflicts - - ansible.builtin.set_fact: - tag_value: "site-{{ 999999 | random | string }}" -# - name: Debug the IP space ID being used for address reservation -# debug: -# var: ip_space - - # Basic tests for Address Block - - name: "create a address" + # Basic tests for Address + - name: Create a Address infoblox.bloxone.ipam_address: - address: "10.1.0.3" + address: "10.0.0.3" space: "{{ ip_space.id }}" state: "present" register: address - - name: "Get information about the address" + - name: Get information about the Address infoblox.bloxone.ipam_address_info: filters: { - address: "10.1.0.3", + address: "10.0.0.3", space: "{{ ip_space.id }}" } register: address_info @@ -38,9 +32,9 @@ - address is not failed - address_info.objects | length != 0 - - name: "create a address(idempotent)" + - name: Create a Address(idempotent) infoblox.bloxone.ipam_address: - address: "10.1.0.3" + address: "10.0.0.3" space: "{{ ip_space.id }}" state: "present" register: address @@ -49,17 +43,17 @@ - address is not changed - address is not failed - - name: "create the address with names" + - name: Create the Address with Name infoblox.bloxone.ipam_address: - address: "10.1.0.8" + address: "10.0.0.8" names: [{"name": "test-1","type": "user"}] space: "{{ ip_space.id }}" state: "present" register: address - - name: "Get information about the address" + - name: Get information about the Address infoblox.bloxone.ipam_address_info: filters: { - address: "10.1.0.8", + address: "10.0.0.8", space: "{{ ip_space.id }}" } register: address_info @@ -69,25 +63,17 @@ - address is not failed - address_info.objects | length > 0 - - name: "create the address with names (check mode)" + - name: Create the Address with comment infoblox.bloxone.ipam_address: - address: "10.1.0.8" - names: [{"name": "test-1","type": "user"}] - space: "{{ ip_space.id }}" - state: "present" - check_mode: true - - - name: "create the address with comment" - infoblox.bloxone.ipam_address: - address: "10.1.0.5" + address: "10.0.0.5" comment: "some comment" space: "{{ ip_space.id }}" state: "present" register: address - - name: "Get information about the address" + - name: Get information about the Address infoblox.bloxone.ipam_address_info: filters: { - address: "10.1.0.5", + address: "10.0.0.5", space: "{{ ip_space.id }}" } register: address_info @@ -97,48 +83,53 @@ - address is not failed - address_info.objects[0].comment == "some comment" - - name: "create the address with hwaddr" + - name: Create the Address with hwaddr (check mode) infoblox.bloxone.ipam_address: - address: "10.1.0.6" + address: "10.0.0.8" hwaddr : "00:11:22:33:44:55" space: "{{ ip_space.id }}" state: "present" check_mode: true - - name: "create the address with interface" + - name: Create the Address with Interface (check mode) infoblox.bloxone.ipam_address: - address: "10.1.0.7" + address: "10.0.0.8" interface: "eth0" space: "{{ ip_space.id }}" state: "present" + register: address check_mode: true - - name: "create the address with tags" + - name: Create the Address with Tags (check mode) infoblox.bloxone.ipam_address: - address: "10.1.0.9" + address: "10.0.0.8" tags: { "tag1": "value1","tag2": "value2",} space: "{{ ip_space.id }}" state: "present" check_mode: true -# - name: "create the address with next available id count" -# infoblox.bloxone.ipam_address: -# next_available_id : "{{ next_available_id }}" -# space: "{{ ip_space.id }}" -# count: 5 -# state: "present" -# check_mode: true + - name: Delete a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + state: "absent" + + - name: Delete a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.5" + space: "{{ ip_space.id }}" + state: "absent" - - name: "Delete a address" + - name: Delete a Address infoblox.bloxone.ipam_address: - address: "10.1.0.3" + address: "10.0.0.8" space: "{{ ip_space.id }}" state: "absent" - register: ip_space + - name: Get information about the address infoblox.bloxone.ipam_address_info: filters: { - address: "10.1.0.3", + address: "10.0.0.3", space: "{{ ip_space.id }}" } register: address_info @@ -148,18 +139,15 @@ - address is not failed - address_info.objects | length == 0 - - - name: "Delete a Subnet" + - name: Delete a Subnet infoblox.bloxone.ipam_subnet: - address: "10.1.0.0/16" + address: "10.0.0.0/16" space: "{{ ip_space.id }}" state: "absent" register: subnet - - name: "Delete IP Space" + - name: Delete IP Space infoblox.bloxone.ipam_ip_space: - # name: ip_space.object.name name: "{{ ip_name }}" state: "absent" - #check_mode: true register: ip_space \ No newline at end of file diff --git a/tests/integration/targets/ipam_address_info/meta/main.yml b/tests/integration/targets/ipam_address_info/meta/main.yml new file mode 100644 index 0000000..f6d2fce --- /dev/null +++ b/tests/integration/targets/ipam_address_info/meta/main.yml @@ -0,0 +1,2 @@ +--- +dependencies: [setup_address] \ No newline at end of file diff --git a/tests/integration/targets/ipam_address_info/tasks/main.yml b/tests/integration/targets/ipam_address_info/tasks/main.yml new file mode 100644 index 0000000..98e1690 --- /dev/null +++ b/tests/integration/targets/ipam_address_info/tasks/main.yml @@ -0,0 +1,69 @@ +--- + +- module_defaults: + group/infoblox.bloxone.all: + csp_url: "{{ csp_url }}" + api_key: "{{ api_key }}" + block: + # Create a random IP space name to avoid conflicts + - ansible.builtin.set_fact: + tag_value: "site-{{ 999999 | random | string }}" + + # Basic tests for Address Block + - name: Create A Address + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + tags: + region: "eu" + state: "present" + register: address + + - name: Get information about the Address by ID + infoblox.bloxone.ipam_address_info: + id: "{{ address.id }}" + register: address_info + - assert: + that: + - address_info.objects | length != 0 + + - name: Get Address Block information by filters + infoblox.bloxone.ipam_address_info: + filters: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + register: address_info + - assert: + that: + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id + + - name: Get information about the Address by tag + infoblox.bloxone.ipam_address_info: + tag_filters: + region : "eu" + register: address_info + - assert: + that: + - address_info.objects | length != 0 + + - name: Get Address information by filter query + infoblox.bloxone.ipam_address_info: + filter_query: "address=='10.0.0.3' and space=='{{ ip_space.id }}'" + register: address_info + - assert: + that: + - address_info.objects | length != 0 + + - name: "Delete a Subnet" + infoblox.bloxone.ipam_subnet: + address: "10.0.0.0/16" + space: "{{ ip_space.id }}" + state: "absent" + register: subnet + + - name: "Delete IP Space" + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "absent" + register: ip_space \ No newline at end of file diff --git a/tests/integration/targets/setup_address/tasks/main.yml b/tests/integration/targets/setup_address/tasks/main.yml index aa0d95f..1db9031 100644 --- a/tests/integration/targets/setup_address/tasks/main.yml +++ b/tests/integration/targets/setup_address/tasks/main.yml @@ -7,10 +7,9 @@ # Create a random IP space name to avoid conflicts - ansible.builtin.set_fact: ip_name: "test-ip-space1.-{{ 999999 | random | string }}" - tag_value: "site-{{ 999999 | random | string }}" - # Basic tests for Address Block - - name: "Create an IP space" + # Basic tests for Address + - name: Create an IP space infoblox.bloxone.ipam_ip_space: name: "{{ ip_name }}" state: "present" @@ -20,9 +19,9 @@ debug: var: ip_space.object.name - - name: "Create a Subnet" + - name: Create a Subnet infoblox.bloxone.ipam_subnet: - address: "10.1.0.0/16" + address: "10.0.0.0/16" space: "{{ ip_space.id }}" state: "present" register: subnet From 03e00bc93abe86d88dc15062761c13dc0ef95e3a Mon Sep 17 00:00:00 2001 From: ybhalchim Date: Wed, 11 Dec 2024 14:56:49 +0530 Subject: [PATCH 3/6] modified Ipam_Address according Sanity test --- plugins/modules/ipam_address.py | 31 +++++++------------ plugins/modules/ipam_address_info.py | 4 +-- .../targets/ipam_address/tasks/main.yml | 6 ++-- .../targets/ipam_address_info/tasks/main.yml | 2 +- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/plugins/modules/ipam_address.py b/plugins/modules/ipam_address.py index c9a370a..c01e688 100644 --- a/plugins/modules/ipam_address.py +++ b/plugins/modules/ipam_address.py @@ -7,8 +7,6 @@ __metaclass__ = type -from plugins.lookup.bloxone import EXAMPLES - DOCUMENTATION = r""" --- module: ipam_address @@ -40,10 +38,6 @@ description: - "The description for the address object. May contain 0 to 1024 characters. Can include UTF-8." type: str - external_keys: - description: - - "The external keys (source key) for this address in JSON format." - type: dict host: description: - "The resource identifier." @@ -164,7 +158,7 @@ - name: Create the Address with Tags (check mode) infoblox.bloxone.ipam_address: address: "10.0.0.8" - tags: { "tag1": "value1","tag2": "value2",} + tags: { "tag1": "value1","tag2": "value2",} space: "{{ ip_space.id }}" state: "present" check_mode: true @@ -335,11 +329,6 @@ - "The discovery metadata for this address in JSON format." type: dict returned: Always - external_keys: - description: - - "The external keys (source key) for this address in JSON format." - type: dict - returned: Always host: description: - "The resource identifier." @@ -530,7 +519,9 @@ def run_command(self): after=item, ) result["object"] = item - result["id"] = self.existing.id if self.existing is not None else item["id"] if (item and "id" in item) else None + result["id"] = ( + self.existing.id if self.existing is not None else item["id"] if (item and "id" in item) else None + ) except ApiException as e: self.fail_json(msg=f"Failed to execute command: {e.status} {e.reason} {e.body}") @@ -543,19 +534,21 @@ def main(): state=dict(type="str", required=False, choices=["present", "absent"], default="present"), address=dict(type="str"), comment=dict(type="str"), - external_keys=dict(type="dict"), host=dict(type="str"), hwaddr=dict(type="str"), interface=dict(type="str"), - names=dict(type="list", elements="dict", options=dict( - name=dict(type="str"), - type=dict(type="str"), - )), + names=dict( + type="list", + elements="dict", + options=dict( + name=dict(type="str"), + type=dict(type="str"), + ), + ), parent=dict(type="str"), range=dict(type="str"), space=dict(type="str"), tags=dict(type="dict"), - ) module = AddressModule( diff --git a/plugins/modules/ipam_address_info.py b/plugins/modules/ipam_address_info.py index d5d8b08..09c3558 100644 --- a/plugins/modules/ipam_address_info.py +++ b/plugins/modules/ipam_address_info.py @@ -7,8 +7,6 @@ __metaclass__ = type -from plugins.lookup.bloxone import EXAMPLES - DOCUMENTATION = r""" --- module: ipam_address_info @@ -129,7 +127,7 @@ name: "{{ ip_name }}" state: "absent" register: ip_space -"""# noqa: E501 +""" # noqa: E501 RETURN = r""" id: diff --git a/tests/integration/targets/ipam_address/tasks/main.yml b/tests/integration/targets/ipam_address/tasks/main.yml index c3294cc..63d06ce 100644 --- a/tests/integration/targets/ipam_address/tasks/main.yml +++ b/tests/integration/targets/ipam_address/tasks/main.yml @@ -46,7 +46,7 @@ - name: Create the Address with Name infoblox.bloxone.ipam_address: address: "10.0.0.8" - names: [{"name": "test-1","type": "user"}] + names: [{"name": "test-1", "type": "user"}] space: "{{ ip_space.id }}" state: "present" register: address @@ -86,7 +86,7 @@ - name: Create the Address with hwaddr (check mode) infoblox.bloxone.ipam_address: address: "10.0.0.8" - hwaddr : "00:11:22:33:44:55" + hwaddr: "00:11:22:33:44:55" space: "{{ ip_space.id }}" state: "present" check_mode: true @@ -103,7 +103,7 @@ - name: Create the Address with Tags (check mode) infoblox.bloxone.ipam_address: address: "10.0.0.8" - tags: { "tag1": "value1","tag2": "value2",} + tags: {"tag1": "value1", "tag2": "value2"} space: "{{ ip_space.id }}" state: "present" check_mode: true diff --git a/tests/integration/targets/ipam_address_info/tasks/main.yml b/tests/integration/targets/ipam_address_info/tasks/main.yml index 98e1690..42a169e 100644 --- a/tests/integration/targets/ipam_address_info/tasks/main.yml +++ b/tests/integration/targets/ipam_address_info/tasks/main.yml @@ -41,7 +41,7 @@ - name: Get information about the Address by tag infoblox.bloxone.ipam_address_info: tag_filters: - region : "eu" + region: "eu" register: address_info - assert: that: From 3e24bf7e2881ccfff52bf90b30a7a72c85d36e2b Mon Sep 17 00:00:00 2001 From: ybhalchim Date: Wed, 11 Dec 2024 20:20:37 +0530 Subject: [PATCH 4/6] modified ipam_address tests --- plugins/modules/ipam_address_info.py | 11 -- .../targets/ipam_address/meta/main.yml | 2 +- .../targets/ipam_address/tasks/main.yml | 154 +++++++++++++----- .../targets/ipam_address_info/meta/main.yml | 2 +- .../targets/ipam_address_info/tasks/main.yml | 17 +- .../targets/setup_address/tasks/main.yml | 4 - 6 files changed, 131 insertions(+), 59 deletions(-) diff --git a/plugins/modules/ipam_address_info.py b/plugins/modules/ipam_address_info.py index 09c3558..77a2b04 100644 --- a/plugins/modules/ipam_address_info.py +++ b/plugins/modules/ipam_address_info.py @@ -31,16 +31,6 @@ - Filter query to filter objects type: str required: false - inherit: - description: - - Return inheritance information - type: str - required: false - choices: - - full - - partial - - none - default: full tag_filters: description: - Filter dict to filter objects by tags @@ -414,7 +404,6 @@ def main(): id=dict(type="str", required=False), filters=dict(type="dict", required=False), filter_query=dict(type="str", required=False), - inherit=dict(type="str", required=False, choices=["full", "partial", "none"], default="full"), tag_filters=dict(type="dict", required=False), tag_filter_query=dict(type="str", required=False), ) diff --git a/tests/integration/targets/ipam_address/meta/main.yml b/tests/integration/targets/ipam_address/meta/main.yml index f6d2fce..973a9c7 100644 --- a/tests/integration/targets/ipam_address/meta/main.yml +++ b/tests/integration/targets/ipam_address/meta/main.yml @@ -1,2 +1,2 @@ --- -dependencies: [setup_address] \ No newline at end of file +dependencies: [setup_address] diff --git a/tests/integration/targets/ipam_address/tasks/main.yml b/tests/integration/targets/ipam_address/tasks/main.yml index 63d06ce..9a40cf4 100644 --- a/tests/integration/targets/ipam_address/tasks/main.yml +++ b/tests/integration/targets/ipam_address/tasks/main.yml @@ -13,6 +13,14 @@ block: # Basic tests for Address + - name: Create a Address (check mode) + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + state: "present" + check_mode: true + register: address + - name: Create a Address infoblox.bloxone.ipam_address: address: "10.0.0.3" @@ -30,7 +38,8 @@ that: - address is changed - address is not failed - - address_info.objects | length != 0 + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id - name: Create a Address(idempotent) infoblox.bloxone.ipam_address: @@ -43,25 +52,64 @@ - address is not changed - address is not failed - - name: Create the Address with Name + - name: Delete a Address (check mode) infoblox.bloxone.ipam_address: - address: "10.0.0.8" - names: [{"name": "test-1", "type": "user"}] + address: "10.0.0.3" space: "{{ ip_space.id }}" - state: "present" + state: "absent" + check_mode: true + + - name: Delete a Address + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + state: "absent" register: address - name: Get information about the Address infoblox.bloxone.ipam_address_info: filters: { - address: "10.0.0.8", - space: "{{ ip_space.id }}" + address: "10.0.0.3", + space: "{{ ip_space.id }}" } register: address_info - assert: that: - address is changed - address is not failed - - address_info.objects | length > 0 + - address_info.objects | length == 0 + + - name: Delete a Address(idempotent) + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + state: "absent" + register: address + - assert: + that: + - address is not changed + - address is not failed + + - name: Create the Address with Name + infoblox.bloxone.ipam_address: + address: "10.0.0.3" + names: + - name: "test-1" + type: "user" + space: "{{ ip_space.id }}" + state: "present" + register: address + - name: Get information about the Address + infoblox.bloxone.ipam_address_info: + filters: + address: "10.0.0.3" + space: "{{ ip_space.id }}" + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length == 1 + - address_info.objects[0].names[0].name == address.object.names[0].name - name: Create the Address with comment infoblox.bloxone.ipam_address: @@ -81,64 +129,88 @@ that: - address is changed - address is not failed - - address_info.objects[0].comment == "some comment" + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id + - address_info.objects[0].comment == address.object.comment - - name: Create the Address with hwaddr (check mode) + - name: Create the Address with hwaddr infoblox.bloxone.ipam_address: - address: "10.0.0.8" + address: "10.0.0.6" hwaddr: "00:11:22:33:44:55" space: "{{ ip_space.id }}" state: "present" - check_mode: true + register: address + - name: Get information about the Address + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.0.0.6", + space: "{{ ip_space.id }}", + hwaddr: "00:11:22:33:44:55" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id + - address_info.objects[0].hwaddr == address.object.hwaddr - - name: Create the Address with Interface (check mode) + - name: Create the Address with Interface infoblox.bloxone.ipam_address: - address: "10.0.0.8" + address: "10.0.0.7" interface: "eth0" space: "{{ ip_space.id }}" state: "present" register: address - check_mode: true + - name: Get information about the Address + infoblox.bloxone.ipam_address_info: + filters: { + address: "10.0.0.7", + space: "{{ ip_space.id }}", + interface: "eth0" + } + register: address_info + - assert: + that: + - address is changed + - address is not failed + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id + - address_info.objects[0].interface == address.object.interface - - name: Create the Address with Tags (check mode) + - name: Create the Address with Tags infoblox.bloxone.ipam_address: address: "10.0.0.8" tags: {"tag1": "value1", "tag2": "value2"} space: "{{ ip_space.id }}" state: "present" - check_mode: true - - - name: Delete a Address - infoblox.bloxone.ipam_address: - address: "10.0.0.3" - space: "{{ ip_space.id }}" - state: "absent" - - - name: Delete a Address - infoblox.bloxone.ipam_address: - address: "10.0.0.5" - space: "{{ ip_space.id }}" - state: "absent" - - - name: Delete a Address - infoblox.bloxone.ipam_address: - address: "10.0.0.8" - space: "{{ ip_space.id }}" - state: "absent" - - - name: Get information about the address + register: address + - name: Get information about the Address infoblox.bloxone.ipam_address_info: filters: { - address: "10.0.0.3", - space: "{{ ip_space.id }}" + address: "10.0.0.8", + space: "{{ ip_space.id }}", } register: address_info + - name: Debug address + debug: + var: address + + - name: Debug address_info + debug: + var: address_info - assert: that: - address is changed - address is not failed - - address_info.objects | length == 0 + - address_info.objects | length == 1 + - address_info.objects[0].id == address.id + - address_info.objects[0].tags.tag1 == "value1" + - address_info.objects[0].tags.tag2 == "value2" + always: + # Cleanup if the test fails - name: Delete a Subnet infoblox.bloxone.ipam_subnet: address: "10.0.0.0/16" @@ -150,4 +222,4 @@ infoblox.bloxone.ipam_ip_space: name: "{{ ip_name }}" state: "absent" - register: ip_space \ No newline at end of file + register: ip_space diff --git a/tests/integration/targets/ipam_address_info/meta/main.yml b/tests/integration/targets/ipam_address_info/meta/main.yml index f6d2fce..973a9c7 100644 --- a/tests/integration/targets/ipam_address_info/meta/main.yml +++ b/tests/integration/targets/ipam_address_info/meta/main.yml @@ -1,2 +1,2 @@ --- -dependencies: [setup_address] \ No newline at end of file +dependencies: [setup_address] diff --git a/tests/integration/targets/ipam_address_info/tasks/main.yml b/tests/integration/targets/ipam_address_info/tasks/main.yml index 42a169e..a58efd8 100644 --- a/tests/integration/targets/ipam_address_info/tasks/main.yml +++ b/tests/integration/targets/ipam_address_info/tasks/main.yml @@ -66,4 +66,19 @@ infoblox.bloxone.ipam_ip_space: name: "{{ ip_name }}" state: "absent" - register: ip_space \ No newline at end of file + register: ip_space + + always: + # Cleanup if the test fails + - name: Delete a Subnet + infoblox.bloxone.ipam_subnet: + address: "10.0.0.0/16" + space: "{{ ip_space.id }}" + state: "absent" + register: subnet + + - name: Delete IP Space + infoblox.bloxone.ipam_ip_space: + name: "{{ ip_name }}" + state: "absent" + register: ip_space diff --git a/tests/integration/targets/setup_address/tasks/main.yml b/tests/integration/targets/setup_address/tasks/main.yml index 1db9031..6e490d0 100644 --- a/tests/integration/targets/setup_address/tasks/main.yml +++ b/tests/integration/targets/setup_address/tasks/main.yml @@ -15,10 +15,6 @@ state: "present" register: ip_space - - name: Debug the IP space name created - debug: - var: ip_space.object.name - - name: Create a Subnet infoblox.bloxone.ipam_subnet: address: "10.0.0.0/16" From de75f1966f5aefcb548d6668f95c2cc933964e7d Mon Sep 17 00:00:00 2001 From: ybhalchim Date: Tue, 17 Dec 2024 12:44:45 +0530 Subject: [PATCH 5/6] added ChangeLog and Deprecation Notice --- changelogs/fragments/55-ipam-address.yml | 3 +++ meta/runtime.yml | 9 +++++++++ plugins/modules/b1_ipam_ipv4_reservation.py | 4 ++++ plugins/modules/b1_ipam_ipv4_reservation_gather.py | 4 ++++ 4 files changed, 20 insertions(+) create mode 100644 changelogs/fragments/55-ipam-address.yml diff --git a/changelogs/fragments/55-ipam-address.yml b/changelogs/fragments/55-ipam-address.yml new file mode 100644 index 0000000..b78172f --- /dev/null +++ b/changelogs/fragments/55-ipam-address.yml @@ -0,0 +1,3 @@ +deprecated_features: + - b1_ipam_ipv4_reservation - is deprecated in favor of 'ipam_address'. + - b1_ipam_ipv4_reservation_gather - is deprecated in favor of 'ipam_address_info'. \ No newline at end of file diff --git a/meta/runtime.yml b/meta/runtime.yml index 7ea457f..a9b37a9 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -69,3 +69,12 @@ plugin_routing: deprecation: removal_version: 3.0.0 warning_text: Use infoblox.bloxone.dns_auth_zone_info instead. + + b1_ipam_ipv4_reservation: + deprecation: + removal_version: 3.0.0 + warning_text: Use infoblox.bloxone.ipam_address instead. + b1_ipam_ipv4_reservation_gather: + deprecation: + removal_version: 3.0.0 + warning_text: Use infoblox.bloxone.ipam_address_info instead. diff --git a/plugins/modules/b1_ipam_ipv4_reservation.py b/plugins/modules/b1_ipam_ipv4_reservation.py index 79d6788..1081e9f 100644 --- a/plugins/modules/b1_ipam_ipv4_reservation.py +++ b/plugins/modules/b1_ipam_ipv4_reservation.py @@ -14,6 +14,10 @@ author: "Amit Mishra (@amishra), Sriram Kannan(@kannans)" short_description: Configure IPv4 address reservation on Infoblox BloxOne DDI version_added: "1.0.1" +deprecated: + removed_in: 3.0.0 + why: This module is deprecated and will be removed in version 3.0.0. Use M(ipam_address) instead. + alternative: Use M(ipam_address) instead. description: - Get, Create, Update and Delete IPv4 address reservation on Infoblox BloxOne DDI. This module manages the IPAM IPv4 address reservation object using BloxOne REST APIs. requirements: diff --git a/plugins/modules/b1_ipam_ipv4_reservation_gather.py b/plugins/modules/b1_ipam_ipv4_reservation_gather.py index 82955f4..197929e 100644 --- a/plugins/modules/b1_ipam_ipv4_reservation_gather.py +++ b/plugins/modules/b1_ipam_ipv4_reservation_gather.py @@ -13,6 +13,10 @@ author: "Amit Mishra (@amishra), Sriram Kannan(@kannans)" short_description: Gather information about Address Block in B1DDI version_added: "1.0.1" +deprecated: + removed_in: 3.0.0 + why: This module is deprecated and will be removed in version 3.0.0. Use M(ipam_address_info) instead. + alternative: Use M(ipam_address_info) instead. description: - Gather information about Address Block object on Infoblox BloxOne DDI. This module gather information about address block object using BloxOne REST APIs. requirements: From 98c4cbe85803183d1b6fed4f916c41eb9b964bf1 Mon Sep 17 00:00:00 2001 From: ybhalchim Date: Tue, 17 Dec 2024 12:46:41 +0530 Subject: [PATCH 6/6] added extra spaces --- changelogs/fragments/55-ipam-address.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/55-ipam-address.yml b/changelogs/fragments/55-ipam-address.yml index b78172f..95ada8c 100644 --- a/changelogs/fragments/55-ipam-address.yml +++ b/changelogs/fragments/55-ipam-address.yml @@ -1,3 +1,3 @@ deprecated_features: - b1_ipam_ipv4_reservation - is deprecated in favor of 'ipam_address'. - - b1_ipam_ipv4_reservation_gather - is deprecated in favor of 'ipam_address_info'. \ No newline at end of file + - b1_ipam_ipv4_reservation_gather - is deprecated in favor of 'ipam_address_info'.