File: //usr/lib/python3.9/site-packages/ansible_collections/cisco/nxos/plugins/modules/nxos_snapshot.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
__metaclass__ = type
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = """
module: nxos_snapshot
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: Manage snapshots of the running states of selected features.
description:
- Create snapshots of the running states of selected features, add new show commands
for snapshot creation, delete and compare existing snapshots.
version_added: 1.0.0
author:
- Gabriele Gerbino (@GGabriele)
notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL
- Unsupported for Cisco MDS
- C(transport=cli) may cause timeout errors.
- The C(element_key1) and C(element_key2) parameter specify the tags used to distinguish
among row entries. In most cases, only the element_key1 parameter needs to specified
to be able to distinguish among row entries.
- C(action=compare) will always store a comparison report on a local file.
options:
action:
description:
- Define what snapshot action the module would perform.
required: true
choices:
- add
- compare
- create
- delete
- delete_all
type: str
snapshot_name:
description:
- Snapshot name, to be used when C(action=create) or C(action=delete).
type: str
description:
description:
- Snapshot description to be used when C(action=create).
type: str
snapshot1:
description:
- First snapshot to be used when C(action=compare).
type: str
snapshot2:
description:
- Second snapshot to be used when C(action=compare).
type: str
comparison_results_file:
description:
- Name of the file where snapshots comparison will be stored when C(action=compare).
type: str
compare_option:
description:
- Snapshot options to be used when C(action=compare).
choices:
- summary
- ipv4routes
- ipv6routes
type: str
section:
description:
- Used to name the show command output, to be used when C(action=add).
type: str
show_command:
description:
- Specify a new show command, to be used when C(action=add).
type: str
row_id:
description:
- Specifies the tag of each row entry of the show command's XML output, to be
used when C(action=add).
type: str
element_key1:
description:
- Specify the tags used to distinguish among row entries, to be used when C(action=add).
type: str
element_key2:
description:
- Specify the tags used to distinguish among row entries, to be used when C(action=add).
type: str
save_snapshot_locally:
description:
- Specify to locally store a new created snapshot, to be used when C(action=create).
type: bool
default: false
path:
description:
- Specify the path of the file where new created snapshot or snapshots comparison
will be stored, to be used when C(action=create) and C(save_snapshot_locally=true)
or C(action=compare).
default: ./
type: str
"""
EXAMPLES = """
# Create a snapshot and store it locally
- cisco.nxos.nxos_snapshot:
action: create
snapshot_name: test_snapshot
description: Done with Ansible
save_snapshot_locally: true
path: /home/user/snapshots/
# Delete a snapshot
- cisco.nxos.nxos_snapshot:
action: delete
snapshot_name: test_snapshot
# Delete all existing snapshots
- cisco.nxos.nxos_snapshot:
action: delete_all
# Add a show command for snapshots creation
- cisco.nxos.nxos_snapshot:
section: myshow
show_command: show ip interface brief
row_id: ROW_intf
element_key1: intf-name
# Compare two snapshots
- cisco.nxos.nxos_snapshot:
action: compare
snapshot1: pre_snapshot
snapshot2: post_snapshot
comparison_results_file: compare_snapshots.txt
compare_option: summary
path: ../snapshot_reports/
"""
RETURN = """
commands:
description: commands sent to the device
returned: verbose mode
type: list
sample: ["snapshot create post_snapshot Post-snapshot"]
"""
import os
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
load_config,
run_commands,
)
def execute_show_command(command, module):
command = [{"command": command, "output": "text"}]
return run_commands(module, command)
def get_existing(module):
existing = []
command = "show snapshots"
body = execute_show_command(command, module)[0]
if body:
split_body = body.splitlines()
snapshot_regex = (
r"(?P<name>\S+)\s+(?P<date>\w+\s+\w+\s+\d+\s+\d+"
r":\d+:\d+\s+\d+)\s+(?P<description>.*)"
)
for snapshot in split_body:
temp = {}
try:
match_snapshot = re.match(snapshot_regex, snapshot, re.DOTALL)
snapshot_group = match_snapshot.groupdict()
temp["name"] = snapshot_group["name"]
temp["date"] = snapshot_group["date"]
temp["description"] = snapshot_group["description"]
existing.append(temp)
except AttributeError:
pass
return existing
def action_create(module, existing_snapshots):
commands = list()
exist = False
for snapshot in existing_snapshots:
if module.params["snapshot_name"] == snapshot["name"]:
exist = True
if exist is False:
commands.append(
"snapshot create {0} {1}".format(
module.params["snapshot_name"],
module.params["description"],
),
)
return commands
def action_add(module, existing_snapshots):
commands = list()
command = "show snapshot sections"
sections = []
body = execute_show_command(command, module)[0]
if body:
section_regex = r".*\[(?P<section>\S+)\].*"
split_body = body.split("\n\n")
for section in split_body:
temp = {}
for line in section.splitlines():
try:
match_section = re.match(section_regex, section, re.DOTALL)
temp["section"] = match_section.groupdict()["section"]
except (AttributeError, KeyError):
pass
if "show command" in line:
temp["show_command"] = line.split("show command: ")[1]
elif "row id" in line:
temp["row_id"] = line.split("row id: ")[1]
elif "key1" in line:
temp["element_key1"] = line.split("key1: ")[1]
elif "key2" in line:
temp["element_key2"] = line.split("key2: ")[1]
if temp:
sections.append(temp)
proposed = {
"section": module.params["section"],
"show_command": module.params["show_command"],
"row_id": module.params["row_id"],
"element_key1": module.params["element_key1"],
"element_key2": module.params["element_key2"] or "-",
}
if proposed not in sections:
if module.params["element_key2"]:
commands.append(
'snapshot section add {0} "{1}" {2} {3} {4}'.format(
module.params["section"],
module.params["show_command"],
module.params["row_id"],
module.params["element_key1"],
module.params["element_key2"],
),
)
else:
commands.append(
'snapshot section add {0} "{1}" {2} {3}'.format(
module.params["section"],
module.params["show_command"],
module.params["row_id"],
module.params["element_key1"],
),
)
return commands
def action_compare(module, existing_snapshots):
command = "show snapshot compare {0} {1}".format(
module.params["snapshot1"],
module.params["snapshot2"],
)
if module.params["compare_option"]:
command += " {0}".format(module.params["compare_option"])
body = execute_show_command(command, module)[0]
return body
def action_delete(module, existing_snapshots):
commands = list()
exist = False
for snapshot in existing_snapshots:
if module.params["snapshot_name"] == snapshot["name"]:
exist = True
if exist:
commands.append("snapshot delete {0}".format(module.params["snapshot_name"]))
return commands
def action_delete_all(module, existing_snapshots):
commands = list()
if existing_snapshots:
commands.append("snapshot delete all")
return commands
def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
return func(*args, **kwargs)
def write_on_file(content, filename, module):
path = module.params["path"]
if path[-1] != "/":
path += "/"
filepath = "{0}{1}".format(path, filename)
try:
report = open(filepath, "w")
report.write(content)
report.close()
except Exception:
module.fail_json(msg="Error while writing on file.")
return filepath
def main():
argument_spec = dict(
action=dict(
required=True,
choices=["create", "add", "compare", "delete", "delete_all"],
),
snapshot_name=dict(type="str"),
description=dict(type="str"),
snapshot1=dict(type="str"),
snapshot2=dict(type="str"),
compare_option=dict(choices=["summary", "ipv4routes", "ipv6routes"]),
comparison_results_file=dict(type="str"),
section=dict(type="str"),
show_command=dict(type="str"),
row_id=dict(type="str"),
element_key1=dict(type="str", no_log=False),
element_key2=dict(type="str", no_log=False),
save_snapshot_locally=dict(type="bool", default=False),
path=dict(type="str", default="./"),
)
required_if = [
(
"action",
"compare",
["snapshot1", "snapshot2", "comparison_results_file"],
),
("action", "create", ["snapshot_name", "description"]),
(
"action",
"add",
["section", "show_command", "row_id", "element_key1"],
),
("action", "delete", ["snapshot_name"]),
]
module = AnsibleModule(
argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True,
)
action = module.params["action"]
comparison_results_file = module.params["comparison_results_file"]
if not os.path.isdir(module.params["path"]):
module.fail_json(msg="{0} is not a valid directory name.".format(module.params["path"]))
existing_snapshots = invoke("get_existing", module)
action_results = invoke("action_%s" % action, module, existing_snapshots)
result = {"changed": False, "commands": []}
if not module.check_mode:
if action == "compare":
result["commands"] = []
if module.params["path"] and comparison_results_file:
snapshot1 = module.params["snapshot1"]
snapshot2 = module.params["snapshot2"]
compare_option = module.params["compare_option"]
command = "show snapshot compare {0} {1}".format(snapshot1, snapshot2)
if compare_option:
command += " {0}".format(compare_option)
content = execute_show_command(command, module)[0]
if content:
write_on_file(content, comparison_results_file, module)
else:
if action_results:
load_config(module, action_results)
result["commands"] = action_results
result["changed"] = True
if (
action == "create"
and module.params["path"]
and module.params["save_snapshot_locally"]
):
command = "show snapshot dump {0} | json".format(module.params["snapshot_name"])
content = execute_show_command(command, module)[0]
if content:
write_on_file(str(content), module.params["snapshot_name"], module)
module.exit_json(**result)
if __name__ == "__main__":
main()