Skip to content

Instantly share code, notes, and snippets.

@komeda-shinji
Created November 15, 2016 14:44
Show Gist options
  • Save komeda-shinji/2f8e5bd4168b75b552e70692afa4899e to your computer and use it in GitHub Desktop.
Save komeda-shinji/2f8e5bd4168b75b552e70692afa4899e to your computer and use it in GitHub Desktop.
ansible IOS telnet support
diff -u ansible/module_utils/ios.py ansible/module_utils/ios.py
--- ansible/module_utils/ios.py 2016-05-11 16:27:12.000000000 +0900
+++ ansible/module_utils/ios.py 2016-05-18 21:57:24.000000000 +0900
@@ -21,11 +21,13 @@
NET_COMMON_ARGS = dict(
host=dict(required=True),
- port=dict(default=22, type='int'),
+ port=dict(type='int'),
username=dict(required=True),
password=dict(no_log=True),
authorize=dict(default=False, type='bool'),
auth_pass=dict(no_log=True),
+ method=dict(type='str', default='ssh', choices=['ssh', 'telnet']),
+ provider=dict()
)
def to_list(val):
@@ -44,13 +46,25 @@
def connect(self, **kwargs):
host = self.module.params['host']
- port = self.module.params['port'] or 22
+ method = self.module.params['method'] or 'ssh'
+ if method == 'telnet':
+ port = self.module.params['port'] or 23
+ else:
+ port = self.module.params['port'] or 22
username = self.module.params['username']
password = self.module.params['password']
- self.shell = Shell()
- self.shell.open(host, port=port, username=username, password=password)
+ if method == 'telnet':
+ self.shell = Telnet()
+ else:
+ self.shell = Shell()
+
+ try:
+ self.shell.open(host, port=port, username=username, password=password)
+ except Exception, exc:
+ msg = 'failed to connecto to %s:%s - %s' % (host, port, str(exc))
+ self.module.fail_json(msg=msg)
def authorize(self):
passwd = self.module.params['auth_pass']
@@ -59,10 +73,16 @@
def send(self, commands):
return self.shell.send(commands)
-class IosModule(AnsibleModule):
+ def receive(self):
+ return self.shell.receive()
+
+ def close(self):
+ return self.shell.close()
+
+class NetworkModule(AnsibleModule):
def __init__(self, *args, **kwargs):
- super(IosModule, self).__init__(*args, **kwargs)
+ super(NetworkModule, self).__init__(*args, **kwargs)
self.connection = None
self._config = None
@@ -72,10 +92,19 @@
self._config = self.get_config()
return self._config
+ def _load_params(self):
+ params = super(NetworkModule, self)._load_params()
+ provider = params.get('provider') or dict()
+ for key, value in provider.items():
+ if key in NET_COMMON_ARGS.keys():
+ params[key] = value
+ return params
+
def connect(self):
try:
self.connection = Cli(self)
self.connection.connect()
+ self.connection.receive()
self.execute('terminal length 0')
if self.params['authorize']:
@@ -92,7 +121,10 @@
return responses
def execute(self, commands, **kwargs):
- return self.connection.send(commands)
+ try:
+ return self.connection.send(commands, **kwargs)
+ except Exception, exc:
+ self.fail_json(msg=exc.message, commands=commands)
def disconnect(self):
self.connection.close()
@@ -107,26 +139,21 @@
return self.execute(cmd)[0]
def get_module(**kwargs):
- """Return instance of IosModule
+ """Return instance of NetworkModule
"""
argument_spec = NET_COMMON_ARGS.copy()
if kwargs.get('argument_spec'):
argument_spec.update(kwargs['argument_spec'])
kwargs['argument_spec'] = argument_spec
- kwargs['check_invalid_arguments'] = False
- module = IosModule(**kwargs)
+ module = NetworkModule(**kwargs)
# HAS_PARAMIKO is set by module_utils/shell.py
- if not HAS_PARAMIKO:
+ method = module.params['method'] or 'ssh'
+ if method == 'ssh' and not HAS_PARAMIKO:
module.fail_json(msg='paramiko is required but does not appear to be installed')
- # copy in values from local action.
- params = json_dict_unicode_to_bytes(json.loads(MODULE_COMPLEX_ARGS))
- for key, value in params.iteritems():
- module.params[key] = value
-
module.connect()
return module
diff -u ansible/modules/core/network/ios/ios_command.py ansible/modules/core/network/ios/ios_command.py
--- ansible/modules/core/network/ios/ios_command.py 2016-05-11 16:37:37.000000000 +0900
+++ ansible/modules/core/network/ios/ios_command.py 2016-05-11 23:06:14.000000000 +0900
@@ -120,7 +120,8 @@
commands=dict(type='list'),
waitfor=dict(type='list'),
retries=dict(default=10, type='int'),
- interval=dict(default=1, type='int')
+ interval=dict(default=1, type='int'),
+ method=dict(type='str', default='ssh', choices=['ssh', 'telnet']),
)
module = get_module(argument_spec=spec,
@@ -163,6 +164,7 @@
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
from ansible.module_utils.shell import *
+from ansible.module_utils.telnet import *
from ansible.module_utils.netcfg import *
from ansible.module_utils.ios import *
if __name__ == '__main__':
cp ansible/module_utils/shell.py ansible/module_utils/telnet.py
diff -u ansible/module_utils/telnet.py ansible/module_utils/telnet.py
--- ansible/module_utils/telnet.py 2016-05-11 16:27:12.000000000 +0900
+++ ansible/module_utils/telnet.py 2016-05-18 21:58:22.000000000 +0900
@@ -18,19 +18,9 @@
#
import re
import socket
+import telnetlib
-# py2 vs py3; replace with six via ziploader
-try:
- from StringIO import StringIO
-except ImportError:
- from io import StringIO
-
-try:
- import paramiko
- HAS_PARAMIKO = True
-except ImportError:
- HAS_PARAMIKO = False
-
+from StringIO import StringIO
ANSI_RE = re.compile(r'(\x1b\[\?1h\x1b=)')
@@ -48,6 +38,8 @@
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")
]
def to_list(val):
@@ -58,10 +50,10 @@
else:
return list()
-class ShellError(Exception):
+class TelnetError(Exception):
def __init__(self, msg, command=None):
- super(ShellError, self).__init__(msg)
+ super(TelnetError, self).__init__(msg)
self.message = msg
self.command = command
@@ -75,11 +67,12 @@
def __str__(self):
return self.command
-class Shell(object):
+class Telnet(object):
def __init__(self):
- self.ssh = None
- self.shell = None
+ self.telnet = None
+
+ self._matched_prompt = None
self.prompts = list()
self.prompts.extend(CLI_PROMPTS_RE)
@@ -87,22 +80,16 @@
self.errors = list()
self.errors.extend(CLI_ERRORS_RE)
- def open(self, host, port=22, username=None, password=None,
- timeout=10, key_filename=None):
-
- self.ssh = paramiko.SSHClient()
- self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
-
- use_keys = password is None
-
- self.ssh.connect(host, port=port, username=username, password=password,
- timeout=timeout, allow_agent=use_keys, look_for_keys=use_keys,
- key_filename=key_filename)
-
- self.shell = self.ssh.invoke_shell()
- self.shell.settimeout(10)
- self.shell.sendall("\n")
- self.receive()
+ def open(self, host, port=23, username=None, password=None,
+ timeout=10, key_filename=None, pkey=None, look_for_keys=None,
+ allow_agent=False):
+
+ self.telnet = telnetlib.Telnet(host, port, timeout)
+ if username:
+ self.telnet.read_until("Username: ", timeout)
+ self.telnet.write(username + "\n")
+ self.telnet.read_until("Password: ", timeout)
+ self.telnet.write(password + "\n")
def strip(self, data):
return ANSI_RE.sub('', data)
@@ -111,10 +98,10 @@
recv = StringIO()
while True:
- data = self.shell.recv(200)
+ data = self.telnet.read_very_eager()
recv.write(data)
- recv.seek(recv.tell() - 200)
+ recv.seek(recv.tell() - len(data))
window = self.strip(recv.read())
@@ -126,7 +113,7 @@
if self.read(window):
resp = self.strip(recv.getvalue())
return self.sanitize(cmd, resp)
- except ShellError, exc:
+ except TelnetError, exc:
exc.command = cmd
raise
@@ -134,15 +121,15 @@
responses = list()
try:
for command in to_list(commands):
- cmd = '%s\r' % str(command)
- self.shell.sendall(cmd)
+ cmd = '%s\n' % str(command)
+ self.telnet.write(cmd)
responses.append(self.receive(command))
except socket.timeout, exc:
- raise ShellError("timeout trying to send command", cmd)
+ raise TelnetError("timeout trying to send command", cmd)
return responses
def close(self):
- self.shell.close()
+ self.telnet.close()
def handle_input(self, resp, prompt, response):
if not prompt or not response:
@@ -155,7 +142,7 @@
match = pr.search(resp)
if match:
cmd = '%s\r' % ans
- self.shell.sendall(cmd)
+ self.telnet.sendall(cmd)
def sanitize(self, cmd, resp):
cleaned = []
@@ -168,17 +155,19 @@
def read(self, response):
for regex in self.errors:
if regex.search(response):
- raise ShellError('%s' % response)
+ raise TelnetError('%s' % response)
for regex in self.prompts:
- if regex.search(response):
+ match = regex.search(response)
+ if match:
+ self._matched_prompt = match.group()
return True
def get_cli_connection(module):
host = module.params['host']
port = module.params['port']
if not port:
- port = 22
+ port = 23
username = module.params['username']
password = module.params['password']
@@ -186,8 +175,6 @@
try:
cli = Cli()
cli.open(host, port=port, username=username, password=password)
- except paramiko.ssh_exception.AuthenticationException, exc:
- module.fail_json(msg=exc.message)
except socket.error, exc:
host = '%s:%s' % (host, port)
module.fail_json(msg=exc.strerror, errno=exc.errno, host=host)
@mjuenema
Copy link

Have you ever tried to submit this as a Pull Request to the Ansible sources?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment