Repository: ansible/ansible

Issue:
'ios_vrf' removes the existing IP addresses from interface
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ios_vrf

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/usr/share/my_modules/']
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
Standard

##### OS / ENVIRONMENT
Ubuntu 16.04.2 LTS

##### SUMMARY
When the module `ios_vrf` is used to add a new VRF, it doesn't add the configuration lines  `address-family [ipv4|ipv6]` under the `vrf definition` section so that IP addresses can be added to interfaces. 
 If these don't exist, the error message `[INTERFACE] is linked to a VRF. Enable IPv4 on that VRF first` is received from the parser. I believe this is what is occurring and is not reported an a warning or error to Ansible.

##### STEPS TO REPRODUCE
<!--- Paste example playbooks or commands between quotes below -->
```yaml
$ cat add_vrf.yml
---

- name: Testing VRFs
  hosts: ios
  connection: local
  gather_facts: false

  tasks:

    - name: Add a "Test" VRF
      ios_vrf:
        provider: "{{ ansible }}"
        name: Test
        rd: 9999:9999
        description: "A Test Routing-Instance"
        interfaces:
          - TenGigabitEthernet3/5
        state: present
```

##### EXPECTED RESULTS
```
#sh run int t3/5
Building configuration...

Current configuration : 136 bytes
!
interface TenGigabitEthernet3/5
 vrf forwarding MyTest
 ip address 10.10.10.10 255.255.255.0
 shutdown
 no snmp trap link-status
end
```

##### ACTUAL RESULTS
Device config before PB was run:
```
#sh run int t3/5
Building configuration...

Current configuration : 113 bytes
!
interface TenGigabitEthernet3/5
 ip address 10.10.10.10 255.255.255.0
 shutdown
 no snmp trap link-status
end
```

Device config after PB was run:
```
#sh run int t3/5
Building configuration...

Current configuration : 111 bytes
!
interface TenGigabitEthernet3/5
 vrf forwarding Test
 no ip address
 shutdown
 no snmp trap link-status
end
```

PB output with added verbosity:
```
$ apn add_vrf.yml --limit bld-vsl-b -vvvv
Using /etc/ansible/ansible.cfg as config file
Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/__init__.pyc

PLAYBOOK: add_vrf.yml ********************************************************************************
1 plays in add_vrf.yml

PLAY [Testing VRFs] **********************************************************************************
META: ran handlers

TASK [Add a "Test" VRF] ******************************************************************************
task path: /home/bl839s/ansible/qos-management/add_vrf.yml:10
<bld-vsl-b> using connection plugin network_cli
<bld-vsl-b> socket_path: /home/bl839s/.ansible/pc/ad8df53ffc
open_shell() returned 0 ok
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/network/ios/ios_vrf.py
<9.17.3.22> ESTABLISH LOCAL CONNECTION FOR USER: bl839s
<9.17.3.22> EXEC /bin/sh -c 'echo ~ && sleep 0'
<9.17.3.22> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/bl839s/.ansible/tmp/ansible-tmp-1499923380.67-99994729987338 `" && echo ansible-tmp-1499923380.67-99994729987338="` echo /home/bl839s/.ans$ble/tmp/ansible-tmp-1499923380.67-99994729987338 `" ) && sleep 0'
<9.17.3.22> PUT /tmp/tmpJdneuI TO /home/bl839s/.ansible/tmp/ansible-tmp-1499923380.67-99994729987338/ios_vrf.py
<9.17.3.22> EXEC /bin/sh -c 'chmod u+x /home/bl839s/.ansible/tmp/ansible-tmp-1499923380.67-99994729987338/ /home/bl839s/.ansible/tmp/ansible-tmp-1499923380.67-99994729987338/ios_vrf.py && sleep 0'
<9.17.3.22> EXEC /bin/sh -c '/usr/bin/env python /home/bl839s/.ansible/tmp/ansible-tmp-1499923380.67-99994729987338/ios_vrf.py; rm -rf "/home/bl839s/.ansible/tmp/ansible-tmp-1499923380.67-99994729987338/" > /dev/null 2>&1 && sleep 0'
changed: [bld-vsl-b] => {
    "changed": true,
    "commands": [
        "interface TenGigabitEthernet3/5",
        "vrf forwarding Test",
        "ip address 10.10.10.10 255.255.255.0"
    ],
    "invocation": {
        "module_args": {
            "auth_pass": null,
            "authorize": null,
            "description": "A Test Routing-Instance",
            "host": null,
            "interfaces": [
                "TenGigabitEthernet3/5"
            ],
            "name": "Test",
            "password": null,
            "port": null,
            "provider": {
                "auth_pass": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                "authorize": true,
                "host": "bld-vsl-b",
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
                "port": 22,
                "ssh_keyfile": null,
                "timeout": null,
                "username": "ansible"
            },
            "purge": false,
            "rd": "9999:9999",
            "ssh_keyfile": null,
            "state": "present",
            "timeout": null,
            "username": null,
            "vrfs": null
        }
    }
}
META: ran handlers
META: ran handlers

PLAY RECAP *******************************************************************************************
bld-vsl-b                  : ok=1    changed=1    unreachable=0    failed=0
```



Discussion:
cc @privateip @rcarrillocruz
[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/ios/ios_vrf.py b/lib/ansible/modules/network/ios/ios_vrf.py
--- a/lib/ansible/modules/network/ios/ios_vrf.py
+++ b/lib/ansible/modules/network/ios/ios_vrf.py
@@ -167,7 +167,11 @@ def get_interface_type(interface):
 
 def add_command_to_vrf(name, cmd, commands):
     if 'vrf definition %s' % name not in commands:
-        commands.append('vrf definition %s' % name)
+        commands.extend([
+            'vrf definition %s' % name,
+            'address-family ipv4', 'exit',
+            'address-family ipv6', 'exit',
+        ])
     commands.append(cmd)
 
 def map_obj_to_commands(updates, module):
@@ -185,7 +189,11 @@ def needs_update(want, have, x):
             continue
 
         if not have.get('state'):
-            commands.append('vrf definition %s' % want['name'])
+            commands.extend([
+                'vrf definition %s' % want['name'],
+                'address-family ipv4', 'exit',
+                'address-family ipv6', 'exit',
+            ])
 
         if needs_update(want, have, 'description'):
             cmd = 'description %s' % want['description']
@@ -337,6 +345,9 @@ def check_declarative_intent_params(want, module):
 
         if rc == 0:
             data = out.strip().split()
+            # data will be empty if the vrf was just added
+            if not data:
+                return
             vrf = data[0]
             interface = data[-1]
 
@@ -356,6 +367,7 @@ def main():
         name=dict(),
         description=dict(),
         rd=dict(),
+
         interfaces=dict(type='list'),
 
         delay=dict(default=10, type='int'),


Test patch:
diff --git a/test/units/modules/network/ios/test_ios_vrf.py b/test/units/modules/network/ios/test_ios_vrf.py
--- a/test/units/modules/network/ios/test_ios_vrf.py
+++ b/test/units/modules/network/ios/test_ios_vrf.py
@@ -53,7 +53,7 @@ def load_fixtures(self, commands=None):
 
     def test_ios_vrf_name(self):
         set_module_args(dict(name='test_4'))
-        commands = ['vrf definition test_4']
+        commands = ['vrf definition test_4', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit']
         self.execute_module(changed=True, commands=commands, sort=False)
 
     def test_ios_vrf_name_unchanged(self):
@@ -62,12 +62,12 @@ def test_ios_vrf_name_unchanged(self):
 
     def test_ios_vrf_description(self):
         set_module_args(dict(name='test_1', description='test string'))
-        commands = ['vrf definition test_1', 'description test string']
+        commands = ['vrf definition test_1', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit', 'description test string']
         self.execute_module(changed=True, commands=commands, sort=False)
 
     def test_ios_vrf_rd(self):
         set_module_args(dict(name='test_1', rd='2:100'))
-        commands = ['vrf definition test_1', 'rd 2:100']
+        commands = ['vrf definition test_1', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit', 'rd 2:100']
         self.execute_module(changed=True, commands=commands, sort=False)
 
     def test_ios_vrf_interfaces(self):
@@ -96,34 +96,39 @@ def test_ios_vrf_purge_all_but_one(self):
     def test_ios_vrfs_no_purge(self):
         vrfs = [{'name': 'test_1'}, {'name': 'test_4'}]
         set_module_args(dict(vrfs=vrfs))
-        commands = ['vrf definition test_4']
+        commands = ['vrf definition test_4',
+                    'address-family ipv4', 'exit',
+                    'address-family ipv6', 'exit']
         self.execute_module(changed=True, commands=commands)
 
     def test_ios_vrfs_purge(self):
         vrfs = [{'name': 'test_1'}, {'name': 'test_4'}]
         set_module_args(dict(vrfs=vrfs, purge=True))
-        commands = ['no vrf definition test_2', 'no vrf definition test_3',
-                    'vrf definition test_4']
+        commands = ['vrf definition test_4',
+                    'address-family ipv4', 'exit',
+                    'address-family ipv6', 'exit',
+                    'no vrf definition test_2',
+                    'no vrf definition test_3']
         self.execute_module(changed=True, commands=commands)
 
     def test_ios_vrfs_global_arg(self):
         vrfs = [{'name': 'test_1'}, {'name': 'test_2'}]
         set_module_args(dict(vrfs=vrfs, description='test string'))
-        commands = ['vrf definition test_1', 'description test string',
-                    'vrf definition test_2', 'description test string']
+        commands = ['vrf definition test_1', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit', 'description test string',
+                    'vrf definition test_2', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit', 'description test string']
         self.execute_module(changed=True, commands=commands, sort=False)
 
     def test_ios_vrfs_local_override_description(self):
         vrfs = [{'name': 'test_1', 'description': 'test vrf 1'},
                 {'name': 'test_2'}]
         set_module_args(dict(vrfs=vrfs, description='test string'))
-        commands = ['vrf definition test_2', 'description test string']
+        commands = ['vrf definition test_2', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit', 'description test string']
         self.execute_module(changed=True, commands=commands, sort=False)
 
     def test_ios_vrfs_local_override_state(self):
         vrfs = [{'name': 'test_1', 'state': 'absent'},
                 {'name': 'test_2'}]
         set_module_args(dict(vrfs=vrfs, description='test string'))
-        commands = ['no vrf definition test_1', 'vrf definition test_2',
+        commands = ['no vrf definition test_1', 'vrf definition test_2', 'address-family ipv4', 'exit', 'address-family ipv6', 'exit',
                     'description test string']
         self.execute_module(changed=True, commands=commands, sort=False)