Repository: ansible/ansible

Issue:
dellos9 module util does not handle correctly existing config
##### ISSUE TYPE

 - Bug Report

##### COMPONENT NAME

ansible.module_utils.dellos9

##### ANSIBLE VERSION
```
ansible 2.2.0.0
```
##### CONFIGURATION
```
[defaults]

inventory          = /srv/ansible/inventories
roles_path         = /srv/ansible/roles

action_plugins     = /usr/share/ansible_plugins/action_plugins
callback_plugins   = /usr/share/ansible_plugins/callback_plugins
connection_plugins = /usr/share/ansible_plugins/connection_plugins
lookup_plugins     = /usr/share/ansible_plugins/lookup_plugins
vars_plugins       = /usr/share/ansible_plugins/vars_plugins
filter_plugins     = /usr/share/ansible_plugins/filter_plugins

remote_tmp         = $HOME/.ansible/tmp
pattern            = *
forks              = 5
poll_interval      = 15
sudo_user          = root
transport          = smart
module_lang        = en_US.UTF-8
gathering          = implicit
sudo_exe           = sudo
timeout            = 10
log_path           = /var/log/ansible.log
ansible_managed    = Ansible managed: {file}. Don't modify it directly. 
fact_caching       = memory
host_key_checking  = false
```

##### OS / ENVIRONMENT

N/A

##### SUMMARY

The dellos9 util module doesn't build correctly the internal  structures used to represent the existing config of the managed network device. 

This leads to apply changes every time the  playbook is run, even if the existing config is the same that the one you are trying to push into the device.

Probably this problem exist also in the dellos6 and dellos10 modules, but I only fixed it in the dellos9 module.

##### STEPS TO REPRODUCE

If you run the following task the module should detect that no changes are required, but instead the module execute the config.

```yaml
- dellos9_config:
    host: "192.168.5.150"
    username: root
    password: *******
    transport: cli
    authorize: yes
    match: line
    replace: line
    update: merge
    timeout: 10
    config: "interface TenGigabitEthernet 0/2\n no ip address\n mtu 12000\n !\n port-channel-protocol LACP\n  port-channel 10 mode active\n no shutdown"
    parents:
      - interface TenGigabitEthernet 0/2
      - port-channel-protocol LACP
    lines:
      - port-channel 10 mode active
    register: result

- debug: var=result
```

##### EXPECTED RESULTS

```
TASK [dellos9_config] ************************************************
ok: [chsbld-0001]
 
TASK [debug] *********************************************************
ok: [chsbld-0001] => {
    "result": {
        "changed": false, 
        "saved": false, 
        "updates": []
    }
}
```

##### ACTUAL RESULTS

```
TASK [dellos9_config] ************************************************
changed: [chsbld-0001]
 
TASK [debug] *********************************************************
ok: [chsbld-0001] => {
    "result": {
        "changed": true, 
        "responses": [
            "", 
            "", 
            "", 
            ""
        ], 
        "saved": false, 
        "updates": [
            "interface TenGigabitEthernet 0/2", 
            "port-channel-protocol LACP", 
            "port-channel 10 mode active"
        ]
    }
}
```


Discussion:

Resolving patch:
diff --git a/lib/ansible/module_utils/dellos10.py b/lib/ansible/module_utils/dellos10.py
--- a/lib/ansible/module_utils/dellos10.py
+++ b/lib/ansible/module_utils/dellos10.py
@@ -42,8 +42,9 @@ def get_config(module):
     if not contents:
         contents = module.config.get_config()
         module.params['config'] = contents
-
-    return NetworkConfig(indent=1, contents=contents[0])
+        return NetworkConfig(indent=1, contents=contents[0])
+    else:
+        return NetworkConfig(indent=1, contents=contents)
 
 
 def get_sublevel_config(running_config, module):
@@ -54,11 +55,13 @@ def get_sublevel_config(running_config, module):
         contents = obj.children
     contents[:0] = module.params['parents']
 
+    indent = 0
     for c in contents:
         if isinstance(c, str):
-            current_config_contents.append(c)
+            current_config_contents.append(c.rjust(len(c) + indent, ' '))
         if isinstance(c, ConfigLine):
             current_config_contents.append(c.raw)
+        indent = indent + 1
     sublevel_config = '\n'.join(current_config_contents)
 
     return sublevel_config
diff --git a/lib/ansible/module_utils/dellos9.py b/lib/ansible/module_utils/dellos9.py
--- a/lib/ansible/module_utils/dellos9.py
+++ b/lib/ansible/module_utils/dellos9.py
@@ -42,8 +42,9 @@ def get_config(module):
     if not contents:
         contents = module.config.get_config()
         module.params['config'] = contents
-
-    return NetworkConfig(indent=1, contents=contents[0])
+        return NetworkConfig(indent=1, contents=contents[0])
+    else:
+        return NetworkConfig(indent=1, contents=contents)
 
 
 def get_sublevel_config(running_config, module):
@@ -54,11 +55,13 @@ def get_sublevel_config(running_config, module):
         contents = obj.children
     contents[:0] = module.params['parents']
 
+    indent = 0
     for c in contents:
         if isinstance(c, str):
-            current_config_contents.append(c)
+            current_config_contents.append(c.rjust(len(c) + indent, ' '))
         if isinstance(c, ConfigLine):
             current_config_contents.append(c.raw)
+        indent = indent + 1
     sublevel_config = '\n'.join(current_config_contents)
 
     return sublevel_config


Test patch: