mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
refactors nxos module to use persistent connections (#21470)
This completes the refactor of the nxos modules to use the persistent connection. It also updates all of the nxos modules to use the new connection module and preserves use of nxapi as well.
This commit is contained in:
parent
eb1453a366
commit
21d993a4b8
72 changed files with 2301 additions and 12933 deletions
|
@ -28,6 +28,7 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.six.moves import zip
|
from ansible.module_utils.six.moves import zip
|
||||||
|
from ansible.module_utils.network_common import to_list
|
||||||
|
|
||||||
DEFAULT_COMMENT_TOKENS = ['#', '!', '/*', '*/']
|
DEFAULT_COMMENT_TOKENS = ['#', '!', '/*', '*/']
|
||||||
|
|
||||||
|
@ -361,3 +362,91 @@ class NetworkConfig(object):
|
||||||
self.items.append(item)
|
self.items.append(item)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomNetworkConfig(NetworkConfig):
|
||||||
|
|
||||||
|
def expand_section(self, configobj, S=None):
|
||||||
|
if S is None:
|
||||||
|
S = list()
|
||||||
|
S.append(configobj)
|
||||||
|
for child in configobj.children:
|
||||||
|
if child in S:
|
||||||
|
continue
|
||||||
|
self.expand_section(child, S)
|
||||||
|
return S
|
||||||
|
|
||||||
|
def get_object(self, path):
|
||||||
|
for item in self.items:
|
||||||
|
if item.text == path[-1]:
|
||||||
|
parents = [p.text for p in item.parents]
|
||||||
|
if parents == path[:-1]:
|
||||||
|
return item
|
||||||
|
|
||||||
|
def to_block(self, section):
|
||||||
|
return '\n'.join([item.raw for item in section])
|
||||||
|
|
||||||
|
def get_section(self, path):
|
||||||
|
try:
|
||||||
|
section = self.get_section_objects(path)
|
||||||
|
return self.to_block(section)
|
||||||
|
except ValueError:
|
||||||
|
return list()
|
||||||
|
|
||||||
|
def get_section_objects(self, path):
|
||||||
|
if not isinstance(path, list):
|
||||||
|
path = [path]
|
||||||
|
obj = self.get_object(path)
|
||||||
|
if not obj:
|
||||||
|
raise ValueError('path does not exist in config')
|
||||||
|
return self.expand_section(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def add(self, lines, parents=None):
|
||||||
|
"""Adds one or lines of configuration
|
||||||
|
"""
|
||||||
|
|
||||||
|
ancestors = list()
|
||||||
|
offset = 0
|
||||||
|
obj = None
|
||||||
|
|
||||||
|
## global config command
|
||||||
|
if not parents:
|
||||||
|
for line in to_list(lines):
|
||||||
|
item = ConfigLine(line)
|
||||||
|
item.raw = line
|
||||||
|
if item not in self.items:
|
||||||
|
self.items.append(item)
|
||||||
|
|
||||||
|
else:
|
||||||
|
for index, p in enumerate(parents):
|
||||||
|
try:
|
||||||
|
i = index + 1
|
||||||
|
obj = self.get_section_objects(parents[:i])[0]
|
||||||
|
ancestors.append(obj)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
# add parent to config
|
||||||
|
offset = index * self.indent
|
||||||
|
obj = ConfigLine(p)
|
||||||
|
obj.raw = p.rjust(len(p) + offset)
|
||||||
|
if ancestors:
|
||||||
|
obj.parents = list(ancestors)
|
||||||
|
ancestors[-1].children.append(obj)
|
||||||
|
self.items.append(obj)
|
||||||
|
ancestors.append(obj)
|
||||||
|
|
||||||
|
# add child objects
|
||||||
|
for line in to_list(lines):
|
||||||
|
# check if child already exists
|
||||||
|
for child in ancestors[-1].children:
|
||||||
|
if child.text == line:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
offset = len(parents) * self.indent
|
||||||
|
item = ConfigLine(line)
|
||||||
|
item.raw = line.rjust(len(line) + offset)
|
||||||
|
item.parents = ancestors
|
||||||
|
ancestors[-1].children.append(item)
|
||||||
|
self.items.append(item)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,80 +1,150 @@
|
||||||
#
|
#
|
||||||
# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
|
# This code is part of Ansible, but is an independent component.
|
||||||
#
|
#
|
||||||
# This file is part of Ansible
|
# This particular file snippet, and this file snippet only, is BSD licensed.
|
||||||
|
# Modules you write using this snippet, which is embedded dynamically by Ansible
|
||||||
|
# still belong to the author of the module, and may assign their own license
|
||||||
|
# to the complete work.
|
||||||
#
|
#
|
||||||
# Ansible is free software: you can redistribute it and/or modify
|
# (c) 2017 Red Hat, Inc.
|
||||||
# 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,
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# are permitted provided that the following conditions are met:
|
||||||
# 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
|
# * Redistributions of source code must retain the above copyright
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||||
|
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#
|
#
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from ansible.module_utils.basic import json, json_dict_bytes_to_unicode
|
from ansible.module_utils.basic import env_fallback
|
||||||
from ansible.module_utils.network import ModuleStub, NetworkError, NetworkModule
|
from ansible.module_utils.network_common import to_list, ComplexList
|
||||||
from ansible.module_utils.network import add_argument, register_transport, to_list
|
from ansible.module_utils.connection import exec_command
|
||||||
from ansible.module_utils.shell import CliBase
|
from ansible.module_utils.six import iteritems
|
||||||
from ansible.module_utils.urls import fetch_url, url_argument_spec
|
from ansible.module_utils.urls import fetch_url
|
||||||
|
|
||||||
add_argument('use_ssl', dict(default=False, type='bool'))
|
_DEVICE_CONNECTION = None
|
||||||
add_argument('validate_certs', dict(default=True, type='bool'))
|
|
||||||
|
|
||||||
class NxapiConfigMixin(object):
|
nxos_argument_spec = {
|
||||||
|
'host': dict(),
|
||||||
|
'port': dict(type='int'),
|
||||||
|
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
|
||||||
|
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
|
||||||
|
'use_ssl': dict(type='bool'),
|
||||||
|
'validate_certs': dict(type='bool'),
|
||||||
|
'timeout': dict(type='int'),
|
||||||
|
'provider': dict(type='dict'),
|
||||||
|
'transport': dict(choices=['cli', 'nxapi'])
|
||||||
|
}
|
||||||
|
|
||||||
def get_config(self, include_defaults=False, **kwargs):
|
def check_args(module, warnings):
|
||||||
|
provider = module.params['provider'] or {}
|
||||||
|
for key in nxos_argument_spec:
|
||||||
|
if key not in ['provider', 'transport'] and module.params[key]:
|
||||||
|
warnings.append('argument %s has been deprecated and will be '
|
||||||
|
'removed in a future version' % key)
|
||||||
|
|
||||||
|
def load_params(module):
|
||||||
|
provider = module.params.get('provider') or dict()
|
||||||
|
for key, value in iteritems(provider):
|
||||||
|
if key in nxos_argument_spec:
|
||||||
|
if module.params.get(key) is None and value is not None:
|
||||||
|
module.params[key] = value
|
||||||
|
|
||||||
|
def get_connection(module):
|
||||||
|
global _DEVICE_CONNECTION
|
||||||
|
if not _DEVICE_CONNECTION:
|
||||||
|
load_params(module)
|
||||||
|
transport = module.params['transport']
|
||||||
|
if transport == 'cli':
|
||||||
|
conn = Cli(module)
|
||||||
|
elif transport == 'nxapi':
|
||||||
|
conn = Nxapi(module)
|
||||||
|
_DEVICE_CONNECTION = conn
|
||||||
|
return _DEVICE_CONNECTION
|
||||||
|
|
||||||
|
class Cli:
|
||||||
|
|
||||||
|
def __init__(self, module):
|
||||||
|
self._module = module
|
||||||
|
self._device_configs = {}
|
||||||
|
|
||||||
|
def exec_command(self, command):
|
||||||
|
if isinstance(command, dict):
|
||||||
|
command = self._module.jsonify(command)
|
||||||
|
return exec_command(self._module, command)
|
||||||
|
|
||||||
|
def get_config(self, flags=[]):
|
||||||
|
"""Retrieves the current config from the device or cache
|
||||||
|
"""
|
||||||
cmd = 'show running-config '
|
cmd = 'show running-config '
|
||||||
if include_defaults:
|
cmd += ' '.join(flags)
|
||||||
cmd += ' all'
|
cmd = cmd.strip()
|
||||||
if isinstance(self, Nxapi):
|
|
||||||
return self.execute([cmd], output='text')[0]
|
try:
|
||||||
|
return self._device_configs[cmd]
|
||||||
|
except KeyError:
|
||||||
|
rc, out, err = self.exec_command(cmd)
|
||||||
|
if rc != 0:
|
||||||
|
self._module.fail_json(msg=err)
|
||||||
|
cfg = str(out).strip()
|
||||||
|
self._device_configs[cmd] = cfg
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def run_commands(self, commands, check_rc=True):
|
||||||
|
"""Run list of commands on remote device and return results
|
||||||
|
"""
|
||||||
|
responses = list()
|
||||||
|
|
||||||
|
for item in to_list(commands):
|
||||||
|
if item['output'] == 'json' and not is_json(item['command']):
|
||||||
|
cmd = '%s | json' % item['command']
|
||||||
|
elif item['output'] == 'text' and is_json(item['command']):
|
||||||
|
cmd = item['command'].split('|')[0]
|
||||||
else:
|
else:
|
||||||
return self.execute([cmd])[0]
|
cmd = item['command']
|
||||||
|
|
||||||
|
rc, out, err = self.exec_command(cmd)
|
||||||
|
|
||||||
|
if check_rc and rc != 0:
|
||||||
|
self._module.fail_json(msg=err)
|
||||||
|
|
||||||
|
try:
|
||||||
|
out = self._module.from_json(out)
|
||||||
|
except ValueError:
|
||||||
|
out = str(out).strip()
|
||||||
|
|
||||||
|
responses.append(out)
|
||||||
|
return responses
|
||||||
|
|
||||||
def load_config(self, config):
|
def load_config(self, config):
|
||||||
checkpoint = 'ansible_%s' % int(time.time())
|
"""Sends configuration commands to the remote device
|
||||||
try:
|
"""
|
||||||
self.execute(['checkpoint %s' % checkpoint], output='text')
|
rc, out, err = self.exec_command('configure')
|
||||||
except TypeError:
|
if rc != 0:
|
||||||
self.execute(['checkpoint %s' % checkpoint])
|
self._module.fail_json(msg='unable to enter configuration mode', output=err)
|
||||||
|
|
||||||
try:
|
for cmd in config:
|
||||||
self.configure(config)
|
rc, out, err = self.exec_command(cmd)
|
||||||
except NetworkError:
|
if rc != 0:
|
||||||
self.load_checkpoint(checkpoint)
|
self._module.fail_json(msg=err)
|
||||||
raise
|
|
||||||
|
|
||||||
try:
|
self.exec_command('end')
|
||||||
self.execute(['no checkpoint %s' % checkpoint], output='text')
|
|
||||||
except TypeError:
|
|
||||||
self.execute(['no checkpoint %s' % checkpoint])
|
|
||||||
|
|
||||||
def save_config(self, **kwargs):
|
class Nxapi:
|
||||||
try:
|
|
||||||
self.execute(['copy running-config startup-config'], output='text')
|
|
||||||
except TypeError:
|
|
||||||
self.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
def load_checkpoint(self, checkpoint):
|
|
||||||
try:
|
|
||||||
self.execute(['rollback running-config checkpoint %s' % checkpoint,
|
|
||||||
'no checkpoint %s' % checkpoint], output='text')
|
|
||||||
except TypeError:
|
|
||||||
self.execute(['rollback running-config checkpoint %s' % checkpoint,
|
|
||||||
'no checkpoint %s' % checkpoint])
|
|
||||||
|
|
||||||
|
|
||||||
class Nxapi(NxapiConfigMixin):
|
|
||||||
|
|
||||||
OUTPUT_TO_COMMAND_TYPE = {
|
OUTPUT_TO_COMMAND_TYPE = {
|
||||||
'text': 'cli_show_ascii',
|
'text': 'cli_show_ascii',
|
||||||
|
@ -83,20 +153,33 @@ class Nxapi(NxapiConfigMixin):
|
||||||
'config': 'cli_conf'
|
'config': 'cli_conf'
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, module):
|
||||||
self.url = None
|
self._module = module
|
||||||
self.url_args = ModuleStub(url_argument_spec(), self._error)
|
|
||||||
self._nxapi_auth = None
|
self._nxapi_auth = None
|
||||||
self.default_output = 'json'
|
self._device_configs = {}
|
||||||
self._connected = False
|
|
||||||
|
self._module.params['url_username'] = self._module.params['username']
|
||||||
|
self._module.params['url_password'] = self._module.params['password']
|
||||||
|
|
||||||
|
host = self._module.params['host']
|
||||||
|
port = self._module.params['port']
|
||||||
|
|
||||||
|
if self._module.params['use_ssl']:
|
||||||
|
proto = 'https'
|
||||||
|
port = port or 443
|
||||||
|
else:
|
||||||
|
proto = 'http'
|
||||||
|
port = port or 80
|
||||||
|
|
||||||
|
self._url = '%s://%s:%s/ins' % (proto, host, port)
|
||||||
|
|
||||||
def _error(self, msg, **kwargs):
|
def _error(self, msg, **kwargs):
|
||||||
self._nxapi_auth = None
|
self._nxapi_auth = None
|
||||||
if 'url' not in kwargs:
|
if 'url' not in kwargs:
|
||||||
kwargs['url'] = self.url
|
kwargs['url'] = self._url
|
||||||
raise NetworkError(msg, **kwargs)
|
self._module.fail_json(msg=msg, **kwargs)
|
||||||
|
|
||||||
def _get_body(self, commands, output, version='1.0', chunk='0', sid=None):
|
def _request_builder(self, commands, output, version='1.0', chunk='0', sid=None):
|
||||||
"""Encodes a NXAPI JSON request message
|
"""Encodes a NXAPI JSON request message
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
@ -120,64 +203,41 @@ class Nxapi(NxapiConfigMixin):
|
||||||
|
|
||||||
return dict(ins_api=msg)
|
return dict(ins_api=msg)
|
||||||
|
|
||||||
def connect(self, params, **kwargs):
|
def send_request(self, commands, output='text'):
|
||||||
host = params['host']
|
# only 10 show commands can be encoded in each request
|
||||||
port = params['port']
|
|
||||||
|
|
||||||
# sets the module_utils/urls.py req parameters
|
|
||||||
self.url_args.params['url_username'] = params['username']
|
|
||||||
self.url_args.params['url_password'] = params['password']
|
|
||||||
self.url_args.params['validate_certs'] = params['validate_certs']
|
|
||||||
self.url_args.params['timeout'] = params['timeout']
|
|
||||||
|
|
||||||
if params['use_ssl']:
|
|
||||||
proto = 'https'
|
|
||||||
port = port or 443
|
|
||||||
else:
|
|
||||||
proto = 'http'
|
|
||||||
port = port or 80
|
|
||||||
|
|
||||||
self.url = '%s://%s:%s/ins' % (proto, host, port)
|
|
||||||
self._connected = True
|
|
||||||
|
|
||||||
def disconnect(self, **kwargs):
|
|
||||||
self.url = None
|
|
||||||
self._nxapi_auth = None
|
|
||||||
self._connected = False
|
|
||||||
|
|
||||||
### Command methods ###
|
|
||||||
|
|
||||||
def execute(self, commands, output=None, **kwargs):
|
|
||||||
commands = collections.deque(commands)
|
|
||||||
output = output or self.default_output
|
|
||||||
|
|
||||||
# only 10 commands can be encoded in each request
|
|
||||||
# messages sent to the remote device
|
# messages sent to the remote device
|
||||||
|
if output != 'config':
|
||||||
|
commands = collections.deque(commands)
|
||||||
stack = list()
|
stack = list()
|
||||||
requests = list()
|
requests = list()
|
||||||
|
|
||||||
while commands:
|
while commands:
|
||||||
stack.append(commands.popleft())
|
stack.append(commands.popleft())
|
||||||
if len(stack) == 10:
|
if len(stack) == 10:
|
||||||
body = self._get_body(stack, output)
|
body = self._request_builder(stack, output)
|
||||||
data = self._jsonify(body)
|
data = self._module.jsonify(body)
|
||||||
requests.append(data)
|
requests.append(data)
|
||||||
stack = list()
|
stack = list()
|
||||||
|
|
||||||
if stack:
|
if stack:
|
||||||
body = self._get_body(stack, output)
|
body = self._request_builder(stack, output)
|
||||||
data = self._jsonify(body)
|
data = self._module.jsonify(body)
|
||||||
requests.append(data)
|
requests.append(data)
|
||||||
|
|
||||||
|
else:
|
||||||
|
requests = commands
|
||||||
|
|
||||||
headers = {'Content-Type': 'application/json'}
|
headers = {'Content-Type': 'application/json'}
|
||||||
result = list()
|
result = list()
|
||||||
timeout = self.url_args.params['timeout']
|
timeout = self._module.params['timeout'] or 10
|
||||||
|
|
||||||
for req in requests:
|
for req in requests:
|
||||||
if self._nxapi_auth:
|
if self._nxapi_auth:
|
||||||
headers['Cookie'] = self._nxapi_auth
|
headers['Cookie'] = self._nxapi_auth
|
||||||
|
|
||||||
response, headers = fetch_url(
|
response, headers = fetch_url(
|
||||||
self.url_args, self.url, data=data, headers=headers, timeout=timeout, method='POST'
|
self._module, self._url, data=data, headers=headers,
|
||||||
|
timeout=timeout, method='POST'
|
||||||
)
|
)
|
||||||
self._nxapi_auth = headers.get('set-cookie')
|
self._nxapi_auth = headers.get('set-cookie')
|
||||||
|
|
||||||
|
@ -185,9 +245,9 @@ class Nxapi(NxapiConfigMixin):
|
||||||
self._error(**headers)
|
self._error(**headers)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = json.loads(response.read())
|
response = self._module.from_json(response.read())
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise NetworkError(msg='unable to load response from device')
|
self._module.fail_json(msg='unable to parse response')
|
||||||
|
|
||||||
output = response['ins_api']['outputs']['output']
|
output = response['ins_api']['outputs']['output']
|
||||||
for item in to_list(output):
|
for item in to_list(output):
|
||||||
|
@ -198,115 +258,96 @@ class Nxapi(NxapiConfigMixin):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def run_commands(self, commands, **kwargs):
|
|
||||||
|
def get_config(self, flags=[]):
|
||||||
|
"""Retrieves the current config from the device or cache
|
||||||
|
"""
|
||||||
|
cmd = 'show running-config '
|
||||||
|
cmd += ' '.join(flags)
|
||||||
|
cmd = cmd.strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self._device_configs[cmd]
|
||||||
|
except KeyError:
|
||||||
|
out = self.send_request(cmd)
|
||||||
|
cfg = str(out['result'][0]['output']).strip()
|
||||||
|
self._device_configs[cmd] = cfg
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
|
||||||
|
def run_commands(self, commands, check_rc=True):
|
||||||
|
"""Run list of commands on remote device and return results
|
||||||
|
"""
|
||||||
output = None
|
output = None
|
||||||
cmds = list()
|
queue = list()
|
||||||
responses = list()
|
responses = list()
|
||||||
|
|
||||||
for cmd in commands:
|
_send = lambda commands, output: self.send_request(commands, output)
|
||||||
if output and output != cmd.output:
|
|
||||||
responses.extend(self.execute(cmds, output=output))
|
|
||||||
cmds = list()
|
|
||||||
|
|
||||||
output = cmd.output
|
for item in to_list(commands):
|
||||||
cmds.append(str(cmd))
|
if is_json(item['command']):
|
||||||
|
item['command'] = str(item['command']).split('|')[0]
|
||||||
|
item['output'] = 'json'
|
||||||
|
|
||||||
if cmds:
|
if all((output == 'json', item['output'] == 'text')) or all((output =='text', item['output'] == 'json')):
|
||||||
responses.extend(self.execute(cmds, output=output))
|
responses.extend(_send(queue, output))
|
||||||
|
queue = list()
|
||||||
|
|
||||||
|
output = item['output'] or 'json'
|
||||||
|
queue.append(item['command'])
|
||||||
|
|
||||||
|
if queue:
|
||||||
|
responses.extend(_send(queue, output))
|
||||||
|
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
|
def load_config(self, config):
|
||||||
### Config methods ###
|
"""Sends the ordered set of commands to the device
|
||||||
|
"""
|
||||||
def configure(self, commands):
|
cmds = ['configure terminal']
|
||||||
commands = to_list(commands)
|
cmds.extend(commands)
|
||||||
return self.execute(commands, output='config')
|
self.send_request(commands, output='config')
|
||||||
|
|
||||||
def _jsonify(self, data):
|
|
||||||
for encoding in ("utf-8", "latin-1"):
|
|
||||||
try:
|
|
||||||
return json.dumps(data, encoding=encoding)
|
|
||||||
# Old systems using old simplejson module does not support encoding keyword.
|
|
||||||
except TypeError:
|
|
||||||
try:
|
|
||||||
new_data = json_dict_bytes_to_unicode(data, encoding=encoding)
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
continue
|
|
||||||
return json.dumps(new_data)
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
continue
|
|
||||||
self._error(msg='Invalid unicode encoding encountered')
|
|
||||||
|
|
||||||
Nxapi = register_transport('nxapi')(Nxapi)
|
|
||||||
|
|
||||||
|
|
||||||
class Cli(NxapiConfigMixin, CliBase):
|
is_json = lambda x: str(x).endswith('| json')
|
||||||
|
is_text = lambda x: not is_json
|
||||||
|
|
||||||
CLI_PROMPTS_RE = [
|
def is_nxapi(module):
|
||||||
re.compile(r'[\r\n]?[a-zA-Z]{1}[a-zA-Z0-9-]*[>|#|%](?:\s*)$'),
|
transport = module.params['transport']
|
||||||
re.compile(r'[\r\n]?[a-zA-Z]{1}[a-zA-Z0-9-]*\(.+\)#(?:\s*)$')
|
provider_transport = (module.params['provider'] or {}).get('transport')
|
||||||
]
|
return 'nxapi' in (transport, provider_transport)
|
||||||
|
|
||||||
CLI_ERRORS_RE = [
|
def to_command(module, commands):
|
||||||
re.compile(r"% ?Error"),
|
if is_nxapi(module):
|
||||||
re.compile(r"^% \w+", re.M),
|
default_output = 'json'
|
||||||
re.compile(r"% ?Bad secret"),
|
else:
|
||||||
re.compile(r"invalid input", re.I),
|
default_output = 'text'
|
||||||
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+"),
|
|
||||||
re.compile(r"syntax error"),
|
|
||||||
re.compile(r"unknown command")
|
|
||||||
]
|
|
||||||
|
|
||||||
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
|
transform = ComplexList(dict(
|
||||||
|
command=dict(key=True),
|
||||||
|
output=dict(default=default_output),
|
||||||
|
prompt=dict(),
|
||||||
|
response=dict()
|
||||||
|
), module)
|
||||||
|
|
||||||
def connect(self, params, **kwargs):
|
commands = transform(to_list(commands))
|
||||||
super(Cli, self).connect(params, kickstart=False, **kwargs)
|
|
||||||
self.shell.send('terminal length 0')
|
|
||||||
|
|
||||||
### Command methods ###
|
for index, item in enumerate(commands):
|
||||||
|
if is_json(item['command']):
|
||||||
|
item['output'] = 'json'
|
||||||
|
elif is_text(item['command']):
|
||||||
|
item['output'] = 'text'
|
||||||
|
|
||||||
def run_commands(self, commands):
|
def get_config(module, flags=[]):
|
||||||
cmds = list(prepare_commands(commands))
|
conn = get_connection(module)
|
||||||
responses = self.execute(cmds)
|
return conn.get_config(flags)
|
||||||
for index, cmd in enumerate(commands):
|
|
||||||
raw = cmd.args.get('raw') or False
|
|
||||||
if cmd.output == 'json' and not raw:
|
|
||||||
try:
|
|
||||||
responses[index] = json.loads(responses[index])
|
|
||||||
except ValueError:
|
|
||||||
raise NetworkError(
|
|
||||||
msg='unable to load response from device',
|
|
||||||
response=responses[index], command=str(cmd)
|
|
||||||
)
|
|
||||||
return responses
|
|
||||||
|
|
||||||
### Config methods ###
|
def run_commands(module, commands, check_rc=True):
|
||||||
|
conn = get_connection(module)
|
||||||
|
to_command(module, commands)
|
||||||
|
return conn.run_commands(commands)
|
||||||
|
|
||||||
def configure(self, commands, **kwargs):
|
def load_config(module, config):
|
||||||
commands = prepare_config(commands)
|
conn = get_connection(module)
|
||||||
responses = self.execute(commands)
|
return conn.load_config(config)
|
||||||
responses.pop(0)
|
|
||||||
return responses
|
|
||||||
|
|
||||||
Cli = register_transport('cli', default=True)(Cli)
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_config(commands):
|
|
||||||
prepared = ['config']
|
|
||||||
prepared.extend(to_list(commands))
|
|
||||||
prepared.append('end')
|
|
||||||
return prepared
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_commands(commands):
|
|
||||||
jsonify = lambda x: '%s | json' % x
|
|
||||||
for cmd in to_list(commands):
|
|
||||||
if cmd.output == 'json':
|
|
||||||
cmd.command_string = jsonify(cmd)
|
|
||||||
if cmd.command.endswith('| json'):
|
|
||||||
cmd.output = 'json'
|
|
||||||
yield cmd
|
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
#
|
|
||||||
# This code is part of Ansible, but is an independent component.
|
|
||||||
#
|
|
||||||
# This particular file snippet, and this file snippet only, is BSD licensed.
|
|
||||||
# Modules you write using this snippet, which is embedded dynamically by Ansible
|
|
||||||
# still belong to the author of the module, and may assign their own license
|
|
||||||
# to the complete work.
|
|
||||||
#
|
|
||||||
# (c) 2017 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
# are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
|
||||||
# and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
||||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.shell import CliBase
|
|
||||||
from ansible.module_utils.basic import env_fallback, get_exception
|
|
||||||
from ansible.module_utils.network_common import to_list
|
|
||||||
from ansible.module_utils.netcli import Command
|
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
from ansible.module_utils.network import NetworkError
|
|
||||||
|
|
||||||
_DEVICE_CONFIGS = {}
|
|
||||||
_DEVICE_CONNECTION = None
|
|
||||||
|
|
||||||
nxos_cli_argument_spec = {
|
|
||||||
'host': dict(),
|
|
||||||
'port': dict(type='int'),
|
|
||||||
|
|
||||||
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
|
|
||||||
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
|
|
||||||
|
|
||||||
'authorize': dict(default=False, fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']), type='bool'),
|
|
||||||
'auth_pass': dict(no_log=True, fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS'])),
|
|
||||||
|
|
||||||
'timeout': dict(type='int', default=10),
|
|
||||||
|
|
||||||
'provider': dict(type='dict'),
|
|
||||||
|
|
||||||
# deprecated in Ansible 2.3
|
|
||||||
'transport': dict(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def check_args(module, warnings):
|
|
||||||
provider = module.params['provider'] or {}
|
|
||||||
for key in ('host', 'username', 'password'):
|
|
||||||
if not module.params[key] and not provider.get(key):
|
|
||||||
module.fail_json(msg='missing required argument %s' % key)
|
|
||||||
|
|
||||||
class Cli(CliBase):
|
|
||||||
|
|
||||||
CLI_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-]*\(.+\)#(?:\s*)$')
|
|
||||||
]
|
|
||||||
|
|
||||||
CLI_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+"),
|
|
||||||
re.compile(r"syntax error"),
|
|
||||||
re.compile(r"unknown command")
|
|
||||||
]
|
|
||||||
|
|
||||||
NET_PASSWD_RE = re.compile(r"[\r\n]?password: $", re.I)
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
self._module = module
|
|
||||||
super(Cli, self).__init__()
|
|
||||||
|
|
||||||
provider = self._module.params.get('provider') or dict()
|
|
||||||
for key, value in iteritems(provider):
|
|
||||||
if key in nxos_cli_argument_spec:
|
|
||||||
if self._module.params.get(key) is None and value is not None:
|
|
||||||
self._module.params[key] = value
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.connect()
|
|
||||||
except NetworkError:
|
|
||||||
exc = get_exception()
|
|
||||||
self._module.fail_json(msg=str(exc))
|
|
||||||
|
|
||||||
if module.params['authorize']:
|
|
||||||
self.authorize()
|
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
super(Cli, self).connect(self._module.params, kickstart=False)
|
|
||||||
self.shell.send('terminal length 0')
|
|
||||||
|
|
||||||
|
|
||||||
def connection(module):
|
|
||||||
global _DEVICE_CONNECTION
|
|
||||||
if not _DEVICE_CONNECTION:
|
|
||||||
cli = Cli(module)
|
|
||||||
_DEVICE_CONNECTION = cli
|
|
||||||
return _DEVICE_CONNECTION
|
|
||||||
|
|
||||||
|
|
||||||
def get_config(module, flags=[]):
|
|
||||||
cmd = 'show running-config '
|
|
||||||
cmd += ' '.join(flags)
|
|
||||||
cmd = cmd.strip()
|
|
||||||
|
|
||||||
try:
|
|
||||||
return _DEVICE_CONFIGS[cmd]
|
|
||||||
except KeyError:
|
|
||||||
conn = connection(module)
|
|
||||||
out = conn.exec_command(cmd)
|
|
||||||
cfg = str(out).strip()
|
|
||||||
_DEVICE_CONFIGS[cmd] = cfg
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
def run_commands(module, commands, check_rc=True):
|
|
||||||
responses = list()
|
|
||||||
conn = connection(module)
|
|
||||||
for cmd in to_list(commands):
|
|
||||||
rc, out, err = conn.exec_command(cmd)
|
|
||||||
if check_rc and rc != 0:
|
|
||||||
module.fail_json(msg=err, rc=rc)
|
|
||||||
responses.append(out)
|
|
||||||
return responses
|
|
||||||
|
|
||||||
def load_config(module, commands):
|
|
||||||
conn = connection(module)
|
|
||||||
rc, out, err = conn.exec_command('configure')
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(msg='unable to enter configuration mode', err=err)
|
|
||||||
|
|
||||||
for command in to_list(commands):
|
|
||||||
if command == 'end':
|
|
||||||
continue
|
|
||||||
rc, out, err = module.exec_command(command)
|
|
||||||
if rc != 0:
|
|
||||||
module.fail_json(msg=err, command=command, rc=rc)
|
|
||||||
|
|
||||||
conn.exec_command('end')
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages AAA server global configuration.
|
short_description: Manages AAA server global configuration.
|
||||||
description:
|
description:
|
||||||
- Manages AAA server global configuration
|
- Manages AAA server global configuration
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -155,220 +154,19 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.six import iteritems
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
body = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
@ -490,9 +288,16 @@ def main():
|
||||||
choices=['enabled', 'disabled', 'default']),
|
choices=['enabled', 'disabled', 'default']),
|
||||||
state=dict(choices=['default', 'present'], default='present'),
|
state=dict(choices=['default', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
server_type = module.params['server_type']
|
server_type = module.params['server_type']
|
||||||
global_key = module.params['global_key']
|
global_key = module.params['global_key']
|
||||||
encrypt_type = module.params['encrypt_type']
|
encrypt_type = module.params['encrypt_type']
|
||||||
|
@ -555,7 +360,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_aaa_server_info(server_type, module)
|
end_state = get_aaa_server_info(server_type, module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -565,6 +370,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -572,3 +378,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages AAA server host-specific configuration.
|
short_description: Manages AAA server host-specific configuration.
|
||||||
description:
|
description:
|
||||||
- Manages AAA server host-specific configuration.
|
- Manages AAA server host-specific configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author: Jason Edelman (@jedelman8)
|
author: Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
- Changes to the AAA server host key (shared secret) are not idempotent.
|
- Changes to the AAA server host key (shared secret) are not idempotent.
|
||||||
|
@ -150,236 +149,11 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
if isinstance(response[0], str):
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
else:
|
|
||||||
body = response
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -387,11 +161,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -495,9 +268,16 @@ def main():
|
||||||
tacacs_port=dict(type='str'),
|
tacacs_port=dict(type='str'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
server_type = module.params['server_type']
|
server_type = module.params['server_type']
|
||||||
address = module.params['address']
|
address = module.params['address']
|
||||||
key = module.params['key']
|
key = module.params['key']
|
||||||
|
@ -565,7 +345,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_aaa_host_info(module, server_type, address)
|
end_state = get_aaa_host_info(module, server_type, address)
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
|
@ -573,6 +353,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -580,3 +361,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages access list entries for ACLs.
|
short_description: Manages access list entries for ACLs.
|
||||||
description:
|
description:
|
||||||
- Manages access list entries for ACLs.
|
- Manages access list entries for ACLs.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -241,227 +240,21 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import collections
|
|
||||||
import json
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, we assume if '^' is found in response,
|
|
||||||
it is an invalid command.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif '^' in response[0]:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
@ -621,24 +414,6 @@ def flatten_list(command_lists):
|
||||||
return flat_command_list
|
return flat_command_list
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
seq=dict(required=False, type='str'),
|
seq=dict(required=False, type='str'),
|
||||||
|
@ -685,9 +460,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
action = module.params['action']
|
action = module.params['action']
|
||||||
remark = module.params['remark']
|
remark = module.params['remark']
|
||||||
|
@ -797,7 +579,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
new_existing_core, end_state, seqs = get_acl(module, name, seq)
|
new_existing_core, end_state, seqs = get_acl(module, name, seq)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
|
@ -806,6 +588,7 @@ def main():
|
||||||
results['proposed'] = proposed
|
results['proposed'] = proposed
|
||||||
results['existing'] = existing_core
|
results['existing'] = existing_core
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
|
@ -814,3 +597,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages applying ACLs to interfaces.
|
short_description: Manages applying ACLs to interfaces.
|
||||||
description:
|
description:
|
||||||
- Manages applying ACLs to interfaces.
|
- Manages applying ACLs to interfaces.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -99,216 +98,11 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import collections
|
|
||||||
import json
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, we assume if '^' is found in response,
|
|
||||||
it is an invalid command.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif '^' in response[0] or 'summary' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -316,11 +110,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'summary' not in command:
|
if 'summary' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -446,24 +239,6 @@ def flatten_list(command_lists):
|
||||||
return flat_command_list
|
return flat_command_list
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
name=dict(required=False, type='str'),
|
name=dict(required=False, type='str'),
|
||||||
|
@ -475,9 +250,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
name = module.params['name']
|
name = module.params['name']
|
||||||
interface = module.params['interface'].lower()
|
interface = module.params['interface'].lower()
|
||||||
|
@ -517,7 +299,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
end_state_acls = get_acl_interface(module, name)
|
end_state_acls = get_acl_interface(module, name)
|
||||||
interfaces_acls, this_dir_acl_intf = other_existing_acl(
|
interfaces_acls, this_dir_acl_intf = other_existing_acl(
|
||||||
|
@ -533,6 +315,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['acl_applied_to'] = end_state_acls
|
results['acl_applied_to'] = end_state_acls
|
||||||
|
|
||||||
|
@ -541,3 +324,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ description:
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- C(state=absent) removes the whole BGP ASN configuration when
|
- C(state=absent) removes the whole BGP ASN configuration when
|
||||||
C(vrf=default) or the whole VRF instance within the BGP process when
|
C(vrf=default) or the whole VRF instance within the BGP process when
|
||||||
|
@ -361,156 +360,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
WARNINGS = []
|
WARNINGS = []
|
||||||
|
@ -902,11 +756,18 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_together=[['timer_bgp_hold',
|
required_together=[['timer_bgp_hold',
|
||||||
'timer_bgp_keepalive']],
|
'timer_bgp_keepalive']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
args = [
|
args = [
|
||||||
"asn",
|
"asn",
|
||||||
|
@ -1010,3 +871,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages BGP Address-family configuration.
|
||||||
description:
|
description:
|
||||||
- Manages BGP Address-family configurations on NX-OS switches.
|
- Manages BGP Address-family configurations on NX-OS switches.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- C(state=absent) removes the whole BGP ASN configuration
|
- C(state=absent) removes the whole BGP ASN configuration
|
||||||
- Default, where supported, restores params default value.
|
- Default, where supported, restores params default value.
|
||||||
|
@ -300,157 +299,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
WARNINGS = []
|
WARNINGS = []
|
||||||
BOOL_PARAMS = [
|
BOOL_PARAMS = [
|
||||||
|
@ -992,13 +845,20 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_together=[DAMPENING_PARAMS,
|
required_together=[DAMPENING_PARAMS,
|
||||||
['distance_ibgp',
|
['distance_ibgp',
|
||||||
'distance_ebgp',
|
'distance_ebgp',
|
||||||
'distance_local']],
|
'distance_local']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
if module.params['dampening_routemap']:
|
if module.params['dampening_routemap']:
|
||||||
for param in DAMPENING_PARAMS:
|
for param in DAMPENING_PARAMS:
|
||||||
|
@ -1108,3 +968,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages BGP neighbors configurations.
|
||||||
description:
|
description:
|
||||||
- Manages BGP neighbors configurations on NX-OS switches.
|
- Manages BGP neighbors configurations on NX-OS switches.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- C(state=absent) removes the whole BGP neighbor configuration.
|
- C(state=absent) removes the whole BGP neighbor configuration.
|
||||||
- Default, where supported, restores params default value.
|
- Default, where supported, restores params default value.
|
||||||
|
@ -245,156 +244,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
WARNINGS = []
|
WARNINGS = []
|
||||||
|
@ -682,11 +536,18 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_together=[['timer_bgp_hold',
|
required_together=[['timer_bgp_hold',
|
||||||
'timer_bgp_keepalive']],
|
'timer_bgp_keepalive']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
if module.params['pwd_type'] == 'default':
|
if module.params['pwd_type'] == 'default':
|
||||||
module.params['pwd_type'] = '0'
|
module.params['pwd_type'] = '0'
|
||||||
|
@ -767,3 +628,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages BGP address-family's neighbors configuration.
|
||||||
description:
|
description:
|
||||||
- Manages BGP address-family's neighbors configurations on NX-OS switches.
|
- Manages BGP address-family's neighbors configurations on NX-OS switches.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- C(state=absent) removes the whole BGP address-family's
|
- C(state=absent) removes the whole BGP address-family's
|
||||||
neighbor configuration.
|
neighbor configuration.
|
||||||
|
@ -322,156 +321,11 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
WARNINGS = []
|
WARNINGS = []
|
||||||
BOOL_PARAMS = [
|
BOOL_PARAMS = [
|
||||||
|
@ -1004,11 +858,18 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['advertise_map_exist',
|
mutually_exclusive=[['advertise_map_exist',
|
||||||
'advertise_map_non_exist']],
|
'advertise_map_non_exist']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
if ((module.params['max_prefix_interval'] or
|
if ((module.params['max_prefix_interval'] or
|
||||||
module.params['max_prefix_warning'] or
|
module.params['max_prefix_warning'] or
|
||||||
|
@ -1120,3 +981,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'status': ['preview'],
|
ANSIBLE_METADATA = {
|
||||||
|
'status': ['preview'],
|
||||||
'supported_by': 'core',
|
'supported_by': 'core',
|
||||||
'version': '1.0'}
|
'version': '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = """
|
||||||
---
|
---
|
||||||
|
@ -31,7 +33,6 @@ description:
|
||||||
read from the device. This module includes an
|
read from the device. This module includes an
|
||||||
argument that will cause the module to wait for a specific condition
|
argument that will cause the module to wait for a specific condition
|
||||||
before returning or timing out if the condition is not met.
|
before returning or timing out if the condition is not met.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
commands:
|
commands:
|
||||||
description:
|
description:
|
||||||
|
@ -152,37 +153,53 @@ failed_conditions:
|
||||||
type: list
|
type: list
|
||||||
sample: ['...', '...']
|
sample: ['...', '...']
|
||||||
"""
|
"""
|
||||||
import ansible.module_utils.nxos
|
import time
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import run_commands
|
||||||
from ansible.module_utils.network import NetworkModule, NetworkError
|
from ansible.module_utils.pycompat24 import get_exception
|
||||||
from ansible.module_utils.netcli import CommandRunner
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcli import FailedConditionsError
|
from ansible.module_utils.six import string_types
|
||||||
from ansible.module_utils.netcli import FailedConditionalError
|
from ansible.module_utils.netcli import Conditional
|
||||||
from ansible.module_utils.netcli import AddCommandError, AddConditionError
|
from ansible.module_utils.network_common import ComplexList
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
VALID_KEYS = ['command', 'output', 'prompt', 'response']
|
|
||||||
|
|
||||||
def to_lines(stdout):
|
def to_lines(stdout):
|
||||||
|
lines = list()
|
||||||
for item in stdout:
|
for item in stdout:
|
||||||
if isinstance(item, basestring):
|
if isinstance(item, basestring):
|
||||||
item = str(item).split('\n')
|
item = str(item).split('\n')
|
||||||
yield item
|
lines.append(item)
|
||||||
|
return lines
|
||||||
|
|
||||||
def parse_commands(module):
|
def parse_commands(module, warnings):
|
||||||
for cmd in module.params['commands']:
|
transform = ComplexList(dict(
|
||||||
if isinstance(cmd, basestring):
|
command=dict(key=True),
|
||||||
cmd = dict(command=cmd, output=None)
|
output=dict(),
|
||||||
elif 'command' not in cmd:
|
prompt=dict(),
|
||||||
module.fail_json(msg='command keyword argument is required')
|
response=dict()
|
||||||
elif cmd.get('output') not in [None, 'text', 'json']:
|
), module)
|
||||||
module.fail_json(msg='invalid output specified for command')
|
|
||||||
elif not set(cmd.keys()).issubset(VALID_KEYS):
|
commands = transform(module.params['commands'])
|
||||||
module.fail_json(msg='unknown keyword specified')
|
|
||||||
yield cmd
|
for index, item in enumerate(commands):
|
||||||
|
if module.check_mode and not item['command'].startswith('show'):
|
||||||
|
warnings.append(
|
||||||
|
'Only show commands are supported when using check_mode, not '
|
||||||
|
'executing %s' % item['command']
|
||||||
|
)
|
||||||
|
|
||||||
|
return commands
|
||||||
|
|
||||||
|
def to_cli(obj):
|
||||||
|
cmd = obj['command']
|
||||||
|
if obj.get('output') == 'json':
|
||||||
|
cmd += ' | json'
|
||||||
|
return cmd
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
spec = dict(
|
"""entry point for module execution
|
||||||
|
"""
|
||||||
|
argument_spec = dict(
|
||||||
# { command: <str>, output: <str>, prompt: <str>, response: <str> }
|
# { command: <str>, output: <str>, prompt: <str>, response: <str> }
|
||||||
commands=dict(type='list', required=True),
|
commands=dict(type='list', required=True),
|
||||||
|
|
||||||
|
@ -193,66 +210,56 @@ def main():
|
||||||
interval=dict(default=1, type='int')
|
interval=dict(default=1, type='int')
|
||||||
)
|
)
|
||||||
|
|
||||||
module = NetworkModule(argument_spec=spec,
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
commands = list(parse_commands(module))
|
|
||||||
conditionals = module.params['wait_for'] or list()
|
result = {'changed': False}
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
runner = CommandRunner(module)
|
commands = parse_commands(module, warnings)
|
||||||
|
|
||||||
for cmd in commands:
|
|
||||||
if module.check_mode and not cmd['command'].startswith('show'):
|
|
||||||
warnings.append('only show commands are supported when using '
|
|
||||||
'check mode, not executing `%s`' % cmd['command'])
|
|
||||||
else:
|
|
||||||
if cmd['command'].startswith('conf'):
|
|
||||||
module.fail_json(msg='nxos_command does not support running '
|
|
||||||
'config mode commands. Please use '
|
|
||||||
'nxos_config instead')
|
|
||||||
try:
|
|
||||||
runner.add_command(**cmd)
|
|
||||||
except AddCommandError:
|
|
||||||
exc = get_exception()
|
|
||||||
warnings.append('duplicate command detected: %s' % cmd)
|
|
||||||
|
|
||||||
try:
|
|
||||||
for item in conditionals:
|
|
||||||
runner.add_conditional(item)
|
|
||||||
except AddConditionError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc), condition=exc.condition)
|
|
||||||
|
|
||||||
runner.retries = module.params['retries']
|
|
||||||
runner.interval = module.params['interval']
|
|
||||||
runner.match = module.params['match']
|
|
||||||
|
|
||||||
try:
|
|
||||||
runner.run()
|
|
||||||
except FailedConditionsError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)
|
|
||||||
except FailedConditionalError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc), failed_conditional=exc.failed_conditional)
|
|
||||||
except NetworkError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc), **exc.kwargs)
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
result['stdout'] = list()
|
|
||||||
for cmd in commands:
|
|
||||||
try:
|
|
||||||
output = runner.get_command(cmd['command'], cmd.get('output'))
|
|
||||||
except ValueError:
|
|
||||||
output = 'command not executed due to check_mode, see warnings'
|
|
||||||
result['stdout'].append(output)
|
|
||||||
|
|
||||||
result['warnings'] = warnings
|
result['warnings'] = warnings
|
||||||
result['stdout_lines'] = list(to_lines(result['stdout']))
|
|
||||||
|
wait_for = module.params['wait_for'] or list()
|
||||||
|
|
||||||
|
try:
|
||||||
|
conditionals = [Conditional(c) for c in wait_for]
|
||||||
|
except AttributeError:
|
||||||
|
exc = get_exception()
|
||||||
|
module.fail_json(msg=str(exc))
|
||||||
|
|
||||||
|
retries = module.params['retries']
|
||||||
|
interval = module.params['interval']
|
||||||
|
match = module.params['match']
|
||||||
|
|
||||||
|
while retries > 0:
|
||||||
|
responses = run_commands(module, commands)
|
||||||
|
|
||||||
|
for item in list(conditionals):
|
||||||
|
if item(responses):
|
||||||
|
if match == 'any':
|
||||||
|
conditionals = list()
|
||||||
|
break
|
||||||
|
conditionals.remove(item)
|
||||||
|
|
||||||
|
if not conditionals:
|
||||||
|
break
|
||||||
|
|
||||||
|
time.sleep(interval)
|
||||||
|
retries -= 1
|
||||||
|
|
||||||
|
if conditionals:
|
||||||
|
failed_conditions = [item.raw for item in conditionals]
|
||||||
|
msg = 'One or more conditional statements have not be satisfied'
|
||||||
|
module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||||
|
|
||||||
|
result.update({
|
||||||
|
'stdout': responses,
|
||||||
|
'stdout_lines': to_lines(responses)
|
||||||
|
})
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'status': ['preview'],
|
ANSIBLE_METADATA = {
|
||||||
|
'status': ['preview'],
|
||||||
'supported_by': 'core',
|
'supported_by': 'core',
|
||||||
'version': '1.0'}
|
'version': '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = """
|
||||||
---
|
---
|
||||||
|
@ -32,7 +34,6 @@ description:
|
||||||
an implementation for working with NXOS configuration sections in
|
an implementation for working with NXOS configuration sections in
|
||||||
a deterministic way. This module works with either CLI or NXAPI
|
a deterministic way. This module works with either CLI or NXAPI
|
||||||
transports.
|
transports.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
lines:
|
lines:
|
||||||
description:
|
description:
|
||||||
|
@ -212,18 +213,28 @@ backup_path:
|
||||||
type: path
|
type: path
|
||||||
sample: /playbooks/ansible/backup/nxos_config.2016-07-16@22:28:34
|
sample: /playbooks/ansible/backup/nxos_config.2016-07-16@22:28:34
|
||||||
"""
|
"""
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
import ansible.module_utils.nxos
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.network import NetworkModule, NetworkError
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, dumps
|
from ansible.module_utils.netcfg import NetworkConfig, dumps
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec
|
||||||
|
from ansible.module_utils.nxos import check_args as nxos_check_args
|
||||||
|
|
||||||
def check_args(module, warnings):
|
def check_args(module, warnings):
|
||||||
|
nxos_check_args(module, warnings)
|
||||||
if module.params['force']:
|
if module.params['force']:
|
||||||
warnings.append('The force argument is deprecated, please use '
|
warnings.append('The force argument is deprecated, please use '
|
||||||
'match=none instead. This argument will be '
|
'match=none instead. This argument will be '
|
||||||
'removed in the future')
|
'removed in the future')
|
||||||
|
|
||||||
|
def get_running_config(module):
|
||||||
|
contents = module.params['config']
|
||||||
|
if not contents:
|
||||||
|
flags = []
|
||||||
|
if module.params['defaults']:
|
||||||
|
flags.append('all')
|
||||||
|
contents = get_config(module, flags=flags)
|
||||||
|
return NetworkConfig(indent=1, contents=contents)
|
||||||
|
|
||||||
def get_candidate(module):
|
def get_candidate(module):
|
||||||
candidate = NetworkConfig(indent=2)
|
candidate = NetworkConfig(indent=2)
|
||||||
if module.params['src']:
|
if module.params['src']:
|
||||||
|
@ -233,13 +244,6 @@ def get_candidate(module):
|
||||||
candidate.add(module.params['lines'], parents=parents)
|
candidate.add(module.params['lines'], parents=parents)
|
||||||
return candidate
|
return candidate
|
||||||
|
|
||||||
def get_config(module):
|
|
||||||
contents = module.params['config']
|
|
||||||
if not contents:
|
|
||||||
defaults = module.params['defaults']
|
|
||||||
contents = module.config.get_config(include_defaults=defaults)
|
|
||||||
return NetworkConfig(indent=2, contents=contents)
|
|
||||||
|
|
||||||
def run(module, result):
|
def run(module, result):
|
||||||
match = module.params['match']
|
match = module.params['match']
|
||||||
replace = module.params['replace']
|
replace = module.params['replace']
|
||||||
|
@ -247,10 +251,9 @@ def run(module, result):
|
||||||
candidate = get_candidate(module)
|
candidate = get_candidate(module)
|
||||||
|
|
||||||
if match != 'none':
|
if match != 'none':
|
||||||
config = get_config(module)
|
config = get_running_config(module)
|
||||||
path = module.params['parents']
|
path = module.params['parents']
|
||||||
configobjs = candidate.difference(config, path=path, match=match,
|
configobjs = candidate.difference(config, match=match, replace=replace, path=path)
|
||||||
replace=replace)
|
|
||||||
else:
|
else:
|
||||||
configobjs = candidate.items
|
configobjs = candidate.items
|
||||||
|
|
||||||
|
@ -264,22 +267,17 @@ def run(module, result):
|
||||||
if module.params['after']:
|
if module.params['after']:
|
||||||
commands.extend(module.params['after'])
|
commands.extend(module.params['after'])
|
||||||
|
|
||||||
|
result['commands'] = commands
|
||||||
result['updates'] = commands
|
result['updates'] = commands
|
||||||
|
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
module.config.load_config(commands)
|
load_config(module, commands)
|
||||||
|
|
||||||
result['changed'] = True
|
result['changed'] = True
|
||||||
|
|
||||||
if module.params['save']:
|
|
||||||
if not module.check_mode:
|
|
||||||
module.config.save_config()
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" main entry point for module execution
|
""" main entry point for module execution
|
||||||
"""
|
"""
|
||||||
|
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
src=dict(type='path'),
|
src=dict(type='path'),
|
||||||
|
|
||||||
|
@ -303,14 +301,15 @@ def main():
|
||||||
save=dict(type='bool', default=False),
|
save=dict(type='bool', default=False),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
mutually_exclusive = [('lines', 'src')]
|
mutually_exclusive = [('lines', 'src')]
|
||||||
|
|
||||||
required_if = [('match', 'strict', ['lines']),
|
required_if = [('match', 'strict', ['lines']),
|
||||||
('match', 'exact', ['lines']),
|
('match', 'exact', ['lines']),
|
||||||
('replace', 'block', ['lines'])]
|
('replace', 'block', ['lines'])]
|
||||||
|
|
||||||
module = NetworkModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
connect_on_load=False,
|
|
||||||
mutually_exclusive=mutually_exclusive,
|
mutually_exclusive=mutually_exclusive,
|
||||||
required_if=required_if,
|
required_if=required_if,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
@ -326,11 +325,13 @@ def main():
|
||||||
if module.params['backup']:
|
if module.params['backup']:
|
||||||
result['__backup__'] = module.config.get_config()
|
result['__backup__'] = module.config.get_config()
|
||||||
|
|
||||||
try:
|
if any((module.params['src'], module.params['lines'])):
|
||||||
run(module, result)
|
run(module, result)
|
||||||
except NetworkError:
|
|
||||||
exc = get_exception()
|
if module.params['save']:
|
||||||
module.fail_json(msg=str(exc), **exc.kwargs)
|
if not module.check_mode:
|
||||||
|
run_commands(module, ['copy running-config startup-config'])
|
||||||
|
result['changed'] = True
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Handles the EVPN control plane for VXLAN.
|
||||||
description:
|
description:
|
||||||
- Handles the EVPN control plane for VXLAN.
|
- Handles the EVPN control plane for VXLAN.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
nv_overlay_evpn:
|
nv_overlay_evpn:
|
||||||
description:
|
description:
|
||||||
|
@ -73,156 +72,11 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'nv_overlay_evpn': 'nv overlay evpn',
|
'nv_overlay_evpn': 'nv overlay evpn',
|
||||||
|
@ -286,9 +140,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
existing = invoke('get_existing', module)
|
existing = invoke('get_existing', module)
|
||||||
end_state = existing
|
end_state = existing
|
||||||
proposed = dict(nv_overlay_evpn=module.params['nv_overlay_evpn'])
|
proposed = dict(nv_overlay_evpn=module.params['nv_overlay_evpn'])
|
||||||
|
@ -314,8 +175,11 @@ def main():
|
||||||
result['existing'] = existing
|
result['existing'] = existing
|
||||||
result['proposed'] = proposed
|
result['proposed'] = proposed
|
||||||
|
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network
|
- Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network
|
||||||
Identifier (VNI) configurations of a Nexus device.
|
Identifier (VNI) configurations of a Nexus device.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- default, where supported, restores params default value.
|
- default, where supported, restores params default value.
|
||||||
- RD override is not permitted. You should set it to the default values
|
- RD override is not permitted. You should set it to the default values
|
||||||
|
@ -127,156 +126,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'vni': 'vni',
|
'vni': 'vni',
|
||||||
|
@ -420,9 +274,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
args = [
|
args = [
|
||||||
'vni',
|
'vni',
|
||||||
|
@ -490,3 +351,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -176,31 +176,24 @@ vlan_list:
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcli import CommandRunner, AddCommandError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.network import NetworkModule, NetworkError
|
from ansible.module_utils.network_common import exec_command
|
||||||
from ansible.module_utils.six import iteritems
|
from ansible.module_utils.six import iteritems
|
||||||
|
|
||||||
|
|
||||||
def add_command(runner, command, output=None):
|
|
||||||
try:
|
|
||||||
runner.add_command(command, output)
|
|
||||||
except AddCommandError:
|
|
||||||
# AddCommandError is raised for any issue adding a command to
|
|
||||||
# the runner. Silently ignore the exception in this case
|
|
||||||
pass
|
|
||||||
|
|
||||||
class FactsBase(object):
|
class FactsBase(object):
|
||||||
|
|
||||||
def __init__(self, module, runner):
|
COMMANDS = frozenset()
|
||||||
self.module = module
|
|
||||||
self.runner = runner
|
|
||||||
self.facts = dict()
|
|
||||||
self.commands()
|
|
||||||
|
|
||||||
def commands(self):
|
def __init__(self, module):
|
||||||
raise NotImplementedError
|
self.module = module
|
||||||
|
self.facts = dict()
|
||||||
|
self.responses = None
|
||||||
|
|
||||||
|
def populate(self):
|
||||||
|
self.responses = run_commands(self.module, list(self.COMMANDS))
|
||||||
|
|
||||||
def transform_dict(self, data, keymap):
|
def transform_dict(self, data, keymap):
|
||||||
transform = dict()
|
transform = dict()
|
||||||
|
@ -224,33 +217,36 @@ class Default(FactsBase):
|
||||||
('host_name', 'hostname')
|
('host_name', 'hostname')
|
||||||
])
|
])
|
||||||
|
|
||||||
def commands(self):
|
COMMANDS = ['show version | json']
|
||||||
add_command(self.runner, 'show version', output='json')
|
|
||||||
|
|
||||||
def populate(self):
|
def populate(self):
|
||||||
data = self.runner.get_command('show version', output='json')
|
super(Default, self).populate()
|
||||||
|
data = self.responses[0]
|
||||||
self.facts.update(self.transform_dict(data, self.VERSION_MAP))
|
self.facts.update(self.transform_dict(data, self.VERSION_MAP))
|
||||||
|
|
||||||
class Config(FactsBase):
|
class Config(FactsBase):
|
||||||
|
|
||||||
def commands(self):
|
COMMANDS = ['show running-config']
|
||||||
add_command(self.runner, 'show running-config')
|
|
||||||
|
|
||||||
def populate(self):
|
def populate(self):
|
||||||
self.facts['config'] = self.runner.get_command('show running-config')
|
super(Config, self).populate()
|
||||||
|
data = self.responses[0]
|
||||||
|
self.facts['config'] = data
|
||||||
|
|
||||||
|
|
||||||
class Hardware(FactsBase):
|
class Hardware(FactsBase):
|
||||||
|
|
||||||
def commands(self):
|
COMMANDS = [
|
||||||
add_command(self.runner, 'dir', output='text')
|
'dir',
|
||||||
add_command(self.runner, 'show system resources', output='json')
|
'show system resources | json'
|
||||||
|
]
|
||||||
|
|
||||||
def populate(self):
|
def populate(self):
|
||||||
data = self.runner.get_command('dir', 'text')
|
super(Hardware, self).populate()
|
||||||
|
data = self.responses[0]
|
||||||
self.facts['filesystems'] = self.parse_filesystems(data)
|
self.facts['filesystems'] = self.parse_filesystems(data)
|
||||||
|
|
||||||
data = self.runner.get_command('show system resources', output='json')
|
data = self.responses[1]
|
||||||
self.facts['memtotal_mb'] = int(data['memory_usage_total']) / 1024
|
self.facts['memtotal_mb'] = int(data['memory_usage_total']) / 1024
|
||||||
self.facts['memfree_mb'] = int(data['memory_usage_free']) / 1024
|
self.facts['memfree_mb'] = int(data['memory_usage_free']) / 1024
|
||||||
|
|
||||||
|
@ -282,38 +278,21 @@ class Interfaces(FactsBase):
|
||||||
('prefix', 'subnet')
|
('prefix', 'subnet')
|
||||||
])
|
])
|
||||||
|
|
||||||
def commands(self):
|
|
||||||
add_command(self.runner, 'show interface', output='json')
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.module.cli('show ipv6 interface', 'json')
|
|
||||||
add_command(self.runner, 'show ipv6 interface', output='json')
|
|
||||||
self.ipv6 = True
|
|
||||||
except NetworkError:
|
|
||||||
self.ipv6 = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.module.cli(['show lldp neighbors'])
|
|
||||||
add_command(self.runner, 'show lldp neighbors', output='json')
|
|
||||||
self.lldp_enabled = True
|
|
||||||
except NetworkError:
|
|
||||||
self.lldp_enabled = False
|
|
||||||
|
|
||||||
def populate(self):
|
def populate(self):
|
||||||
self.facts['all_ipv4_addresses'] = list()
|
self.facts['all_ipv4_addresses'] = list()
|
||||||
self.facts['all_ipv6_addresses'] = list()
|
self.facts['all_ipv6_addresses'] = list()
|
||||||
|
|
||||||
data = self.runner.get_command('show interface', 'json')
|
data = run_commands(self.module, ['show interface | json'])[0]
|
||||||
self.facts['interfaces'] = self.populate_interfaces(data)
|
self.facts['interfaces'] = self.populate_interfaces(data)
|
||||||
|
|
||||||
if self.ipv6:
|
rc, out, err = exec_command(self.module, 'show ipv6 interface | json')
|
||||||
data = self.runner.get_command('show ipv6 interface', 'json')
|
if rc == 0:
|
||||||
if data:
|
if out:
|
||||||
self.parse_ipv6_interfaces(data)
|
self.parse_ipv6_interfaces(out)
|
||||||
|
|
||||||
if self.lldp_enabled:
|
rc, out, err = exec_command(self.module, 'show lldp neighbors')
|
||||||
data = self.runner.get_command('show lldp neighbors', 'json')
|
if rc == 0:
|
||||||
self.facts['neighbors'] = self.populate_neighbors(data)
|
self.facts['neighbors'] = self.populate_neighbors(out)
|
||||||
|
|
||||||
def populate_interfaces(self, data):
|
def populate_interfaces(self, data):
|
||||||
interfaces = dict()
|
interfaces = dict()
|
||||||
|
@ -390,27 +369,30 @@ class Legacy(FactsBase):
|
||||||
('total_capa', 'total_capacity')
|
('total_capa', 'total_capacity')
|
||||||
])
|
])
|
||||||
|
|
||||||
def commands(self):
|
COMMANDS = [
|
||||||
add_command(self.runner, 'show version', output='json')
|
'show version | json',
|
||||||
add_command(self.runner, 'show module', output='json')
|
'show module | json',
|
||||||
add_command(self.runner, 'show environment', output='json')
|
'show environment | json',
|
||||||
add_command(self.runner, 'show interface', output='json')
|
'show interface | json',
|
||||||
add_command(self.runner, 'show vlan brief', output='json')
|
'show vlan brief | json'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def populate(self):
|
def populate(self):
|
||||||
data = self.runner.get_command('show version', 'json')
|
super(Legacy, self).populate()
|
||||||
|
data = self.responses[0]
|
||||||
self.facts.update(self.transform_dict(data, self.VERSION_MAP))
|
self.facts.update(self.transform_dict(data, self.VERSION_MAP))
|
||||||
|
|
||||||
data = self.runner.get_command('show interface', 'json')
|
data = self.responses[3]
|
||||||
self.facts['_interfaces_list'] = self.parse_interfaces(data)
|
self.facts['_interfaces_list'] = self.parse_interfaces(data)
|
||||||
|
|
||||||
data = self.runner.get_command('show vlan brief', 'json')
|
data = self.responses[4]
|
||||||
self.facts['_vlan_list'] = self.parse_vlans(data)
|
self.facts['_vlan_list'] = self.parse_vlans(data)
|
||||||
|
|
||||||
data = self.runner.get_command('show module', 'json')
|
data = self.responses[1]
|
||||||
self.facts['_module'] = self.parse_module(data)
|
self.facts['_module'] = self.parse_module(data)
|
||||||
|
|
||||||
data = self.runner.get_command('show environment', 'json')
|
data = self.responses[2]
|
||||||
self.facts['_fan_info'] = self.parse_fan_info(data)
|
self.facts['_fan_info'] = self.parse_fan_info(data)
|
||||||
self.facts['_power_supply_info'] = self.parse_power_supply_info(data)
|
self.facts['_power_supply_info'] = self.parse_power_supply_info(data)
|
||||||
|
|
||||||
|
@ -463,7 +445,12 @@ def main():
|
||||||
gather_subset=dict(default=['!config'], type='list')
|
gather_subset=dict(default=['!config'], type='list')
|
||||||
)
|
)
|
||||||
|
|
||||||
module = NetworkModule(argument_spec=spec, supports_check_mode=True)
|
spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=spec, supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
gather_subset = module.params['gather_subset']
|
gather_subset = module.params['gather_subset']
|
||||||
|
|
||||||
|
@ -501,25 +488,13 @@ def main():
|
||||||
facts = dict()
|
facts = dict()
|
||||||
facts['gather_subset'] = list(runable_subsets)
|
facts['gather_subset'] = list(runable_subsets)
|
||||||
|
|
||||||
runner = CommandRunner(module)
|
|
||||||
|
|
||||||
instances = list()
|
instances = list()
|
||||||
for key in runable_subsets:
|
for key in runable_subsets:
|
||||||
instances.append(FACT_SUBSETS[key](module, runner))
|
instances.append(FACT_SUBSETS[key](module))
|
||||||
|
|
||||||
try:
|
|
||||||
runner.run()
|
|
||||||
except NetworkError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc), **exc.kwargs)
|
|
||||||
|
|
||||||
try:
|
|
||||||
for inst in instances:
|
for inst in instances:
|
||||||
inst.populate()
|
inst.populate()
|
||||||
facts.update(inst.facts)
|
facts.update(inst.facts)
|
||||||
except Exception:
|
|
||||||
raise
|
|
||||||
module.exit_json(out=module.from_json(runner.items))
|
|
||||||
|
|
||||||
ansible_facts = dict()
|
ansible_facts = dict()
|
||||||
for key, value in iteritems(facts):
|
for key, value in iteritems(facts):
|
||||||
|
@ -530,7 +505,7 @@ def main():
|
||||||
key = 'ansible_net_%s' % key
|
key = 'ansible_net_%s' % key
|
||||||
ansible_facts[key] = value
|
ansible_facts[key] = value
|
||||||
|
|
||||||
module.exit_json(ansible_facts=ansible_facts)
|
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.1"
|
||||||
short_description: Manage features in NX-OS switches.
|
short_description: Manage features in NX-OS switches.
|
||||||
description:
|
description:
|
||||||
- Offers ability to enable and disable features in NX-OS.
|
- Offers ability to enable and disable features in NX-OS.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -97,244 +96,11 @@ feature:
|
||||||
type: string
|
type: string
|
||||||
sample: "vpc"
|
sample: "vpc"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
|
||||||
import collections
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
|
||||||
if module.params['transport'] == 'cli':
|
|
||||||
command += ' | json'
|
|
||||||
cmds = [command]
|
|
||||||
response = execute_show(cmds, module)
|
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
|
||||||
cmds = [command]
|
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
|
||||||
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def apply_key_map(key_map, table):
|
def apply_key_map(key_map, table):
|
||||||
new_dict = {}
|
new_dict = {}
|
||||||
|
@ -354,7 +120,8 @@ def get_available_features(feature, module):
|
||||||
feature_regex = '(?P<feature>\S+)\s+\d+\s+(?P<state>.*)'
|
feature_regex = '(?P<feature>\S+)\s+\d+\s+(?P<state>.*)'
|
||||||
command = 'show feature'
|
command = 'show feature'
|
||||||
|
|
||||||
body = execute_show_command(command, module, command_type='cli_show_ascii')
|
command = {'command': command, 'output': 'text'}
|
||||||
|
body = run_commands(module, [command])
|
||||||
split_body = body[0].splitlines()
|
split_body = body[0].splitlines()
|
||||||
|
|
||||||
for line in split_body:
|
for line in split_body:
|
||||||
|
@ -451,9 +218,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
feature = validate_feature(module)
|
feature = validate_feature(module)
|
||||||
state = module.params['state'].lower()
|
state = module.params['state'].lower()
|
||||||
|
|
||||||
|
@ -477,7 +251,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
updated_features = get_available_features(feature, module)
|
updated_features = get_available_features(feature, module)
|
||||||
existstate = updated_features[feature]
|
existstate = updated_features[feature]
|
||||||
|
@ -491,6 +265,7 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['feature'] = module.params['feature']
|
results['feature'] = module.params['feature']
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -498,3 +273,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ description:
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- The feature must be enabled with feature scp-server.
|
- The feature must be enabled with feature scp-server.
|
||||||
- If the file is already present (md5 sums match), no transfer will
|
- If the file is already present (md5 sums match), no transfer will
|
||||||
|
@ -82,205 +81,29 @@ remote_file:
|
||||||
type: string
|
type: string
|
||||||
sample: '/path/to/remote/file'
|
sample: '/path/to/remote/file'
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from scp import SCPClient
|
import re
|
||||||
import paramiko
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
import paramiko
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ansible.module_utils.nxos import get_module
|
from scp import SCPClient
|
||||||
|
HAS_SCP = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
HAS_SCP = False
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -372,9 +195,21 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
if not HAS_SCP:
|
||||||
|
module.fail_json(
|
||||||
|
msg='library scp is required but does not appear to be '
|
||||||
|
'installed. It can be installed using `pip install scp`'
|
||||||
|
)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
local_file = module.params['local_file']
|
local_file = module.params['local_file']
|
||||||
remote_file = module.params['remote_file']
|
remote_file = module.params['remote_file']
|
||||||
file_system = module.params['file_system']
|
file_system = module.params['file_system']
|
||||||
|
@ -409,8 +244,10 @@ def main():
|
||||||
transfer_status=transfer_status,
|
transfer_status=transfer_status,
|
||||||
local_file=local_file,
|
local_file=local_file,
|
||||||
remote_file=remote_file,
|
remote_file=remote_file,
|
||||||
|
warnings=warnings,
|
||||||
file_system=file_system)
|
file_system=file_system)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Trigger a graceful removal or insertion (GIR) of the switch.
|
short_description: Trigger a graceful removal or insertion (GIR) of the switch.
|
||||||
description:
|
description:
|
||||||
- Trigger a graceful removal or insertion (GIR) of the switch.
|
- Trigger a graceful removal or insertion (GIR) of the switch.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
|
@ -161,220 +160,22 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show_ascii'):
|
def execute_show_command(command, module, command_type='cli_show_ascii'):
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
body = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_system_mode(module):
|
def get_system_mode(module):
|
||||||
command = 'show system mode'
|
command = 'show system mode'
|
||||||
body = execute_show_command(command, module)[0]
|
body = execute_show_command(command, module)[0]
|
||||||
|
@ -468,7 +269,10 @@ def main():
|
||||||
state=dict(choices=['absent', 'present', 'default'],
|
state=dict(choices=['absent', 'present', 'default'],
|
||||||
default='present', required=False)
|
default='present', required=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[[
|
mutually_exclusive=[[
|
||||||
'system_mode_maintenance',
|
'system_mode_maintenance',
|
||||||
'system_mode_maintenance_dont_generate_profile',
|
'system_mode_maintenance_dont_generate_profile',
|
||||||
|
@ -485,6 +289,10 @@ def main():
|
||||||
]],
|
]],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
mode = get_system_mode(module)
|
mode = get_system_mode(module)
|
||||||
commands = get_commands(module, state, mode)
|
commands = get_commands(module, state, mode)
|
||||||
|
@ -493,7 +301,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=commands)
|
module.exit_json(changed=True, commands=commands)
|
||||||
else:
|
else:
|
||||||
execute_config_command(commands, module)
|
load_config(module, commands)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
|
@ -504,8 +312,11 @@ def main():
|
||||||
result['final_system_mode'] = final_system_mode
|
result['final_system_mode'] = final_system_mode
|
||||||
result['updates'] = commands
|
result['updates'] = commands
|
||||||
|
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- Manage a maintenance-mode or normal-mode profile with configuration
|
- Manage a maintenance-mode or normal-mode profile with configuration
|
||||||
commands that can be applied during graceful removal
|
commands that can be applied during graceful removal
|
||||||
or graceful insertion.
|
or graceful insertion.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
|
@ -117,159 +116,11 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def get_existing(module):
|
def get_existing(module):
|
||||||
|
@ -315,24 +166,6 @@ def invoke(name, *args, **kwargs):
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
commands=dict(required=False, type='list'),
|
commands=dict(required=False, type='list'),
|
||||||
|
@ -342,9 +175,16 @@ def main():
|
||||||
include_defaults=dict(default=False),
|
include_defaults=dict(default=False),
|
||||||
config=dict()
|
config=dict()
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
commands = module.params['commands'] or []
|
commands = module.params['commands'] or []
|
||||||
|
|
||||||
|
@ -363,7 +203,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
end_state = invoke('get_existing', module)
|
end_state = invoke('get_existing', module)
|
||||||
|
|
||||||
|
@ -376,8 +216,11 @@ def main():
|
||||||
result['proposed'] = commands
|
result['proposed'] = commands
|
||||||
result['updates'] = cmds
|
result['updates'] = cmds
|
||||||
|
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages HSRP configuration on NX-OS switches.
|
short_description: Manages HSRP configuration on NX-OS switches.
|
||||||
description:
|
description:
|
||||||
- Manages HSRP configuration on NX-OS switches.
|
- Manages HSRP configuration on NX-OS switches.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -143,239 +142,21 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
|
|
||||||
import ansible.module_utils.nxos
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
output = module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
output = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
response = response[0].replace(command + '\n\n', '').strip()
|
|
||||||
body = [json.loads(response)]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -619,9 +400,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
interface = module.params['interface'].lower()
|
interface = module.params['interface'].lower()
|
||||||
group = module.params['group']
|
group = module.params['group']
|
||||||
version = module.params['version']
|
version = module.params['version']
|
||||||
|
@ -699,7 +487,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=commands)
|
module.exit_json(changed=True, commands=commands)
|
||||||
else:
|
else:
|
||||||
body = execute_config_command(commands, module)
|
load_config(module, commands)
|
||||||
if transport == 'cli':
|
if transport == 'cli':
|
||||||
validate_config(body, vip, module)
|
validate_config(body, vip, module)
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -713,9 +501,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = commands
|
results['updates'] = commands
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages IGMP global configuration.
|
short_description: Manages IGMP global configuration.
|
||||||
description:
|
description:
|
||||||
- Manages IGMP global configuration configuration settings.
|
- Manages IGMP global configuration configuration settings.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -110,159 +109,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'flush_routes': 'ip igmp flush-routes',
|
'flush_routes': 'ip igmp flush-routes',
|
||||||
|
@ -343,9 +194,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
restart = module.params['restart']
|
restart = module.params['restart']
|
||||||
|
|
||||||
|
@ -392,8 +250,11 @@ def main():
|
||||||
result['existing'] = existing
|
result['existing'] = existing
|
||||||
result['proposed'] = proposed
|
result['proposed'] = proposed
|
||||||
|
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages IGMP interface configuration.
|
short_description: Manages IGMP interface configuration.
|
||||||
description:
|
description:
|
||||||
- Manages IGMP interface configuration settings.
|
- Manages IGMP interface configuration settings.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -233,225 +232,21 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import collections
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
response = response[0].replace(command + '\n\n', '').strip()
|
|
||||||
body = [json.loads(response)]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -712,24 +507,6 @@ def config_remove_oif(existing, existing_oif_prefix_source):
|
||||||
return commands
|
return commands
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
interface=dict(required=True, type='str'),
|
interface=dict(required=True, type='str'),
|
||||||
|
@ -755,9 +532,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
interface = module.params['interface']
|
interface = module.params['interface']
|
||||||
oif_prefix = module.params['oif_prefix']
|
oif_prefix = module.params['oif_prefix']
|
||||||
|
@ -891,7 +675,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
end_state = get_igmp_interface(module, interface)
|
end_state = get_igmp_interface(module, interface)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
|
@ -901,6 +685,7 @@ def main():
|
||||||
results['existing'] = existing_copy
|
results['existing'] = existing_copy
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -908,3 +693,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ description:
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- When C(state=default), params will be reset to a default state.
|
- When C(state=default), params will be reset to a default state.
|
||||||
- C(group_timeout) also accepts I(never) as an input.
|
- C(group_timeout) also accepts I(never) as an input.
|
||||||
|
@ -129,229 +128,23 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
if isinstance(response[0], str):
|
|
||||||
response = response[0].replace(command + '\n\n', '').strip()
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
else:
|
|
||||||
body = response
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -366,24 +159,6 @@ def flatten_list(command_lists):
|
||||||
return flat_command_list
|
return flat_command_list
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_group_timeout(config):
|
def get_group_timeout(config):
|
||||||
command = 'ip igmp snooping group-timeout'
|
command = 'ip igmp snooping group-timeout'
|
||||||
REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(command), re.M)
|
REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(command), re.M)
|
||||||
|
@ -492,9 +267,16 @@ def main():
|
||||||
v3_report_supp=dict(required=False, type='bool'),
|
v3_report_supp=dict(required=False, type='bool'),
|
||||||
state=dict(choices=['present', 'default'], default='present'),
|
state=dict(choices=['present', 'default'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
snooping = module.params['snooping']
|
snooping = module.params['snooping']
|
||||||
link_local_grp_supp = module.params['link_local_grp_supp']
|
link_local_grp_supp = module.params['link_local_grp_supp']
|
||||||
report_supp = module.params['report_supp']
|
report_supp = module.params['report_supp']
|
||||||
|
@ -539,7 +321,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_igmp_snooping(module)
|
end_state = get_igmp_snooping(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -548,9 +330,11 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -115,220 +115,23 @@ install_state:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show_ascii'):
|
def execute_show_command(command, module, command_type='cli_show_ascii'):
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
body = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_boot_options(module):
|
def get_boot_options(module):
|
||||||
"""Get current boot variables
|
"""Get current boot variables
|
||||||
like system image and kickstart image.
|
like system image and kickstart image.
|
||||||
|
@ -376,7 +179,7 @@ def set_boot_options(module, image_name, kickstart=None):
|
||||||
else:
|
else:
|
||||||
commands.append(
|
commands.append(
|
||||||
'install all system %s kickstart %s' % (image_name, kickstart))
|
'install all system %s kickstart %s' % (image_name, kickstart))
|
||||||
execute_config_command(commands, module)
|
load_config(module, commands)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -384,9 +187,16 @@ def main():
|
||||||
system_image_file=dict(required=True),
|
system_image_file=dict(required=True),
|
||||||
kickstart_image_file=dict(required=False),
|
kickstart_image_file=dict(required=False),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
system_image_file = module.params['system_image_file']
|
system_image_file = module.params['system_image_file']
|
||||||
kickstart_image_file = module.params['kickstart_image_file']
|
kickstart_image_file = module.params['kickstart_image_file']
|
||||||
|
|
||||||
|
@ -413,8 +223,9 @@ def main():
|
||||||
else:
|
else:
|
||||||
install_state = current_boot_options
|
install_state = current_boot_options
|
||||||
|
|
||||||
module.exit_json(changed=changed, install_state=install_state)
|
module.exit_json(changed=changed, install_state=install_state, warnings=warnings)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -162,157 +162,12 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
# COMMON CODE FOR MIGRATION
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
import ansible.module_utils.nxos
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def is_default_interface(interface, module):
|
def is_default_interface(interface, module):
|
||||||
|
@ -710,95 +565,23 @@ def smart_existing(module, intf_type, normalized_interface):
|
||||||
return existing, is_default
|
return existing, is_default
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [{'command': command, 'output': 'json'}]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
def execute_modified_show_for_cli_text(command, module):
|
def execute_modified_show_for_cli_text(command, module):
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
else:
|
else:
|
||||||
response = execute_show(cmds, module, command_type='cli_show_ascii')
|
body = run_commands(module, cmds)
|
||||||
body = response
|
body = response
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -840,10 +623,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['interface', 'interface_type']],
|
mutually_exclusive=[['interface', 'interface_type']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
interface = module.params['interface']
|
interface = module.params['interface']
|
||||||
interface_type = module.params['interface_type']
|
interface_type = module.params['interface_type']
|
||||||
admin_state = module.params['admin_state']
|
admin_state = module.params['admin_state']
|
||||||
|
@ -937,7 +726,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
if module.params['interface']:
|
if module.params['interface']:
|
||||||
if delta.get('mode'): # or delta.get('admin_state'):
|
if delta.get('mode'): # or delta.get('admin_state'):
|
||||||
|
@ -948,7 +737,7 @@ def main():
|
||||||
c1 = 'interface {0}'.format(normalized_interface)
|
c1 = 'interface {0}'.format(normalized_interface)
|
||||||
c2 = get_admin_state(delta, normalized_interface, admin_state)
|
c2 = get_admin_state(delta, normalized_interface, admin_state)
|
||||||
cmds2 = [c1, c2]
|
cmds2 = [c1, c2]
|
||||||
execute_config_command(cmds2, module)
|
load_config(module, cmds2)
|
||||||
cmds.extend(cmds2)
|
cmds.extend(cmds2)
|
||||||
end_state, is_default = smart_existing(module, intf_type,
|
end_state, is_default = smart_existing(module, intf_type,
|
||||||
normalized_interface)
|
normalized_interface)
|
||||||
|
@ -962,9 +751,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages configuration of an OSPF interface instance.
|
||||||
description:
|
description:
|
||||||
- Manages configuration of an OSPF interface instance.
|
- Manages configuration of an OSPF interface instance.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- Default, where supported, restores params default value.
|
- Default, where supported, restores params default value.
|
||||||
- To remove an existing authentication configuration you should use
|
- To remove an existing authentication configuration you should use
|
||||||
|
@ -167,156 +166,11 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
BOOL_PARAMS = [
|
BOOL_PARAMS = [
|
||||||
'passive_interface',
|
'passive_interface',
|
||||||
|
@ -597,7 +451,10 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_together=[['message_digest_key_id',
|
required_together=[['message_digest_key_id',
|
||||||
'message_digest_algorithm_type',
|
'message_digest_algorithm_type',
|
||||||
'message_digest_encryption_type',
|
'message_digest_encryption_type',
|
||||||
|
@ -607,6 +464,9 @@ def main():
|
||||||
if not module.params['interface'].startswith('loopback'):
|
if not module.params['interface'].startswith('loopback'):
|
||||||
module.params['interface'] = module.params['interface'].capitalize()
|
module.params['interface'] = module.params['interface'].capitalize()
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
for param in ['message_digest_encryption_type',
|
for param in ['message_digest_encryption_type',
|
||||||
'message_digest_algorithm_type',
|
'message_digest_algorithm_type',
|
||||||
'message_digest_password']:
|
'message_digest_password']:
|
||||||
|
@ -674,8 +534,11 @@ def main():
|
||||||
result['existing'] = existing
|
result['existing'] = existing
|
||||||
result['proposed'] = proposed_args
|
result['proposed'] = proposed_args
|
||||||
|
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.1"
|
||||||
short_description: Manages L3 attributes for IPv4 and IPv6 interfaces.
|
short_description: Manages L3 attributes for IPv4 and IPv6 interfaces.
|
||||||
description:
|
description:
|
||||||
- Manages Layer 3 attributes for IPv4 and IPv6 interfaces.
|
- Manages Layer 3 attributes for IPv4 and IPv6 interfaces.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -111,242 +110,21 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import collections
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, we assume if '^' is found in response,
|
|
||||||
it is an invalid command.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif '^' in response[0] or 'show run' in response[0] or response[0] == '\n':
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -640,9 +418,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
addr = module.params['addr']
|
addr = module.params['addr']
|
||||||
version = module.params['version']
|
version = module.params['version']
|
||||||
mask = module.params['mask']
|
mask = module.params['mask']
|
||||||
|
@ -702,7 +487,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
end_state, address_list = get_ip_interface(interface, version,
|
end_state, address_list = get_ip_interface(interface, version,
|
||||||
module)
|
module)
|
||||||
|
@ -715,9 +500,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages MTU settings on Nexus switch.
|
short_description: Manages MTU settings on Nexus switch.
|
||||||
description:
|
description:
|
||||||
- Manages MTU settings on Nexus switch.
|
- Manages MTU settings on Nexus switch.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -118,245 +117,19 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
import json
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
@ -503,10 +276,16 @@ def main():
|
||||||
sysmtu=dict(type='str'),
|
sysmtu=dict(type='str'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_together=[['mtu', 'interface']],
|
required_together=[['mtu', 'interface']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
interface = module.params['interface']
|
interface = module.params['interface']
|
||||||
mtu = module.params['mtu']
|
mtu = module.params['mtu']
|
||||||
sysmtu = module.params['sysmtu']
|
sysmtu = module.params['sysmtu']
|
||||||
|
@ -576,7 +355,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
if interface:
|
if interface:
|
||||||
end_state = get_mtu(interface, module)
|
end_state = get_mtu(interface, module)
|
||||||
else:
|
else:
|
||||||
|
@ -590,9 +369,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages core NTP configuration.
|
short_description: Manages core NTP configuration.
|
||||||
description:
|
description:
|
||||||
- Manages core NTP configuration.
|
- Manages core NTP configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
options:
|
options:
|
||||||
|
@ -125,243 +124,23 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -530,12 +309,19 @@ def main():
|
||||||
source_int=dict(type='str'),
|
source_int=dict(type='str'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
['server','peer'],
|
['server','peer'],
|
||||||
['source_addr','source_int']],
|
['source_addr','source_int']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
server = module.params['server'] or None
|
server = module.params['server'] or None
|
||||||
peer = module.params['peer'] or None
|
peer = module.params['peer'] or None
|
||||||
key_id = module.params['key_id']
|
key_id = module.params['key_id']
|
||||||
|
@ -616,7 +402,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_ntp_existing(address, peer_type, module)[0]
|
end_state = get_ntp_existing(address, peer_type, module)[0]
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -626,6 +412,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['peer_server_list'] = peer_server_list
|
results['peer_server_list'] = peer_server_list
|
||||||
|
|
||||||
|
@ -635,3 +422,4 @@ def main():
|
||||||
from ansible.module_utils.basic import *
|
from ansible.module_utils.basic import *
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages NTP authentication.
|
short_description: Manages NTP authentication.
|
||||||
description:
|
description:
|
||||||
- Manages NTP authentication.
|
- Manages NTP authentication.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -123,243 +122,23 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -503,9 +282,16 @@ def main():
|
||||||
authentication=dict(choices=['on', 'off']),
|
authentication=dict(choices=['on', 'off']),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
key_id = module.params['key_id']
|
key_id = module.params['key_id']
|
||||||
md5string = module.params['md5string']
|
md5string = module.params['md5string']
|
||||||
auth_type = module.params['auth_type']
|
auth_type = module.params['auth_type']
|
||||||
|
@ -548,7 +334,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
except ShellError:
|
except ShellError:
|
||||||
clie = get_exception()
|
clie = get_exception()
|
||||||
module.fail_json(msg=str(clie) + ": " + cmds)
|
module.fail_json(msg=str(clie) + ": " + cmds)
|
||||||
|
@ -564,9 +350,11 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages NTP options.
|
short_description: Manages NTP options.
|
||||||
description:
|
description:
|
||||||
- Manages NTP options, e.g. authoritative server and logging.
|
- Manages NTP options, e.g. authoritative server and logging.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -104,243 +103,24 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -442,10 +222,17 @@ def main():
|
||||||
logging=dict(required=False, type='bool'),
|
logging=dict(required=False, type='bool'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_one_of=[['master', 'logging']],
|
required_one_of=[['master', 'logging']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
master = module.params['master']
|
master = module.params['master']
|
||||||
stratum = module.params['stratum']
|
stratum = module.params['stratum']
|
||||||
logging = module.params['logging']
|
logging = module.params['logging']
|
||||||
|
@ -500,7 +287,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_ntp_options(module)
|
end_state = get_ntp_options(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -510,6 +297,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -517,3 +305,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,11 @@
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {
|
||||||
ANSIBLE_METADATA = {'status': ['preview'],
|
'status': ['preview'],
|
||||||
'supported_by': 'core',
|
'supported_by': 'core',
|
||||||
'version': '1.0'}
|
'version': '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = """
|
||||||
---
|
---
|
||||||
|
@ -32,7 +33,6 @@ description:
|
||||||
NXAPI feature is absent from the configuration by default. Since
|
NXAPI feature is absent from the configuration by default. Since
|
||||||
this module manages the NXAPI feature it only supports the use
|
this module manages the NXAPI feature it only supports the use
|
||||||
of the C(Cli) transport.
|
of the C(Cli) transport.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
http_port:
|
http_port:
|
||||||
description:
|
description:
|
||||||
|
@ -84,15 +84,6 @@ options:
|
||||||
default: no
|
default: no
|
||||||
choices: ['yes', 'no']
|
choices: ['yes', 'no']
|
||||||
aliases: ['enable_sandbox']
|
aliases: ['enable_sandbox']
|
||||||
config:
|
|
||||||
description:
|
|
||||||
- The C(config) argument provides an optional argument to
|
|
||||||
specify the device running-config to used as the basis for
|
|
||||||
configuring the remote system. The C(config) argument accepts
|
|
||||||
a string value that represents the device configuration.
|
|
||||||
required: false
|
|
||||||
default: null
|
|
||||||
version_added: "2.2"
|
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- The C(state) argument controls whether or not the NXAPI
|
- The C(state) argument controls whether or not the NXAPI
|
||||||
|
@ -106,17 +97,9 @@ options:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
EXAMPLES = """
|
EXAMPLES = """
|
||||||
# Note: examples below use the following provider dict to handle
|
|
||||||
# transport and authentication to the node.
|
|
||||||
vars:
|
|
||||||
cli:
|
|
||||||
host: "{{ inventory_hostname }}"
|
|
||||||
username: admin
|
|
||||||
password: admin
|
|
||||||
|
|
||||||
- name: Enable NXAPI access with default configuration
|
- name: Enable NXAPI access with default configuration
|
||||||
nxos_nxapi:
|
nxos_nxapi:
|
||||||
provider: "{{ cli }}"
|
state: present
|
||||||
|
|
||||||
- name: Enable NXAPI with no HTTP, HTTPS at port 9443 and sandbox disabled
|
- name: Enable NXAPI with no HTTP, HTTPS at port 9443 and sandbox disabled
|
||||||
nxos_nxapi:
|
nxos_nxapi:
|
||||||
|
@ -124,12 +107,10 @@ vars:
|
||||||
https_port: 9443
|
https_port: 9443
|
||||||
https: yes
|
https: yes
|
||||||
enable_sandbox: no
|
enable_sandbox: no
|
||||||
provider: "{{ cli }}"
|
|
||||||
|
|
||||||
- name: remove NXAPI configuration
|
- name: remove NXAPI configuration
|
||||||
nxos_nxapi:
|
nxos_nxapi:
|
||||||
state: absent
|
state: absent
|
||||||
provider: "{{ cli }}"
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RETURN = """
|
RETURN = """
|
||||||
|
@ -142,189 +123,172 @@ updates:
|
||||||
sample: ['no feature nxapi']
|
sample: ['no feature nxapi']
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
import time
|
|
||||||
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, dumps
|
from functools import partial
|
||||||
from ansible.module_utils.nxos import NetworkModule, NetworkError
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
|
|
||||||
PRIVATE_KEYS_RE = re.compile('__.+__')
|
from ansible.module_utils.nxos import run_commands, load_config
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec
|
||||||
|
from ansible.module_utils.nxos import check_args as nxos_check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import NetworkConfig
|
||||||
|
from ansible.module_utils.six import iteritems
|
||||||
|
|
||||||
def invoke(name, *args, **kwargs):
|
def check_args(module, warnings):
|
||||||
func = globals().get(name)
|
nxos_check_args(module, warnings)
|
||||||
if func:
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_instance(module):
|
state = module.params['state']
|
||||||
instance = dict(state='absent')
|
|
||||||
try:
|
|
||||||
resp = module.cli('show nxapi', 'json')
|
|
||||||
except NetworkError:
|
|
||||||
return instance
|
|
||||||
|
|
||||||
instance['state'] = 'present'
|
if state == 'started':
|
||||||
|
module.params['state'] = 'present'
|
||||||
|
warnings.append('state=started is deprecated and will be removed in a '
|
||||||
|
'a future release. Please use state=present instead')
|
||||||
|
elif state == 'stopped':
|
||||||
|
module.params['state'] = 'absent'
|
||||||
|
warnings.append('state=stopped is deprecated and will be removed in a '
|
||||||
|
'a future release. Please use state=absent instead')
|
||||||
|
|
||||||
instance['http'] = 'http_port' in resp[0]
|
if module.params['transport'] == 'nxapi':
|
||||||
instance['http_port'] = resp[0].get('http_port') or 80
|
module.fail_json(msg='module not supported over nxapi transport')
|
||||||
|
|
||||||
instance['https'] = 'https_port' in resp[0]
|
for key in ['config']:
|
||||||
instance['https_port'] = resp[0].get('https_port') or 443
|
if module.params[key]:
|
||||||
|
warnings.append('argument %s is deprecated and will be ignored' % key)
|
||||||
|
|
||||||
instance['sandbox'] = resp[0]['sandbox_status']
|
return warnings
|
||||||
|
|
||||||
return instance
|
def map_obj_to_commands(updates, module):
|
||||||
|
commands = list()
|
||||||
|
want, have = updates
|
||||||
|
|
||||||
def present(module, instance, commands):
|
needs_update = lambda x: want.get(x) is not None and (want.get(x) != have.get(x))
|
||||||
|
|
||||||
|
if needs_update('state'):
|
||||||
|
if want['state'] == 'absent':
|
||||||
|
return ['no feature nxapi']
|
||||||
commands.append('feature nxapi')
|
commands.append('feature nxapi')
|
||||||
setters = set()
|
|
||||||
for key, value in module.argument_spec.items():
|
|
||||||
setter = value.get('setter') or 'set_%s' % key
|
|
||||||
if setter not in setters:
|
|
||||||
setters.add(setter)
|
|
||||||
if module.params[key] is not None:
|
|
||||||
invoke(setter, module, instance, commands)
|
|
||||||
|
|
||||||
def absent(module, instance, commands):
|
if any((needs_update('http'), needs_update('http_port'))):
|
||||||
if instance['state'] != 'absent':
|
if want['http'] is True or (want['http'] is None and have['http'] is True):
|
||||||
commands.append('no feature nxapi')
|
port = want['http_port'] or 80
|
||||||
|
|
||||||
def set_http(module, instance, commands):
|
|
||||||
port = module.params['http_port']
|
|
||||||
if not 0 <= port <= 65535:
|
|
||||||
module.fail_json(msg='http_port must be between 1 and 65535')
|
|
||||||
elif module.params['http'] is True:
|
|
||||||
commands.append('nxapi http port %s' % port)
|
commands.append('nxapi http port %s' % port)
|
||||||
elif module.params['http'] is False:
|
elif want['http'] is False:
|
||||||
commands.append('no nxapi http')
|
commands.append('no nxapi http')
|
||||||
|
|
||||||
def set_https(module, instance, commands):
|
if any((needs_update('https'), needs_update('https_port'))):
|
||||||
port = module.params['https_port']
|
if want['https'] is True or (want['https'] is None and have['https'] is True):
|
||||||
if not 0 <= port <= 65535:
|
port = want['https_port'] or 443
|
||||||
module.fail_json(msg='https_port must be between 1 and 65535')
|
|
||||||
elif module.params['https'] is True:
|
|
||||||
commands.append('nxapi https port %s' % port)
|
commands.append('nxapi https port %s' % port)
|
||||||
elif module.params['https'] is False:
|
elif want['https'] is False:
|
||||||
commands.append('no nxapi https')
|
commands.append('no nxapi https')
|
||||||
|
|
||||||
def set_sandbox(module, instance, commands):
|
if needs_update('sandbox'):
|
||||||
if module.params['sandbox'] is True:
|
cmd = 'nxapi sandbox'
|
||||||
commands.append('nxapi sandbox')
|
if not want['sandbox']:
|
||||||
elif module.params['sandbox'] is False:
|
cmd = 'no %s' % cmd
|
||||||
commands.append('no nxapi sandbox')
|
commands.append(cmd)
|
||||||
|
|
||||||
def get_config(module):
|
return commands
|
||||||
contents = module.params['config']
|
|
||||||
if not contents:
|
|
||||||
try:
|
|
||||||
contents = module.cli(['show running-config nxapi all'])[0]
|
|
||||||
except NetworkError:
|
|
||||||
contents = None
|
|
||||||
config = NetworkConfig(indent=2)
|
|
||||||
if contents:
|
|
||||||
config.load(contents)
|
|
||||||
return config
|
|
||||||
|
|
||||||
def load_checkpoint(module, result):
|
def parse_http(data):
|
||||||
try:
|
match = re.search('HTTP Port:\s+(\d+)', data, re.M)
|
||||||
checkpoint = result['__checkpoint__']
|
if match:
|
||||||
module.cli(['rollback running-config checkpoint %s' % checkpoint,
|
return {'http': True, 'http_port': match.group(1)}
|
||||||
'no checkpoint %s' % checkpoint], output='text')
|
else:
|
||||||
except KeyError:
|
return {'http': False, 'http_port': None}
|
||||||
module.fail_json(msg='unable to rollback, checkpoint not found')
|
|
||||||
except NetworkError:
|
|
||||||
exc = get_exception()
|
|
||||||
msg = 'unable to rollback configuration'
|
|
||||||
module.fail_json(msg=msg, checkpoint=checkpoint, **exc.kwargs)
|
|
||||||
|
|
||||||
def load_config(module, commands, result):
|
def parse_https(data):
|
||||||
# create a config checkpoint
|
match = re.search('HTTPS Port:\s+(\d+)', data, re.M)
|
||||||
checkpoint = 'ansible_%s' % int(time.time())
|
if match:
|
||||||
module.cli(['checkpoint %s' % checkpoint], output='text')
|
return {'https': True, 'https_port': match.group(1)}
|
||||||
result['__checkpoint__'] = checkpoint
|
else:
|
||||||
|
return {'https': False, 'https_port': None}
|
||||||
|
|
||||||
# load the config into the device
|
def parse_sandbox(data):
|
||||||
module.config.load_config(commands)
|
match = re.search('Sandbox:\s+(.+)$', data, re.M)
|
||||||
|
return {'sandbox': match.group(1) == 'Enabled'}
|
||||||
|
|
||||||
# load was successfully, remove the config checkpoint
|
def map_config_to_obj(module):
|
||||||
module.cli(['no checkpoint %s' % checkpoint])
|
out = run_commands(module, ['show nxapi'], check_rc=False)
|
||||||
|
if not out[0]:
|
||||||
|
return {'state': 'absent'}
|
||||||
|
|
||||||
def load(module, commands, result):
|
out = str(out[0]).strip()
|
||||||
candidate = NetworkConfig(indent=2, contents='\n'.join(commands))
|
|
||||||
config = get_config(module)
|
|
||||||
configobjs = candidate.difference(config)
|
|
||||||
|
|
||||||
if configobjs:
|
obj = {'state': 'present'}
|
||||||
commands = dumps(configobjs, 'commands').split('\n')
|
obj.update(parse_http(out))
|
||||||
result['updates'] = commands
|
obj.update(parse_https(out))
|
||||||
if not module.check_mode:
|
obj.update(parse_sandbox(out))
|
||||||
load_config(module, commands, result)
|
|
||||||
result['changed'] = True
|
|
||||||
|
|
||||||
def clean_result(result):
|
return obj
|
||||||
# strip out any keys that have two leading and two trailing
|
|
||||||
# underscore characters
|
|
||||||
for key in result.keys():
|
|
||||||
if PRIVATE_KEYS_RE.match(key):
|
|
||||||
del result[key]
|
|
||||||
|
|
||||||
|
def validate_http_port(value, module):
|
||||||
|
if not 1 <= module.params['http_port'] <= 65535:
|
||||||
|
module.fail_json(msg='http_port must be between 1 and 65535')
|
||||||
|
|
||||||
|
def validate_https_port(value, module):
|
||||||
|
if not 1 <= module.params['https_port'] <= 65535:
|
||||||
|
module.fail_json(msg='https_port must be between 1 and 65535')
|
||||||
|
|
||||||
|
def map_params_to_obj(module):
|
||||||
|
obj = {
|
||||||
|
'http': module.params['http'],
|
||||||
|
'http_port': module.params['http_port'],
|
||||||
|
'https': module.params['https'],
|
||||||
|
'https_port': module.params['https_port'],
|
||||||
|
'sandbox': module.params['sandbox'],
|
||||||
|
'state': module.params['state']
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in iteritems(obj):
|
||||||
|
if value:
|
||||||
|
validator = globals().get('validate_%s' % key)
|
||||||
|
if validator:
|
||||||
|
validator(value, module)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" main entry point for module execution
|
""" main entry point for module execution
|
||||||
"""
|
"""
|
||||||
|
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
http=dict(aliases=['enable_http'], default=True, type='bool', setter='set_http'),
|
http=dict(aliases=['enable_http'], type='bool'),
|
||||||
http_port=dict(default=80, type='int', setter='set_http'),
|
http_port=dict(type='int'),
|
||||||
|
|
||||||
https=dict(aliases=['enable_https'], default=False, type='bool', setter='set_https'),
|
https=dict(aliases=['enable_https'], type='bool'),
|
||||||
https_port=dict(default=443, type='int', setter='set_https'),
|
https_port=dict(type='int'),
|
||||||
|
|
||||||
sandbox=dict(aliases=['enable_sandbox'], default=False, type='bool'),
|
sandbox=dict(aliases=['enable_sandbox'], type='bool'),
|
||||||
|
|
||||||
# Only allow configuration of NXAPI using cli transport
|
|
||||||
transport=dict(required=True, choices=['cli']),
|
|
||||||
|
|
||||||
|
# deprecated (Ansible 2.3) arguments
|
||||||
config=dict(),
|
config=dict(),
|
||||||
|
|
||||||
# Support for started and stopped is for backwards capability only and
|
|
||||||
# will be removed in a future version
|
|
||||||
state=dict(default='present', choices=['started', 'stopped', 'present', 'absent'])
|
state=dict(default='present', choices=['started', 'stopped', 'present', 'absent'])
|
||||||
)
|
)
|
||||||
|
|
||||||
module = NetworkModule(argument_spec=argument_spec,
|
argument_spec.update(nxos_argument_spec)
|
||||||
connect_on_load=False,
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
state = module.params['state']
|
|
||||||
|
result = {'changed': False}
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
result = dict(changed=False, warnings=warnings)
|
want = map_params_to_obj(module)
|
||||||
|
have = map_config_to_obj(module)
|
||||||
|
|
||||||
if state == 'started':
|
commands = map_obj_to_commands((want, have), module)
|
||||||
state = 'present'
|
result['commands'] = commands
|
||||||
warnings.append('state=started is deprecated and will be removed in a '
|
|
||||||
'a future release. Please use state=present instead')
|
|
||||||
elif state == 'stopped':
|
|
||||||
state = 'absent'
|
|
||||||
warnings.append('state=stopped is deprecated and will be removed in a '
|
|
||||||
'a future release. Please use state=absent instead')
|
|
||||||
|
|
||||||
commands = list()
|
if commands:
|
||||||
instance = get_instance(module)
|
if not module.check_mode:
|
||||||
|
load_config(module, commands)
|
||||||
|
result['changed'] = True
|
||||||
|
|
||||||
invoke(state, module, instance, commands)
|
|
||||||
|
|
||||||
try:
|
|
||||||
load(module, commands, result)
|
|
||||||
except (ValueError, NetworkError):
|
|
||||||
load_checkpoint(module, result)
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc), **exc.kwargs)
|
|
||||||
|
|
||||||
clean_result(result)
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages configuration of an ospf instance.
|
||||||
description:
|
description:
|
||||||
- Manages configuration of an ospf instance.
|
- Manages configuration of an ospf instance.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
ospf:
|
ospf:
|
||||||
description:
|
description:
|
||||||
|
@ -81,156 +80,13 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
import ansible.module_utils.nxos
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'ospf': 'router ospf'
|
'ospf': 'router ospf'
|
||||||
|
@ -304,9 +160,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
ospf = str(module.params['ospf'])
|
ospf = str(module.params['ospf'])
|
||||||
|
|
||||||
|
@ -345,3 +208,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages a VRF for an OSPF router.
|
||||||
description:
|
description:
|
||||||
- Manages a VRF for an OSPF router.
|
- Manages a VRF for an OSPF router.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- Value I(default) restores params default value, if any.
|
- Value I(default) restores params default value, if any.
|
||||||
Otherwise it removes the existing param configuration.
|
Otherwise it removes the existing param configuration.
|
||||||
|
@ -172,156 +171,13 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
import ansible.module_utils.nxos
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
|
@ -527,9 +383,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
args = [
|
args = [
|
||||||
'vrf',
|
'vrf',
|
||||||
|
@ -591,3 +454,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Configures anycast gateway MAC of the switch.
|
||||||
description:
|
description:
|
||||||
- Configures anycast gateway MAC of the switch.
|
- Configures anycast gateway MAC of the switch.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- Default restores params default value
|
- Default restores params default value
|
||||||
- Supported MAC address format are "E.E.E", "EE-EE-EE-EE-EE-EE",
|
- Supported MAC address format are "E.E.E", "EE-EE-EE-EE-EE-EE",
|
||||||
|
@ -109,159 +108,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'anycast_gateway_mac': 'fabric forwarding anycast-gateway-mac',
|
'anycast_gateway_mac': 'fabric forwarding anycast-gateway-mac',
|
||||||
|
@ -372,13 +223,16 @@ def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
anycast_gateway_mac=dict(required=True, type='str'),
|
anycast_gateway_mac=dict(required=True, type='str'),
|
||||||
m_facts=dict(required=False, default=False, type='bool'),
|
m_facts=dict(required=False, default=False, type='bool'),
|
||||||
include_defaults=dict(default=True),
|
|
||||||
config=dict(),
|
|
||||||
save=dict(type='bool', default=False)
|
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
'anycast_gateway_mac'
|
'anycast_gateway_mac'
|
||||||
]
|
]
|
||||||
|
@ -392,22 +246,20 @@ def main():
|
||||||
candidate = CustomNetworkConfig(indent=3)
|
candidate = CustomNetworkConfig(indent=3)
|
||||||
invoke('get_commands', module, existing, proposed, candidate)
|
invoke('get_commands', module, existing, proposed, candidate)
|
||||||
|
|
||||||
try:
|
if not module.check_mode:
|
||||||
response = load_config(module, candidate)
|
load_config(module, candidate)
|
||||||
result.update(response)
|
|
||||||
except ShellError:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc))
|
|
||||||
|
|
||||||
result['connected'] = module.connected
|
|
||||||
if module._verbosity > 0:
|
if module._verbosity > 0:
|
||||||
end_state = invoke('get_existing', module, args)
|
end_state = invoke('get_existing', module, args)
|
||||||
result['end_state'] = end_state
|
result['end_state'] = end_state
|
||||||
result['existing'] = existing
|
result['existing'] = existing
|
||||||
result['proposed'] = proposed
|
result['proposed'] = proposed
|
||||||
|
|
||||||
|
result['warnings'] = True
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages configuration of a PIM instance.
|
||||||
description:
|
description:
|
||||||
- Manages configuration of a Protocol Independent Multicast (PIM) instance.
|
- Manages configuration of a Protocol Independent Multicast (PIM) instance.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
ssm_range:
|
ssm_range:
|
||||||
description:
|
description:
|
||||||
|
@ -73,159 +72,13 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
import re
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
|
@ -291,9 +144,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
splitted_ssm_range = module.params['ssm_range'].split('.')
|
splitted_ssm_range = module.params['ssm_range'].split('.')
|
||||||
if len(splitted_ssm_range) != 4 and module.params['ssm_range'] != 'none':
|
if len(splitted_ssm_range) != 4 and module.params['ssm_range'] != 'none':
|
||||||
module.fail_json(msg="Valid ssm_range values are multicast addresses "
|
module.fail_json(msg="Valid ssm_range values are multicast addresses "
|
||||||
|
@ -334,3 +194,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages PIM interface configuration.
|
short_description: Manages PIM interface configuration.
|
||||||
description:
|
description:
|
||||||
- Manages PIM interface configuration settings.
|
- Manages PIM interface configuration settings.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -187,232 +186,15 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module, text=False):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n' or '^' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command or text:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show', text=False):
|
def execute_show_command(command, module, command_type='cli_show', text=False):
|
||||||
|
@ -420,11 +202,10 @@ def execute_show_command(command, module, command_type='cli_show', text=False):
|
||||||
if 'show run' not in command and text is False:
|
if 'show run' not in command and text is False:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module, text=text)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -814,9 +595,16 @@ def main():
|
||||||
state=dict(choices=['present', 'absent', 'default'],
|
state=dict(choices=['present', 'absent', 'default'],
|
||||||
default='present'),
|
default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
sparse = module.params['sparse']
|
sparse = module.params['sparse']
|
||||||
|
@ -912,7 +700,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
get_existing = get_pim_interface(module, interface)
|
get_existing = get_pim_interface(module, interface)
|
||||||
end_state, jp_bidir, isauth = local_existing(get_existing)
|
end_state, jp_bidir, isauth = local_existing(get_existing)
|
||||||
|
@ -923,6 +711,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -930,3 +719,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- Manages configuration of an Protocol Independent Multicast (PIM) static
|
- Manages configuration of an Protocol Independent Multicast (PIM) static
|
||||||
rendezvous point (RP) address instance.
|
rendezvous point (RP) address instance.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- C(state=absent) remove the whole rp-address configuration, if existing.
|
- C(state=absent) remove the whole rp-address configuration, if existing.
|
||||||
options:
|
options:
|
||||||
|
@ -103,159 +102,13 @@ changed:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
import re
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
BOOL_PARAMS = ['bidir']
|
BOOL_PARAMS = ['bidir']
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
|
@ -357,12 +210,19 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['group_list', 'route_map'],
|
mutually_exclusive=[['group_list', 'route_map'],
|
||||||
['group_list', 'prefix_list'],
|
['group_list', 'prefix_list'],
|
||||||
['route_map', 'prefix_list']],
|
['route_map', 'prefix_list']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
|
@ -414,3 +274,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.1"
|
||||||
short_description: Tests reachability using ping from Nexus switch.
|
short_description: Tests reachability using ping from Nexus switch.
|
||||||
description:
|
description:
|
||||||
- Tests reachability using ping from switch to a remote destination.
|
- Tests reachability using ping from switch to a remote destination.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -115,163 +114,9 @@ packet_loss:
|
||||||
type: string
|
type: string
|
||||||
sample: "0.00%"
|
sample: "0.00%"
|
||||||
'''
|
'''
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import json
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
import collections
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def get_summary(results_list, reference_point):
|
def get_summary(results_list, reference_point):
|
||||||
summary_string = results_list[reference_point+1]
|
summary_string = results_list[reference_point+1]
|
||||||
|
@ -313,48 +158,9 @@ def get_statistics_summary_line(response_as_list):
|
||||||
return index
|
return index
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command_ping(command, module, command_type='cli_show_ascii'):
|
|
||||||
cmds = [command]
|
|
||||||
if module.params['transport'] == 'cli':
|
|
||||||
body = execute_show(cmds, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def get_ping_results(command, module, transport):
|
def get_ping_results(command, module, transport):
|
||||||
ping = execute_show_command_ping(command, module)[0]
|
cmd = {'command': command, 'output': 'text'}
|
||||||
|
ping = run_commands(module, [cmd])[0]
|
||||||
|
|
||||||
if not ping:
|
if not ping:
|
||||||
module.fail_json(msg="An unexpected error occurred. Check all params.",
|
module.fail_json(msg="An unexpected error occurred. Check all params.",
|
||||||
|
@ -388,9 +194,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
destination = module.params['dest']
|
destination = module.params['dest']
|
||||||
count = module.params['count']
|
count = module.params['count']
|
||||||
vrf = module.params['vrf']
|
vrf = module.params['vrf']
|
||||||
|
@ -444,3 +257,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages port-channel interfaces.
|
short_description: Manages port-channel interfaces.
|
||||||
description:
|
description:
|
||||||
- Manages port-channel specific configuration parameters.
|
- Manages port-channel specific configuration parameters.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -125,162 +124,15 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import json
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
WARNINGS = []
|
WARNINGS = []
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'min_links': 'lacp min-links'
|
'min_links': 'lacp min-links'
|
||||||
|
@ -326,75 +178,15 @@ def get_custom_value(arg, config, module):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
output = module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
output = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show port-channel summary' in command:
|
if 'show port-channel summary' in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -654,9 +446,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
group = str(module.params['group'])
|
group = str(module.params['group'])
|
||||||
mode = module.params['mode']
|
mode = module.params['mode']
|
||||||
min_links = module.params['min_links']
|
min_links = module.params['min_links']
|
||||||
|
@ -723,7 +522,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
output = execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
end_state, interface_exist = get_existing(module, args)
|
end_state, interface_exist = get_existing(module, args)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
|
@ -735,6 +534,7 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
if WARNINGS:
|
if WARNINGS:
|
||||||
results['warnings'] = WARNINGS
|
results['warnings'] = WARNINGS
|
||||||
|
@ -744,3 +544,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: 2.2
|
||||||
short_description: Reboot a network device.
|
short_description: Reboot a network device.
|
||||||
description:
|
description:
|
||||||
- Reboot a network device.
|
- Reboot a network device.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -58,244 +57,40 @@ rebooted:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import collections
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def reboot(module):
|
def reboot(module):
|
||||||
disable_confirmation(module)
|
cmds = [
|
||||||
execute_show_command(['reload'], module, command_type='cli_show_ascii')
|
{'command': 'terminal-dont-ask'},
|
||||||
|
{'command': 'reload', 'output': 'text'}
|
||||||
|
]
|
||||||
def execute_show(cmds, module, command_type=None):
|
run_commands(module, cmds)
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
|
||||||
if module.params['transport'] == 'cli':
|
|
||||||
body = execute_show(command, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
|
||||||
body = execute_show(command, module, command_type=command_type)
|
|
||||||
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def disable_confirmation(module):
|
|
||||||
command = ['terminal dont-ask']
|
|
||||||
body = execute_show_command(command, module, command_type='cli_show_ascii')[0]
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = {}
|
||||||
confirm=dict(required=True, type='bool'),
|
argument_spec.update(nxos_argument_spec)
|
||||||
include_defaults=dict(default=False),
|
|
||||||
config=dict(),
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
save=dict(type='bool', default=False)
|
|
||||||
)
|
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
confirm = module.params['confirm']
|
warnings = list()
|
||||||
if not confirm:
|
check_args(module, warnings)
|
||||||
module.fail_json(msg='confirm must be set to true for this '
|
|
||||||
'module to work.')
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
rebooted = False
|
|
||||||
|
|
||||||
|
if not module.check_mode:
|
||||||
reboot(module)
|
reboot(module)
|
||||||
|
|
||||||
changed = True
|
changed = True
|
||||||
rebooted = True
|
|
||||||
|
|
||||||
results = {}
|
results = {
|
||||||
results['changed'] = changed
|
'changed': True,
|
||||||
results['rebooted'] = rebooted
|
'warnings': warnings
|
||||||
|
}
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- This module offers the ability to set a configuration checkpoint
|
- This module offers the ability to set a configuration checkpoint
|
||||||
file or rollback to a configuration checkpoint file on Cisco NXOS
|
file or rollback to a configuration checkpoint file on Cisco NXOS
|
||||||
switches.
|
switches.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -77,159 +76,13 @@ status:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
import re
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_commands(cmds, module, command_type=None):
|
def execute_commands(cmds, module, command_type=None):
|
||||||
|
@ -297,7 +150,10 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['checkpoint_file',
|
mutually_exclusive=[['checkpoint_file',
|
||||||
'rollback_to']],
|
'rollback_to']],
|
||||||
supports_check_mode=False)
|
supports_check_mode=False)
|
||||||
|
@ -326,3 +182,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Perform SMUs on Cisco NX-OS devices.
|
short_description: Perform SMUs on Cisco NX-OS devices.
|
||||||
description:
|
description:
|
||||||
- Perform software maintenance upgrades (SMUs) on Cisco NX-OS devices.
|
- Perform software maintenance upgrades (SMUs) on Cisco NX-OS devices.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
- The module can only activate and commit a package,
|
- The module can only activate and commit a package,
|
||||||
|
@ -80,202 +79,24 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import json
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -288,28 +109,9 @@ def remote_file_exists(module, dst, file_system='bootflash:'):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
output = module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
output = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def apply_patch(module, commands):
|
def apply_patch(module, commands):
|
||||||
for command in commands:
|
for command in commands:
|
||||||
response = execute_config_command([command], module)
|
load_config(module, [command])
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
if 'failed' in response:
|
if 'failed' in response:
|
||||||
module.fail_json(msg="Operation failed!", response=response)
|
module.fail_json(msg="Operation failed!", response=response)
|
||||||
|
@ -350,9 +152,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
pkg = module.params['pkg']
|
pkg = module.params['pkg']
|
||||||
file_system = module.params['file_system']
|
file_system = module.params['file_system']
|
||||||
changed = False
|
changed = False
|
||||||
|
@ -382,3 +191,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- Create snapshots of the running states of selected features, add
|
- Create snapshots of the running states of selected features, add
|
||||||
new show commands for snapshot creation, delete and compare
|
new show commands for snapshot creation, delete and compare
|
||||||
existing snapshots.
|
existing snapshots.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
|
@ -209,199 +208,22 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
import os
|
import os
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show_ascii'):
|
def execute_show_command(command, module, command_type='cli_show_ascii'):
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
body = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -535,24 +357,6 @@ def invoke(name, *args, **kwargs):
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_snapshot(module):
|
def get_snapshot(module):
|
||||||
command = 'show snapshot dump {0}'.format(module.params['snapshot_name'])
|
command = 'show snapshot dump {0}'.format(module.params['snapshot_name'])
|
||||||
body = execute_show_command(command, module)[0]
|
body = execute_show_command(command, module)[0]
|
||||||
|
@ -594,11 +398,18 @@ def main():
|
||||||
default=False),
|
default=False),
|
||||||
path=dict(required=False, type='str', default='./')
|
path=dict(required=False, type='str', default='./')
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['delete_all',
|
mutually_exclusive=[['delete_all',
|
||||||
'delete_snapshot']],
|
'delete_snapshot']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
action = module.params['action']
|
action = module.params['action']
|
||||||
comparison_results_file = module.params['comparison_results_file']
|
comparison_results_file = module.params['comparison_results_file']
|
||||||
|
|
||||||
|
@ -647,7 +458,7 @@ def main():
|
||||||
result['updates'] = []
|
result['updates'] = []
|
||||||
else:
|
else:
|
||||||
if action_results:
|
if action_results:
|
||||||
execute_config_command(action_results, module)
|
load_config(module, action_results)
|
||||||
changed = True
|
changed = True
|
||||||
final_snapshots = invoke('get_existing', module)
|
final_snapshots = invoke('get_existing', module)
|
||||||
result['updates'] = action_results
|
result['updates'] = action_results
|
||||||
|
@ -672,3 +483,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages SNMP community configs.
|
short_description: Manages SNMP community configs.
|
||||||
description:
|
description:
|
||||||
- Manages SNMP community configuration.
|
- Manages SNMP community configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -98,231 +97,14 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -330,11 +112,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -435,11 +216,18 @@ def main():
|
||||||
acl=dict(type='str'),
|
acl=dict(type='str'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_one_of=[['access', 'group']],
|
required_one_of=[['access', 'group']],
|
||||||
mutually_exclusive=[['access', 'group']],
|
mutually_exclusive=[['access', 'group']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
access = module.params['access']
|
access = module.params['access']
|
||||||
group = module.params['group']
|
group = module.params['group']
|
||||||
community = module.params['community']
|
community = module.params['community']
|
||||||
|
@ -484,7 +272,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_snmp_community(module, community)
|
end_state = get_snmp_community(module, community)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -495,9 +283,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages SNMP contact info.
|
short_description: Manages SNMP contact info.
|
||||||
description:
|
description:
|
||||||
- Manages SNMP contact information.
|
- Manages SNMP contact information.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -83,231 +82,14 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -315,11 +97,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -357,9 +138,16 @@ def main():
|
||||||
state=dict(choices=['absent', 'present'],
|
state=dict(choices=['absent', 'present'],
|
||||||
default='present')
|
default='present')
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
contact = module.params['contact']
|
contact = module.params['contact']
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
|
@ -382,7 +170,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_snmp_contact(module)
|
end_state = get_snmp_contact(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -393,9 +181,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages SNMP host configuration.
|
short_description: Manages SNMP host configuration.
|
||||||
description:
|
description:
|
||||||
- Manages SNMP host configuration parameters.
|
- Manages SNMP host configuration parameters.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -131,231 +130,14 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -363,11 +145,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -540,9 +321,16 @@ def main():
|
||||||
snmp_type=dict(choices=['trap', 'inform'], default='trap'),
|
snmp_type=dict(choices=['trap', 'inform'], default='trap'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
snmp_host = module.params['snmp_host']
|
snmp_host = module.params['snmp_host']
|
||||||
community = module.params['community']
|
community = module.params['community']
|
||||||
|
@ -620,7 +408,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_snmp_host(snmp_host, module)
|
end_state = get_snmp_host(snmp_host, module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -634,9 +422,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages SNMP location information.
|
short_description: Manages SNMP location information.
|
||||||
description:
|
description:
|
||||||
- Manages SNMP location configuration.
|
- Manages SNMP location configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -90,231 +89,14 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -322,11 +104,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -376,9 +157,16 @@ def main():
|
||||||
state=dict(choices=['absent', 'present'],
|
state=dict(choices=['absent', 'present'],
|
||||||
default='present')
|
default='present')
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
location = module.params['location']
|
location = module.params['location']
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
@ -402,7 +190,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_snmp_location(module)
|
end_state = get_snmp_location(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -413,6 +201,7 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
@ -420,3 +209,4 @@ def main():
|
||||||
from ansible.module_utils.basic import *
|
from ansible.module_utils.basic import *
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages SNMP traps.
|
short_description: Manages SNMP traps.
|
||||||
description:
|
description:
|
||||||
- Manages SNMP traps configurations.
|
- Manages SNMP traps configurations.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -101,231 +100,14 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -333,11 +115,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -461,9 +242,16 @@ def main():
|
||||||
'sysmgr', 'system', 'upgrade', 'vtp', 'all'],
|
'sysmgr', 'system', 'upgrade', 'vtp', 'all'],
|
||||||
required=True),
|
required=True),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
group = module.params['group'].lower()
|
group = module.params['group'].lower()
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
|
@ -480,7 +268,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_snmp_traps(group, module)
|
end_state = get_snmp_traps(group, module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -491,9 +279,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages SNMP users for monitoring.
|
short_description: Manages SNMP users for monitoring.
|
||||||
description:
|
description:
|
||||||
- Manages SNMP user configuration.
|
- Manages SNMP user configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -112,231 +111,14 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module, text=False):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command or text:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show', text=False):
|
def execute_show_command(command, module, command_type='cli_show', text=False):
|
||||||
|
@ -344,11 +126,10 @@ def execute_show_command(command, module, command_type='cli_show', text=False):
|
||||||
if 'show run' not in command and text is False:
|
if 'show run' not in command and text is False:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module, text=text)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -462,11 +243,18 @@ def main():
|
||||||
encrypt=dict(type='bool'),
|
encrypt=dict(type='bool'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_together=[['authentication', 'pwd'],
|
required_together=[['authentication', 'pwd'],
|
||||||
['encrypt', 'privacy']],
|
['encrypt', 'privacy']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
user = module.params['user']
|
user = module.params['user']
|
||||||
group = module.params['group']
|
group = module.params['group']
|
||||||
pwd = module.params['pwd']
|
pwd = module.params['pwd']
|
||||||
|
@ -540,7 +328,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_snmp_user(user, module)
|
end_state = get_snmp_user(user, module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -552,6 +340,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -559,3 +348,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ short_description: Manages static route configuration
|
||||||
description:
|
description:
|
||||||
- Manages static route configuration
|
- Manages static route configuration
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- If no vrf is supplied, vrf is set to default.
|
- If no vrf is supplied, vrf is set to default.
|
||||||
- If C(state=absent), the route will be removed, regardless of the
|
- If C(state=absent), the route will be removed, regardless of the
|
||||||
|
@ -111,159 +110,12 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine, dumps
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.network import NetworkModule
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
if self._device_os == 'junos':
|
|
||||||
return dumps(section, output='lines')
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def invoke(name, *args, **kwargs):
|
def invoke(name, *args, **kwargs):
|
||||||
func = globals().get(name)
|
func = globals().get(name)
|
||||||
|
@ -429,9 +281,14 @@ def main():
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
result = dict(changed=False)
|
result = dict(changed=False)
|
||||||
|
@ -448,12 +305,7 @@ def main():
|
||||||
candidate = CustomNetworkConfig(indent=3)
|
candidate = CustomNetworkConfig(indent=3)
|
||||||
invoke('state_%s' % state, module, candidate, prefix)
|
invoke('state_%s' % state, module, candidate, prefix)
|
||||||
|
|
||||||
try:
|
load_config(module, candidate)
|
||||||
response = load_config(module, candidate)
|
|
||||||
result.update(response)
|
|
||||||
except Exception:
|
|
||||||
exc = get_exception()
|
|
||||||
module.fail_json(msg=str(exc))
|
|
||||||
else:
|
else:
|
||||||
result['updates'] = []
|
result['updates'] = []
|
||||||
|
|
||||||
|
@ -470,3 +322,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ DOCUMENTATION = '''
|
||||||
module: nxos_switchport
|
module: nxos_switchport
|
||||||
version_added: "2.1"
|
version_added: "2.1"
|
||||||
short_description: Manages Layer 2 switchport interfaces.
|
short_description: Manages Layer 2 switchport interfaces.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
description:
|
description:
|
||||||
- Manages Layer 2 interfaces
|
- Manages Layer 2 interfaces
|
||||||
author: Jason Edelman (@jedelman8)
|
author: Jason Edelman (@jedelman8)
|
||||||
|
@ -154,161 +153,14 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def get_interface_type(interface):
|
def get_interface_type(interface):
|
||||||
"""Gets the type of interface
|
"""Gets the type of interface
|
||||||
|
@ -605,86 +457,15 @@ def apply_value_map(value_map, resource):
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'status' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'status' not in command:
|
if 'status' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -711,12 +492,19 @@ def main():
|
||||||
state=dict(choices=['absent', 'present', 'unconfigured'],
|
state=dict(choices=['absent', 'present', 'unconfigured'],
|
||||||
default='present')
|
default='present')
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['access_vlan', 'trunk_vlans'],
|
mutually_exclusive=[['access_vlan', 'trunk_vlans'],
|
||||||
['access_vlan', 'native_vlan'],
|
['access_vlan', 'native_vlan'],
|
||||||
['access_vlan', 'trunk_allowed_vlans']],
|
['access_vlan', 'trunk_allowed_vlans']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
interface = module.params['interface']
|
interface = module.params['interface']
|
||||||
mode = module.params['mode']
|
mode = module.params['mode']
|
||||||
access_vlan = module.params['access_vlan']
|
access_vlan = module.params['access_vlan']
|
||||||
|
@ -817,7 +605,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_switchport(interface, module)
|
end_state = get_switchport(interface, module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -828,8 +616,10 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages UDLD global configuration params.
|
short_description: Manages UDLD global configuration params.
|
||||||
description:
|
description:
|
||||||
- Manages UDLD global configuration params.
|
- Manages UDLD global configuration params.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -108,231 +107,14 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -340,11 +122,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -435,10 +216,17 @@ def main():
|
||||||
reset=dict(required=False, type='bool'),
|
reset=dict(required=False, type='bool'),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
required_one_of=[['aggressive', 'msg_time', 'reset']],
|
required_one_of=[['aggressive', 'msg_time', 'reset']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
aggressive = module.params['aggressive']
|
aggressive = module.params['aggressive']
|
||||||
msg_time = module.params['msg_time']
|
msg_time = module.params['msg_time']
|
||||||
reset = module.params['reset']
|
reset = module.params['reset']
|
||||||
|
@ -486,7 +274,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_udld_global(module)
|
end_state = get_udld_global(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -497,9 +285,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages UDLD interface configuration params.
|
short_description: Manages UDLD interface configuration params.
|
||||||
description:
|
description:
|
||||||
- Manages UDLD interface configuration params.
|
- Manages UDLD interface configuration params.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
notes:
|
notes:
|
||||||
|
@ -107,231 +106,14 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -339,11 +121,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -466,9 +247,16 @@ def main():
|
||||||
interface=dict(type='str', required=True),
|
interface=dict(type='str', required=True),
|
||||||
state=dict(choices=['absent', 'present'], default='present'),
|
state=dict(choices=['absent', 'present'], default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
interface = module.params['interface'].lower()
|
interface = module.params['interface'].lower()
|
||||||
mode = module.params['mode']
|
mode = module.params['mode']
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
@ -500,7 +288,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_udld_interface(module, interface)
|
end_state = get_udld_interface(module, interface)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -511,8 +299,10 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
357
lib/ansible/modules/network/nxos/nxos_user.py
Normal file
357
lib/ansible/modules/network/nxos/nxos_user.py
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {
|
||||||
|
'status': ['preview'],
|
||||||
|
'supported_by': 'core',
|
||||||
|
'version': '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
DOCUMENTATION = """
|
||||||
|
---
|
||||||
|
module: nxos_user
|
||||||
|
version_added: "2.3"
|
||||||
|
author: "Peter Sprygada (@privateip)"
|
||||||
|
short_description: Manage the collection of local users on Nexus devices
|
||||||
|
description:
|
||||||
|
- This module provides declarative management of the local usernames
|
||||||
|
configured on Cisco Nexus devices. It allows playbooks to manage
|
||||||
|
either individual usernames or the collection of usernames in the
|
||||||
|
current running config. It also supports purging usernames from the
|
||||||
|
configuration that are not explicitly defined.
|
||||||
|
options:
|
||||||
|
users:
|
||||||
|
description:
|
||||||
|
- The set of username objects to be configured on the remote
|
||||||
|
Cisco Nexus device. The list entries can either be the username
|
||||||
|
or a hash of username and properties. This argument is mutually
|
||||||
|
exclusive with the C(name) argument.
|
||||||
|
required: false
|
||||||
|
default: null
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The username to be configured on the remote Cisco Nexus
|
||||||
|
device. This argument accepts a stringv value and is mutually
|
||||||
|
exclusive with the C(users) argument.
|
||||||
|
required: false
|
||||||
|
default: null
|
||||||
|
update_password:
|
||||||
|
description:
|
||||||
|
- Since passwords are encrypted in the device running config, this
|
||||||
|
argument will instruct the module when to change the password. When
|
||||||
|
set to C(always), the password will always be updated in the device
|
||||||
|
and when set to C(on_create) the password will be updated only if
|
||||||
|
the username is created.
|
||||||
|
required: false
|
||||||
|
default: always
|
||||||
|
choices: ['on_create', 'always']
|
||||||
|
role:
|
||||||
|
description:
|
||||||
|
- The C(role) argument configures the role for the username in the
|
||||||
|
device running configuration. The argument accepts a string value
|
||||||
|
defining the role name. This argument does not check if the role
|
||||||
|
has been configured on the device.
|
||||||
|
required: false
|
||||||
|
default: null
|
||||||
|
sshkey:
|
||||||
|
description:
|
||||||
|
- The C(sshkey) argument defines the SSH public key to configure
|
||||||
|
for the username. This argument accepts a valid SSH key value.
|
||||||
|
required: false
|
||||||
|
default: null
|
||||||
|
purge:
|
||||||
|
description:
|
||||||
|
- The C(purge) argument instructs the module to consider the
|
||||||
|
resource definition absolute. It will remove any previously
|
||||||
|
configured usernames on the device with the exception of the
|
||||||
|
`admin` user which cannot be deleted per nxos constraints.
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- The C(state) argument configures the state of the username definition
|
||||||
|
as it relates to the device operational configuration. When set
|
||||||
|
to I(present), the username(s) should be configured in the device active
|
||||||
|
configuration and when set to I(absent) the username(s) should not be
|
||||||
|
in the device active configuration
|
||||||
|
required: false
|
||||||
|
default: present
|
||||||
|
choices: ['present', 'absent']
|
||||||
|
"""
|
||||||
|
|
||||||
|
EXAMPLES = """
|
||||||
|
- name: create a new user
|
||||||
|
nxos_user:
|
||||||
|
name: ansible
|
||||||
|
sshkey: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: remove all users except admin
|
||||||
|
nxos_user:
|
||||||
|
purge: yes
|
||||||
|
|
||||||
|
- name: set multiple users role
|
||||||
|
users:
|
||||||
|
- name: netop
|
||||||
|
- name: netend
|
||||||
|
role: network-operator
|
||||||
|
state: present
|
||||||
|
"""
|
||||||
|
|
||||||
|
RETURN = """
|
||||||
|
commands:
|
||||||
|
description: The list of configuration mode commands to send to the device
|
||||||
|
returned: always
|
||||||
|
type: list
|
||||||
|
sample:
|
||||||
|
- name ansible
|
||||||
|
- name ansible password password
|
||||||
|
start:
|
||||||
|
description: The time the job started
|
||||||
|
returned: always
|
||||||
|
type: str
|
||||||
|
sample: "2016-11-16 10:38:15.126146"
|
||||||
|
end:
|
||||||
|
description: The time the job ended
|
||||||
|
returned: always
|
||||||
|
type: str
|
||||||
|
sample: "2016-11-16 10:38:25.595612"
|
||||||
|
delta:
|
||||||
|
description: The time elapsed to perform all operations
|
||||||
|
returned: always
|
||||||
|
type: str
|
||||||
|
sample: "0:00:10.469466"
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from ansible.module_utils.nxos import run_commands, load_config
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.six import string_types, iteritems
|
||||||
|
from ansible.module_utils.network_common import to_list
|
||||||
|
|
||||||
|
VALID_ROLES = ['network-admin', 'network-operator', 'vdc-admin', 'vdc-operator',
|
||||||
|
'priv-15', 'priv-14', 'priv-13', 'priv-12', 'priv-11', 'priv-10',
|
||||||
|
'priv-9', 'priv-8', 'priv-7', 'priv-6', 'priv-5', 'priv-4',
|
||||||
|
'priv-3', 'priv-2', 'priv-1', 'priv-0']
|
||||||
|
|
||||||
|
|
||||||
|
def validate_roles(value, module):
|
||||||
|
for item in value:
|
||||||
|
if item not in VALID_ROLES:
|
||||||
|
module.fail_json(msg='invalid role specified')
|
||||||
|
|
||||||
|
def map_obj_to_commands(updates, module):
|
||||||
|
commands = list()
|
||||||
|
state = module.params['state']
|
||||||
|
update_password = module.params['update_password']
|
||||||
|
|
||||||
|
for update in updates:
|
||||||
|
want, have = update
|
||||||
|
|
||||||
|
needs_update = lambda x: want.get(x) and (want.get(x) != have.get(x))
|
||||||
|
add = lambda x: commands.append('username %s %s' % (want['name'], x))
|
||||||
|
remove = lambda x: commands.append('no username %s %s' % (want['name'], x))
|
||||||
|
|
||||||
|
if want['state'] == 'absent':
|
||||||
|
commands.append('no username %s' % want['name'])
|
||||||
|
continue
|
||||||
|
|
||||||
|
if want['state'] == 'present' and not have:
|
||||||
|
commands.append('username %s' % want['name'])
|
||||||
|
|
||||||
|
if needs_update('password'):
|
||||||
|
if update_password == 'always' or not have:
|
||||||
|
add('password %s' % want['password'])
|
||||||
|
|
||||||
|
if needs_update('sshkey'):
|
||||||
|
add('sshkey %s' % want['sshkey'])
|
||||||
|
|
||||||
|
|
||||||
|
if want['roles']:
|
||||||
|
if have:
|
||||||
|
for item in set(have['roles']).difference(want['roles']):
|
||||||
|
remove('role %s' % item)
|
||||||
|
|
||||||
|
for item in set(want['roles']).difference(have['roles']):
|
||||||
|
add('role %s' % item)
|
||||||
|
else:
|
||||||
|
for item in want['roles']:
|
||||||
|
add('role %s' % item)
|
||||||
|
|
||||||
|
|
||||||
|
return commands
|
||||||
|
|
||||||
|
def parse_password(data):
|
||||||
|
if not data.get('remote_login'):
|
||||||
|
return '<PASSWORD>'
|
||||||
|
|
||||||
|
def parse_roles(data):
|
||||||
|
configured_roles = data.get('TABLE_role')['ROW_role']
|
||||||
|
roles = list()
|
||||||
|
if configured_roles:
|
||||||
|
for item in to_list(configured_roles):
|
||||||
|
roles.append(item['role'])
|
||||||
|
return roles
|
||||||
|
|
||||||
|
def map_config_to_obj(module):
|
||||||
|
out = run_commands(module, ['show user-account | json'])
|
||||||
|
data = out[0]
|
||||||
|
|
||||||
|
objects = list()
|
||||||
|
|
||||||
|
for item in to_list(data['TABLE_template']['ROW_template']):
|
||||||
|
objects.append({
|
||||||
|
'name': item['usr_name'],
|
||||||
|
'password': parse_password(item),
|
||||||
|
'sshkey': item.get('sshkey_info'),
|
||||||
|
'roles': parse_roles(item),
|
||||||
|
'state': 'present'
|
||||||
|
})
|
||||||
|
return objects
|
||||||
|
|
||||||
|
def get_param_value(key, item, module):
|
||||||
|
# if key doesn't exist in the item, get it from module.params
|
||||||
|
if not item.get(key):
|
||||||
|
value = module.params[key]
|
||||||
|
|
||||||
|
# if key does exist, do a type check on it to validate it
|
||||||
|
else:
|
||||||
|
value_type = module.argument_spec[key].get('type', 'str')
|
||||||
|
type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type]
|
||||||
|
type_checker(item[key])
|
||||||
|
value = item[key]
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def map_params_to_obj(module):
|
||||||
|
users = module.params['users']
|
||||||
|
if not users:
|
||||||
|
if not module.params['name'] and module.params['purge']:
|
||||||
|
return list()
|
||||||
|
elif not module.params['name']:
|
||||||
|
module.fail_json(msg='username is required')
|
||||||
|
else:
|
||||||
|
collection = [{'name': module.params['name']}]
|
||||||
|
else:
|
||||||
|
collection = list()
|
||||||
|
for item in users:
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
collection.append({'name': item})
|
||||||
|
elif 'name' not in item:
|
||||||
|
module.fail_json(msg='name is required')
|
||||||
|
else:
|
||||||
|
collection.append(item)
|
||||||
|
|
||||||
|
objects = list()
|
||||||
|
|
||||||
|
for item in collection:
|
||||||
|
get_value = partial(get_param_value, item=item, module=module)
|
||||||
|
item.update({
|
||||||
|
'password': get_value('password'),
|
||||||
|
'sshkey': get_value('sshkey'),
|
||||||
|
'roles': get_value('roles'),
|
||||||
|
'state': get_value('state')
|
||||||
|
})
|
||||||
|
|
||||||
|
for key, value in iteritems(item):
|
||||||
|
if value:
|
||||||
|
# validate the param value (if validator func exists)
|
||||||
|
validator = globals().get('validate_%s' % key)
|
||||||
|
if all((value, validator)):
|
||||||
|
validator(value, module)
|
||||||
|
|
||||||
|
objects.append(item)
|
||||||
|
|
||||||
|
return objects
|
||||||
|
|
||||||
|
def update_objects(want, have):
|
||||||
|
updates = list()
|
||||||
|
for entry in want:
|
||||||
|
item = next((i for i in have if i['name'] == entry['name']), None)
|
||||||
|
if all((item is None, entry['state'] == 'present')):
|
||||||
|
updates.append((entry, {}))
|
||||||
|
elif item:
|
||||||
|
for key, value in iteritems(entry):
|
||||||
|
if value and value != item[key]:
|
||||||
|
updates.append((entry, item))
|
||||||
|
return updates
|
||||||
|
|
||||||
|
def main():
|
||||||
|
""" main entry point for module execution
|
||||||
|
"""
|
||||||
|
argument_spec = dict(
|
||||||
|
users=dict(type='list', no_log=True),
|
||||||
|
name=dict(),
|
||||||
|
|
||||||
|
password=dict(no_log=True),
|
||||||
|
update_password=dict(default='always', choices=['on_create', 'always']),
|
||||||
|
|
||||||
|
roles=dict(type='list'),
|
||||||
|
|
||||||
|
sshkey=dict(),
|
||||||
|
|
||||||
|
purge=dict(type='bool', default=False),
|
||||||
|
state=dict(default='present', choices=['present', 'absent'])
|
||||||
|
)
|
||||||
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
mutually_exclusive = [('name', 'users')]
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
|
mutually_exclusive=mutually_exclusive,
|
||||||
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
|
||||||
|
result = {'changed': False}
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
result['warnings'] = warnings
|
||||||
|
|
||||||
|
want = map_params_to_obj(module)
|
||||||
|
have = map_config_to_obj(module)
|
||||||
|
|
||||||
|
commands = map_obj_to_commands(update_objects(want, have), module)
|
||||||
|
|
||||||
|
if module.params['purge']:
|
||||||
|
want_users = [x['name'] for x in want]
|
||||||
|
have_users = [x['name'] for x in have]
|
||||||
|
for item in set(have_users).difference(want_users):
|
||||||
|
if item != 'admin':
|
||||||
|
commands.append('no username %s' % item)
|
||||||
|
|
||||||
|
result['commands'] = commands
|
||||||
|
|
||||||
|
# the nxos cli prevents this by rule so capture it and display
|
||||||
|
# a nice failure message
|
||||||
|
if 'no username admin' in commands:
|
||||||
|
module.fail_json(msg='cannot delete the `admin` account')
|
||||||
|
|
||||||
|
if commands:
|
||||||
|
if not module.check_mode:
|
||||||
|
load_config(module, commands)
|
||||||
|
result['changed'] = True
|
||||||
|
|
||||||
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -28,7 +28,6 @@ short_description: Manages VLAN resources and attributes.
|
||||||
description:
|
description:
|
||||||
- Manages VLAN configurations on NX-OS switches.
|
- Manages VLAN configurations on NX-OS switches.
|
||||||
author: Jason Edelman (@jedelman8)
|
author: Jason Edelman (@jedelman8)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
options:
|
options:
|
||||||
vlan_id:
|
vlan_id:
|
||||||
description:
|
description:
|
||||||
|
@ -153,163 +152,15 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
import json
|
|
||||||
import collections
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import run_commands, load_config, get_config
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def vlan_range_to_list(vlans):
|
def vlan_range_to_list(vlans):
|
||||||
result = []
|
result = []
|
||||||
|
@ -396,8 +247,7 @@ def get_vlan_config_commands(vlan, vid):
|
||||||
|
|
||||||
|
|
||||||
def get_list_of_vlans(module):
|
def get_list_of_vlans(module):
|
||||||
command = 'show vlan'
|
body = run_commands(module, ['show vlan | json'])
|
||||||
body = execute_show_command(command, module)
|
|
||||||
vlan_list = []
|
vlan_list = []
|
||||||
vlan_table = body[0].get('TABLE_vlanbrief')['ROW_vlanbrief']
|
vlan_table = body[0].get('TABLE_vlanbrief')['ROW_vlanbrief']
|
||||||
|
|
||||||
|
@ -411,8 +261,10 @@ def get_list_of_vlans(module):
|
||||||
|
|
||||||
|
|
||||||
def get_vni(vlanid, module):
|
def get_vni(vlanid, module):
|
||||||
command = 'show run all | section vlan.{0}'.format(vlanid)
|
flags = str('all | section vlan.{0}'.format(vlanid)).split(' ')
|
||||||
body = execute_show_command(command, module, command_type='cli_show_ascii')[0]
|
body = get_config(module, flags=flags)
|
||||||
|
#command = 'show run all | section vlan.{0}'.format(vlanid)
|
||||||
|
#body = execute_show_command(command, module, command_type='cli_show_ascii')[0]
|
||||||
value = ''
|
value = ''
|
||||||
if body:
|
if body:
|
||||||
REGEX = re.compile(r'(?:vn-segment\s)(?P<value>.*)$', re.M)
|
REGEX = re.compile(r'(?:vn-segment\s)(?P<value>.*)$', re.M)
|
||||||
|
@ -424,10 +276,11 @@ def get_vni(vlanid, module):
|
||||||
def get_vlan(vlanid, module):
|
def get_vlan(vlanid, module):
|
||||||
"""Get instance of VLAN as a dictionary
|
"""Get instance of VLAN as a dictionary
|
||||||
"""
|
"""
|
||||||
|
command = 'show vlan id %s | json' % vlanid
|
||||||
|
body = run_commands(module, [command])
|
||||||
|
|
||||||
command = 'show vlan id ' + vlanid
|
#command = 'show vlan id ' + vlanid
|
||||||
|
#body = execute_show_command(command, module)
|
||||||
body = execute_show_command(command, module)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vlan_table = body[0]['TABLE_vlanbriefid']['ROW_vlanbriefid']
|
vlan_table = body[0]['TABLE_vlanbriefid']['ROW_vlanbriefid']
|
||||||
|
@ -469,90 +322,6 @@ def apply_value_map(value_map, resource):
|
||||||
resource[key] = value[resource.get(key)]
|
resource[key] = value[resource.get(key)]
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet.
|
|
||||||
"""
|
|
||||||
if 'show run' in command or response[0] == '\n':
|
|
||||||
body = response
|
|
||||||
elif 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
|
||||||
if module.params['transport'] == 'cli':
|
|
||||||
if 'show run' not in command:
|
|
||||||
command += ' | json'
|
|
||||||
cmds = [command]
|
|
||||||
response = execute_show(cmds, module)
|
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
|
||||||
cmds = [command]
|
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
|
||||||
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
vlan_id=dict(required=False, type='str'),
|
vlan_id=dict(required=False, type='str'),
|
||||||
|
@ -567,11 +336,24 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['vlan_range', 'name'],
|
mutually_exclusive=[['vlan_range', 'name'],
|
||||||
['vlan_id', 'vlan_range']],
|
['vlan_id', 'vlan_range']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
vlan_range = module.params['vlan_range']
|
vlan_range = module.params['vlan_range']
|
||||||
vlan_id = module.params['vlan_id']
|
vlan_id = module.params['vlan_id']
|
||||||
name = module.params['name']
|
name = module.params['name']
|
||||||
|
@ -636,7 +418,7 @@ def main():
|
||||||
module.exit_json(changed=True,
|
module.exit_json(changed=True,
|
||||||
commands=commands)
|
commands=commands)
|
||||||
else:
|
else:
|
||||||
execute_config_command(commands, module)
|
load_config(module, commands)
|
||||||
changed = True
|
changed = True
|
||||||
end_state_vlans_list = numerical_sort(get_list_of_vlans(module))
|
end_state_vlans_list = numerical_sort(get_list_of_vlans(module))
|
||||||
if 'configure' in commands:
|
if 'configure' in commands:
|
||||||
|
@ -653,9 +435,12 @@ def main():
|
||||||
results['end_state_vlans_list'] = end_state_vlans_list
|
results['end_state_vlans_list'] = end_state_vlans_list
|
||||||
results['updates'] = commands
|
results['updates'] = commands
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages global VPC configuration
|
short_description: Manages global VPC configuration
|
||||||
description:
|
description:
|
||||||
- Manages global VPC configuration
|
- Manages global VPC configuration
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -145,243 +144,20 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import collections
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
# COMMON CODE FOR MIGRATION
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh.
|
|
||||||
"""
|
|
||||||
if '^' == response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'running' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
if command in response[0]:
|
|
||||||
response = [response[0].split(command)[1]]
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if "section" not in command:
|
if "section" not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
@ -577,9 +353,15 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
domain = module.params['domain']
|
domain = module.params['domain']
|
||||||
role_priority = module.params['role_priority']
|
role_priority = module.params['role_priority']
|
||||||
system_priority = module.params['system_priority']
|
system_priority = module.params['system_priority']
|
||||||
|
@ -640,7 +422,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_vpc(module)
|
end_state = get_vpc(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -651,9 +433,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.2"
|
||||||
short_description: Manages interface VPC configuration
|
short_description: Manages interface VPC configuration
|
||||||
description:
|
description:
|
||||||
- Manages interface VPC configuration
|
- Manages interface VPC configuration
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -97,240 +96,19 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import collections
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
import json
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
# COMMON CODE FOR MIGRATION
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
import re
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
response = module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh.
|
|
||||||
"""
|
|
||||||
if '^' == response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'running' in command or 'xml' in response[0]:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -485,10 +263,17 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
mutually_exclusive=[['vpc', 'peer_link']],
|
mutually_exclusive=[['vpc', 'peer_link']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
portchannel = module.params['portchannel']
|
portchannel = module.params['portchannel']
|
||||||
vpc = module.params['vpc']
|
vpc = module.params['vpc']
|
||||||
peer_link = module.params['peer_link']
|
peer_link = module.params['peer_link']
|
||||||
|
@ -570,7 +355,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
output = execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
output = ' '.join(output)
|
output = ' '.join(output)
|
||||||
if 'error' in output.lower():
|
if 'error' in output.lower():
|
||||||
|
@ -585,9 +370,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.1"
|
||||||
short_description: Manages global VRF configuration.
|
short_description: Manages global VRF configuration.
|
||||||
description:
|
description:
|
||||||
- Manages global VRF configuration.
|
- Manages global VRF configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -117,238 +116,23 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import ansible.module_utils.nxos
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
from ansible.module_utils.network import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh_vrf(module, command, response):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when using multiple |.
|
|
||||||
"""
|
|
||||||
command_splitted = command.split('|')
|
|
||||||
if len(command_splitted) > 2 or 'show run' in command:
|
|
||||||
body = response
|
|
||||||
elif 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
else:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
transport = module.params['provider']['transport']
|
||||||
|
if transport in ['cli', None]:
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh_vrf(module, command, response)
|
else:
|
||||||
elif module.params['transport'] == 'nxapi':
|
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
|
@ -457,9 +241,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
vrf = module.params['vrf']
|
vrf = module.params['vrf']
|
||||||
admin_state = module.params['admin_state'].lower()
|
admin_state = module.params['admin_state'].lower()
|
||||||
description = module.params['description']
|
description = module.params['description']
|
||||||
|
@ -512,7 +303,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=commands)
|
module.exit_json(changed=True, commands=commands)
|
||||||
else:
|
else:
|
||||||
execute_config_command(commands, module)
|
load_config(module, commands)
|
||||||
changed = True
|
changed = True
|
||||||
end_state = get_vrf(vrf, module)
|
end_state = get_vrf(vrf, module)
|
||||||
if 'configure' in commands:
|
if 'configure' in commands:
|
||||||
|
@ -524,9 +315,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = commands
|
results['updates'] = commands
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ short_description: Manages VRF AF.
|
||||||
description:
|
description:
|
||||||
- Manages VRF AF
|
- Manages VRF AF
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- Default, where supported, restores params default value.
|
- Default, where supported, restores params default value.
|
||||||
options:
|
options:
|
||||||
|
@ -104,159 +103,12 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
BOOL_PARAMS = ['route_target_both_auto_evpn']
|
BOOL_PARAMS = ['route_target_both_auto_evpn']
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
|
@ -378,9 +230,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
|
@ -434,3 +293,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ version_added: "2.1"
|
||||||
short_description: Manages interface specific VRF configuration.
|
short_description: Manages interface specific VRF configuration.
|
||||||
description:
|
description:
|
||||||
- Manages interface specific VRF configuration.
|
- Manages interface specific VRF configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -97,240 +96,25 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
|
||||||
import collections
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
WARNINGS = []
|
WARNINGS = []
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh_vrf_interface(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. As such,
|
|
||||||
we assume if '^' is found in response, it is an invalid command. Instead,
|
|
||||||
the output will be a raw string when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if '^' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh_vrf_interface(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -428,9 +212,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
vrf = module.params['vrf']
|
vrf = module.params['vrf']
|
||||||
interface = module.params['interface'].lower()
|
interface = module.params['interface'].lower()
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
@ -486,7 +277,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=commands)
|
module.exit_json(changed=True, commands=commands)
|
||||||
else:
|
else:
|
||||||
execute_config_command(commands, module)
|
load_config(module, commands)
|
||||||
changed = True
|
changed = True
|
||||||
changed_vrf = get_interface_info(interface, module)
|
changed_vrf = get_interface_info(interface, module)
|
||||||
end_state = dict(interface=interface, vrf=changed_vrf)
|
end_state = dict(interface=interface, vrf=changed_vrf)
|
||||||
|
@ -508,3 +299,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.1"
|
||||||
short_description: Manages VRRP configuration on NX-OS switches.
|
short_description: Manages VRRP configuration on NX-OS switches.
|
||||||
description:
|
description:
|
||||||
- Manages VRRP configuration on NX-OS switches.
|
- Manages VRRP configuration on NX-OS switches.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Jason Edelman (@jedelman8)
|
- Jason Edelman (@jedelman8)
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
|
@ -135,245 +134,21 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
|
||||||
import collections
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh_vrrp(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0]:
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
response = response[0].replace(command + '\n\n', '').strip()
|
|
||||||
body = [json.loads(response)]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh_vrrp(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -587,9 +362,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
interface = module.params['interface'].lower()
|
interface = module.params['interface'].lower()
|
||||||
group = module.params['group']
|
group = module.params['group']
|
||||||
|
@ -648,7 +430,7 @@ def main():
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
changed = True
|
changed = True
|
||||||
end_state = get_existing_vrrp(interface, group, module, name)
|
end_state = get_existing_vrrp(interface, group, module, name)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
|
@ -659,6 +441,7 @@ def main():
|
||||||
results['existing'] = existing
|
results['existing'] = existing
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
@ -666,3 +449,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ version_added: "2.2"
|
||||||
short_description: Manages VTP domain configuration.
|
short_description: Manages VTP domain configuration.
|
||||||
description:
|
description:
|
||||||
- Manages VTP domain configuration.
|
- Manages VTP domain configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
|
@ -84,243 +83,22 @@ changed:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
# COMMON CODE FOR MIGRATION
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'status' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'status' not in command:
|
if 'status' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -380,9 +158,16 @@ def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
domain=dict(type='str', required=True),
|
domain=dict(type='str', required=True),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
domain = module.params['domain']
|
domain = module.params['domain']
|
||||||
|
|
||||||
existing = get_vtp_config(module)
|
existing = get_vtp_config(module)
|
||||||
|
@ -404,7 +189,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_vtp_config(module)
|
end_state = get_vtp_config(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -415,9 +200,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages VTP password configuration.
|
short_description: Manages VTP password configuration.
|
||||||
description:
|
description:
|
||||||
- Manages VTP password configuration.
|
- Manages VTP password configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
|
@ -101,243 +100,22 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
# COMMON CODE FOR MIGRATION
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'show run' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'show run' not in command:
|
if 'show run' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -412,9 +190,16 @@ def main():
|
||||||
state=dict(choices=['absent', 'present'],
|
state=dict(choices=['absent', 'present'],
|
||||||
default='present'),
|
default='present'),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
vtp_password = module.params['vtp_password'] or None
|
vtp_password = module.params['vtp_password'] or None
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
|
|
||||||
|
@ -461,7 +246,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_vtp_config(module)
|
end_state = get_vtp_config(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -472,9 +257,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ version_added: "2.2"
|
||||||
short_description: Manages VTP version configuration.
|
short_description: Manages VTP version configuration.
|
||||||
description:
|
description:
|
||||||
- Manages VTP version configuration.
|
- Manages VTP version configuration.
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
author:
|
author:
|
||||||
- Gabriele Gerbino (@GGabriele)
|
- Gabriele Gerbino (@GGabriele)
|
||||||
notes:
|
notes:
|
||||||
|
@ -79,231 +78,14 @@ changed:
|
||||||
type: boolean
|
type: boolean
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
import json
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
import re
|
||||||
from ansible.module_utils.basic import get_exception
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
|
||||||
from ansible.module_utils.shell import ShellError
|
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
|
|
||||||
def execute_config_command(commands, module):
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
commands.insert(0, 'configure')
|
|
||||||
module.cli.add_commands(commands, output='config')
|
|
||||||
module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending CLI commands',
|
|
||||||
error=str(clie), commands=commands)
|
|
||||||
|
|
||||||
|
|
||||||
def get_cli_body_ssh(command, response, module):
|
|
||||||
"""Get response for when transport=cli. This is kind of a hack and mainly
|
|
||||||
needed because these modules were originally written for NX-API. And
|
|
||||||
not every command supports "| json" when using cli/ssh. As such, we assume
|
|
||||||
if | json returns an XML string, it is a valid command, but that the
|
|
||||||
resource doesn't exist yet. Instead, the output will be a raw string
|
|
||||||
when issuing commands containing 'show run'.
|
|
||||||
"""
|
|
||||||
if 'xml' in response[0] or response[0] == '\n':
|
|
||||||
body = []
|
|
||||||
elif 'status' in command:
|
|
||||||
body = response
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
body = [json.loads(response[0])]
|
|
||||||
except ValueError:
|
|
||||||
module.fail_json(msg='Command does not support JSON output',
|
|
||||||
command=command)
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show(cmds, module, command_type=None):
|
|
||||||
command_type_map = {
|
|
||||||
'cli_show': 'json',
|
|
||||||
'cli_show_ascii': 'text'
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
response = module.execute(cmds, command_type=command_type)
|
|
||||||
else:
|
|
||||||
response = module.execute(cmds)
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
if command_type:
|
|
||||||
command_type = command_type_map.get(command_type)
|
|
||||||
module.cli.add_commands(cmds, output=command_type)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
else:
|
|
||||||
module.cli.add_commands(cmds, raw=True)
|
|
||||||
response = module.cli.run_commands()
|
|
||||||
except ShellError:
|
|
||||||
clie = get_exception()
|
|
||||||
module.fail_json(msg='Error sending {0}'.format(cmds),
|
|
||||||
error=str(clie))
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
|
@ -311,11 +93,10 @@ def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if 'status' not in command:
|
if 'status' not in command:
|
||||||
command += ' | json'
|
command += ' | json'
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
response = execute_show(cmds, module)
|
body = run_commands(module, cmds)
|
||||||
body = get_cli_body_ssh(command, response, module)
|
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = execute_show(cmds, module, command_type=command_type)
|
body = run_commands(module, cmds)
|
||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -375,9 +156,16 @@ def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
version=dict(type='str', choices=['1', '2'], required=True),
|
version=dict(type='str', choices=['1', '2'], required=True),
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
version = module.params['version']
|
version = module.params['version']
|
||||||
|
|
||||||
existing = get_vtp_config(module)
|
existing = get_vtp_config(module)
|
||||||
|
@ -399,7 +187,7 @@ def main():
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
execute_config_command(cmds, module)
|
load_config(module, cmds)
|
||||||
end_state = get_vtp_config(module)
|
end_state = get_vtp_config(module)
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
@ -410,9 +198,11 @@ def main():
|
||||||
results['end_state'] = end_state
|
results['end_state'] = end_state
|
||||||
results['updates'] = cmds
|
results['updates'] = cmds
|
||||||
results['changed'] = changed
|
results['changed'] = changed
|
||||||
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- Manages VXLAN Network Virtualization Endpoint (NVE) overlay interface
|
- Manages VXLAN Network Virtualization Endpoint (NVE) overlay interface
|
||||||
that terminates VXLAN tunnels.
|
that terminates VXLAN tunnels.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- The module is used to manage NVE properties, not to create NVE
|
- The module is used to manage NVE properties, not to create NVE
|
||||||
interfaces. Use M(nxos_interface) if you wish to do so.
|
interfaces. Use M(nxos_interface) if you wish to do so.
|
||||||
|
@ -124,159 +123,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
BOOL_PARAMS = [
|
BOOL_PARAMS = [
|
||||||
'shutdown',
|
'shutdown',
|
||||||
|
@ -458,9 +309,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
interface = module.params['interface'].lower()
|
interface = module.params['interface'].lower()
|
||||||
|
|
||||||
|
@ -528,3 +386,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ description:
|
||||||
- Creates a Virtual Network Identifier member (VNI) for an NVE
|
- Creates a Virtual Network Identifier member (VNI) for an NVE
|
||||||
overlay interface.
|
overlay interface.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
extends_documentation_fragment: nxos
|
|
||||||
notes:
|
notes:
|
||||||
- default, where supported, restores params default value.
|
- default, where supported, restores params default value.
|
||||||
options:
|
options:
|
||||||
|
@ -143,159 +142,11 @@ changed:
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# COMMON CODE FOR MIGRATION
|
|
||||||
import re
|
import re
|
||||||
|
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
||||||
from ansible.module_utils.basic import get_exception
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.netcfg import NetworkConfig, ConfigLine
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.shell import ShellError
|
from ansible.module_utils.netcfg import CustomNetworkConfig
|
||||||
|
|
||||||
try:
|
|
||||||
from ansible.module_utils.nxos import get_module
|
|
||||||
except ImportError:
|
|
||||||
from ansible.module_utils.nxos import NetworkModule
|
|
||||||
|
|
||||||
|
|
||||||
def to_list(val):
|
|
||||||
if isinstance(val, (list, tuple)):
|
|
||||||
return list(val)
|
|
||||||
elif val is not None:
|
|
||||||
return [val]
|
|
||||||
else:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomNetworkConfig(NetworkConfig):
|
|
||||||
|
|
||||||
def expand_section(self, configobj, S=None):
|
|
||||||
if S is None:
|
|
||||||
S = list()
|
|
||||||
S.append(configobj)
|
|
||||||
for child in configobj.children:
|
|
||||||
if child in S:
|
|
||||||
continue
|
|
||||||
self.expand_section(child, S)
|
|
||||||
return S
|
|
||||||
|
|
||||||
def get_object(self, path):
|
|
||||||
for item in self.items:
|
|
||||||
if item.text == path[-1]:
|
|
||||||
parents = [p.text for p in item.parents]
|
|
||||||
if parents == path[:-1]:
|
|
||||||
return item
|
|
||||||
|
|
||||||
def to_block(self, section):
|
|
||||||
return '\n'.join([item.raw for item in section])
|
|
||||||
|
|
||||||
def get_section(self, path):
|
|
||||||
try:
|
|
||||||
section = self.get_section_objects(path)
|
|
||||||
return self.to_block(section)
|
|
||||||
except ValueError:
|
|
||||||
return list()
|
|
||||||
|
|
||||||
def get_section_objects(self, path):
|
|
||||||
if not isinstance(path, list):
|
|
||||||
path = [path]
|
|
||||||
obj = self.get_object(path)
|
|
||||||
if not obj:
|
|
||||||
raise ValueError('path does not exist in config')
|
|
||||||
return self.expand_section(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def add(self, lines, parents=None):
|
|
||||||
"""Adds one or lines of configuration
|
|
||||||
"""
|
|
||||||
|
|
||||||
ancestors = list()
|
|
||||||
offset = 0
|
|
||||||
obj = None
|
|
||||||
|
|
||||||
## global config command
|
|
||||||
if not parents:
|
|
||||||
for line in to_list(lines):
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line
|
|
||||||
if item not in self.items:
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
else:
|
|
||||||
for index, p in enumerate(parents):
|
|
||||||
try:
|
|
||||||
i = index + 1
|
|
||||||
obj = self.get_section_objects(parents[:i])[0]
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
# add parent to config
|
|
||||||
offset = index * self.indent
|
|
||||||
obj = ConfigLine(p)
|
|
||||||
obj.raw = p.rjust(len(p) + offset)
|
|
||||||
if ancestors:
|
|
||||||
obj.parents = list(ancestors)
|
|
||||||
ancestors[-1].children.append(obj)
|
|
||||||
self.items.append(obj)
|
|
||||||
ancestors.append(obj)
|
|
||||||
|
|
||||||
# add child objects
|
|
||||||
for line in to_list(lines):
|
|
||||||
# check if child already exists
|
|
||||||
for child in ancestors[-1].children:
|
|
||||||
if child.text == line:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = len(parents) * self.indent
|
|
||||||
item = ConfigLine(line)
|
|
||||||
item.raw = line.rjust(len(line) + offset)
|
|
||||||
item.parents = ancestors
|
|
||||||
ancestors[-1].children.append(item)
|
|
||||||
self.items.append(item)
|
|
||||||
|
|
||||||
|
|
||||||
def get_network_module(**kwargs):
|
|
||||||
try:
|
|
||||||
return get_module(**kwargs)
|
|
||||||
except NameError:
|
|
||||||
return NetworkModule(**kwargs)
|
|
||||||
|
|
||||||
def get_config(module, include_defaults=False):
|
|
||||||
config = module.params['config']
|
|
||||||
if not config:
|
|
||||||
try:
|
|
||||||
config = module.get_config()
|
|
||||||
except AttributeError:
|
|
||||||
defaults = module.params['include_defaults']
|
|
||||||
config = module.config.get_config(include_defaults=defaults)
|
|
||||||
return CustomNetworkConfig(indent=2, contents=config)
|
|
||||||
|
|
||||||
def load_config(module, candidate):
|
|
||||||
config = get_config(module)
|
|
||||||
|
|
||||||
commands = candidate.difference(config)
|
|
||||||
commands = [str(c).strip() for c in commands]
|
|
||||||
|
|
||||||
save_config = module.params['save']
|
|
||||||
|
|
||||||
result = dict(changed=False)
|
|
||||||
|
|
||||||
if commands:
|
|
||||||
if not module.check_mode:
|
|
||||||
try:
|
|
||||||
module.configure(commands)
|
|
||||||
except AttributeError:
|
|
||||||
module.config(commands)
|
|
||||||
|
|
||||||
if save_config:
|
|
||||||
try:
|
|
||||||
module.config.save_config()
|
|
||||||
except AttributeError:
|
|
||||||
module.execute(['copy running-config startup-config'])
|
|
||||||
|
|
||||||
result['changed'] = True
|
|
||||||
result['updates'] = commands
|
|
||||||
|
|
||||||
return result
|
|
||||||
# END OF COMMON CODE
|
|
||||||
|
|
||||||
BOOL_PARAMS = ['suppress_arp']
|
BOOL_PARAMS = ['suppress_arp']
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
|
@ -496,9 +347,16 @@ def main():
|
||||||
config=dict(),
|
config=dict(),
|
||||||
save=dict(type='bool', default=False)
|
save=dict(type='bool', default=False)
|
||||||
)
|
)
|
||||||
module = get_network_module(argument_spec=argument_spec,
|
|
||||||
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
warnings = list()
|
||||||
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
if module.params['assoc_vrf']:
|
if module.params['assoc_vrf']:
|
||||||
mutually_exclusive_params = ['multicast_group',
|
mutually_exclusive_params = ['multicast_group',
|
||||||
'suppress_arp',
|
'suppress_arp',
|
||||||
|
@ -587,3 +445,4 @@ def main():
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
112
lib/ansible/plugins/action/nxos.py
Normal file
112
lib/ansible/plugins/action/nxos.py
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#
|
||||||
|
# (c) 2016 Red Hat Inc.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from ansible.plugins.action.normal import ActionModule as _ActionModule
|
||||||
|
from ansible.utils.path import unfrackpath
|
||||||
|
from ansible.plugins import connection_loader
|
||||||
|
from ansible.compat.six import iteritems
|
||||||
|
from ansible.module_utils.nxos import nxos_argument_spec
|
||||||
|
from ansible.module_utils.basic import AnsibleFallbackNotFound
|
||||||
|
from ansible.module_utils._text import to_bytes
|
||||||
|
|
||||||
|
class ActionModule(_ActionModule):
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
|
||||||
|
provider = self.load_provider()
|
||||||
|
transport = provider['transport']
|
||||||
|
|
||||||
|
if not transport or 'cli' in transport:
|
||||||
|
pc = copy.deepcopy(self._play_context)
|
||||||
|
pc.connection = 'network_cli'
|
||||||
|
pc.network_os = 'nxos'
|
||||||
|
pc.port = provider['port'] or self._play_context.port or 22
|
||||||
|
pc.remote_user = provider['username'] or self._play_context.connection_user
|
||||||
|
pc.password = provider['password'] or self._play_context.password
|
||||||
|
|
||||||
|
socket_path = self._get_socket_path(pc)
|
||||||
|
if not os.path.exists(socket_path):
|
||||||
|
# start the connection if it isn't started
|
||||||
|
connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin)
|
||||||
|
connection.exec_command('EXEC: show version')
|
||||||
|
|
||||||
|
task_vars['ansible_socket'] = socket_path
|
||||||
|
|
||||||
|
else:
|
||||||
|
if provider['host'] is None:
|
||||||
|
self._task.args['host'] = self._play_context.remote_addr
|
||||||
|
if provider['username'] is None:
|
||||||
|
self._task.args['username'] = self._play_context.connection_user
|
||||||
|
if provider['password'] is None:
|
||||||
|
self._task.args['password'] = self._play_context.password
|
||||||
|
if provider['timeout'] is None:
|
||||||
|
self._task.args['timeout'] = self._play_context.timeout
|
||||||
|
if task_vars.get('nxapi_use_ssl'):
|
||||||
|
self._task.args['use_ssl'] = task_vars['nxapi_use_ssl']
|
||||||
|
if task_vars.get('nxapi_validate_certs'):
|
||||||
|
self._task.args['validate_certs'] = task_vars['nxapi_validate_certs']
|
||||||
|
|
||||||
|
|
||||||
|
transport = self._task.args.get('transport')
|
||||||
|
if not transport:
|
||||||
|
transport = self._task.args.get('provider', {}).get('transport')
|
||||||
|
self._task.args['transport'] = transport or 'cli'
|
||||||
|
|
||||||
|
return super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
|
||||||
|
def _get_socket_path(self, play_context):
|
||||||
|
ssh = connection_loader.get('ssh', class_only=True)
|
||||||
|
cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user)
|
||||||
|
path = unfrackpath("$HOME/.ansible/pc")
|
||||||
|
return cp % dict(directory=path)
|
||||||
|
|
||||||
|
def load_provider(self):
|
||||||
|
provider = self._task.args.get('provider', {})
|
||||||
|
for key, value in iteritems(nxos_argument_spec):
|
||||||
|
if key != 'provider' and key not in provider:
|
||||||
|
if key in self._task.args:
|
||||||
|
provider[key] = self._task.args[key]
|
||||||
|
elif 'fallback' in value:
|
||||||
|
provider[key] = self._fallback(value['fallback'])
|
||||||
|
elif key not in provider:
|
||||||
|
provider[key] = None
|
||||||
|
return provider
|
||||||
|
|
||||||
|
def _fallback(self, fallback):
|
||||||
|
strategy = fallback[0]
|
||||||
|
args = []
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
for item in fallback[1:]:
|
||||||
|
if isinstance(item, dict):
|
||||||
|
kwargs = item
|
||||||
|
else:
|
||||||
|
args = item
|
||||||
|
try:
|
||||||
|
return strategy(*args, **kwargs)
|
||||||
|
except AnsibleFallbackNotFound:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,95 @@
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
from ansible.plugins.action import ActionBase
|
import os
|
||||||
from ansible.plugins.action.net_config import ActionModule as NetActionModule
|
import re
|
||||||
|
import time
|
||||||
|
import glob
|
||||||
|
|
||||||
class ActionModule(NetActionModule, ActionBase):
|
from ansible.plugins.action.nxos import ActionModule as _ActionModule
|
||||||
pass
|
from ansible.module_utils._text import to_text
|
||||||
|
from ansible.module_utils.six.moves.urllib.parse import urlsplit
|
||||||
|
from ansible.utils.vars import merge_hash
|
||||||
|
|
||||||
|
|
||||||
|
PRIVATE_KEYS_RE = re.compile('__.+__')
|
||||||
|
|
||||||
|
|
||||||
|
class ActionModule(_ActionModule):
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
|
||||||
|
if self._task.args.get('src'):
|
||||||
|
try:
|
||||||
|
self._handle_template()
|
||||||
|
except ValueError as exc:
|
||||||
|
return dict(failed=True, msg=exc.message)
|
||||||
|
|
||||||
|
result = super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
|
||||||
|
if self._task.args.get('backup') and result.get('__backup__'):
|
||||||
|
# User requested backup and no error occurred in module.
|
||||||
|
# NOTE: If there is a parameter error, _backup key may not be in results.
|
||||||
|
filepath = self._write_backup(task_vars['inventory_hostname'],
|
||||||
|
result['__backup__'])
|
||||||
|
|
||||||
|
result['backup_path'] = filepath
|
||||||
|
|
||||||
|
# strip out any keys that have two leading and two trailing
|
||||||
|
# underscore characters
|
||||||
|
for key in result.keys():
|
||||||
|
if PRIVATE_KEYS_RE.match(key):
|
||||||
|
del result[key]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_working_path(self):
|
||||||
|
cwd = self._loader.get_basedir()
|
||||||
|
if self._task._role is not None:
|
||||||
|
cwd = self._task._role._role_path
|
||||||
|
return cwd
|
||||||
|
|
||||||
|
def _write_backup(self, host, contents):
|
||||||
|
backup_path = self._get_working_path() + '/backup'
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
os.mkdir(backup_path)
|
||||||
|
for fn in glob.glob('%s/%s*' % (backup_path, host)):
|
||||||
|
os.remove(fn)
|
||||||
|
tstamp = time.strftime("%Y-%m-%d@%H:%M:%S", time.localtime(time.time()))
|
||||||
|
filename = '%s/%s_config.%s' % (backup_path, host, tstamp)
|
||||||
|
open(filename, 'w').write(contents)
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def _handle_template(self):
|
||||||
|
src = self._task.args.get('src')
|
||||||
|
working_path = self._get_working_path()
|
||||||
|
|
||||||
|
if os.path.isabs(src) or urlsplit('src').scheme:
|
||||||
|
source = src
|
||||||
|
else:
|
||||||
|
source = self._loader.path_dwim_relative(working_path, 'templates', src)
|
||||||
|
if not source:
|
||||||
|
source = self._loader.path_dwim_relative(working_path, src)
|
||||||
|
|
||||||
|
if not os.path.exists(source):
|
||||||
|
raise ValueError('path specified in src not found')
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(source, 'r') as f:
|
||||||
|
template_data = to_text(f.read())
|
||||||
|
except IOError:
|
||||||
|
return dict(failed=True, msg='unable to load src file')
|
||||||
|
|
||||||
|
# Create a template search path in the following order:
|
||||||
|
# [working_path, self_role_path, dependent_role_paths, dirname(source)]
|
||||||
|
searchpath = [working_path]
|
||||||
|
if self._task._role is not None:
|
||||||
|
searchpath.append(self._task._role._role_path)
|
||||||
|
if hasattr(self._task, "_block:"):
|
||||||
|
dep_chain = self._task._block.get_dep_chain()
|
||||||
|
if dep_chain is not None:
|
||||||
|
for role in dep_chain:
|
||||||
|
searchpath.append(role._role_path)
|
||||||
|
searchpath.append(os.path.dirname(source))
|
||||||
|
self._templar.environment.loader.searchpath = searchpath
|
||||||
|
self._task.args['src'] = self._templar.template(template_data)
|
||||||
|
|
|
@ -19,9 +19,84 @@
|
||||||
from __future__ import (absolute_import, division, print_function)
|
from __future__ import (absolute_import, division, print_function)
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
from ansible.plugins.action import ActionBase
|
import os
|
||||||
from ansible.plugins.action.net_template import ActionModule as NetActionModule
|
import time
|
||||||
|
import glob
|
||||||
|
import urlparse
|
||||||
|
|
||||||
class ActionModule(NetActionModule, ActionBase):
|
from ansible.module_utils._text import to_text
|
||||||
pass
|
from ansible.plugins.action.nxos import ActionModule as _ActionModule
|
||||||
|
|
||||||
|
class ActionModule(_ActionModule):
|
||||||
|
|
||||||
|
def run(self, tmp=None, task_vars=None):
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._handle_template()
|
||||||
|
except (ValueError, AttributeError) as exc:
|
||||||
|
return dict(failed=True, msg=exc.message)
|
||||||
|
|
||||||
|
result = super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
|
||||||
|
if self._task.args.get('backup') and result.get('__backup__'):
|
||||||
|
# User requested backup and no error occurred in module.
|
||||||
|
# NOTE: If there is a parameter error, __backup__ key may not be in results.
|
||||||
|
self._write_backup(task_vars['inventory_hostname'], result['__backup__'])
|
||||||
|
|
||||||
|
if '__backup__' in result:
|
||||||
|
del result['__backup__']
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_working_path(self):
|
||||||
|
cwd = self._loader.get_basedir()
|
||||||
|
if self._task._role is not None:
|
||||||
|
cwd = self._task._role._role_path
|
||||||
|
return cwd
|
||||||
|
|
||||||
|
def _write_backup(self, host, contents):
|
||||||
|
backup_path = self._get_working_path() + '/backup'
|
||||||
|
if not os.path.exists(backup_path):
|
||||||
|
os.mkdir(backup_path)
|
||||||
|
for fn in glob.glob('%s/%s*' % (backup_path, host)):
|
||||||
|
os.remove(fn)
|
||||||
|
tstamp = time.strftime("%Y-%m-%d@%H:%M:%S", time.localtime(time.time()))
|
||||||
|
filename = '%s/%s_config.%s' % (backup_path, host, tstamp)
|
||||||
|
open(filename, 'w').write(contents)
|
||||||
|
|
||||||
|
def _handle_template(self):
|
||||||
|
src = self._task.args.get('src')
|
||||||
|
if not src:
|
||||||
|
raise ValueError('missing required arguments: src')
|
||||||
|
|
||||||
|
working_path = self._get_working_path()
|
||||||
|
|
||||||
|
if os.path.isabs(src) or urlparse.urlsplit(src).scheme:
|
||||||
|
source = src
|
||||||
|
else:
|
||||||
|
source = self._loader.path_dwim_relative(working_path, 'templates', src)
|
||||||
|
if not source:
|
||||||
|
source = self._loader.path_dwim_relative(working_path, src)
|
||||||
|
|
||||||
|
if not os.path.exists(source):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(source, 'r') as f:
|
||||||
|
template_data = to_text(f.read())
|
||||||
|
except IOError:
|
||||||
|
return dict(failed=True, msg='unable to load src file')
|
||||||
|
|
||||||
|
# Create a template search path in the following order:
|
||||||
|
# [working_path, self_role_path, dependent_role_paths, dirname(source)]
|
||||||
|
searchpath = [working_path]
|
||||||
|
if self._task._role is not None:
|
||||||
|
searchpath.append(self._task._role._role_path)
|
||||||
|
if hasattr(self._task, "_block:"):
|
||||||
|
dep_chain = self._task._block.get_dep_chain()
|
||||||
|
if dep_chain is not None:
|
||||||
|
for role in dep_chain:
|
||||||
|
searchpath.append(role._role_path)
|
||||||
|
searchpath.append(os.path.dirname(source))
|
||||||
|
self._templar.environment.loader.searchpath = searchpath
|
||||||
|
self._task.args['src'] = self._templar.template(template_data)
|
||||||
|
|
|
@ -52,11 +52,3 @@ class TerminalModule(TerminalBase):
|
||||||
except AnsibleConnectionFailure:
|
except AnsibleConnectionFailure:
|
||||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def guess_network_os(conn):
|
|
||||||
stdin, stdout, stderr = conn.exec_command('show version')
|
|
||||||
if 'NX-OS' in stdout.read():
|
|
||||||
return 'nxos'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue