ansible-collections / community.routeros

Ansible modules for managing MikroTik RouterOS instances.
https://galaxy.ansible.com/ui/repo/published/community/routeros/
GNU General Public License v3.0
99 stars 45 forks source link

api_modify does not support regexp in "ip dns static" #141

Closed cosandr closed 1 year ago

cosandr commented 1 year ago
SUMMARY

If my DNS entries have regexp, the api_modify module breaks (fails with KeyError while parsing data from the router).

The api module successfully adds it for example:

- name: Add with api
  delegate_to: localhost
  community.routeros.api:
    path: ip dns static
    add: 'address=10.1.2.3 regexp=".*\\.ansible\\.test\$"'

api_modify complains about missing "name".

If I comment out satisfy_keys I can at least add the normal A record with a name, although I see regexp is missing from fields. RouterOS requires either name or regexp to be present, I'm not familiar enough with the code to tell if that's possible to check for unfortunately.

ISSUE TYPE
COMPONENT NAME

community.routeros.api_modify

ANSIBLE VERSION
ansible [core 2.14.1]
  config file = /home/andrei/src/homelab/ansible/ansible.cfg
  configured module search path = ['/home/andrei/src/homelab/ansible/library']
  ansible python module location = /usr/lib/python3.10/site-packages/ansible
  ansible collection location = /home/andrei/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.8 (main, Nov  1 2022, 14:18:21) [GCC 12.2.0] (/usr/bin/python)
  jinja version = 3.1.2
  libyaml = True
COLLECTION VERSION
# /home/andrei/.ansible/collections/ansible_collections
Collection         Version
------------------ -------
community.routeros 2.5.0
OS / ENVIRONMENT

RouterOS 7.6

STEPS TO REPRODUCE

For this to fail, a record without name must exist on the router.

- name: MikroTik playbook
  hosts: mikrotik
  gather_facts: false
  module_defaults:
    group/community.routeros.api:
      hostname: "{{ ansible_host }}"
      username: "{{ api_username }}"
      password: "{{ api_password }}"
      tls: true
      validate_certs: true
      ca_path: "{{ inventory_dir }}/files/{{ inventory_hostname }}/ca.crt"

  tasks:
    - name: Add with api
      delegate_to: localhost
      community.routeros.api:
        path: ip dns static
        add: "address=10.1.2.3 name=test.ansible ttl=1h"

    - name: Add with api_modify
      delegate_to: localhost
      community.routeros.api_modify:
        path: ip dns static
        data:
          - address: "10.1.2.3"
            name: "test.ansible"
            ttl: 1h
EXPECTED RESULTS

DNS entry to be added/modified.

ACTUAL RESULTS
TASK [Add with api_modify] ***************************************************************************************************************************************************
task path: /home/andrei/src/homelab/ansible/playbooks/mikrotik.yml:26
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: andrei
<localhost> EXEC /bin/sh -c 'echo ~andrei && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/andrei/.ansible/tmp `"&& mkdir "` echo /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458 `" && echo ansible-tmp-1671659966.5005448-644021-192568282386458="` echo /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458 `" ) && sleep 0'
Using module file /home/andrei/.ansible/collections/ansible_collections/community/routeros/plugins/modules/api_modify.py
<localhost> PUT /home/andrei/.ansible/tmp/ansible-local-643933cjncgwro/tmpjdb_rrab TO /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/ /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py", line 107, in <module>
    _ansiballz_main()
  File "/home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py", line 99, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py", line 47, in invoke_module
    runpy.run_module(mod_name='ansible_collections.community.routeros.plugins.modules.api_modify', init_globals=dict(_module_fqn='ansible_collections.community.routeros.plugins.modules.api_modify', _modlib_path=modlib_path),
  File "/usr/lib/python3.10/runpy.py", line 224, in run_module
    return _run_module_code(code, init_globals, run_name, mod_spec)
  File "/usr/lib/python3.10/runpy.py", line 96, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py", line 989, in <module>
  File "/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py", line 985, in main
  File "/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py", line 554, in sync_list
  File "/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py", line 554, in <genexpr>
KeyError: 'name'
fatal: [mt-router -> localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/andrei/.ansible/tmp/ansible-tmp-1671659966.5005448-644021-192568282386458/AnsiballZ_api_modify.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.community.routeros.plugins.modules.api_modify', init_globals=dict(_module_fqn='ansible_collections.community.routeros.plugins.modules.api_modify', _modlib_path=modlib_path),\n  File \"/usr/lib/python3.10/runpy.py\", line 224, in run_module\n    return _run_module_code(code, init_globals, run_name, mod_spec)\n  File \"/usr/lib/python3.10/runpy.py\", line 96, in _run_module_code\n    _run_code(code, mod_globals, init_globals,\n  File \"/usr/lib/python3.10/runpy.py\", line 86, in _run_code\n    exec(code, run_globals)\n  File \"/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py\", line 989, in <module>\n  File \"/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py\", line 985, in main\n  File \"/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py\", line 554, in sync_list\n  File \"/tmp/ansible_community.routeros.api_modify_payload_eoa88wdc/ansible_community.routeros.api_modify_payload.zip/ansible_collections/community/routeros/plugins/modules/api_modify.py\", line 554, in <genexpr>\nKeyError: 'name'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error",
    "rc": 1
}
cosandr commented 1 year ago

This patch seems to fix it for me, I can also add regexp as expected, it does somewhat break check mode since it accepts invalid entries (missing both name and regexp).

diff --git a/plugins/module_utils/_api_data.py b/plugins/module_utils/_api_data.py
index 04f1f2b..37e5232 100644
--- a/plugins/module_utils/_api_data.py
+++ b/plugins/module_utils/_api_data.py
@@ -1329,7 +1329,7 @@ PATHS = {
     ),
     ('ip', 'dns', 'static'): APIData(
         fully_understood=True,
-        stratify_keys=('name', ),
+        # stratify_keys=('name', ),
         fields={
             'address': KeyInfo(),
             'cname': KeyInfo(),
@@ -1338,8 +1338,9 @@ PATHS = {
             'forward-to': KeyInfo(),
             'mx-exchange': KeyInfo(),
             'mx-preference': KeyInfo(),
-            'name': KeyInfo(required=True),
+            'name': KeyInfo(),
             'ns': KeyInfo(),
+            'regexp': KeyInfo(),
             'srv-port': KeyInfo(),
             'srv-priority': KeyInfo(),
             'srv-target': KeyInfo(),
- name: Add with api_modify
  delegate_to: localhost
  community.routeros.api_modify:
    path: ip dns static
    data:
      - address: "10.1.2.3"
        regexp: '.*\.ansible\.test$'
        ttl: 1h