From a5fe046f909953750ac881d7c13e73352851ecb2 Mon Sep 17 00:00:00 2001
From: chouseknecht <chouseknecht@ansible.com>
Date: Tue, 9 Feb 2016 15:03:44 -0500
Subject: [PATCH] Add ios_commnand module.

---
 .../modules/network/ios/ios_command.py        | 181 ++++++++++++++++++
 1 file changed, 181 insertions(+)
 create mode 100644 lib/ansible/modules/network/ios/ios_command.py

diff --git a/lib/ansible/modules/network/ios/ios_command.py b/lib/ansible/modules/network/ios/ios_command.py
new file mode 100644
index 00000000000..b23112ffa40
--- /dev/null
+++ b/lib/ansible/modules/network/ios/ios_command.py
@@ -0,0 +1,181 @@
+#!/usr/bin/python
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+DOCUMENTATION = """
+---
+module: ios_command
+version_added: "2.1"
+author: "Peter sprygada (@privateip)"
+short_description: Run arbitrary commands on ios devices.
+description:
+  - Sends arbitrary commands to an ios node and returns the results
+    read from the device. The M(ios_command) module includes an
+    argument that will cause the module to wait for a specific condition
+    before returning or timing out if the condition is not met.
+extends_documentation_fragment: ios
+options:
+  commands:
+    description:
+      - List of commands to send to the remote ios device over the
+        configured provider. The resulting output from the command
+        is returned. If the I(waitfor) argument is provided, the
+        module is not returned until the condition is satisfied or
+        the number of retires as expired.
+    required: true
+  waitfor:
+    description:
+      - List of conditions to evaluate against the output of the
+        command. The task will wait for a each condition to be true
+        before moving forward. If the conditional is not true
+        within the configured number of retries, the task fails.
+        See examples.
+    required: false
+    default: null
+  retries:
+    description:
+      - Specifies the number of retries a command should by tried
+        before it is considered failed. The command is run on the
+        target device every retry and evaluated against the
+        waitfor conditions.
+    required: false
+    default: 10
+  interval:
+    description:
+      - Configures the interval in seconds to wait between retries
+        of the command. If the command does not pass the specified
+        conditions, the interval indicates how long to wait before
+        trying the command again.
+    required: false
+    default: 1
+
+"""
+
+EXAMPLES = """
+
+- ios_command:
+    commands:
+      - show version
+  register: output
+
+- ios_command:
+    commands:
+      - show version
+    waitfor:
+      - "result[0] contains 4.15.0F"
+
+- ios_command:
+  commands:
+    - show version
+    - show interfaces
+    - show version
+  waitfor:
+    - "result[2] contains '4.15.0F'"
+    - "result[1].interfaces.Management1.interfaceAddress[0].primaryIp.maskLen eq 24"
+    - "result[0].modelName == 'vios'"
+
+"""
+
+RETURN = """
+
+result:
+  description: the set of responses from the commands
+  returned: always
+  type: list
+  sample: ['...', '...']
+
+failed_conditionals:
+  description: the conditionals that failed
+  retured: failed
+  type: list
+  sample: ['...', '...']
+
+"""
+
+import time
+import shlex
+import re
+import json
+
+INDEX_RE = re.compile(r'(\[\d+\])')
+
+
+def get_response(data):
+    try:
+        json_data = json.loads(data)
+    except ValueError:
+        json_data = None
+    return dict(data=data, json=json_data)
+
+
+def main():
+    spec = dict(
+        commands=dict(type='list'),
+        waitfor=dict(type='list'),
+        retries=dict(default=10, type='int'),
+        interval=dict(default=1, type='int')
+    )
+
+    module = get_module(argument_spec=spec,
+                        supports_check_mode=True)
+
+    commands = module.params['commands']
+
+    retries = module.params['retries']
+    interval = module.params['interval']
+
+    try:
+        queue = set()
+        for entry in (module.params['waitfor'] or list()):
+            queue.add(Conditional(entry))
+    except AttributeError, exc:
+        module.fail_json(msg=exc.message)
+
+    result = dict(changed=False, result=list())
+
+    while retries > 0:
+        response = module.execute(commands)
+        result['result'] = response
+
+        for index, cmd in enumerate(commands):
+            if cmd.endswith('json'):
+                response[index] = json.loads(response[index])
+
+        for item in list(queue):
+            if item(response):
+                queue.remove(item)
+
+        if not queue:
+            break
+
+        time.sleep(interval)
+        retries -= 1
+    else:
+        failed_conditions = [item.raw for item in queue]
+        module.fail_json(msg='timeout waiting for value', failed_conditions=failed_conditions)
+
+    return module.exit_json(**result)
+
+
+from ansible.module_utils.basic import *
+from ansible.module_utils.urls import *
+from ansible.module_utils.shell import *
+from ansible.module_utils.netcfg import *
+from ansible.module_utils.ios import *
+if __name__ == '__main__':
+        main()
+