IOS-XR NetConf and Cliconf plugin work (#33332)

*  - Netconf plugin addition for iosxr
 - Utilities refactoring to support netconf and cliconf
 - iosx_banner refactoring for netconf and cliconf
 - Integration testcases changes to accomodate above changes

* Fix sanity failures, shippable errors and review comments

* fix pep8 issue

* changes run_command method to send specific command args

* - Review comment fixes
- iosxr_command changes to remove ComplexDict based command_spec

* - Move namespaces removal method from utils to netconf plugin

* Minor refactoring in utils and change in deprecation message

* rewrite build_xml logic and import changes for new utils dir structure

* - Review comment changes and minor changes to documentation

* * refactor common code and docs updates
This commit is contained in:
Kedar Kekan 2017-12-06 22:37:31 +05:30 committed by GitHub
parent 4b6061ce3e
commit 2bc4c4f156
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1090 additions and 247 deletions

View file

@ -16,32 +16,32 @@ DOCUMENTATION = """
---
module: iosxr_banner
version_added: "2.4"
author: "Trishna Guha (@trishnaguha)"
author:
- Trishna Guha (@trishnaguha)
- Kedar Kekan (@kedarX)
short_description: Manage multiline banners on Cisco IOS XR devices
description:
- This will configure both exec and motd banners on remote devices
running Cisco IOS XR. It allows playbooks to add or remote
banner text from the active running configuration.
- This module will configure both exec and motd banners on remote device
running Cisco IOS XR. It allows playbooks to add or remove
banner text from the running configuration.
extends_documentation_fragment: iosxr
notes:
- Tested against IOS XR 6.1.2
- Tested against IOS XRv 6.1.2
options:
banner:
description:
- Specifies which banner that should be
configured on the remote device.
- Specifies the type of banner to configure on remote device.
required: true
default: null
choices: ['login', 'motd']
text:
description:
- The banner text that should be
present in the remote device running configuration. This argument
accepts a multiline string, with no empty lines. Requires I(state=present).
- Banner text to be configured. Accepts multiline string,
without empty lines. Requires I(state=present).
default: null
state:
description:
- Specifies whether or not the configuration is present in the current
devices active running configuration.
- Existential state of the configuration on the device.
default: present
choices: ['present', 'absent']
"""
@ -79,60 +79,130 @@ commands:
"""
import re
import collections
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import get_config, load_config
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, check_args
from ansible.module_utils.network.iosxr.iosxr import get_config_diff, commit_config
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, discard_config
from ansible.module_utils.network.iosxr.iosxr import build_xml, is_cliconf, is_netconf
from ansible.module_utils.network.iosxr.iosxr import etree_find, etree_findall
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
state = module.params['state']
class ConfigBase(object):
def __init__(self, module):
self._module = module
self._result = {'changed': False, 'warnings': []}
self._want = {}
self._have = {}
if state == 'absent':
if have.get('state') != 'absent' and ('text' in have.keys() and have['text']):
commands.append('no banner %s' % module.params['banner'])
elif state == 'present':
if (want['text'] and
want['text'].encode().decode('unicode_escape').strip("'") != have.get('text')):
banner_cmd = 'banner %s ' % module.params['banner']
banner_cmd += want['text'].strip()
commands.append(banner_cmd)
return commands
def map_params_to_obj(self):
text = self._module.params['text']
if text:
text = "{!r}".format(str(text).strip())
self._want.update({
'banner': self._module.params['banner'],
'text': text,
'state': self._module.params['state']
})
def map_config_to_obj(module):
flags = 'banner %s' % module.params['banner']
output = get_config(module, flags=[flags])
class CliConfiguration(ConfigBase):
def __init__(self, module):
super(CliConfiguration, self).__init__(module)
match = re.search(r'banner (\S+) (.*)', output, re.DOTALL)
if match:
text = match.group(2).strip("'")
else:
text = None
def map_obj_to_commands(self):
commands = list()
state = self._module.params['state']
if state == 'absent':
if self._have.get('state') != 'absent' and ('text' in self._have.keys() and self._have['text']):
commands.append('no banner {!s}'.format(self._module.params['banner']))
elif state == 'present':
if (self._want['text'] and
self._want['text'].encode().decode('unicode_escape').strip("'") != self._have.get('text')):
banner_cmd = 'banner {!s} '.format(self._module.params['banner'])
banner_cmd += self._want['text'].strip()
commands.append(banner_cmd)
self._result['commands'] = commands
if commands:
if not self._module.check_mode:
load_config(self._module, commands, self._result['warnings'], commit=True)
self._result['changed'] = True
obj = {'banner': module.params['banner'], 'state': 'absent'}
def map_config_to_obj(self):
cli_filter = 'banner {!s}'.format(self._module.params['banner'])
output = get_config(self._module, config_filter=cli_filter)
match = re.search(r'banner (\S+) (.*)', output, re.DOTALL)
if match:
text = match.group(2).strip("'")
else:
text = None
obj = {'banner': self._module.params['banner'], 'state': 'absent'}
if output:
obj['text'] = text
obj['state'] = 'present'
self._have.update(obj)
if output:
obj['text'] = text
obj['state'] = 'present'
def run(self):
self.map_params_to_obj()
self.map_config_to_obj()
self.map_obj_to_commands()
return obj
return self._result
def map_params_to_obj(module):
text = module.params['text']
if text:
text = "%r" % (str(text).strip())
class NCConfiguration(ConfigBase):
def __init__(self, module):
super(NCConfiguration, self).__init__(module)
self._banners_meta = collections.OrderedDict()
self._banners_meta.update([
('banner', {'xpath': 'banners/banner', 'tag': True, 'attrib': "operation"}),
('a:banner', {'xpath': 'banner/banner-name'}),
('a:text', {'xpath': 'banner/banner-text', 'operation': 'edit'})
])
return {
'banner': module.params['banner'],
'text': text,
'state': module.params['state']
}
def map_obj_to_commands(self):
state = self._module.params['state']
_get_filter = build_xml('banners', xmap=self._banners_meta, params=self._module.params, opcode="filter")
running = get_config(self._module, source='running', config_filter=_get_filter)
banner_name = None
banner_text = None
if etree_find(running, 'banner-text') is not None:
banner_name = etree_find(running, 'banner-name').text
banner_text = etree_find(running, 'banner-text').text
opcode = None
if state == 'absent' and banner_name == self._module.params['banner'] and len(banner_text):
opcode = "delete"
elif state == 'present':
opcode = 'merge'
self._result['commands'] = []
if opcode:
_edit_filter = build_xml('banners', xmap=self._banners_meta, params=self._module.params, opcode=opcode)
if _edit_filter is not None:
if not self._module.check_mode:
load_config(self._module, _edit_filter, self._result['warnings'])
candidate = get_config(self._module, source='candidate', config_filter=_get_filter)
diff = get_config_diff(self._module, running, candidate)
if diff:
commit_config(self._module)
self._result['changed'] = True
self._result['commands'] = _edit_filter
if self._module._diff:
self._result['diff'] = {'prepared': diff}
else:
discard_config(self._module)
def run(self):
self.map_params_to_obj()
self.map_obj_to_commands()
return self._result
def main():
@ -152,23 +222,13 @@ def main():
required_if=required_if,
supports_check_mode=True)
warnings = list()
check_args(module, warnings)
result = {'changed': False}
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands, result['warnings'], commit=True)
result['changed'] = True
if is_cliconf(module):
module.deprecate("cli support for 'iosxr_banner' is deprecated. Use transport netconf instead', version='4 releases from v2.5")
config_object = CliConfiguration(module)
elif is_netconf(module):
config_object = NCConfiguration(module)
result = config_object.run()
module.exit_json(**result)