Repository: ansible/ansible

Issue:
meraki_config_template should accept net_id
##### SUMMARY
`meraki_config_template` accepts net_id and org_id but doesn't do anything with it. It should handle both parameters properly.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
meraki_config_template

##### ANSIBLE VERSION
```
ansible 2.7.0.dev0 (meraki/config_template_use ecfaf3ba75) last updated 2018/07/04 22:06:34 (GMT -500)
  config file = None
  configured module search path = ['/Users/kbreit/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/kbreit/Documents/Programming/ansible/lib/ansible
  executable location = /Users/kbreit/Documents/Programming/ansible/bin/ansible
  python version = 3.5.4 (default, Feb 25 2018, 14:56:02) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]

```



Discussion:
Files identified in the description:
* [lib/ansible/modules/network/meraki/meraki_config_template.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/meraki/meraki_config_template.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @dagwieers
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/network/meraki/meraki_config_template.py b/lib/ansible/modules/network/meraki/meraki_config_template.py
--- a/lib/ansible/modules/network/meraki/meraki_config_template.py
+++ b/lib/ansible/modules/network/meraki/meraki_config_template.py
@@ -44,6 +44,9 @@
     net_name:
         description:
         - Name of the network to bind or unbind configuration template to.
+    net_id:
+        description:
+        - ID of the network to bind or unbind configuration template to.
     auto_bind:
         description:
         - Optional boolean indicating whether the network's switches should automatically bind to profiles of the same model.
@@ -95,9 +98,9 @@ def get_template_id(meraki, name, data):
     meraki.fail_json(msg='No configuration template named {0} found'.format(name))
 
 
-def is_network_bound(meraki, nets, net_name, template_id):
+def is_network_bound(meraki, nets, net_id, template_id):
     for net in nets:
-        if net['name'] == net_name:
+        if net['id'] == net_id:
             try:
                 if net['configTemplateId'] == template_id:
                     return True
@@ -116,12 +119,9 @@ def delete_template(meraki, org_id, name, data):
     return response
 
 
-def bind(meraki, org_name, net_name, name, data):
+def bind(meraki, org_name, net_id, nets, name, data):
     template_id = get_template_id(meraki, name, data)
-    nets = meraki.get_nets(org_name=org_name)
-    # meraki.fail_json(msg='Nets', nets=nets)
-    if is_network_bound(meraki, nets, net_name, template_id) is False:
-        net_id = meraki.get_net_id(net_name=net_name, data=nets)
+    if is_network_bound(meraki, nets, net_id, template_id) is False:
         path = meraki.construct_path('bind', function='config_template', net_id=net_id)
         payload = dict()
         payload['configTemplateId'] = template_id
@@ -134,11 +134,9 @@ def bind(meraki, org_name, net_name, name, data):
         return r
 
 
-def unbind(meraki, org_name, net_name, name, data):
+def unbind(meraki, org_name, net_id, nets, name, data):
     template_id = get_template_id(meraki, name, data)
-    nets = meraki.get_nets(org_name=org_name)
-    net_id = meraki.get_net_id(net_name=net_name, data=nets)
-    if is_network_bound(meraki, nets, net_name, template_id) is True:
+    if is_network_bound(meraki, nets, net_id, template_id) is True:
         path = meraki.construct_path('unbind', function='config_template', net_id=net_id)
         meraki.result['changed'] = True
         r = meraki.request(path, method='POST')
@@ -157,6 +155,7 @@ def main():
                          org_id=dict(type='int'),
                          config_template=dict(type='str', aliases=['name']),
                          net_name=dict(type='str'),
+                         net_id=dict(type='str'),
                          # config_template_id=dict(type='str', aliases=['id']),
                          auto_bind=dict(type='bool'),
                          )
@@ -210,9 +209,12 @@ def main():
     # manipulate or modify the state as needed (this is going to be the
     # part where your module will do what it needs to do)
     org_id = meraki.params['org_id']
-
     if meraki.params['org_name']:
         org_id = meraki.get_org_id(meraki.params['org_name'])
+    net_id = meraki.params['net_id']
+    nets = meraki.get_nets(org_id=org_id)
+    if meraki.params['net_name']:
+        net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets)
 
     if meraki.params['state'] == 'query':
         meraki.result['data'] = get_config_templates(meraki, org_id)
@@ -220,7 +222,8 @@ def main():
         if meraki.params['net_name']:
             template_bind = bind(meraki,
                                  meraki.params['org_name'],
-                                 meraki.params['net_name'],
+                                 net_id,
+                                 nets,
                                  meraki.params['config_template'],
                                  get_config_templates(meraki, org_id))
     elif meraki.params['state'] == 'absent':
@@ -232,7 +235,8 @@ def main():
         else:
             config_unbind = unbind(meraki,
                                    meraki.params['org_name'],
-                                   meraki.params['net_name'],
+                                   net_id,
+                                   nets,
                                    meraki.params['config_template'],
                                    get_config_templates(meraki, org_id))
 


Test patch:
diff --git a/test/integration/targets/meraki_config_template/tasks/main.yml b/test/integration/targets/meraki_config_template/tasks/main.yml
--- a/test/integration/targets/meraki_config_template/tasks/main.yml
+++ b/test/integration/targets/meraki_config_template/tasks/main.yml
@@ -3,119 +3,115 @@
 
 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
 ---
-# - name: Test an API key is provided
-#   fail:
-#     msg: Please define an API key
-#   when: auth_key is not defined
-  
-# - name: Use an invalid domain
-#   meraki_config_template:
-#     auth_key: '{{ auth_key }}'
-#     host: marrrraki.com
-#     state: query
-#     org_name: DevTestOrg
-#     output_level: debug
-#   delegate_to: localhost
-#   register: invalid_domain
-#   ignore_errors: yes
-
-# - name: Connection assertions
-#   assert:
-#     that:
-#       - '"Failed to connect to" in invalid_domain.msg'
-
-- name: Query all configuration templates
-  meraki_config_template:
-    auth_key: '{{auth_key}}'
-    state: query
-    org_name: DevTestOrg
-  register: get_all
-
-- name: Delete non-existant configuration template
-  meraki_config_template:
-    auth_key: '{{auth_key}}'
-    state: absent
-    org_name: DevTestOrg
-    config_template: DevConfigTemplateInvalid
-  register: deleted
-  ignore_errors: yes
-
-- debug:
-    msg: '{{deleted}}'
-
-- assert:
-    that:
-      - '"No configuration template named" in deleted.msg'
-
-- name: Create a network
-  meraki_network:
-    auth_key: '{{auth_key}}'
-    state: present
-    org_name: '{{ test_org_name }}'
-    net_name: '{{ test_net_name }}'
-    type: appliance
-  delegate_to: localhost
-
-- name: Bind a template to a network
-  meraki_config_template:
-    auth_key: '{{auth_key}}'
-    state: present
-    org_name: '{{ test_org_name }}'
-    net_name: '{{ test_net_name }}'
-    config_template: DevConfigTemplate
-  register: bind
-
-- assert:
-    that:
-      bind.changed == True
-
-- name: Bind a template to a network when it's already bound
-  meraki_config_template:
-    auth_key: '{{auth_key}}'
-    state: present
-    org_name: '{{ test_org_name }}'
-    net_name: '{{ test_net_name }}'
-    config_template: DevConfigTemplate
-  register: bind_invalid
-  ignore_errors: yes
-
-- debug:
-    msg: '{{bind_invalid}}'
-
-- assert:
-    that:
-      - bind_invalid.changed == False
-
-- name: Unbind a template from a network
-  meraki_config_template:
-    auth_key: '{{auth_key}}'
-    state: absent
-    org_name: '{{ test_org_name }}'
-    net_name: '{{ test_net_name }}'
-    config_template: DevConfigTemplate
-  register: unbind
-
-- assert:
-    that:
-      unbind.changed == True
-
-- name: Unbind a template from a network when it's not bound
-  meraki_config_template:
-    auth_key: '{{auth_key}}'
-    state: absent
-    org_name: '{{ test_org_name }}'
-    net_name: '{{ test_net_name }}'
-    config_template: DevConfigTemplate
-  register: unbind_invalid
-
-- assert:
-    that:
-      unbind_invalid.changed == False
-
-- name: Delete network
-  meraki_network:
-    auth_key: '{{auth_key}}'
-    state: absent
-    org_name: '{{ test_org_name }}'
-    net_name: '{{ test_net_name }}'
-  delegate_to: localhost
\ No newline at end of file
+- block:
+  - name: Test an API key is provided
+    fail:
+      msg: Please define an API key
+    when: auth_key is not defined
+    
+  - name: Use an invalid domain
+    meraki_config_template:
+      auth_key: '{{ auth_key }}'
+      host: marrrraki.com
+      state: query
+      org_name: DevTestOrg
+      output_level: debug
+    delegate_to: localhost
+    register: invalid_domain
+    ignore_errors: yes
+
+  - name: Connection assertions
+    assert:
+      that:
+        - '"Failed to connect to" in invalid_domain.msg'
+
+  - name: Query all configuration templates
+    meraki_config_template:
+      auth_key: '{{auth_key}}'
+      state: query
+      org_name: DevTestOrg
+    register: get_all
+
+  - name: Delete non-existant configuration template
+    meraki_config_template:
+      auth_key: '{{auth_key}}'
+      state: absent
+      org_name: DevTestOrg
+      config_template: DevConfigTemplateInvalid
+    register: deleted
+    ignore_errors: yes
+
+  - assert:
+      that:
+        - '"No configuration template named" in deleted.msg'
+
+  - name: Create a network
+    meraki_network:
+      auth_key: '{{auth_key}}'
+      state: present
+      org_name: '{{ test_org_name }}'
+      net_name: '{{ test_net_name }}'
+      type: appliance
+    delegate_to: localhost
+
+  - name: Bind a template to a network
+    meraki_config_template:
+      auth_key: '{{auth_key}}'
+      state: present
+      org_name: '{{ test_org_name }}'
+      net_name: '{{ test_net_name }}'
+      config_template: DevConfigTemplate
+    register: bind
+
+  - assert:
+      that:
+        bind.changed == True
+
+  - name: Bind a template to a network when it's already bound
+    meraki_config_template:
+      auth_key: '{{auth_key}}'
+      state: present
+      org_name: '{{ test_org_name }}'
+      net_name: '{{ test_net_name }}'
+      config_template: DevConfigTemplate
+    register: bind_invalid
+    ignore_errors: yes
+
+  - assert:
+      that:
+        - bind_invalid.changed == False
+
+  - name: Unbind a template from a network
+    meraki_config_template:
+      auth_key: '{{auth_key}}'
+      state: absent
+      org_name: '{{ test_org_name }}'
+      net_name: '{{ test_net_name }}'
+      config_template: DevConfigTemplate
+    register: unbind
+
+  - assert:
+      that:
+        unbind.changed == True
+
+  - name: Unbind a template from a network when it's not bound
+    meraki_config_template:
+      auth_key: '{{auth_key}}'
+      state: absent
+      org_name: '{{ test_org_name }}'
+      net_name: '{{ test_net_name }}'
+      config_template: DevConfigTemplate
+    register: unbind_invalid
+
+  - assert:
+      that:
+        unbind_invalid.changed == False
+
+  always:
+  - name: Delete network
+    meraki_network:
+      auth_key: '{{auth_key}}'
+      state: absent
+      org_name: '{{ test_org_name }}'
+      net_name: '{{ test_net_name }}'
+    delegate_to: localhost
\ No newline at end of file