nautobot / nautobot-ansible

Ansible Collection for managing Nautobot Data
https://nautobot-ansible.readthedocs.io/en/latest/
GNU General Public License v3.0
48 stars 32 forks source link

plugin module fails to create nautobot-device-lifecycle-mgmt hardware entries #306

Open pugnacity opened 9 months ago

pugnacity commented 9 months ago
ISSUE TYPE
SOFTWARE VERSIONS
pynautobot

2.0.2

Ansible:

2.16.2

Nautobot:

2.1.1

Collection:

5.1.0

SUMMARY

creating new hardware EoL entries will fail

STEPS TO REPRODUCE

Run the following task

    - name: Init InfraDB | Device Lifecycle | hardware_notice
      networktocode.nautobot.plugin:
        url: "{{ nautobot_url }}"
        token: "{{ nautobot_token }}"
        validate_certs: "{{ documentation_nautobot['validate_certs'] }}"
        plugin: nautobot-device-lifecycle-mgmt
        endpoint: hardware
        identifiers:
          device_type: "{{ hardware_notice['device'] }}"
        attrs:
          device_type:
            model: "{{ hardware_notice['device'] }}"
          release_date: "{{ hardware_notice['release_date'] }}"
          end_of_sale: "{{ hardware_notice['end_of_sale'] }}"
          end_of_support: "{{ hardware_notice['end_of_support'] }}"
          # yamllint disable-line rule:line-length
          end_of_sw_releases: "{{ hardware_notice['end_of_sw_releases'] | default(omit) }}"
          # yamllint disable-line rule:line-length
          end_of_security_patches: "{{ hardware_notice['end_of_security_patches'] | default(omit) }}"
          # yamllint disable-line rule:line-length
          documentation_url: "{{ hardware_notice['documentation_url'] | default(omit) }}"
        state: present
      loop: "{{ hardware_notices }}"
      loop_control:
        loop_var: hardware_notice

the used variable looks like this

hardware_notices:
  - device: UCS-FI-6332-16UP
    release_date: "2016-11-01"
    end_of_sale: "2023-10-30"
    end_of_support: "2028-10-31"
    end_of_sw_releases: "2028-10-31"
    end_of_security_patches: "2028-10-31"
    # yamllint disable-line rule:line-length
    documentation_url: https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html
EXPECTED RESULTS

Hardware notice is created or at least reported as not changed

ACTUAL RESULTS
failed: [localhost] (item={'device': 'UCS-FI-6332-16UP', 'release_date': '2016-11-01', 'end_of_sale': '2023-10-30', 'end_of_support': '2028-10-31', 'end_of_sw_releases': '2028-10-31', 'end_of_security_patches': '2028-10-31', 'documentation_url': 'https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html'}) => {
    "ansible_loop_var": "hardware_notice",
    "changed": false,
    "hardware_notice": {
        "device": "UCS-FI-6332-16UP",
        "documentation_url": "https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html",
        "end_of_sale": "2023-10-30",
        "end_of_security_patches": "2028-10-31",
        "end_of_support": "2028-10-31",
        "end_of_sw_releases": "2028-10-31",
        "release_date": "2016-11-01"
    },
    "invocation": {
        "module_args": {
            "api_version": null,
            "attrs": {
                "device_type": {
                    "model": "UCS-FI-6332-16UP"
                },
                "documentation_url": "https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html",
                "end_of_sale": "2023-10-30",
                "end_of_security_patches": "2028-10-31",
                "end_of_support": "2028-10-31",
                "end_of_sw_releases": "2028-10-31",
                "release_date": "2016-11-01"
            },
            "endpoint": "hardware",
            "identifiers": {
                "device_type": "UCS-FI-6332-16UP"
            },
            "plugin": "nautobot-device-lifecycle-mgmt",
            "query_params": null,
            "state": "present",
            "token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "url": "https://nautobot.domain.tld",
            "validate_certs": false
        }
    },
    "msg": "{\"device_type\":[\"Select a valid choice. 4f6c1d14-2571-42f1-83d6-24e870d4ef80 is not one of the available choices.\"]}"
}

When I change the identifiers to:

identifiers:
  device_type_id: "{{ hardware_notice['device'] }}"

The error is changed to

 "msg": "{\"device_type_id\":[\"“UCS-FI-6332-16UP” is not a valid UUID.\"]}"

What I don't understand is, why there is one time the error message with the correct ID and one time with the model name.

joewesch commented 9 months ago

Can you try changing this:

        identifiers:
          device_type: "{{ hardware_notice['device'] }}"
        attrs:
          device_type:
            model: "{{ hardware_notice['device'] }}"

To this:

        identifiers:
          device_type:
            model: "{{ hardware_notice['device'] }}"
        attrs:
          device_type:
            model: "{{ hardware_notice['device'] }}"
pugnacity commented 9 months ago

The error is still the same:

failed: [localhost] (item={'device': 'UCS-FI-6332-16UP', 'release_date': '2016-11-01', 'end_of_sale': '2023-10-30', 'end_of_support': '2028-10-31', 'end_of_sw_releases': '2028-10-31', 'end_of_security_patches': '2028-10-31', 'documentation_url': 'https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html'}) => {
    "ansible_loop_var": "hardware_notice",
    "changed": false,
    "hardware_notice": {
        "device": "UCS-FI-6332-16UP",
        "documentation_url": "https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html",
        "end_of_sale": "2023-10-30",
        "end_of_security_patches": "2028-10-31",
        "end_of_support": "2028-10-31",
        "end_of_sw_releases": "2028-10-31",
        "release_date": "2016-11-01"
    },
    "invocation": {
        "module_args": {
            "api_version": null,
            "attrs": {
                "device_type": {
                    "model": "UCS-FI-6332-16UP"
                },
                "documentation_url": "https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html",
                "end_of_sale": "2023-10-30",
                "end_of_security_patches": "2028-10-31",
                "end_of_support": "2028-10-31",
                "end_of_sw_releases": "2028-10-31",
                "release_date": "2016-11-01"
            },
            "endpoint": "hardware",
            "identifiers": {
                "device_type": {
                    "model": "UCS-FI-6332-16UP"
                }
            },
            "plugin": "nautobot-device-lifecycle-mgmt",
            "query_params": null,
            "state": "present",
            "token": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "url": "https://nautobot.domain.tld",
            "validate_certs": false
        }
    },
    "msg": "{\"device_type\":[\"Select a valid choice. 4f6c1d14-2571-42f1-83d6-24e870d4ef80 is not one of the available choices.\"]}"
}

what I don't understand is why sometimes the UUID is used and sometimes the name string.

pszulczewski commented 9 months ago

I'll check that.

pszulczewski commented 9 months ago

We use lookup for known core models to automatically extract object UUIDs for well known attributes. In this case for device_type. I see it fails on verification step after creation, because LCM plugin API expects device_type to be a model string, not UUID, so the get methods fails.

https://github.com/nautobot/nautobot-app-device-lifecycle-mgmt/blob/68e6b781a418607b9ad833793be231d36b413e23/nautobot_device_lifecycle_mgmt/filters.py#L32

I can create it with device_type_id as an identifier. I had to use lookup module to extract it.

    - set_fact:
        attrs:
          device_type: "Cisco Test"
          release_date: "2016-11-01"
          end_of_sale: "2023-10-30"
          end_of_support: "2028-10-31"
          end_of_sw_releases: "2028-10-31"
          end_of_security_patches: "2028-10-31"
          documentation_url: "https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html"

    - set_fact:
        api_filter_str: 'model="{{ attrs.device_type }}"'

    - set_fact:
        device_type: "{{ lookup('networktocode.nautobot.lookup', 'device-types', api_endpoint=nautobot_url, token=nautobot_token, api_filter=api_filter_str) }}"

    - name: Plugin module issue 306 - Create
      networktocode.nautobot.plugin:
        url: "{{ nautobot_url }}"
        token: "{{ nautobot_token }}"
        plugin: nautobot-device-lifecycle-mgmt
        endpoint: hardware
        identifiers:
          device_type_id: "{{ device_type['key'] }}"
        attrs: "{{ attrs }}"
        state: present

    - name: Plugin module issue 306 - Create duplicate
      networktocode.nautobot.plugin:
        url: "{{ nautobot_url }}"
        token: "{{ nautobot_token }}"
        plugin: nautobot-device-lifecycle-mgmt
        endpoint: hardware
        identifiers:
          device_type_id: "{{ device_type['key'] }}"
        attrs: "{{ attrs }}"
        state: present

But unfortunately only on first creation, idempotently it fails.

TASK [regression-latest : Plugin module issue 306] *****************************
changed: [testhost]

TASK [regression-latest : Plugin module issue 306 DUP] *************************
fatal: [testhost]: FAILED! => {"changed": false, "msg": "device_type_id does not exist on existing object. Check to make sure valid field."}

That would need changes in the API in lifecycle_management_app.

I tested that locally by building local test env with lcm branch where I changed api filter. https://github.com/nautobot/nautobot-app-device-lifecycle-mgmt/commit/053d7d4b380518bd19a24158bc13343db2a45a9f

And it works fine:

    - set_fact:
        attrs:
          device_type: "Cisco Test"
          release_date: "2016-11-01"
          end_of_sale: "2023-10-30"
          end_of_support: "2028-10-31"
          end_of_sw_releases: "2028-10-31"
          end_of_security_patches: "2028-10-31"
          documentation_url: "https://www.cisco.com/c/en/us/products/collateral/servers-unified-computing/ucs-fabric-interconnect-f1-6332-16up-eol.html"

    - name: Plugin module issue 306 - Create
      networktocode.nautobot.plugin:
        url: "{{ nautobot_url }}"
        token: "{{ nautobot_token }}"
        plugin: nautobot-device-lifecycle-mgmt
        endpoint: hardware
        identifiers:
          device_type: "{{ attrs['device_type'] }}"
        attrs: "{{ attrs }}"
        state: present

    - name: Plugin module issue 306 - Create duplicate
      networktocode.nautobot.plugin:
        url: "{{ nautobot_url }}"
        token: "{{ nautobot_token }}"
        plugin: nautobot-device-lifecycle-mgmt
        endpoint: hardware
        identifiers:
          device_type: "{{ attrs['device_type'] }}"
        attrs: "{{ attrs }}"
        state: present
TASK [regression-latest : set_fact] ********************************************
ok: [testhost]

TASK [regression-latest : Plugin module issue 306 - Create] ********************
changed: [testhost]

TASK [regression-latest : Plugin module issue 306 - Create duplicate] **********
ok: [testhost]

I will submit this branch as a pull request, but that will require new major release of lcm app as this is a backwards breaking change.