Add support for commit label in iosxr_config (#42931)

* Add support for commit label in iosxr_config

* sanity pep8 etc fixes
This commit is contained in:
Deepak Agrawal 2018-07-24 17:16:26 +05:30 committed by GitHub
parent 4f1746ee1d
commit bf544c2200
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 8 deletions

View file

@ -27,6 +27,7 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# #
import json import json
import re
from difflib import Differ from difflib import Differ
from copy import deepcopy from copy import deepcopy
from time import sleep from time import sleep
@ -334,7 +335,8 @@ def discard_config(module):
conn.discard_changes() conn.discard_changes()
def commit_config(module, comment=None, confirmed=False, confirm_timeout=None, persist=False, check=False): def commit_config(module, comment=None, confirmed=False, confirm_timeout=None,
persist=False, check=False, label=None):
conn = get_connection(module) conn = get_connection(module)
reply = None reply = None
@ -344,7 +346,7 @@ def commit_config(module, comment=None, confirmed=False, confirm_timeout=None, p
if is_netconf(module): if is_netconf(module):
reply = conn.commit(confirmed=confirmed, timeout=confirm_timeout, persist=persist) reply = conn.commit(confirmed=confirmed, timeout=confirm_timeout, persist=persist)
elif is_cliconf(module): elif is_cliconf(module):
reply = conn.commit(comment=comment) reply = conn.commit(comment=comment, label=label)
return reply return reply
@ -373,8 +375,18 @@ def get_config(module, config_filter=None, source='running'):
return cfg return cfg
def check_existing_commit_labels(conn, label):
out = conn.get(command='show configuration history detail | include %s' % label)
label_exist = re.search(label, out, re.M)
if label_exist:
return True
else:
return False
def load_config(module, command_filter, commit=False, replace=False, def load_config(module, command_filter, commit=False, replace=False,
comment=None, admin=False, running=None, nc_get_filter=None): comment=None, admin=False, running=None, nc_get_filter=None,
label=None):
conn = get_connection(module) conn = get_connection(module)
@ -404,6 +416,16 @@ def load_config(module, command_filter, commit=False, replace=False,
elif is_cliconf(module): elif is_cliconf(module):
# to keep the pre-cliconf behaviour, make a copy, avoid adding commands to input list # to keep the pre-cliconf behaviour, make a copy, avoid adding commands to input list
cmd_filter = deepcopy(command_filter) cmd_filter = deepcopy(command_filter)
# If label is present check if label already exist before entering
# config mode
if label:
old_label = check_existing_commit_labels(conn, label)
if old_label:
module.fail_json(
msg='commit label {%s} is already used for'
' an earlier commit, please choose a different label'
' and rerun task' % label
)
cmd_filter.insert(0, 'configure terminal') cmd_filter.insert(0, 'configure terminal')
if admin: if admin:
cmd_filter.insert(0, 'admin') cmd_filter.insert(0, 'admin')
@ -424,7 +446,7 @@ def load_config(module, command_filter, commit=False, replace=False,
cmd.append('end') cmd.append('end')
conn.edit_config(cmd) conn.edit_config(cmd)
elif commit: elif commit:
commit_config(module, comment=comment) commit_config(module, comment=comment, label=label)
conn.edit_config('end') conn.edit_config('end')
if admin: if admin:
conn.edit_config('exit') conn.edit_config('exit')

View file

@ -135,6 +135,14 @@ options:
type: bool type: bool
default: 'no' default: 'no'
version_added: "2.4" version_added: "2.4"
label:
description:
- Allows a commit label to be specified to be included when the
configuration is committed. A valid label must begin with an alphabet
and not exceed 30 characters, only alphabets, digits, hyphens and
underscores are allowed. If the configuration is not changed or
committed, this argument is ignored.
version_added: "2.7"
""" """
EXAMPLES = """ EXAMPLES = """
@ -239,6 +247,18 @@ def check_args(module, warnings):
if module.params['comment']: if module.params['comment']:
if len(module.params['comment']) > 60: if len(module.params['comment']) > 60:
module.fail_json(msg='comment argument cannot be more than 60 characters') module.fail_json(msg='comment argument cannot be more than 60 characters')
if module.params['label']:
label = module.params['label']
if len(label) > 30:
module.fail_json(msg='label argument cannot be more than 30 characters')
if not label[0].isalpha():
module.fail_json(msg='label argument must begin with an alphabet')
valid_chars = re.match(r'[\w-]*$', label)
if not valid_chars:
module.fail_json(
msg='label argument must only contain alphabets,' +
'digits, underscores or hyphens'
)
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 '
@ -340,6 +360,7 @@ def run(module, result):
comment = module.params['comment'] comment = module.params['comment']
admin = module.params['admin'] admin = module.params['admin']
check_mode = module.check_mode check_mode = module.check_mode
label = module.params['label']
candidate_config = get_candidate(module) candidate_config = get_candidate(module)
running_config = get_running_config(module) running_config = get_running_config(module)
@ -376,7 +397,11 @@ def run(module, result):
result['commands'] = commands result['commands'] = commands
commit = not check_mode commit = not check_mode
diff = load_config(module, commands, commit=commit, replace=replace_config, comment=comment, admin=admin) diff = load_config(
module, commands, commit=commit,
replace=replace_config, comment=comment, admin=admin,
label=label
)
if diff: if diff:
result['diff'] = dict(prepared=diff) result['diff'] = dict(prepared=diff)
@ -405,7 +430,8 @@ def main():
config=dict(), config=dict(),
backup=dict(type='bool', default=False), backup=dict(type='bool', default=False),
comment=dict(default=DEFAULT_COMMIT_COMMENT), comment=dict(default=DEFAULT_COMMIT_COMMENT),
admin=dict(type='bool', default=False) admin=dict(type='bool', default=False),
label=dict()
) )
argument_spec.update(iosxr_argument_spec) argument_spec.update(iosxr_argument_spec)

View file

@ -89,9 +89,13 @@ class Cliconf(CliconfBase):
def get(self, command=None, prompt=None, answer=None, sendonly=False, newline=True): def get(self, command=None, prompt=None, answer=None, sendonly=False, newline=True):
return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline) return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline)
def commit(self, comment=None): def commit(self, comment=None, label=None):
if comment: if comment and label:
command = 'commit label {0} comment {1}'.format(label, comment)
elif comment:
command = 'commit comment {0}'.format(comment) command = 'commit comment {0}'.format(comment)
elif label:
command = 'commit label {0}'.format(label)
else: else:
command = 'commit' command = 'commit'
self.send_command(command) self.send_command(command)

View file

@ -0,0 +1,70 @@
---
- debug: msg="START cli/commit_label.yaml on connection={{ ansible_connection }}"
- name: setup
iosxr_config:
commands:
- no description
- no shutdown
parents:
- interface Loopback999
match: none
- name: get a unique and valid label
set_fact:
label: "ansible_{{ 1001 | random | to_uuid | truncate(20, true, '_') }}"
- name: configure device with a label and a comment
iosxr_config:
src: basic/config.j2
comment: "this is sensible commit message"
label: "{{ label }}"
register: result
- assert:
that:
- "result.changed == true"
- name: setup
iosxr_config:
commands:
- no description
- no shutdown
parents:
- interface Loopback999
match: none
- name: Try to commit with old label, fail with a msg that label is alreay used
iosxr_config:
src: basic/config.j2
label: "{{ label }}"
register: result
ignore_errors: true
- assert:
that:
- "result.changed == false"
- "'already used' in result.msg"
- name: setup
iosxr_config:
commands:
- no description
- no shutdown
parents:
- interface Loopback999
match: none
- name: Try to commit with invalid chars($) in label
iosxr_config:
src: basic/config.j2
label: 'ansible_iosxr_config_$'
register: result
ignore_errors: true
- assert:
that:
- "result.changed == false"
- "'only contain alphabets' in result.msg"
- debug: msg="END cli/commit_label.yaml on connection={{ ansible_connection }}"