188 lines
5.5 KiB
Python
188 lines
5.5 KiB
Python
#!/usr/bin/python
|
|
# Copyright: Ansible Project
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
DOCUMENTATION = """
|
|
module: cli_command
|
|
author: Nathaniel Case (@Qalthos)
|
|
short_description: Run a cli command on cli-based network devices
|
|
description:
|
|
- Sends a command to a network device and returns the result read from the device.
|
|
version_added: 1.0.0
|
|
extends_documentation_fragment:
|
|
- ansible.netcommon.network_agnostic
|
|
options:
|
|
command:
|
|
description:
|
|
- The command to send to the remote network device. The resulting output from
|
|
the command is returned, unless I(sendonly) is set.
|
|
required: true
|
|
type: str
|
|
prompt:
|
|
description:
|
|
- A single regex pattern or a sequence of patterns to evaluate the expected prompt
|
|
from I(command).
|
|
required: false
|
|
type: list
|
|
elements: str
|
|
answer:
|
|
description:
|
|
- The answer to reply with if I(prompt) is matched. The value can be a single
|
|
answer or a list of answer for multiple prompts. In case the command execution
|
|
results in multiple prompts the sequence of the prompt and excepted answer should
|
|
be in same order.
|
|
required: false
|
|
type: list
|
|
elements: str
|
|
sendonly:
|
|
description:
|
|
- The boolean value, that when set to true will send I(command) to the device
|
|
but not wait for a result.
|
|
type: bool
|
|
default: false
|
|
required: false
|
|
newline:
|
|
description:
|
|
- The boolean value, that when set to false will send I(answer) to the device
|
|
without a trailing newline.
|
|
type: bool
|
|
default: true
|
|
required: false
|
|
check_all:
|
|
description:
|
|
- By default if any one of the prompts mentioned in C(prompt) option is matched
|
|
it won't check for other prompts. This boolean flag, that when set to I(True)
|
|
will check for all the prompts mentioned in C(prompt) option in the given order.
|
|
If the option is set to I(True) all the prompts should be received from remote
|
|
host if not it will result in timeout.
|
|
type: bool
|
|
default: false
|
|
"""
|
|
|
|
EXAMPLES = """
|
|
- name: run show version on remote devices
|
|
ansible.netcommon.cli_command:
|
|
command: show version
|
|
|
|
- name: run command with json formatted output
|
|
ansible.netcommon.cli_command:
|
|
command: show version | json
|
|
|
|
- name: run command expecting user confirmation
|
|
ansible.netcommon.cli_command:
|
|
command: commit replace
|
|
prompt: This commit will replace or remove the entire running configuration
|
|
answer: "yes"
|
|
|
|
- name: run command expecting user confirmation
|
|
ansible.netcommon.cli_command:
|
|
command: show interface summary
|
|
prompt: Press any key to continue
|
|
answer: y
|
|
newline: false
|
|
|
|
- name: run config mode command and handle prompt/answer
|
|
ansible.netcommon.cli_command:
|
|
command: "{{ item }}"
|
|
prompt:
|
|
- Exit with uncommitted changes
|
|
answer: y
|
|
loop:
|
|
- configure
|
|
- set system syslog file test any any
|
|
- exit
|
|
|
|
- name: multiple prompt, multiple answer (mandatory check for all prompts)
|
|
ansible.netcommon.cli_command:
|
|
command: copy sftp sftp://user@host//user/test.img
|
|
check_all: true
|
|
prompt:
|
|
- Confirm download operation
|
|
- Password
|
|
- Do you want to change that to the standby image
|
|
answer:
|
|
- y
|
|
- <password>
|
|
- y
|
|
"""
|
|
|
|
RETURN = """
|
|
stdout:
|
|
description: The response from the command
|
|
returned: when sendonly is false
|
|
type: str
|
|
sample: 'Version: VyOS 1.1.7[...]'
|
|
|
|
json:
|
|
description: A dictionary representing a JSON-formatted response
|
|
returned: when the device response is valid JSON
|
|
type: dict
|
|
sample: |
|
|
{
|
|
"architecture": "i386",
|
|
"bootupTimestamp": 1532649700.56,
|
|
"modelName": "vEOS",
|
|
"version": "4.15.9M"
|
|
[...]
|
|
}
|
|
"""
|
|
|
|
from ansible.module_utils._text import to_text
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.connection import Connection, ConnectionError
|
|
|
|
|
|
def main():
|
|
"""entry point for module execution"""
|
|
argument_spec = dict(
|
|
command=dict(type="str", required=True),
|
|
prompt=dict(type="list", elements="str", required=False),
|
|
answer=dict(type="list", elements="str", required=False),
|
|
newline=dict(type="bool", default=True, required=False),
|
|
sendonly=dict(type="bool", default=False, required=False),
|
|
check_all=dict(type="bool", default=False, required=False),
|
|
)
|
|
required_together = [["prompt", "answer"]]
|
|
module = AnsibleModule(
|
|
argument_spec=argument_spec,
|
|
required_together=required_together,
|
|
supports_check_mode=True,
|
|
)
|
|
|
|
if module.check_mode and not module.params["command"].startswith("show"):
|
|
module.fail_json(
|
|
msg="Only show commands are supported when using check_mode, not "
|
|
"executing %s" % module.params["command"]
|
|
)
|
|
|
|
warnings = list()
|
|
result = {"changed": False, "warnings": warnings}
|
|
|
|
connection = Connection(module._socket_path)
|
|
response = ""
|
|
try:
|
|
response = connection.get(**module.params)
|
|
except ConnectionError as exc:
|
|
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
|
|
|
|
if not module.params["sendonly"]:
|
|
try:
|
|
result["json"] = module.from_json(response)
|
|
except ValueError:
|
|
pass
|
|
|
|
result.update({"stdout": response})
|
|
|
|
module.exit_json(**result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|