updates the ios shared module with new shell

This update refactor the ios shared module to use the new shell shared
library instead of issh and cli.  It also adds the ios documentation
fragment to be used when building ios based modules.
pull/13777/head
Peter Sprygada 9 years ago
parent e6ee59fafe
commit d1dacfb3ca

@ -16,165 +16,118 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
"""
Adds shared module support for connecting to and configuring Cisco
IOS devices. This shared module builds on module_utils/ssh.py and
implements the Shell object.
** Note: The order of the import statements does matter. **
from ansible.module_utils.basic import *
from ansible.module_utils.ssh import *
from ansible.module_utils.ios import *
This module provides the following common argument spec for creating
ios connections:
* enable_mode (bool) - Forces the shell connection into IOS enable mode
* enable_password (str) - Configures the IOS enable mode password to be
send to the device to authorize the session
* device (dict) - Accepts the set of configuration parameters as a
dict object
Note: These shared arguments are in addition to the arguments provided by
the module_utils/ssh.py shared module
"""
import socket
IOS_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-]*\(.+\)#$'),
re.compile(r'\x1b.*$')
]
IOS_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+"),
]
IOS_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
IOS_COMMON_ARGS = dict(
host=dict(),
port=dict(type='int', default=22),
username=dict(),
password=dict(),
enable_mode=dict(default=False, type='bool'),
enable_password=dict(),
connect_timeout=dict(type='int', default=10),
device=dict()
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
NET_COMMON_ARGS = dict(
host=dict(required=True),
port=dict(default=22, type='int'),
username=dict(required=True),
password=dict(no_log=True),
authorize=dict(default=False, type='bool'),
auth_pass=dict(no_log=True),
)
def to_list(val):
if isinstance(val, (list, tuple)):
return list(val)
elif val is not None:
return [val]
else:
return list()
def ios_module(**kwargs):
"""Append the common args to the argument_spec
"""
spec = kwargs.get('argument_spec') or dict()
class Cli(object):
argument_spec = shell_argument_spec()
argument_spec.update(IOS_COMMON_ARGS)
if kwargs.get('argument_spec'):
argument_spec.update(kwargs['argument_spec'])
kwargs['argument_spec'] = argument_spec
def __init__(self, module):
self.module = module
self.shell = None
module = AnsibleModule(**kwargs)
def connect(self, **kwargs):
host = self.module.params['host']
port = self.module.params['port'] or 22
device = module.params.get('device') or dict()
for key, value in device.iteritems():
if key in IOS_COMMON_ARGS:
module.params[key] = value
username = self.module.params['username']
password = self.module.params['password']
params = json_dict_unicode_to_bytes(json.loads(MODULE_COMPLEX_ARGS))
for key, value in params.iteritems():
if key != 'device':
module.params[key] = value
self.shell = Shell()
self.shell.open(host, port=port, username=username, password=password)
return module
def authorize(self):
passwd = self.module.params['auth_pass']
self.send(Command('enable', prompt=NET_PASSWD_RE, response=passwd))
def to_list(arg):
"""Try to force the arg to a list object
"""
if isinstance(arg, (list, tuple)):
return list(arg)
elif arg is not None:
return [arg]
else:
return []
def send(self, commands):
return self.shell.send(commands)
class IosShell(object):
class IosModule(AnsibleModule):
def __init__(self):
def __init__(self, *args, **kwargs):
super(IosModule, self).__init__(*args, **kwargs)
self.connection = None
self._config = None
def connect(self, host, username, password, **kwargs):
port = kwargs.get('port') or 22
timeout = kwargs.get('timeout') or 10
@property
def config(self):
if not self._config:
self._config = self.get_config()
return self._config
self.connection = Shell()
def connect(self):
try:
self.connection = Cli(self)
self.connection.connect()
self.execute('terminal length 0')
self.connection.prompts.extend(IOS_PROMPTS_RE)
self.connection.errors.extend(IOS_ERRORS_RE)
if self.params['authorize']:
self.connection.authorize()
self.connection.open(host, port=port, username=username,
password=password, timeout=timeout)
def authorize(self, passwd=None):
command = Command('enable', prompt=IOS_PASSWD_RE, response=passwd)
self.send(command)
except Exception, exc:
self.fail_json(msg=exc.message)
def configure(self, commands):
commands = to_list(commands)
commands.insert(0, 'configure terminal')
commands.append('end')
responses = self.execute(commands)
responses.pop(0)
return responses
resp = self.send(commands)
resp.pop(0)
resp.pop()
def execute(self, commands, **kwargs):
return self.connection.send(commands)
return resp
def disconnect(self):
self.connection.close()
def send(self, commands):
responses = list()
for cmd in to_list(commands):
response = self.connection.send(cmd)
responses.append(response)
return responses
def parse_config(self, cfg):
return parse(cfg, indent=1)
def ios_connection(module):
"""Creates a connection to an IOS device based on the module arguments
"""
host = module.params['host']
port = module.params['port']
def get_config(self):
cmd = 'show running-config'
if self.params['include_defaults']:
cmd += ' all'
return self.execute(cmd)[0]
username = module.params['username']
password = module.params['password']
def get_module(**kwargs):
"""Return instance of IosModule
"""
timeout = module.params['connect_timeout']
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
try:
shell = IosShell()
shell.connect(host, port=port, username=username, password=password,
timeout=timeout)
shell.send('terminal length 0')
except paramiko.ssh_exception.AuthenticationException, exc:
module.fail_json(msg=exc.message)
except socket.error, exc:
module.fail_json(msg=exc.strerror, errno=exc.errno)
module = IosModule(**kwargs)
if module.params['enable_mode']:
shell.authorize(module.params['enable_password'])
# HAS_PARAMIKO is set by module_utils/shell.py
if not HAS_PARAMIKO:
module.fail_json(msg='paramiko is required but does not appear to be installed')
return shell
# 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

@ -0,0 +1,67 @@
#
# (c) 2015, Peter Sprygada <psprygada@ansible.com>
#
# 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/>.
class ModuleDocFragment(object):
# Standard files documentation fragment
DOCUMENTATION = """
options:
host:
description:
- Specifies the DNS host name or address for connecting to the remote
device over the specified transport. The value of host is used as
the destination address for the transport.
required: true
port:
description:
- Specifies the port to use when buiding the connection to the remote
device. The port value will default to the well known SSH port
of 22
required: false
default: 22
username:
description:
- Configures the usename to use to authenticate the connection to
the remote device. The value of I(username) is used to authenticate
the SSH session
required: true
password:
description:
- Specifies the password to use when authentication the connection to
the remote device. The value of I(password) is used to authenticate
the SSH session
required: false
default: null
authorize:
description:
- Instructs the module to enter priviledged mode on the remote device
before sending any commands. If not specified, the device will
attempt to excecute all commands in non-priviledged mode.
required: false
default: false
choices: BOOLEANS
auth_pass:
description:
- Specifies the password to use if required to enter privileged mode
on the remote device. If I(authorize) is false, then this argument
does nothing
required: false
default: none
"""
Loading…
Cancel
Save