Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

updates eos shared module #17189

Merged
merged 1 commit into from
Aug 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
235 changes: 80 additions & 155 deletions lib/ansible/module_utils/eos.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,186 +29,87 @@
import re

from ansible.module_utils.basic import json, get_exception
from ansible.module_utils.network import NetworkModule, NetworkError
from ansible.module_utils.network import NetCli, Command, ModuleStub
from ansible.module_utils.network import NetworkModule, NetworkError, ModuleStub
from ansible.module_utils.network import add_argument, register_transport, to_list
from ansible.module_utils.netcfg import NetworkConfig
from ansible.module_utils.netcli import Command
from ansible.module_utils.shell import CliBase
from ansible.module_utils.urls import fetch_url, url_argument_spec

# temporary fix until modules are update. to be removed before 2.2 final
from ansible.module_utils.network import get_module

EAPI_FORMATS = ['json', 'text']

add_argument('use_ssl', dict(default=True, type='bool'))
add_argument('validate_certs', dict(default=True, type='bool'))

def argument_spec():
return dict(
# config options
running_config=dict(aliases=['config']),
config_session=dict(default='ansible_session'),
save_config=dict(default=False, aliases=['save']),
force=dict(type='bool', default=False)
)
eos_argument_spec = argument_spec()

def get_config(module):
config = module.params['running_config']
if not config:
config = module.config.get_config(include_defaults=False)
return NetworkConfig(indent=3, contents=config)

def load_config(module, candidate):

if not module.params['force']:
config = get_config(module)
commands = candidate.difference(config)
else:
commands = str(candidate)

commands = [str(c).strip() for c in commands]

session = module.params['config_session']
save_config = module.params['save_config']

result = dict(changed=False)

if commands:
if module._diff:
diff = module.config.load_config(commands, session_name=session)
if diff:
result['diff'] = dict(prepared=diff)

if not module.check_mode:
module.config.commit_config(session)
if save_config:
module.config.save_config()
else:
module.config.abort_config(session_name=session)

if not module.check_mode:
module.config(commands)
if save_config:
module.config.save_config()

result['changed'] = True
result['updates'] = commands

return result

def expand_intf_range(interfaces):
match = re.match(r'([a-zA-Z]+)(.+)', interfaces)
if not match:
raise ValueError('could not parse interface range')

name = match.group(1)
values = match.group(2).split(',')

indicies = list()

for val in values:
tokens = val.split('-')

# single index value to handle
if len(tokens) == 1:
indicies.append(tokens[0])

elif len(tokens) == 2:
pairs = list()
mod = 0

for token in tokens:
parts = token.split('/')

if len(parts) == 1:
port = parts[0]
if port == '$':
port = last_port
pairs.append((mod, int(port)))

elif len(parts) == 2:
mod = int(parts[0])
port = parts[1]
if port == '$':
port = last_port
pairs.append((mod, int(port)))

else:
raise ValueError('unable to parse interface')

if pairs[0][0] == pairs[1][0]:
# same module
mod = pairs[0][0]
start = pairs[0][1]
end = pairs[1][1] + 1

for i in range(start, end):
if mod == 0:
indicies.append(i)
else:
indicies.append('%s/%s' % (mod, i))
else:
# span modules
start_mod, start_port = pairs[0]
end_mod, end_port = pairs[1]
end_port += 1

for i in range(start_port, last_port+1):
indicies.append('%s/%s' % (start_mod, i))

for i in range(first_port, end_port):
indicies.append('%s/%s' % (end_mod, i))

return ['%s%s' % (name, index) for index in indicies]

class EosConfigMixin(object):

### implementation of netcfg.Config ###

def configure(self, commands, **kwargs):
commands = prepare_config(commands)
cmds = ['configure terminal']
cmds.extend(to_list(commands))
cmds.append('end')
responses = self.execute(commands)
responses.pop(0)
return responses
return responses[1:-1]

def get_config(self, **kwargs):
def get_config(self, include_defaults=False, **kwargs):
cmd = 'show running-config'
if kwargs.get('include_defaults') is True:
if include_defaults:
cmd += ' all'
return self.execute([cmd])[0]

def load_config(self, commands, session_name='ansible_temp_session', **kwargs):
commands = to_list(commands)
commands.insert(0, 'configure session %s' % session_name)
commands.append('show session-config diffs')
commands.append('end')
responses = self.execute(commands)
return responses[-2]
def load_config(self, config, session, commit=False, replace=False, **kwargs):
""" Loads the configuration into the remote device

def replace_config(self, contents, params, **kwargs):
remote_user = params['username']
remote_path = '/home/%s/ansible-config' % remote_user
This method handles the actual loading of the config
commands into the remote EOS device. By default the
config specified is merged with the current running-config.

commands = [
'bash echo "%s" > %s' % (contents, remote_path),
'diff running-config file:/%s' % remote_path,
'config replace file:/%s' % remote_path,
]
:param config: ordered list of config commands to load
:param replace: replace current config when True otherwise merge

responses = self.run_commands(commands)
return responses[-2]
:returns list: ordered set of responses from device
"""
commands = ['configure session %s' % session]
if replace:
commands.append('rollback clean-config')

def commit_config(self, session_name):
session = 'configure session %s' % session_name
commands = [session, 'commit', 'no %s' % session]
self.execute(commands)
commands.extend(config)

def abort_config(self, session_name):
command = 'no configure session %s' % session_name
self.execute([command])
if commands[-1] != 'end':
commands.append('end')

try:
self.execute(commands)
diff = self.diff_config(session)
if commit:
self.commit_config(session)
except NetworkError:
self.abort_config(session)
diff = None
raise
return diff

def save_config(self):
self.execute(['copy running-config startup-config'])

### end netcfg.Config ###

def diff_config(self, session):
commands = ['configure session %s' % session,
'show session-config diffs',
'end']
response = self.execute(commands)
return response[-2]

def commit_config(self, session):
commands = ['configure session %s' % session, 'commit']
self.execute(commands)

def abort_config(self, session):
commands = ['configure session %s' % session, 'abort']
self.execute(commands)

class Eapi(EosConfigMixin):

def __init__(self):
Expand Down Expand Up @@ -295,6 +196,7 @@ def execute(self, commands, format='json', **kwargs):
"""
if self.url is None:
raise NetworkError('Not connected to endpoint.')

if self.enable is not None:
commands.insert(0, self.enable)

Expand Down Expand Up @@ -330,10 +232,12 @@ def execute(self, commands, format='json', **kwargs):

def get_config(self, **kwargs):
return self.run_commands(['show running-config'], format='text')[0]

Eapi = register_transport('eapi')(Eapi)


class Cli(NetCli, EosConfigMixin):
class Cli(CliBase, EosConfigMixin):

CLI_PROMPTS_RE = [
re.compile(r"[\r\n]?[\w+\-\.:\/\[\]]+(?:\([^\)]+\)){,3}(?:>|#) ?$"),
re.compile(r"\[\w+\@[\w\-\.]+(?: [^\]])\] ?[>#\$] ?$")
Expand All @@ -357,9 +261,23 @@ def connect(self, params, **kwargs):
super(Cli, self).connect(params, kickstart=True, **kwargs)
self.shell.send('terminal length 0')

def authorize(self, params, **kwargs):
passwd = params['auth_pass']
self.execute(Command('enable', prompt=self.NET_PASSWD_RE, response=passwd))

### implementation of network.Cli ###

def run_commands(self, commands):
"""execute the ordered set of commands on the remote host

This method will take a list of Command objects, convert them
to a list of dict objects and execute them on the shell
connection.

:param commands: list of Command objects

:returns: set of ordered responses
"""
cmds = list(prepare_commands(commands))
responses = self.execute(cmds)
for index, cmd in enumerate(commands):
Expand All @@ -372,16 +290,23 @@ def run_commands(self, commands):
response=responses[index]
)
return responses

Cli = register_transport('cli', default=True)(Cli)


def prepare_config(commands):
commands = to_list(commands)
commands.insert(0, 'configure terminal')
commands.append('end')
return commands


def prepare_commands(commands):
""" transforms a list of Command objects to dict

:param commands: list of Command objects

:returns: list of dict objects
"""
jsonify = lambda x: '%s | json' % x
for cmd in to_list(commands):
if cmd.output == 'json':
Expand Down
28 changes: 28 additions & 0 deletions lib/ansible/plugins/action/eos_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#
# Copyright 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/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible.plugins.action import ActionBase
from ansible.plugins.action.net_config import ActionModule as NetActionModule

class ActionModule(NetActionModule, ActionBase):
pass