Repository: ansible/ansible

Issue:
nxos_bgp_neighbor issues
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Name of the module, plugin, task or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
nxos_bgp_neighbor

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0 (devel 788010d0f0) last updated 2018/01/08 11:49:21 (GMT -400)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /root/agents-ci/ansible/lib/ansible
  executable location = /root/agents-ci/ansible/bin/ansible
  python version = 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4]
```

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Ansible Server : Ubuntu 14.04
Device: N9K running 7.0(3)I5(2)

##### SUMMARY
<!--- Explain the problem briefly -->

There are few issues with nxos_bgp_neighbor module.

1. timer parameters are not idempotent when set to default.
2. pwd and pwd_type are required together but the current code does not enforce it.
3. Documentation errors
4. Integrations tests do not cover all parameters and also not cover multiple vrfs.

Note: I will open a PR shortly to address these above issues


Discussion:
Files identified in the description:
* [lib/ansible/modules/network/nxos/nxos_bgp_neighbor.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/network/nxos/nxos_bgp_neighbor.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 @GGabriele @kedarX @mikewiebe @privateip @rahushen @rcarrillocruz @trishnaguha
[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/nxos/nxos_bgp_neighbor.py b/lib/ansible/modules/network/nxos/nxos_bgp_neighbor.py
--- a/lib/ansible/modules/network/nxos/nxos_bgp_neighbor.py
+++ b/lib/ansible/modules/network/nxos/nxos_bgp_neighbor.py
@@ -107,7 +107,8 @@
     description:
       - Specify Maximum number of peers for this neighbor prefix
         Valid values are between 1 and 1000, or 'default', which does
-        not impose the limit.
+        not impose the limit. Note that this parameter is accepted
+        only on neighbors with address/prefix.
     required: false
     default: null
   pwd:
@@ -118,9 +119,9 @@
   pwd_type:
     description:
       - Specify the encryption type the password will use. Valid values
-        are '3des' or 'cisco_type_7' encryption.
+        are '3des' or 'cisco_type_7' encryption or keyword 'default'.
     required: false
-    choices: ['3des', 'cisco_type_7']
+    choices: ['3des', 'cisco_type_7', 'default']
     default: null
   remote_as:
     description:
@@ -170,8 +171,6 @@
         Valid values are 'true', 'false', and 'default', which defaults
         to 'false'. This property can only be configured when the
         neighbor is in 'ip' address format without prefix length.
-        This property and the transport_passive_mode property are
-        mutually exclusive.
     required: false
     choices: ['true','false']
     default: null
@@ -388,11 +387,13 @@ def state_present(module, existing, proposed, candidate):
                     command = '{0} {1}'.format(key, value)
                     commands.append(command)
             elif key == 'timers':
-                command = 'timers {0} {1}'.format(
-                    proposed['timers_keepalive'],
-                    proposed['timers_holdtime'])
-                if command not in commands:
-                    commands.append(command)
+                if (proposed['timers_keepalive'] != PARAM_TO_DEFAULT_KEYMAP.get('timers_keepalive') or
+                        proposed['timers_holdtime'] != PARAM_TO_DEFAULT_KEYMAP.get('timers_holdtime')):
+                    command = 'timers {0} {1}'.format(
+                        proposed['timers_keepalive'],
+                        proposed['timers_holdtime'])
+                    if command not in commands:
+                        commands.append(command)
             else:
                 command = '{0} {1}'.format(key, value)
                 commands.append(command)
@@ -437,7 +438,7 @@ def main():
         low_memory_exempt=dict(required=False, type='bool'),
         maximum_peers=dict(required=False, type='str'),
         pwd=dict(required=False, type='str'),
-        pwd_type=dict(required=False, type='str', choices=['cleartext', '3des', 'cisco_type_7', 'default']),
+        pwd_type=dict(required=False, type='str', choices=['3des', 'cisco_type_7', 'default']),
         remote_as=dict(required=False, type='str'),
         remove_private_as=dict(required=False, type='str', choices=['enable', 'disable', 'all', 'replace-as']),
         shutdown=dict(required=False, type='bool'),
@@ -452,7 +453,7 @@ def main():
 
     module = AnsibleModule(
         argument_spec=argument_spec,
-        required_together=[['timers_holdtime', 'timers_keepalive']],
+        required_together=[['timers_holdtime', 'timers_keepalive'], ['pwd', 'pwd_type']],
         supports_check_mode=True,
     )
 


Test patch:
diff --git a/test/integration/targets/nxos_bgp_neighbor/defaults/main.yaml b/test/integration/targets/nxos_bgp_neighbor/defaults/main.yaml
--- a/test/integration/targets/nxos_bgp_neighbor/defaults/main.yaml
+++ b/test/integration/targets/nxos_bgp_neighbor/defaults/main.yaml
@@ -1,2 +1,5 @@
 ---
 testcase: "*"
+vrfs:
+  - default
+  - myvrf
diff --git a/test/integration/targets/nxos_bgp_neighbor/tests/common/sanity.yaml b/test/integration/targets/nxos_bgp_neighbor/tests/common/sanity.yaml
--- a/test/integration/targets/nxos_bgp_neighbor/tests/common/sanity.yaml
+++ b/test/integration/targets/nxos_bgp_neighbor/tests/common/sanity.yaml
@@ -5,10 +5,14 @@
 
 - set_fact: intname="{{ nxos_int1 }}"
 
-- set_fact: log_neighbor_changes="enable"
-  when: not titanium
+- set_fact: log_neighbor_changese="enable"
+  when: (imagetag and (imagetag is version_compare('D1', 'ne')) and (imagetag is version_compare('N1', 'ne')))
+- set_fact: log_neighbor_changesd="disable"
+  when: (imagetag and (imagetag is version_compare('D1', 'ne')) and (imagetag is version_compare('N1', 'ne')))
 
-- set_fact: remove_private_as="all"
+- set_fact: remove_private_asa="all"
+  when: not titanium
+- set_fact: remove_private_asr="replace-as"
   when: not titanium
 
 - name: "Enable feature BGP"
@@ -19,23 +23,49 @@
   ignore_errors: yes
 
 - name: "Setup"
-  nxos_bgp: &remove
+  nxos_bgp_neighbor: &removenp
+    asn: 65535
+    neighbor: 3.3.3.3
+    vrf: "{{ item }}"
+    state: absent
+    provider: "{{ connection }}"
+  with_items: "{{ vrfs }}"
+  ignore_errors: yes
+
+- name: "Setup"
+  nxos_bgp_neighbor: &remove
     asn: 65535
+    neighbor: 3.3.3.3/32
+    vrf: "{{ item }}"
     state: absent
     provider: "{{ connection }}"
+  with_items: "{{ vrfs }}"
   ignore_errors: yes
 
 - block:
-  - name: "Configure BGP neighbor defaults"
-    nxos_bgp_neighbor: &configure_default
+  - name: "Configure BGP neighbor1"
+    nxos_bgp_neighbor: &configure1
       asn: 65535
-      neighbor: 3.3.3.3
-      local_as: 20
-      remote_as: 30
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
+      connected_check: true
+      capability_negotiation: true
+      dynamic_capability: true
+      ebgp_multihop: 2
+      low_memory_exempt: true
+      maximum_peers: 10
+      suppress_4_byte_as: true
+      timers_keepalive: 90
+      timers_holdtime: 270
+      log_neighbor_changes: "{{log_neighbor_changese|default(omit)}}"
+      local_as: 22.33
+      remote_as: 33.22
       description: "just a description"
       update_source: "{{ intname.capitalize() }}"
+      shutdown: true
       state: present
       provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: &true
@@ -43,57 +73,107 @@
         - "result.changed == true"
 
   - name: "Check Idempotence"
-    nxos_bgp_neighbor: *configure_default
+    nxos_bgp_neighbor: *configure1
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: &false
       that:
         - "result.changed == false"
 
+  - name: "Configure BGP neighbor2"
+    nxos_bgp_neighbor: &configure2
+      asn: 65535
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
+      connected_check: False
+      capability_negotiation: False
+      dynamic_capability: False
+      ebgp_multihop: default
+      low_memory_exempt: False
+      maximum_peers: default
+      suppress_4_byte_as: False
+      timers_keepalive: default
+      timers_holdtime: default
+      log_neighbor_changes: "{{log_neighbor_changesd|default(omit)}}"
+      local_as: default
+      remote_as: default
+      description: default
+      update_source: default
+      shutdown: False
+      state: present
+      provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Check Idempotence"
+    nxos_bgp_neighbor: *configure2
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *false
+
   - name: "Remove BGP"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *remove
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
 
   - name: "Check Idempotence"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *remove
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *false
 
-  - name: "Configure BGP neighbor non-defaults"
-    nxos_bgp_neighbor: &configure_non_default
+  - name: "Configure BGP neighbor3"
+    nxos_bgp_neighbor: &configure3
       asn: 65535
-      neighbor: 3.3.3.3
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
       description: "tested by ansible"
-      connected_check: true
-      capability_negotiation: true
-      dynamic_capability: true
-      ebgp_multihop: 2
-      log_neighbor_changes: "{{log_neighbor_changes|default(omit)}}"
-      low_memory_exempt: true
-      remote_as: 12.1
-      remove_private_as: "{{remove_private_as|default(omit)}}"
-      shutdown: true
-      suppress_4_byte_as: true
-      timers_keepalive: 90
-      timers_holdtime: 270
-      update_source: loopback151
+      remove_private_as: "{{remove_private_asa|default(omit)}}"
+      state: present
+      provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Check Idempotence"
+    nxos_bgp_neighbor: *configure3
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *false
+
+  - name: "Configure BGP neighbor4"
+    nxos_bgp_neighbor: &configure4
+      asn: 65535
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
+      description: "tested by ansible"
+      remove_private_as: "{{remove_private_asr|default(omit)}}"
       state: present
       provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
 
   - name: "Check Idempotence"
-    nxos_bgp_neighbor: *configure_non_default
+    nxos_bgp_neighbor: *configure4
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *false
 
   - name: "Remove BGP"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *remove
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
@@ -101,23 +181,27 @@
   - name: "Configure BGP neighbor 3des password"
     nxos_bgp_neighbor: &configure_3des_password
       asn: 65535
-      neighbor: 3.3.3.3
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
       remote_as: 30
       pwd: '386c0565965f89de'
       pwd_type: 3des
       provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
 
   - name: "Check Idempotence"
     nxos_bgp_neighbor: *configure_3des_password
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *false
 
   - name: "Remove BGP"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *remove
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
@@ -125,23 +209,48 @@
   - name: "Configure BGP neighbor type 7 password"
     nxos_bgp_neighbor: &configure_type7_password
       asn: 65535
-      neighbor: 3.3.3.3
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
       remote_as: 30
       pwd: '386c0565965f89de'
       pwd_type: cisco_type_7
       provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
 
   - name: "Check Idempotence"
     nxos_bgp_neighbor: *configure_type7_password
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *false
+
+  - name: "Remove BGP neighbor password"
+    nxos_bgp_neighbor: &remove_password
+      asn: 65535
+      neighbor: 3.3.3.3/32
+      vrf: "{{ item }}"
+      remote_as: 30
+      pwd: default
+      pwd_type: default
+      provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *true
+
+  - name: "Check Idempotence"
+    nxos_bgp_neighbor: *remove_password
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *false
 
   - name: "Remove BGP"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *remove
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
@@ -150,49 +259,60 @@
     nxos_bgp_neighbor: &configure_transport_passive
       asn: 65535
       neighbor: 3.3.3.3
+      vrf: "{{ item }}"
       remote_as: 30
       transport_passive_only: true
       provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
     register: result
+
   - assert: *true
 
   - name: "Check Idempotence"
     nxos_bgp_neighbor: *configure_transport_passive
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *false
 
-  - name: "Remove BGP"
-    nxos_bgp: *remove
-    register: result
-
-  - assert: *true
-
   - name: "Configure BGP neighbor transport type default"
     nxos_bgp_neighbor: &configure_transport_default
       asn: 65535
       neighbor: 3.3.3.3
+      vrf: "{{ item }}"
       remote_as: 30
       transport_passive_only: false
       provider: "{{ connection }}"
+    with_items: "{{ vrfs }}"
     register: result
+
   - assert: *true
 
   - name: "Check Idempotence"
     nxos_bgp_neighbor: *configure_transport_default
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *false
 
   - name: "Remove BGP"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *removenp
+    with_items: "{{ vrfs }}"
     register: result
 
   - assert: *true
 
+  - name: "Check Idempotence"
+    nxos_bgp_neighbor: *removenp
+    with_items: "{{ vrfs }}"
+    register: result
+
+  - assert: *false
+
   rescue:
   - name: "Cleanup BGP"
-    nxos_bgp: *remove
+    nxos_bgp_neighbor: *remove
+    with_items: "{{ vrfs }}"
     ignore_errors: yes
 
   always: