iosxr_config crash if config has route-policy with multiple levels of 'ifelseif' and other caveats (#41091)

* diff in as-path-set or prefix-set

* fix caveat diff can not have last line with comma in prefix-set/as-path/community-set

* Simplify fix to include indentation before parse

* remove debugger

* route-policy diffs

* fix iosxr_config crash issue

* new changes in iosxr_config after git add

* end-policy-map and end-class-map are properly indented so match misplaced children only when end-* is at the beigining also fix pep8

* Remaining config blocks of route-policy which needs exclusion from diff. added new tests

* pylint/pep8 warnings

* Review comments , sanity test fix

* shbang warning

* remove unused import
This commit is contained in:
Deepak Agrawal 2018-06-05 07:56:53 +05:30 committed by GitHub
commit 2db6a8c26a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 374 additions and 28 deletions

View file

@ -186,7 +186,38 @@ from ansible.module_utils.network.common.config import NetworkConfig, dumps
DEFAULT_COMMIT_COMMENT = 'configured by iosxr_config'
CONFIG_MISPLACED_CHILDREN = [
re.compile(r'end-\s*(.+)$')
re.compile(r'^end-\s*(.+)$')
]
# Objects defined in Route-policy Language guide of IOS_XR.
# Reconfiguring these objects replace existing configurations.
# Hence these objects should be played direcly from candidate
# configurations
CONFIG_BLOCKS_FORCED_IN_DIFF = [
{
'start': re.compile(r'route-policy'),
'end': re.compile(r'end-policy')
},
{
'start': re.compile(r'prefix-set'),
'end': re.compile(r'end-set')
},
{
'start': re.compile(r'as-path-set'),
'end': re.compile(r'end-set')
},
{
'start': re.compile(r'community-set'),
'end': re.compile(r'end-set')
},
{
'start': re.compile(r'rd-set'),
'end': re.compile(r'end-set')
},
{
'start': re.compile(r'extcommunity-set'),
'end': re.compile(r'end-set')
}
]
@ -214,46 +245,93 @@ def check_args(module, warnings):
'removed in the future')
# A list of commands like {end-set, end-policy, ...} are part of configuration
# block like { prefix-set, as-path-set , ... } but they are not indented properly
# to be included with their parent. sanitize_config will add indentation to
# end-* commands so they are included with their parents
def sanitize_config(config, force_diff_prefix=None):
conf_lines = config.split('\n')
for regex in CONFIG_MISPLACED_CHILDREN:
for index, line in enumerate(conf_lines):
m = regex.search(line)
if m and m.group(0):
if force_diff_prefix:
conf_lines[index] = ' ' + m.group(0) + force_diff_prefix
else:
conf_lines[index] = ' ' + m.group(0)
conf = ('\n').join(conf_lines)
return conf
def mask_config_blocks_from_diff(config, candidate, force_diff_prefix):
conf_lines = config.split('\n')
candidate_lines = candidate.split('\n')
for regex in CONFIG_BLOCKS_FORCED_IN_DIFF:
block_index_start_end = []
for index, line in enumerate(candidate_lines):
startre = regex['start'].search(line)
if startre and startre.group(0):
start_index = index
else:
endre = regex['end'].search(line)
if endre and endre.group(0):
end_index = index
new_block = True
for prev_start, prev_end in block_index_start_end:
if start_index == prev_start:
# This might be end-set of another regex
# otherwise we would be having new start
new_block = False
break
if new_block:
block_index_start_end.append((start_index, end_index))
for start, end in block_index_start_end:
diff = False
if candidate_lines[start] in conf_lines:
run_conf_start_index = conf_lines.index(candidate_lines[start])
else:
diff = False
continue
for i in range(start, end + 1):
if conf_lines[run_conf_start_index] == candidate_lines[i]:
run_conf_start_index = run_conf_start_index + 1
else:
diff = True
break
if diff:
run_conf_start_index = conf_lines.index(candidate_lines[start])
for i in range(start, end + 1):
conf_lines[run_conf_start_index] = conf_lines[run_conf_start_index] + force_diff_prefix
run_conf_start_index = run_conf_start_index + 1
conf = ('\n').join(conf_lines)
return conf
def get_running_config(module):
contents = module.params['config']
if not contents:
contents = get_config(module)
if module.params['src']:
contents = mask_config_blocks_from_diff(contents, module.params['src'], "ansible")
contents = sanitize_config(contents)
return NetworkConfig(indent=1, contents=contents)
def get_candidate(module):
candidate = NetworkConfig(indent=1)
if module.params['src']:
candidate.load(module.params['src'])
config = module.params['src']
config = sanitize_config(config)
candidate.load(config)
elif module.params['lines']:
parents = module.params['parents'] or list()
candidate.add(module.params['lines'], parents=parents)
return candidate
def sanitize_candidate_config(config):
last_parents = None
for regex in CONFIG_MISPLACED_CHILDREN:
for index, line in enumerate(config):
if line._parents:
last_parents = line._parents
m = regex.search(line.text)
if m and m.group(0):
config[index]._parents = last_parents
def sanitize_running_config(config):
last_parents = None
for regex in CONFIG_MISPLACED_CHILDREN:
for index, line in enumerate(config):
if line._parents:
last_parents = line._parents
m = regex.search(line.text)
if m and m.group(0):
config[index].text = ' ' + m.group(0)
config[index]._parents = last_parents
def run(module, result):
match = module.params['match']
replace = module.params['replace']
@ -266,9 +344,6 @@ def run(module, result):
candidate_config = get_candidate(module)
running_config = get_running_config(module)
sanitize_candidate_config(candidate_config.items)
sanitize_running_config(running_config.items)
commands = None
if match != 'none' and replace != 'config':
commands = candidate_config.difference(running_config, path=path, match=match, replace=replace)