Skip to content

Commit 3b50805

Browse files
New module cli_backup (#488)
* Initial cli_backup module * Fix tests * Add changelog * Remove unused imports * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 2da8e08 commit 3b50805

File tree

7 files changed

+325
-13
lines changed

7 files changed

+325
-13
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Name | Description
6767
### Modules
6868
Name | Description
6969
--- | ---
70+
[ansible.netcommon.cli_backup](https://github.com/ansible-collections/ansible.netcommon/blob/main/docs/ansible.netcommon.cli_backup_module.rst)|Back up device configuration from network devices over network_cli
7071
[ansible.netcommon.cli_command](https://github.com/ansible-collections/ansible.netcommon/blob/main/docs/ansible.netcommon.cli_command_module.rst)|Run a cli command on cli-based network devices
7172
[ansible.netcommon.cli_config](https://github.com/ansible-collections/ansible.netcommon/blob/main/docs/ansible.netcommon.cli_config_module.rst)|Push text based configuration to network devices over network_cli
7273
[ansible.netcommon.grpc_config](https://github.com/ansible-collections/ansible.netcommon/blob/main/docs/ansible.netcommon.grpc_config_module.rst)|Fetch configuration/state data from gRPC enabled target hosts.

changelogs/fragments/cli_backup.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
minor_changes:
3+
- Add new module cli_backup that exclusively handles configuration backup.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
.. _ansible.netcommon.cli_backup_module:
2+
3+
4+
****************************
5+
ansible.netcommon.cli_backup
6+
****************************
7+
8+
**Back up device configuration from network devices over network_cli**
9+
10+
11+
Version added: 4.2.0
12+
13+
.. contents::
14+
:local:
15+
:depth: 1
16+
17+
18+
Synopsis
19+
--------
20+
- This module provides platform agnostic way of backing up text based configuration from network devices over network_cli connection plugin.
21+
22+
23+
24+
25+
Parameters
26+
----------
27+
28+
.. raw:: html
29+
30+
<table border=0 cellpadding=0 class="documentation-table">
31+
<tr>
32+
<th colspan="1">Parameter</th>
33+
<th>Choices/<font color="blue">Defaults</font></th>
34+
<th width="100%">Comments</th>
35+
</tr>
36+
<tr>
37+
<td colspan="1">
38+
<div class="ansibleOptionAnchor" id="parameter-"></div>
39+
<b>defaults</b>
40+
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
41+
<div style="font-size: small">
42+
<span style="color: purple">boolean</span>
43+
</div>
44+
</td>
45+
<td>
46+
<ul style="margin: 0; padding: 0"><b>Choices:</b>
47+
<li><div style="color: blue"><b>no</b>&nbsp;&larr;</div></li>
48+
<li>yes</li>
49+
</ul>
50+
</td>
51+
<td>
52+
<div>The <em>defaults</em> argument will influence how the running-config is collected from the device. When the value is set to true, the command used to collect the running-config is append with the all keyword. When the value is set to false, the command is issued without the all keyword.</div>
53+
</td>
54+
</tr>
55+
<tr>
56+
<td colspan="1">
57+
<div class="ansibleOptionAnchor" id="parameter-"></div>
58+
<b>dir_path</b>
59+
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
60+
<div style="font-size: small">
61+
<span style="color: purple">path</span>
62+
</div>
63+
</td>
64+
<td>
65+
</td>
66+
<td>
67+
<div>This option provides the path ending with directory name in which the backup configuration file will be stored. If the directory does not exist it will be first created and the filename is either the value of <code>filename</code> or default filename as described in <code>filename</code> options description. If the path value is not given in that case a <em>backup</em> directory will be created in the current working directory and backup configuration will be copied in <code>filename</code> within <em>backup</em> directory.</div>
68+
</td>
69+
</tr>
70+
<tr>
71+
<td colspan="1">
72+
<div class="ansibleOptionAnchor" id="parameter-"></div>
73+
<b>filename</b>
74+
<a class="ansibleOptionLink" href="#parameter-" title="Permalink to this option"></a>
75+
<div style="font-size: small">
76+
<span style="color: purple">string</span>
77+
</div>
78+
</td>
79+
<td>
80+
</td>
81+
<td>
82+
<div>The filename to be used to store the backup configuration. If the filename is not given it will be generated based on the hostname, current time and date in format defined by &lt;hostname&gt;_config.&lt;current-date&gt;@&lt;current-time&gt;</div>
83+
</td>
84+
</tr>
85+
</table>
86+
<br/>
87+
88+
89+
Notes
90+
-----
91+
92+
.. note::
93+
- This module is supported on ``ansible_network_os`` network platforms. See the :ref:`Network Platform Options <platform_options>` for details.
94+
95+
96+
97+
Examples
98+
--------
99+
100+
.. code-block:: yaml
101+
102+
- name: configurable backup path
103+
ansible.netcommon.cli_backup:
104+
filename: backup.cfg
105+
dir_path: /home/user
106+
107+
108+
109+
Return Values
110+
-------------
111+
Common return values are documented `here <https://docs.ansible.com/ansible/latest/reference_appendices/common_return_values.html#common-return-values>`_, the following are the fields unique to this module:
112+
113+
.. raw:: html
114+
115+
<table border=0 cellpadding=0 class="documentation-table">
116+
<tr>
117+
<th colspan="1">Key</th>
118+
<th>Returned</th>
119+
<th width="100%">Description</th>
120+
</tr>
121+
<tr>
122+
<td colspan="1">
123+
<div class="ansibleOptionAnchor" id="return-"></div>
124+
<b>backup_path</b>
125+
<a class="ansibleOptionLink" href="#return-" title="Permalink to this return value"></a>
126+
<div style="font-size: small">
127+
<span style="color: purple">string</span>
128+
</div>
129+
</td>
130+
<td>always</td>
131+
<td>
132+
<div>The full path to the backup file</div>
133+
<br/>
134+
<div style="font-size: smaller"><b>Sample:</b></div>
135+
<div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">/playbooks/ansible/backup/hostname_config.2016-07-16@22:28:34</div>
136+
</td>
137+
</tr>
138+
</table>
139+
<br/><br/>
140+
141+
142+
Status
143+
------
144+
145+
146+
Authors
147+
~~~~~~~
148+
149+
- Kate Case (@Qalthos)

plugins/action/cli_backup.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#
2+
# Copyright 2018 Red Hat Inc.
3+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
4+
# SPDX-License-Identifier: GPL-3.0-or-later
5+
6+
from __future__ import absolute_import, division, print_function
7+
8+
9+
__metaclass__ = type
10+
11+
from ansible_collections.ansible.netcommon.plugins.action.network import (
12+
ActionModule as ActionNetworkModule,
13+
)
14+
15+
16+
class ActionModule(ActionNetworkModule):
17+
def run(self, tmp=None, task_vars=None):
18+
if self._play_context.connection.split(".")[-1] != "network_cli":
19+
return {
20+
"failed": True,
21+
"msg": "Connection type %s is not valid for this module"
22+
% self._play_context.connection,
23+
}
24+
result = super(ActionModule, self).run(task_vars=task_vars)
25+
self._handle_backup_option(
26+
result,
27+
task_vars,
28+
self._task.args,
29+
)
30+
31+
return result

plugins/action/network.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,15 @@ def run(self, tmp=None, task_vars=None):
8181
result = super(ActionModule, self).run(task_vars=task_vars)
8282

8383
if config_module and self._task.args.get("backup") and not result.get("failed"):
84-
self._handle_backup_option(result, task_vars)
84+
self._handle_backup_option(
85+
result,
86+
task_vars,
87+
self._task.args.get("backup_options"),
88+
)
8589

8690
return result
8791

88-
def _handle_backup_option(self, result, task_vars):
92+
def _handle_backup_option(self, result, task_vars, backup_options):
8993
filename = None
9094
backup_path = None
9195
try:
@@ -99,7 +103,6 @@ def _handle_backup_option(self, result, task_vars):
99103
except KeyError:
100104
raise AnsibleError("Failed while reading configuration backup")
101105

102-
backup_options = self._task.args.get("backup_options")
103106
if backup_options:
104107
filename = backup_options.get("filename")
105108
backup_path = backup_options.get("dir_path")

plugins/modules/cli_backup.py

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# (c) 2018, Ansible by Red Hat, inc
5+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
6+
# SPDX-License-Identifier: GPL-3.0-or-later
7+
8+
from __future__ import absolute_import, division, print_function
9+
10+
11+
__metaclass__ = type
12+
13+
14+
DOCUMENTATION = """
15+
module: cli_backup
16+
author: Kate Case (@Qalthos)
17+
short_description: Back up device configuration from network devices over network_cli
18+
description:
19+
- This module provides platform agnostic way of backing up text based configuration from
20+
network devices over network_cli connection plugin.
21+
version_added: 4.2.0
22+
extends_documentation_fragment:
23+
- ansible.netcommon.network_agnostic
24+
options:
25+
defaults:
26+
description:
27+
- The I(defaults) argument will influence how the running-config is collected
28+
from the device. When the value is set to true, the command used to collect
29+
the running-config is append with the all keyword. When the value is set to
30+
false, the command is issued without the all keyword.
31+
default: no
32+
type: bool
33+
filename:
34+
description:
35+
- The filename to be used to store the backup configuration. If the filename
36+
is not given it will be generated based on the hostname, current time and
37+
date in format defined by <hostname>_config.<current-date>@<current-time>
38+
type: str
39+
dir_path:
40+
description:
41+
- This option provides the path ending with directory name in which the backup
42+
configuration file will be stored. If the directory does not exist it will
43+
be first created and the filename is either the value of C(filename) or
44+
default filename as described in C(filename) options description. If the
45+
path value is not given in that case a I(backup) directory will be created
46+
in the current working directory and backup configuration will be copied
47+
in C(filename) within I(backup) directory.
48+
type: path
49+
"""
50+
51+
EXAMPLES = """
52+
- name: configurable backup path
53+
ansible.netcommon.cli_backup:
54+
filename: backup.cfg
55+
dir_path: /home/user
56+
"""
57+
58+
RETURN = """
59+
backup_path:
60+
description: The full path to the backup file
61+
returned: always
62+
type: str
63+
sample: /playbooks/ansible/backup/hostname_config.2016-07-16@22:28:34
64+
"""
65+
66+
from ansible.module_utils.basic import AnsibleModule
67+
from ansible.module_utils.connection import Connection
68+
69+
70+
def validate_args(module, device_operations):
71+
"""validate param if it is supported on the platform"""
72+
feature_list = [
73+
"defaults",
74+
]
75+
76+
for feature in feature_list:
77+
if module.params[feature]:
78+
supports_feature = device_operations.get("supports_%s" % feature)
79+
if supports_feature is None:
80+
module.fail_json(
81+
msg="This platform does not specify whether %s is supported or not. "
82+
"Please report an issue against this platform's cliconf plugin." % feature
83+
)
84+
elif not supports_feature:
85+
module.fail_json(msg="Option %s is not supported on this platform" % feature)
86+
87+
88+
def main():
89+
"""main entry point for execution"""
90+
argument_spec = dict(
91+
defaults=dict(default=False, type="bool"),
92+
filename=dict(),
93+
dir_path=dict(type="path"),
94+
)
95+
96+
module = AnsibleModule(
97+
argument_spec=argument_spec,
98+
)
99+
100+
result = {"changed": False}
101+
102+
connection = Connection(module._socket_path)
103+
capabilities = module.from_json(connection.get_capabilities())
104+
105+
if capabilities:
106+
device_operations = capabilities.get("device_operations", dict())
107+
validate_args(module, device_operations)
108+
else:
109+
device_operations = dict()
110+
111+
if module.params["defaults"]:
112+
if "get_default_flag" in capabilities.get("rpc"):
113+
flags = connection.get_default_flag()
114+
else:
115+
flags = "all"
116+
else:
117+
flags = []
118+
119+
running = connection.get_config(flags=flags)
120+
result["__backup__"] = running
121+
122+
module.exit_json(**result)
123+
124+
125+
if __name__ == "__main__":
126+
main()

0 commit comments

Comments
 (0)