Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Cannot assign existing IP to network interface #975

Open
cfiehe opened this issue Apr 9, 2023 · 5 comments
Open

[Bug]: Cannot assign existing IP to network interface #975

cfiehe opened this issue Apr 9, 2023 · 5 comments
Labels
bug Something isn't working

Comments

@cfiehe
Copy link
Contributor

cfiehe commented Apr 9, 2023

Ansible NetBox Collection version

v3.12.0

Ansible version

ansible [core 2.14.1]
  config file = /home/ansible/automation/projects/provisioning/ansible.cfg
  configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ansible/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/ansible/automation/projects/provisioning/collections
  executable location = /home/ansible/.local/bin/ansible
  python version = 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True

NetBox version

v3.4.7

Python version

3.10

Steps to Reproduce

If an IP address already exists in our Netbox, it cannot be assigned to an interface. The module always instructs the Netbox to create a new IP. This results in a Duplicate IP address exception, when Enforce unique space is enabled, or in a duplicate IP.

- name: Create an IP address
  netbox.netbox.netbox_ip_address:
    netbox_url: "{{ netbox_url }}"
    netbox_token: "{{ netbox_token }}"
    data:
      address: 10.1.60.232/24
      tenant: my_company
      vrf: my_company-RFC1918
      status: Active
    state: present

- name: Assign IP address to existing interface
  netbox.netbox.netbox_ip_address:
    netbox_url: "{{ netbox_url }}"
    netbox_token: "{{ netbox_token }}"
    data:
      address: 10.1.60.232/24
      assigned_object:
        virtual_machine: any_vm.local
        name: ens224
      tenant: my_company
      vrf: my_company-RFC1918
      status: Active
    state: present

Expected Behavior

The existing IP should be modified and should be attached to the specified interface.

Observed Behavior

The VRF my_company-RFC1918 uses the option Enforce unique space , that is why the assignment fails with a Duplicate IP address, because the module tries to create a new IP instead of modifying the existing one:

fatal: [any_vm.local -> localhost]: FAILED! => {"changed": false, "msg": "{\"address\":[\"Duplicate IP address found in VRF my_company-RFC1918: 10.1.60.232/24\"]}"}
@cfiehe cfiehe added the bug Something isn't working label Apr 9, 2023
@Zwordi
Copy link

Zwordi commented Oct 18, 2023

Hello,

My 2cents here as I’m looking of kind of the same topic.
The "Enforce unique space" is the good way. As by having an unique address you’re being certain that this one will not be treated differently.
The need here is about being able to update an ip record with the "state:present" attribute.

@d-zalewski
Copy link

d-zalewski commented Feb 25, 2024

I'm seeing exact same behaviour. My scenario is that sometimes I need to reserve bunch of IPs ahead of time to get network teams to update firewall rules (which can take days). Once firewall rules are in place I can then build VMs and I would like them to use reserved IPs.

Based on below I would expect to be able to assign an existing IP to a VM if the state is set to present.

https://github.com/netbox-community/ansible_modules/blob/devel/plugins/modules/netbox_ip_address.py#L51

With state C(present), if an interface is given, it will ensure
            that an IP inside this prefix (and vrf, if given) is attached
            to this interface. Otherwise, it will get the next available IP
            of this prefix and attach it.
            With state C(new), it will force to get the next available IP in
            this prefix. If an interface is given, it will also force to attach
            it.

@Ido-Don
Copy link
Contributor

Ido-Don commented Aug 30, 2024

Issue Summary

The current behavior of the Ansible netbox_ip_address module does not align with the NetBox API's expectations for assigning IP addresses. Specifically, the API uses assigned_object_type and assigned_object_id to identify the target of the IP assignment, whereas the Ansible module accepts assigned_object and interface, which are more descriptive names.

Proposed Solution

To align the Ansible module with the NetBox API, we need to convert the human-readable names provided by the user into the appropriate assigned_object_type and assigned_object_id values. This can be achieved by modifying the NetboxIpamModule.run() function in the netbox_ipam.py file.

A psudo-code exemple for the fix to the issue:

def run(self):
    ...
    assigned_object = data.get("assigned_object")
    if assigned_object:
        # Determine the type and ID of the assigned object
        interface_name = assigned_object.get("name")
        if assigned_object.get("device"):
            device_name = assigned_object.get("device")
            device_type = DCIM_INTERFACE
        elif assigned_object.get("virtual_machine"):
            device_name = assigned_object.get("virtual_machine")
            device_type = VIRTUALIZATION_VIRTUAL_MACHINE
        else:
            raise Exception("Invalid assigned_object parameters. Device or virtual machine must be specified.")
        
        # Fetch the interface object based on the type and name
        interface = netbox_object.get(device_type, device_name, interface_name)
        if not interface:
            raise Exception(f"No interface found with device '{device_name}' and name '{interface_name}'")
        
        # Update the data dictionary with the required fields for the API
        data.update({
            "assigned_object_type": device_type,
            "assigned_object_id": interface.id
        })

    # Process the 'interface' argument similarly
    interface = data.get("interface")
    if interface:
        interface_name = interface.get("name")
        device_name = data.get("device")
        device_type = DCIM_INTERFACE
        
        # Fetch the interface object based on the type and name
        interface = netbox_object.get(device_type, device_name, interface_name)
        if not interface:
            raise Exception(f"No interface found with device '{device_name}' and name '{interface_name}'")
        
        # Update the data dictionary
        data.update({
            "assigned_object_type": device_type,
            "assigned_object_id": interface.id
        })

    ...
    # Update the NetBox instance with the modified data
    update_netbox(data)

Explanation

  • Mapping Names to IDs: The assigned_object and interface fields contain descriptive names, which are not directly usable by the NetBox API. The proposed changes convert these names into the required assigned_object_type and assigned_object_id.

  • Exception Handling: Improved error messages and checks ensure that the module fails gracefully if the provided names do not match any known interfaces.

Footnotes

@Ido-Don
Copy link
Contributor

Ido-Don commented Sep 13, 2024

BTW If anyone want's to take these changes and make a pull request, she/he is welcome to do so.
I'm not doing it myself since it is a lot of work to create the development environment for proper testing of the fix.
Might come around some weekend to do so.
If you beat me to the punch then you have my blessing 😄

@Ido-Don Ido-Don mentioned this issue Sep 20, 2024
3 tasks
Ido-Don added a commit to Ido-Don/ansible_modules that referenced this issue Oct 12, 2024
@Ido-Don
Copy link
Contributor

Ido-Don commented Oct 12, 2024

So I made some more debugging and wanted to document what i found.

first of all there is no problem with assigned_object, I started debugging it for real and saw it was working fine.
When debugging i saw that the program DOES check what is the id of an interface it just doesn't put it in the assigned_object_id value but the interface value instead.
I added a pr that adds the assigned_object_id and assigned_object_type to the request so that netbox can use it.
anyone is welcome to check it out #1323

Ido-Don added a commit to Ido-Don/ansible_modules that referenced this issue Oct 17, 2024
sc68cal added a commit that referenced this issue Oct 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants