mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 21:00:22 -07:00
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.
This commit is contained in:
parent
e6ee59fafe
commit
d1dacfb3ca
2 changed files with 156 additions and 136 deletions
|
@ -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. **
|
||||
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
|
||||
|
||||
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_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 ios_module(**kwargs):
|
||||
"""Append the common args to the argument_spec
|
||||
"""
|
||||
spec = kwargs.get('argument_spec') or dict()
|
||||
|
||||
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
|
||||
|
||||
module = AnsibleModule(**kwargs)
|
||||
|
||||
device = module.params.get('device') or dict()
|
||||
for key, value in device.iteritems():
|
||||
if key in IOS_COMMON_ARGS:
|
||||
module.params[key] = value
|
||||
|
||||
params = json_dict_unicode_to_bytes(json.loads(MODULE_COMPLEX_ARGS))
|
||||
for key, value in params.iteritems():
|
||||
if key != 'device':
|
||||
module.params[key] = value
|
||||
|
||||
return module
|
||||
|
||||
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]
|
||||
def to_list(val):
|
||||
if isinstance(val, (list, tuple)):
|
||||
return list(val)
|
||||
elif val is not None:
|
||||
return [val]
|
||||
else:
|
||||
return []
|
||||
return list()
|
||||
|
||||
class IosShell(object):
|
||||
class Cli(object):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.shell = None
|
||||
|
||||
def connect(self, **kwargs):
|
||||
host = self.module.params['host']
|
||||
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)
|
||||
|
||||
def authorize(self):
|
||||
passwd = self.module.params['auth_pass']
|
||||
self.send(Command('enable', prompt=NET_PASSWD_RE, response=passwd))
|
||||
|
||||
def send(self, commands):
|
||||
return self.shell.send(commands)
|
||||
|
||||
class IosModule(AnsibleModule):
|
||||
|
||||
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')
|
||||
|
||||
resp = self.send(commands)
|
||||
resp.pop(0)
|
||||
resp.pop()
|
||||
|
||||
return resp
|
||||
|
||||
def send(self, commands):
|
||||
responses = list()
|
||||
for cmd in to_list(commands):
|
||||
response = self.connection.send(cmd)
|
||||
responses.append(response)
|
||||
responses = self.execute(commands)
|
||||
responses.pop(0)
|
||||
return responses
|
||||
|
||||
def ios_connection(module):
|
||||
"""Creates a connection to an IOS device based on the module arguments
|
||||
def execute(self, commands, **kwargs):
|
||||
return self.connection.send(commands)
|
||||
|
||||
def disconnect(self):
|
||||
self.connection.close()
|
||||
|
||||
def parse_config(self, cfg):
|
||||
return parse(cfg, indent=1)
|
||||
|
||||
def get_config(self):
|
||||
cmd = 'show running-config'
|
||||
if self.params['include_defaults']:
|
||||
cmd += ' all'
|
||||
return self.execute(cmd)[0]
|
||||
|
||||
def get_module(**kwargs):
|
||||
"""Return instance of IosModule
|
||||
"""
|
||||
host = module.params['host']
|
||||
port = module.params['port']
|
||||
|
||||
username = module.params['username']
|
||||
password = module.params['password']
|
||||
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
|
||||
|
||||
timeout = module.params['connect_timeout']
|
||||
module = IosModule(**kwargs)
|
||||
|
||||
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)
|
||||
# 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')
|
||||
|
||||
if module.params['enable_mode']:
|
||||
shell.authorize(module.params['enable_password'])
|
||||
|
||||
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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue