Skip to content

Commit

Permalink
Merge branch 'IP-space-UTs' of github.com:VishrutiBuddhadev/bloxone-a…
Browse files Browse the repository at this point in the history
…nsible into IP-space-UTs
  • Loading branch information
VishrutiBuddhadev committed Jan 21, 2025
2 parents 7ca9017 + c45c8ac commit f870bb6
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 88 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/48-address-block.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
major_changes:
- added support for creating and retrieving next available address blocks.
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ action_groups:
- ipam_address_block_info
- ipam_host
- ipam_host_info
- ipam_next_available_address_block_info

infra:
- infra_join_token
Expand Down
75 changes: 45 additions & 30 deletions plugins/modules/ipam_address_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
- "The minimum percentage of addresses that must be available outside of the DHCP ranges and fixed addresses when making a suggested change.."
type: int
reenable_date:
description: ""
description: "The date at which notifications will be re-enabled automatically."
type: str
cidr:
description:
Expand Down Expand Up @@ -727,42 +727,42 @@
description:
- "The low threshold value for the percentage of used IP addresses relative to the total IP addresses available in the scope of the object. Thresholds are inclusive in the comparison test."
type: int
next_available_id:
description:
- "The resource identifier for the address block where the next available address block should be generated."
type: str
extends_documentation_fragment:
- infoblox.bloxone.common
""" # noqa: E501

EXAMPLES = r"""
- name: "Create an ip space"
- name: "Create an IP space (required as parent)"
infoblox.bloxone.ipam_ip_space:
name: "my-ip-space"
state: "present"
register: ip_space
register: ip_space
- name: "Create an address block"
infoblox.bloxone.ipam_address_block:
address: "10.0.0.0/24"
address: "10.0.0.0/16"
space: "{{ ip_space.id }}"
state: "present"
register: address_block
- name: "Delete an Address Block"
- name: "Create Next Available Address Block"
infoblox.bloxone.ipam_address_block:
address: "10.0.0.0/16"
space: "{{ ip_space.id }}"
state: "absent"
cidr: 20
next_available_id: "{{ address_block.id }}"
state: "present"
- name: "Create an Address Block with separate cidr"
- name: "Create an Address Block with Additional Fields"
infoblox.bloxone.ipam_address_block:
address: "10.0.0.0"
cidr: 16
space: "{{ ip_space.id }}"
state: "present"
- name: "Create an Address Block with DHCP config overridden"
infoblox.bloxone.ipam_address_block:
address: "10.0.0.0/16"
space: "{{ ip_space.id }}"
state: "present"
dhcp_config:
lease_time: 3600
inheritance_sources:
Expand All @@ -789,14 +789,14 @@
action: inherit
lease_time_v6:
action: inherit
tags:
location: "site-1"
- name: "Create an Address Block with tags"
- name: "Delete an Address Block"
infoblox.bloxone.ipam_address_block:
address: "10.0.0.0/16"
space: "{{ ip_space.id }}"
state: "present"
tags:
location: "site-1"
state: "absent"
"""

RETURN = r"""
Expand Down Expand Up @@ -868,7 +868,7 @@
type: int
returned: Always
reenable_date:
description: ""
description: "The date at which notifications will be re-enabled automatically."
type: str
returned: Always
asm_scope_flag:
Expand Down Expand Up @@ -2316,23 +2316,23 @@
returned: Always
contains:
abandoned:
description: ""
description: "The number of IP addresses in the scope of the object which are in the abandoned state (issued by a DHCP server and then declined by the client)."
type: str
returned: Always
dynamic:
description: ""
description: "The number of IP addresses handed out by DHCP in the scope of the object. This includes all leased addresses, fixed addresses that are defined but not currently leased and abandoned leases."
type: str
returned: Always
static:
description: ""
description: "The number of defined IP addresses such as reservations or DNS records. It can be computed as _static_ = _used_ - _dynamic_."
type: str
returned: Always
total:
description: ""
description: "The total number of IP addresses available in the scope of the object."
type: str
returned: Always
used:
description: ""
description: "The number of IP addresses used in the scope of the object."
type: str
returned: Always
""" # noqa: E501
Expand All @@ -2349,12 +2349,15 @@
class AddressBlockModule(BloxoneAnsibleModule):
def __init__(self, *args, **kwargs):
super(AddressBlockModule, self).__init__(*args, **kwargs)
self.next_available_id = self.params.get("next_available_id")

if "/" in self.params["address"]:
self.params["address"], netmask = self.params["address"].split("/")
self.params["cidr"] = int(netmask)
# If address is None, next_available_id will be utilized along with a separately provided CIDR value
if self.params["address"] is not None:
if "/" in self.params["address"]:
self.params["address"], netmask = self.params["address"].split("/")
self.params["cidr"] = int(netmask)

exclude = ["state", "csp_url", "api_key", "id"]
exclude = ["state", "csp_url", "api_key", "id", "next_available_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 = AddressBlock.from_dict(self._payload_params)

Expand Down Expand Up @@ -2399,6 +2402,9 @@ def find(self):
return None
raise e
else:
# If address is None, return None, indicating next_available_address block should be created
if self.params["address"] is None:
return None
filter = f"address=='{self.params['address']}' and space=='{self.params['space']}' and cidr=={self.params['cidr']}"
resp = AddressBlockApi(self.client).list(filter=filter, inherit="full")
if len(resp.results) == 1:
Expand All @@ -2412,6 +2418,11 @@ def create(self):
if self.check_mode:
return None

# If next_available_id is not None, set the address to the next available ID.
if self.next_available_id is not None:
naId = f"{self.next_available_id}/nextavailableaddressblock"
self._payload.address = naId

resp = AddressBlockApi(self.client).create(body=self.payload, inherit="full")
return resp.result.model_dump(by_alias=True, exclude_none=True)

Expand Down Expand Up @@ -2475,7 +2486,8 @@ 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"),
address=dict(type="str", required=False),
next_available_id=dict(type="str", required=False),
asm_config=dict(
type="dict",
options=dict(
Expand Down Expand Up @@ -2766,7 +2778,10 @@ def main():
module = AddressBlockModule(
argument_spec=module_args,
supports_check_mode=True,
required_if=[("state", "present", ["address", "space"])],
mutually_exclusive=[["address", "next_available_id"]],
required_if=[("state", "present", ["space"])],
required_one_of=[["address", "next_available_id"]],
required_together=[["cidr", "next_available_id"]],
)

module.run_command()
Expand Down
17 changes: 8 additions & 9 deletions plugins/modules/ipam_address_block_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@
address: "10.0.0.0/16"
space: "{{ ip_space.id }}"
tags:
location: "{{ tag_value }}"
location: "site-1"
state: "present"
register: address_block
- name: Get Address Block information by ID
infoblox.bloxone.ipam_address_block_info:
Expand All @@ -83,7 +82,7 @@
- name: Get Address Block information by tag filters
infoblox.bloxone.ipam_address_block_info:
tag_filters:
location: "{{ tag_value }}"
location: "site-1"
"""

RETURN = r"""
Expand Down Expand Up @@ -156,7 +155,7 @@
type: int
returned: Always
reenable_date:
description: ""
description: "The date at which notifications will be re-enabled automatically."
type: str
returned: Always
asm_scope_flag:
Expand Down Expand Up @@ -1619,23 +1618,23 @@
returned: Always
contains:
abandoned:
description: ""
description: "The number of IP addresses in the scope of the object which are in the abandoned state (issued by a DHCP server and then declined by the client)."
type: str
returned: Always
dynamic:
description: ""
description: "The number of IP addresses handed out by DHCP in the scope of the object. This includes all leased addresses, fixed addresses that are defined but not currently leased and abandoned leases."
type: str
returned: Always
static:
description: ""
description: "The number of defined IP addresses such as reservations or DNS records. It can be computed as _static_ = _used_ - _dynamic_."
type: str
returned: Always
total:
description: ""
description: "The total number of IP addresses available in the scope of the object."
type: str
returned: Always
used:
description: ""
description: "The number of IP addresses used in the scope of the object."
type: str
returned: Always
""" # noqa: E501
Expand Down
139 changes: 139 additions & 0 deletions plugins/modules/ipam_next_available_address_block_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/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_next_available_address_block_info
short_description: Manage NextAvailableAddressBlock
description:
- Manage NextAvailableAddressBlock
version_added: 2.0.0
author: Infoblox Inc. (@infobloxopen)
options:
id:
description:
- ID of the object
type: str
required: true
cidr:
description:
- The CIDR value of the object
type: int
required: true
count:
description:
- Number of objects to generate. Default 1 if not set
type: int
required: false
extends_documentation_fragment:
- infoblox.bloxone.common
""" # noqa: E501

EXAMPLES = r"""
- name: "Create an Address Block"
infoblox.bloxone.ipam_address_block:
address: "10.0.0.0/16"
space: "{{ ip_space.id }}"
state: "present"
- name: Get Next Available Address Block Information by ID
infoblox.bloxone.ipam_next_available_address_block_info:
id: "{{ address_block.id }}"
cidr: 20
- name: Get Next Available Address Block Information by ID and Count
infoblox.bloxone.ipam_next_available_address_block_info:
id: "{{ address_block.id }}"
cidr: 24
count: 5
"""

RETURN = r"""
id:
description:
- ID of the AddressBlock object.
type: str
returned: Always
objects:
description:
- List of next available address block's addresses.
type: list
elements: str
returned: Always
"""

from ansible_collections.infoblox.bloxone.plugins.module_utils.modules import BloxoneAnsibleModule

try:
from bloxone_client import ApiException
from ipam import AddressBlockApi
except ImportError:
pass # Handled by BloxoneAnsibleModule


class NextAvailableAddressBlockInfoModule(BloxoneAnsibleModule):
def __init__(self, *args, **kwargs):
super(NextAvailableAddressBlockInfoModule, self).__init__(*args, **kwargs)
self._existing = None
self._limit = 1000

def find(self):
all_results = []
offset = 0
while True:
try:
resp = AddressBlockApi(self.client).list_next_available_ab(
id=self.params["id"], cidr=self.params["cidr"], count=self.params["count"]
)
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:
# The expected output is a list of addresses as strings.
# Therefore, we extract only the 'address' field from each object in the results.
all_results.append(r.address)

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=True),
cidr=dict(type="int", required=True),
count=dict(type="int", required=False),
)

module = NextAvailableAddressBlockInfoModule(
argument_spec=module_args,
supports_check_mode=True,
)
module.run_command()


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions tests/integration/targets/ipam_address_block/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
dependencies: [setup_ip_space]
Loading

0 comments on commit f870bb6

Please sign in to comment.