mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-25 11:51:26 -07: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,7 +28,6 @@ version_added: "2.2"
|
|||
short_description: Manages NTP authentication.
|
||||
description:
|
||||
- Manages NTP authentication.
|
||||
extends_documentation_fragment: nxos
|
||||
author:
|
||||
- Jason Edelman (@jedelman8)
|
||||
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
|
||||
|
||||
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'):
|
||||
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)
|
||||
body = run_commands(module, cmds)
|
||||
elif module.params['transport'] == 'nxapi':
|
||||
cmds = [command]
|
||||
body = execute_show(cmds, module, command_type=command_type)
|
||||
body = run_commands(module, cmds)
|
||||
|
||||
return body
|
||||
|
||||
|
@ -503,9 +282,16 @@ def main():
|
|||
authentication=dict(choices=['on', 'off']),
|
||||
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)
|
||||
|
||||
warnings = list()
|
||||
check_args(module, warnings)
|
||||
|
||||
|
||||
key_id = module.params['key_id']
|
||||
md5string = module.params['md5string']
|
||||
auth_type = module.params['auth_type']
|
||||
|
@ -548,7 +334,7 @@ def main():
|
|||
module.exit_json(changed=True, commands=cmds)
|
||||
else:
|
||||
try:
|
||||
execute_config_command(cmds, module)
|
||||
load_config(module, cmds)
|
||||
except ShellError:
|
||||
clie = get_exception()
|
||||
module.fail_json(msg=str(clie) + ": " + cmds)
|
||||
|
@ -564,9 +350,11 @@ def main():
|
|||
results['existing'] = existing
|
||||
results['updates'] = cmds
|
||||
results['changed'] = changed
|
||||
results['warnings'] = warnings
|
||||
results['end_state'] = end_state
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue