2
2
# -*- coding: utf-8 -*-
3
3
4
4
# Copyright: (c) 2023, Shreyas Srish (@shrsr) <[email protected] >
5
+ # Copyright: (c) 2025, Sabari Jaganathan (@sajagana) <[email protected] >
5
6
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7
7
8
from __future__ import absolute_import , division , print_function
19
20
- Manages backup of the cluster configuration.
20
21
author:
21
22
- Shreyas Srish (@shrsr)
23
+ - Sabari Jaganathan (@sajagana)
22
24
options:
23
25
name:
24
26
description:
28
30
encryption_key:
29
31
description:
30
32
- The encryption_key for a backup file.
33
+ - A minimum of 8 alphanumeric characters is required.
31
34
type: str
32
35
file_location:
33
36
description:
34
37
- The download path and file name for a backup.
38
+ - When O(file_location) is specified, the backup will be created and automatically downloaded to the local machine at the designated path.
35
39
type: str
36
40
backup_key:
37
41
description:
38
42
- The key generated by ND during creation of a backup.
39
43
- This key is required when querying or deleting a backup among multiple backups that have the same name.
40
44
- This key can be obtained by querying the backup.
45
+ - This parameter is not supported on ND v3.2.1 and later.
41
46
type: str
47
+ remote_location:
48
+ description:
49
+ - The name of the remote storage location. This parameter is only supported on ND v3.2.1 and later.
50
+ - If the O(remote_location) parameter is not specified or O(remote_location="") during backup creation, a local backup will be created.
51
+ type: str
52
+ backup_type:
53
+ description:
54
+ - This parameter is only supported on ND v3.2.1 and later.
55
+ - The O(backup_type=config_only) option creates a snapshot that specifically captures the configuration settings of the Nexus Dashboard.
56
+ - The O(backup_type=full) option creates a complete snapshot of the entire Nexus Dashboard.
57
+ type: str
58
+ choices: [ config_only, full ]
59
+ default: config_only
60
+ aliases: [ type ]
42
61
state:
43
62
description:
44
- - Use C(backup) for creating a backup of the cluster config.
45
- - Use C(query) for listing all the backed up files.
46
- - Use C(absent) for deleting a backup job.
63
+ - Use O(state=backup) for creating and downloading a backup of the cluster config for the ND versions < 3.2.1.
64
+ - Use O(state=backup) to create a cluster configuration backup. Automatic download is not supported for the ND versions >= 3.2.1.
65
+ - After creation, use O(state=download) to download the backup file.
66
+ - Use O(state=download) downloading a backup to the local machine, the O(state=download) is only supported on ND v3.2.1 and later.
67
+ - Use O(state=query) for listing all the backed up files.
68
+ - Use O(state=absent) for deleting a backup job.
47
69
type: str
48
- choices: [ backup, query, absent ]
70
+ choices: [ backup, download, query, absent ]
49
71
default: backup
50
72
extends_documentation_fragment:
51
73
- cisco.nd.modules
52
74
- cisco.nd.check_mode
53
75
"""
54
76
55
77
EXAMPLES = r"""
56
- - name: Create a Backup
78
+ - name: Create a backup for ND versions < 3.2.1
57
79
cisco.nd.nd_backup:
58
80
name: nexus
59
81
encryption_key: testtest
60
82
file_location: ./nexus.tgz
61
83
state: backup
62
84
85
+ - name: Create a remote backup for ND versions >= 3.2.1
86
+ cisco.nd.nd_backup:
87
+ name: nexus
88
+ encryption_key: testtest1
89
+ remote_location: remote_machine
90
+ state: backup
91
+
92
+ - name: Create a local backup for ND versions >= 3.2.1
93
+ cisco.nd.nd_backup:
94
+ name: nexus
95
+ encryption_key: testtest1
96
+ state: backup
97
+
98
+ - name: Create a backup and download it to the local machine for ND versions >= 3.2.1
99
+ cisco.nd.nd_backup:
100
+ name: nexus
101
+ file_location: ./nexus.tgz
102
+ encryption_key: testtest1
103
+ state: backup
104
+
105
+ - name: Download a local/remote backup for ND versions >= 3.2.1
106
+ cisco.nd.nd_backup:
107
+ name: nexus
108
+ state: download
109
+ file_location: ./nexus.tgz
110
+
63
111
- name: Query a Backup job
64
112
cisco.nd.nd_backup:
65
113
name: nexus
83
131
from ansible .module_utils ._text import to_bytes
84
132
from ansible .module_utils .basic import AnsibleModule
85
133
from ansible_collections .cisco .nd .plugins .module_utils .nd import NDModule , nd_argument_spec , write_file
134
+ from ansible_collections .cisco .nd .plugins .module_utils .utils import snake_to_camel
86
135
87
136
88
137
def main ():
@@ -92,15 +141,18 @@ def main():
92
141
encryption_key = dict (type = "str" , no_log = False ),
93
142
file_location = dict (type = "str" ),
94
143
backup_key = dict (type = "str" , no_log = False ),
95
- state = dict (type = "str" , default = "backup" , choices = ["backup" , "query" , "absent" ]),
144
+ remote_location = dict (type = "str" ),
145
+ backup_type = dict (type = "str" , default = "config_only" , choices = ["config_only" , "full" ], aliases = ["type" ]),
146
+ state = dict (type = "str" , default = "backup" , choices = ["backup" , "download" , "query" , "absent" ]),
96
147
)
97
148
98
149
module = AnsibleModule (
99
150
argument_spec = argument_spec ,
100
151
supports_check_mode = True ,
101
152
required_if = [
102
- ["state" , "backup" , ["name" , "encryption_key" , "file_location" ]],
153
+ ["state" , "backup" , ["name" , "encryption_key" ]],
103
154
["state" , "absent" , ["name" ]],
155
+ ["state" , "download" , ["name" , "file_location" ]],
104
156
],
105
157
)
106
158
@@ -110,8 +162,75 @@ def main():
110
162
encryption_key = nd .params .get ("encryption_key" )
111
163
backup_key = nd .params .get ("backup_key" )
112
164
file_location = nd .params .get ("file_location" )
165
+ remote_location = nd .params .get ("remote_location" )
166
+ backup_type = nd .params .get ("backup_type" )
113
167
state = nd .params .get ("state" )
114
168
169
+ if nd .version < "3.2.1" :
170
+ if not file_location and state in ["backup" , "download" ]:
171
+ nd .fail_json ("Parameter 'file_location' is required when state is 'backup|download' for ND versions < 3.2.1." )
172
+ nd_backup_before_3_2_1 (module , nd , name , encryption_key , file_location , backup_key , state )
173
+ elif nd .version >= "3.2.1" :
174
+ nd_backup_from_3_2_1 (module , nd , name , encryption_key , file_location , remote_location , backup_type , state )
175
+
176
+ nd .exit_json ()
177
+
178
+
179
+ def nd_backup_from_3_2_1 (module , nd , name , encryption_key , file_location , remote_location , backup_type , state ):
180
+ if encryption_key is not None :
181
+ if len (encryption_key ) < 8 :
182
+ nd .fail_json ("Please provide a minimum of 8 alphanumeric characters for the encryption key." )
183
+ elif not (any (char .isalpha () for char in encryption_key ) and any (char .isdigit () for char in encryption_key ) and encryption_key .isalnum ()):
184
+ nd .fail_json ("The encryption_key must contain at least one letter and one number, and have a minimum length of 8 characters." )
185
+
186
+ path = "/api/v1/infra/backups"
187
+ backups = nd .query_obj (path )
188
+ if name and backups :
189
+ for backup in backups .get ("backups" , []):
190
+ if backup .get ("name" ) == name :
191
+ nd .existing = backup
192
+ break
193
+ else :
194
+ nd .existing = backups .get ("backups" , [])
195
+
196
+ if state == "absent" and nd .existing :
197
+ nd .previous = nd .existing
198
+ if not module .check_mode :
199
+ nd .request ("{0}/{1}" .format (path , name ), method = "DELETE" )
200
+ nd .existing = {}
201
+
202
+ elif state == "backup" :
203
+ if not nd .existing :
204
+ payload = {
205
+ "name" : name ,
206
+ "type" : snake_to_camel (backup_type ),
207
+ "destination" : remote_location if remote_location else "" ,
208
+ "encryptionKey" : encryption_key ,
209
+ }
210
+ nd .sanitize (payload , collate = True )
211
+
212
+ if not module .check_mode :
213
+ # Creates backup file and returns None
214
+ nd .request (path , method = "POST" , data = payload )
215
+
216
+ # Fetching the backup object details to set module current value
217
+ nd .existing = nd .request ("{0}/{1}" .format (path , name ), method = "GET" )
218
+
219
+ if file_location :
220
+ response = nd .request ("{0}/{1}/actions/download" .format (path , name ), method = "GET" , data = None , output_format = "raw" )
221
+ write_file (module , file_location , to_bytes (response ))
222
+ elif module .check_mode :
223
+ nd .existing = nd .proposed
224
+ else :
225
+ nd .previous = nd .existing
226
+
227
+ elif state == "download" and file_location and nd .existing :
228
+ if not module .check_mode :
229
+ response = nd .request ("{0}/{1}/actions/download" .format (path , name ), method = "GET" , data = None , output_format = "raw" )
230
+ write_file (module , file_location , to_bytes (response ))
231
+
232
+
233
+ def nd_backup_before_3_2_1 (module , nd , name , encryption_key , file_location , backup_key , state ):
115
234
if encryption_key is not None and len (encryption_key ) < 8 :
116
235
nd .fail_json ("Please provide a minimum of 8 characters for the encryption key." )
117
236
@@ -158,8 +277,6 @@ def main():
158
277
write_file (module , file_location , to_bytes (response ))
159
278
nd .existing = nd .proposed
160
279
161
- nd .exit_json ()
162
-
163
280
164
281
if __name__ == "__main__" :
165
282
main ()
0 commit comments