diff --git a/lib/ansible/module_utils/ios.py b/lib/ansible/module_utils/ios.py
index 085b68dcd28..550a2de6d53 100644
--- a/lib/ansible/module_utils/ios.py
+++ b/lib/ansible/module_utils/ios.py
@@ -16,165 +16,118 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
-"""
-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
diff --git a/lib/ansible/utils/module_docs_fragments/ios.py b/lib/ansible/utils/module_docs_fragments/ios.py
new file mode 100644
index 00000000000..5f07bbfde76
--- /dev/null
+++ b/lib/ansible/utils/module_docs_fragments/ios.py
@@ -0,0 +1,67 @@
+#
+# (c) 2015, Peter Sprygada
+#
+# 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 .
+
+
+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
+
+"""