Repository: ansible/ansible-modules-core

Issue:
"Unable to load config: ConfigLoadError(severity: warning, bad_element: None, message: warning: statement not found)"junos_config: 
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

junos_config
##### ANSIBLE VERSION

```
ansible 2.2.0 (devel 1c7890bf86) last updated 2016/09/26 10:42:34 (GMT +100)
  lib/ansible/modules/core: (devel cf243860ff) last updated 2016/09/26 10:42:39 (GMT +100)
  lib/ansible/modules/extras: (devel 7aab9cd93b) last updated 2016/09/26 10:42:41 (GMT +100)

```
##### CONFIGURATION
##### OS / ENVIRONMENT
##### SUMMARY
##### STEPS TO REPRODUCE

``` yaml
- name: setup
  junos_config:
    lines:
      - "set system host-name {{ inventory_hostname }}"
      - "delete interfaces lo0"
    provider: "{{ netconf }}"
  register: test

```
##### EXPECTED RESULTS
##### ACTUAL RESULTS

``` yaml
TASK [test_junos_config : setup] ***********************************************
task path: /home/johnb/git/ansible-inc/test-junos/roles/test_junos_config/tests/netconf/multiple.yaml:4
Using module file /home/johnb/git/ansible-inc/ansible/lib/ansible/modules/core/network/junos/junos_config.py
<vsrx01> ESTABLISH LOCAL CONNECTION FOR USER: johnb
<vsrx01> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737 `" && echo ansible-tmp-1474911943.93-57045240123737="` echo $HOME/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737 `" ) && sleep 0'
<vsrx01> PUT /tmp/tmpN4Hbzl TO /home/johnb/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737/junos_config.py
<vsrx01> EXEC /bin/sh -c 'chmod u+x /home/johnb/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737/ /home/johnb/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737/junos_config.py && sleep 0'
<vsrx01> EXEC /bin/sh -c 'python /home/johnb/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737/junos_config.py; rm -rf "/home/johnb/.ansible/tmp/ansible-tmp-1474911943.93-57045240123737/" > /dev/null 2>&1 && sleep 0'
fatal: [vsrx01]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "auth_pass": null, 
            "authorize": false, 
            "backup": false, 
            "comment": "configured by junos_config", 
            "confirm": 0, 
            "host": "vsrx01", 
            "lines": [
                "set system host-name vsrx01", 
                "delete interfaces lo0"
            ], 
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
            "port": null, 
            "provider": {
                "host": "vsrx01", 
                "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER", 
                "transport": "netconf", 
                "username": "ansible"
            }, 
            "replace": false, 
            "rollback": null, 
            "src": null, 
            "src_format": null, 
            "ssh_keyfile": null, 
            "timeout": 10, 
            "transport": "netconf", 
            "username": "ansible", 
            "zeroize": false
        }
    }, 
    "msg": "Unable to load config: ConfigLoadError(severity: warning, bad_element: None, message: warning: statement not found)"
}

```



Discussion:
Once fixed `test-junos/roles/test_junos_config/tests/*/multiple.yaml` needs revising

The issue seems to be `delete interfaces lo0`, which fails under certain circumstances with the error.


Resolving patch:
diff --git a/network/junos/junos_config.py b/network/junos/junos_config.py
--- a/network/junos/junos_config.py
+++ b/network/junos/junos_config.py
@@ -166,6 +166,7 @@
 
 from ansible.module_utils.basic import get_exception
 from ansible.module_utils.network import NetworkModule, NetworkError
+from ansible.module_utils.netcfg import NetworkConfig
 
 
 DEFAULT_COMMENT = 'configured by junos_config'
@@ -189,6 +190,46 @@ def guess_format(config):
 
     return 'text'
 
+def config_to_commands(config):
+    set_format = config.startswith('set') or config.startswith('delete')
+    candidate = NetworkConfig(indent=4, contents=config, device_os='junos')
+    if not set_format:
+        candidate = [c.line for c in candidate.items]
+        commands = list()
+        # this filters out less specific lines
+        for item in candidate:
+            for index, entry in enumerate(commands):
+                if item.startswith(entry):
+                    del commands[index]
+                    break
+            commands.append(item)
+
+    else:
+        commands = str(candidate).split('\n')
+
+    return commands
+
+def diff_commands(commands, config):
+    config = [str(c).replace("'", '') for c in config]
+
+    updates = list()
+    visited = set()
+
+    for item in commands:
+        if not item.startswith('set') and not item.startswith('delete'):
+            raise ValueError('line must start with either `set` or `delete`')
+
+        elif item.startswith('set') and item[4:] not in config:
+            updates.append(item)
+
+        elif item.startswith('delete'):
+            for entry in config:
+                if entry.startswith(item[7:]) and item not in visited:
+                    updates.append(item)
+                    visited.add(item)
+
+    return updates
+
 def load_config(module, result):
     candidate =  module.params['lines'] or module.params['src']
 
@@ -204,6 +245,13 @@ def load_config(module, result):
         config_format = 'set'
     kwargs['config_format'] = config_format
 
+    # this is done to filter out `delete ...` statements which map to
+    # nothing in the config as that will cause an exception to be raised
+    if config_format == 'set':
+        config = module.config.get_config()
+        config = config_to_commands(config)
+        candidate = diff_commands(candidate, config)
+
     diff = module.config.load_config(candidate, **kwargs)
 
     if diff:


Test patch: