diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py
index c50486e5ea..1eac6f05bf 100644
--- a/lib/ansible/playbook/play_context.py
+++ b/lib/ansible/playbook/play_context.py
@@ -60,6 +60,7 @@ MAGIC_VARIABLE_MAPPING = dict(
private_key_file = ('ansible_ssh_private_key_file', 'ansible_private_key_file'),
pipelining = ('ansible_ssh_pipelining', 'ansible_pipelining'),
shell = ('ansible_shell_type',),
+ network_os = ('ansible_network_os',),
become = ('ansible_become',),
become_method = ('ansible_become_method',),
become_user = ('ansible_become_user',),
@@ -164,6 +165,7 @@ class PlayContext(Base):
_private_key_file = FieldAttribute(isa='string', default=C.DEFAULT_PRIVATE_KEY_FILE)
_timeout = FieldAttribute(isa='int', default=C.DEFAULT_TIMEOUT)
_shell = FieldAttribute(isa='string')
+ _network_os = FieldAttribute(isa='string')
_ssh_args = FieldAttribute(isa='string', default=C.ANSIBLE_SSH_ARGS)
_ssh_common_args = FieldAttribute(isa='string')
_sftp_extra_args = FieldAttribute(isa='string')
diff --git a/lib/ansible/plugins/__init__.py b/lib/ansible/plugins/__init__.py
index 9e2229e8c9..e457e7e02d 100644
--- a/lib/ansible/plugins/__init__.py
+++ b/lib/ansible/plugins/__init__.py
@@ -509,3 +509,11 @@ strategy_loader = PluginLoader(
'strategy_plugins',
required_base_class='StrategyBase',
)
+
+terminal_loader = PluginLoader(
+ 'TerminalModule',
+ 'ansible.plugins.terminal',
+ 'terminal_plugins',
+ 'terminal_plugins'
+)
+
diff --git a/lib/ansible/plugins/connection/network_cli.py b/lib/ansible/plugins/connection/network_cli.py
new file mode 100644
index 0000000000..c7491d352a
--- /dev/null
+++ b/lib/ansible/plugins/connection/network_cli.py
@@ -0,0 +1,190 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import socket
+import json
+import signal
+import datetime
+
+from ansible.errors import AnsibleConnectionFailure
+from ansible.module_utils.six.moves import StringIO
+from ansible.plugins import terminal_loader
+from ansible.plugins.connection.paramiko_ssh import Connection as _Connection
+
+
+class Connection(_Connection):
+ ''' CLI SSH based connections on Paramiko '''
+
+ transport = 'network_cli'
+ has_pipelining = False
+
+ def __init__(self, play_context, new_stdin, *args, **kwargs):
+ super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
+
+ assert self._play_context.network_os, 'ansible_network_os must be set'
+
+ self._terminal = terminal_loader.get(self._play_context.network_os, self)
+ if not self._terminal:
+ raise AnsibleConnectionFailure('network os %s is not supported' % self._play_context.network_os)
+
+ self._shell = None
+
+ self._matched_prompt = None
+ self._matched_pattern = None
+ self._last_response = None
+ self._history = list()
+
+ def update_play_context(self, play_context):
+ if self._play_context.become is False and play_context.become is True:
+ auth_pass = play_context.become_pass
+ self._terminal.on_authorize(passwd=auth_pass)
+
+ elif self._play_context.become is True and not play_context.become:
+ self._terminal.on_deauthorize()
+
+ self._play_context = play_context
+
+ def _connect(self):
+ super(Connection, self)._connect()
+ return (0, 'connected', '')
+
+ def open_shell(self, timeout=10):
+ self._shell = self.ssh.invoke_shell()
+ self._shell.settimeout(self._play_context.timeout)
+
+ self.receive()
+
+ if self._shell:
+ self._terminal.on_open_shell()
+
+ if hasattr(self._play_context, 'become'):
+ if self._play_context.become:
+ auth_pass = self._play_context.become_pass
+ self._terminal.on_authorize(passwd=auth_pass)
+
+ def close(self):
+ self.close_shell()
+ super(Connection, self).close()
+
+ def close_shell(self):
+ if self._shell:
+ self._terminal.on_close_shell()
+
+ if self._terminal.supports_multiplexing and self._shell:
+ self._shell.close()
+ self._shell = None
+
+ return (0, 'shell closed', '')
+
+ def receive(self, obj=None):
+ recv = StringIO()
+ handled = False
+
+ self._matched_prompt = None
+
+ while True:
+ data = self._shell.recv(256)
+
+ recv.write(data)
+ recv.seek(recv.tell() - 256)
+
+ window = self._strip(recv.read())
+
+ if obj and (obj.get('prompt') and not handled):
+ handled = self._handle_prompt(window, obj)
+
+ if self._find_prompt(window):
+ self._last_response = recv.getvalue()
+ resp = self._strip(self._last_response)
+ return self._sanitize(resp, obj)
+
+ def send(self, obj):
+ try:
+ command = obj['command']
+ self._history.append(command)
+ self._shell.sendall('%s\r' % command)
+ return self.receive(obj)
+ except (socket.timeout, AttributeError):
+ raise AnsibleConnectionFailure("timeout trying to send command: %s" % command.strip())
+
+ def _strip(self, data):
+ for regex in self._terminal.ansi_re:
+ data = regex.sub('', data)
+ return data
+
+ def _handle_prompt(self, resp, obj):
+ prompt = re.compile(obj['prompt'], re.I)
+ answer = obj['answer']
+ match = prompt.search(resp)
+ if match:
+ self._shell.sendall('%s\r' % answer)
+ return True
+
+ def _sanitize(self, resp, obj=None):
+ cleaned = []
+ command = obj.get('command') if obj else None
+ for line in resp.splitlines():
+ if (command and line.startswith(command.strip())) or self._find_prompt(line):
+ continue
+ cleaned.append(line)
+ return str("\n".join(cleaned)).strip()
+
+ def _find_prompt(self, response):
+ for regex in self._terminal.terminal_errors_re:
+ if regex.search(response):
+ raise AnsibleConnectionFailure(response)
+
+ for regex in self._terminal.terminal_prompts_re:
+ match = regex.search(response)
+ if match:
+ self._matched_pattern = regex.pattern
+ self._matched_prompt = match.group()
+ return True
+
+ def alarm_handler(self, signum, frame):
+ self.close_shell()
+
+ def exec_command(self, cmd):
+ ''' {'command': , 'prompt': , 'answer': } '''
+
+ try:
+ obj = json.loads(cmd)
+ except ValueError:
+ obj = {'command': str(cmd).strip()}
+
+ if obj['command'] == 'close_shell()':
+ return self.close_shell()
+ elif obj['command'] == 'prompt()':
+ return (0, self._matched_prompt, '')
+ elif obj['command'] == 'history()':
+ return (0, self._history, '')
+
+ try:
+ if self._shell is None:
+ self.open_shell()
+ except AnsibleConnectionFailure as exc:
+ return (1, '', str(exc))
+
+ try:
+ out = self.send(obj)
+ return (0, out, '')
+ except (AnsibleConnectionFailure, ValueError) as exc:
+ return (1, '', str(exc))
diff --git a/lib/ansible/plugins/connection/paramiko_ssh.py b/lib/ansible/plugins/connection/paramiko_ssh.py
index 9a0dbf7e27..eac773620a 100644
--- a/lib/ansible/plugins/connection/paramiko_ssh.py
+++ b/lib/ansible/plugins/connection/paramiko_ssh.py
@@ -421,8 +421,9 @@ class Connection(ConnectionBase):
SSH_CONNECTION_CACHE.pop(cache_key, None)
SFTP_CONNECTION_CACHE.pop(cache_key, None)
- if self.sftp is not None:
- self.sftp.close()
+ if hasattr(self, 'sftp'):
+ if self.sftp is not None:
+ self.sftp.close()
if C.HOST_KEY_CHECKING and C.PARAMIKO_RECORD_HOST_KEYS and self._any_keys_added():
diff --git a/lib/ansible/plugins/terminal/__init__.py b/lib/ansible/plugins/terminal/__init__.py
new file mode 100644
index 0000000000..3242559084
--- /dev/null
+++ b/lib/ansible/plugins/terminal/__init__.py
@@ -0,0 +1,71 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+from abc import ABCMeta, abstractmethod
+
+from ansible.compat.six import with_metaclass
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalBase(with_metaclass(ABCMeta, object)):
+ '''
+ A base class for implementing cli connections
+ '''
+
+ terminalprompts_re = []
+
+ terminalerrors_re = []
+
+ ansi_re = [
+ re.compile(r'(\x1b\[\?1h\x1b=)'),
+ re.compile(r'\x08.')
+ ]
+
+ supports_multiplexing = True
+
+ def __init__(self, connection):
+ self._connection = connection
+
+ def _exec_cli_command(self, cmd, check_rc=True):
+ rc, out, err = self._connection.exec_command(cmd)
+ if check_rc and rc != 0:
+ raise AnsibleConnectionFailure(err)
+ return rc, out, err
+
+ def _get_prompt(self):
+ for cmd in ['\n', 'prompt()']:
+ rc, out, err = self._exec_cli_command(cmd)
+ return out
+
+ def on_open_shell(self):
+ pass
+
+ def on_close_shell(self):
+ pass
+
+ def on_authorize(self, passwd=None):
+ pass
+
+ def on_deauthorize(self):
+ pass
+
diff --git a/lib/ansible/plugins/terminal/asa.py b/lib/ansible/plugins/terminal/asa.py
new file mode 100644
index 0000000000..8737b72c48
--- /dev/null
+++ b/lib/ansible/plugins/terminal/asa.py
@@ -0,0 +1,75 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import json
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"% ?Error"),
+ re.compile(r"^% \w+", re.M),
+ re.compile(r"% ?Bad secret"),
+ re.compile(r"invalid input", re.I),
+ re.compile(r"(?:incomplete|ambiguous) command", re.I),
+ re.compile(r"connection timed out", re.I),
+ re.compile(r"[^\r\n]+ not found", re.I),
+ re.compile(r"'[^']' +returned error code: ?\d+"),
+ ]
+
+ def authorize(self, passwd=None):
+ if self._get_prompt().endswith('#'):
+ return
+
+ cmd = {'command': 'enable'}
+ if passwd:
+ cmd['prompt'] = r"[\r\n]?password: $"
+ cmd['answer'] = passwd
+
+ try:
+ self._exec_cli_command(json.dumps(cmd))
+ self._exec_cli_command('terminal pager 0')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to elevate privilege to enable mode')
+
+ def on_deauthorize(self):
+ prompt = self._get_prompt()
+ if prompt is None:
+ # if prompt is None most likely the terminal is hung up at a prompt
+ return
+
+ if '(config' in prompt:
+ self._exec_cli_command('end')
+ self._exec_cli_command('disable')
+
+ elif prompt.endswith('#'):
+ self._exec_cli_command('disable')
+
+
diff --git a/lib/ansible/plugins/terminal/eos.py b/lib/ansible/plugins/terminal/eos.py
new file mode 100644
index 0000000000..d84c6fb1d6
--- /dev/null
+++ b/lib/ansible/plugins/terminal/eos.py
@@ -0,0 +1,82 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import json
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"% ?Error"),
+ re.compile(r"^% \w+", re.M),
+ re.compile(r"% User not present"),
+ re.compile(r"% ?Bad secret"),
+ re.compile(r"invalid input", re.I),
+ re.compile(r"(?:incomplete|ambiguous) command", re.I),
+ re.compile(r"connection timed out", re.I),
+ re.compile(r"[^\r\n]+ not found", re.I),
+ re.compile(r"'[^']' +returned error code: ?\d+"),
+ re.compile(r"[^\r\n]\/bin\/(?:ba)?sh")
+ ]
+
+ def on_open_shell(self):
+ try:
+ self._exec_cli_command('terminal length 0')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
+
+ def on_authorize(self, passwd=None):
+ if self._get_prompt().endswith('#'):
+ return
+
+ cmd = {'command': 'enable'}
+ if passwd:
+ cmd['prompt'] = r"[\r\n]?password: $"
+ cmd['answer'] = passwd
+
+ try:
+ self._exec_cli_command(json.dumps(cmd))
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to elevate privilege to enable mode')
+
+ def on_deauthorize(self):
+ prompt = self._get_prompt()
+ if prompt is None:
+ # if prompt is None most likely the terminal is hung up at a prompt
+ return
+
+ if '(config' in prompt:
+ self._exec_cli_command('end')
+ self._exec_cli_command('disable')
+
+ elif prompt.endswith('#'):
+ self._exec_cli_command('disable')
+
+
diff --git a/lib/ansible/plugins/terminal/ios.py b/lib/ansible/plugins/terminal/ios.py
new file mode 100644
index 0000000000..5a39acf06d
--- /dev/null
+++ b/lib/ansible/plugins/terminal/ios.py
@@ -0,0 +1,82 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import json
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"% ?Error"),
+ re.compile(r"^% \w+", re.M),
+ re.compile(r"% ?Bad secret"),
+ re.compile(r"invalid input", re.I),
+ re.compile(r"(?:incomplete|ambiguous) command", re.I),
+ re.compile(r"connection timed out", re.I),
+ re.compile(r"[^\r\n]+ not found", re.I),
+ re.compile(r"'[^']' +returned error code: ?\d+"),
+ ]
+
+ supports_multiplexing = False
+
+ def on_open_shell(self):
+ try:
+ self._exec_cli_command('terminal length 0')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
+
+ def on_authorize(self, passwd=None):
+ if self._get_prompt().endswith('#'):
+ return
+
+ cmd = {'command': 'enable'}
+ if passwd:
+ cmd['prompt'] = r"[\r\n]?password: $"
+ cmd['answer'] = passwd
+
+ try:
+ self._exec_cli_command(json.dumps(cmd))
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to elevate privilege to enable mode')
+
+ def on_deauthorize(self):
+ prompt = self._get_prompt()
+ if prompt is None:
+ # if prompt is None most likely the terminal is hung up at a prompt
+ return
+
+ if '(config' in prompt:
+ self._exec_cli_command('end')
+ self._exec_cli_command('disable')
+
+ elif prompt.endswith('#'):
+ self._exec_cli_command('disable')
+
+
diff --git a/lib/ansible/plugins/terminal/iosxr.py b/lib/ansible/plugins/terminal/iosxr.py
new file mode 100644
index 0000000000..bcbd73a0d6
--- /dev/null
+++ b/lib/ansible/plugins/terminal/iosxr.py
@@ -0,0 +1,55 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import json
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"% ?Error"),
+ re.compile(r"% ?Bad secret"),
+ re.compile(r"invalid input", re.I),
+ re.compile(r"(?:incomplete|ambiguous) command", re.I),
+ re.compile(r"connection timed out", re.I),
+ re.compile(r"[^\r\n]+ not found", re.I),
+ re.compile(r"'[^']' +returned error code: ?\d+"),
+ ]
+
+ supports_multiplexing = False
+
+ def on_open_shell(self):
+ try:
+ for cmd in ['terminal length 0', 'terminal exec prompt no-timestamp']:
+ self._connection.exec_command(cmd)
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
+
+
diff --git a/lib/ansible/plugins/terminal/junos.py b/lib/ansible/plugins/terminal/junos.py
new file mode 100644
index 0000000000..10d8b41da7
--- /dev/null
+++ b/lib/ansible/plugins/terminal/junos.py
@@ -0,0 +1,47 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"unknown command"),
+ re.compile(r"syntax error,")
+ ]
+
+ def on_open_shell(self):
+ try:
+ for c in ['set cli timestamp disable', 'set cli screen-length 0']:
+ self._exec_cli_command(c)
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
+
diff --git a/lib/ansible/plugins/terminal/nxos.py b/lib/ansible/plugins/terminal/nxos.py
new file mode 100644
index 0000000000..23bd755590
--- /dev/null
+++ b/lib/ansible/plugins/terminal/nxos.py
@@ -0,0 +1,56 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r'[\r\n]?[a-zA-Z]{1}[a-zA-Z0-9-]*[>|#|%](?:\s*)$'),
+ re.compile(r'[\r\n]?[a-zA-Z]{1}[a-zA-Z0-9-]*\(.+\)#(?:\s*)$')
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"% ?Error"),
+ re.compile(r"^% \w+", re.M),
+ re.compile(r"% ?Bad secret"),
+ re.compile(r"invalid input", re.I),
+ re.compile(r"(?:incomplete|ambiguous) command", re.I),
+ re.compile(r"connection timed out", re.I),
+ re.compile(r"[^\r\n]+ not found", re.I),
+ re.compile(r"'[^']' +returned error code: ?\d+"),
+ re.compile(r"syntax error"),
+ re.compile(r"unknown command"),
+ re.compile(r"user not present")
+ ]
+
+ def on_open_shell(self):
+ try:
+ self._exec_cli_command('terminal length 0')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
+
+
+
diff --git a/lib/ansible/plugins/terminal/sros.py b/lib/ansible/plugins/terminal/sros.py
new file mode 100644
index 0000000000..d4ce99a267
--- /dev/null
+++ b/lib/ansible/plugins/terminal/sros.py
@@ -0,0 +1,46 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+import json
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"^\r\nError:"),
+ ]
+
+ supports_multiplexing = False
+
+ def on_open_shell(self):
+ try:
+ self._exec_cli_command('environment no more')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
diff --git a/lib/ansible/plugins/terminal/vyos.py b/lib/ansible/plugins/terminal/vyos.py
new file mode 100644
index 0000000000..664025bb36
--- /dev/null
+++ b/lib/ansible/plugins/terminal/vyos.py
@@ -0,0 +1,47 @@
+#
+# (c) 2016 Red Hat Inc.
+#
+# 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 .
+#
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+from ansible.plugins.terminal import TerminalBase
+from ansible.errors import AnsibleConnectionFailure
+
+
+class TerminalModule(TerminalBase):
+
+ terminal_prompts_re = [
+ re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
+ re.compile(r"\@[\w\-\.]+:\S+?[>#\$] ?$")
+ ]
+
+ terminal_errors_re = [
+ re.compile(r"\n\s*Invalid command:"),
+ re.compile(r"\nCommit failed"),
+ re.compile(r"\n\s+Set failed"),
+ ]
+
+ def on_open_shell(self):
+ try:
+ self._exec_cli_command('set terminal length 0')
+ except AnsibleConnectionFailure:
+ raise AnsibleConnectionFailure('unable to set terminal parameters')
+
+