37
37
required: false
38
38
type: str
39
39
aliases: ["running_config_file_path"]
40
+ force:
41
+ description:
42
+ - Update already existing templates. When template is attached to devices, reattach new version.
43
+ required: false
44
+ type: bool
45
+ default: false
46
+ timeout_seconds:
47
+ description:
48
+ - The timeout in seconds for attaching the template. Default is 300.
49
+ type: int
40
50
author:
41
51
- Arkadiusz Cichon ([email protected] )
42
52
extends_documentation_fragment:
81
91
sample: "abc123"
82
92
"""
83
93
84
- from typing import Literal , Optional , get_args
94
+ from typing import List , Literal , Optional , get_args
85
95
86
96
from catalystwan .api .template_api import CLITemplate
97
+ from catalystwan .api .templates .device_template .device_template import DeviceTemplateConfigAttached
98
+ from catalystwan .dataclasses import Device
87
99
from catalystwan .models .common import DeviceModel
88
100
from catalystwan .models .templates import DeviceTemplateInformation
89
101
from catalystwan .session import ManagerHTTPError
90
102
from catalystwan .typed_list import DataSequence
103
+ from ciscoconfparse import CiscoConfParse # type: ignore
104
+ from pydantic import BaseModel , Field
91
105
92
106
from ..module_utils .result import ModuleResult
93
107
from ..module_utils .vmanage_module import AnsibleCatalystwanModule
94
108
95
109
State = Literal ["present" , "absent" ]
96
110
97
111
112
+ class ExtendedManagerResponse (BaseModel ):
113
+ process_id : Optional [str ] = Field (default = None , validation_alias = "processId" , serialization_alias = "processId" )
114
+ attached_configs : Optional [List [DeviceTemplateConfigAttached ]] = Field (
115
+ default = None , validation_alias = "attachedDevices" , serialization_alias = "attachedDevices"
116
+ )
117
+
118
+
98
119
def run_module ():
99
120
module_args = dict (
100
121
state = dict (
@@ -106,6 +127,8 @@ def run_module():
106
127
template_description = dict (type = "str" , default = None ),
107
128
device_model = dict (type = "str" , aliases = ["device_type" ], choices = list (get_args (DeviceModel )), default = None ),
108
129
config_file = dict (type = "str" , aliases = ["running_config_file_path" ]),
130
+ force = dict (type = "bool" , default = False ),
131
+ timeout_seconds = dict (type = "int" , default = 300 ),
109
132
)
110
133
result = ModuleResult ()
111
134
@@ -138,11 +161,57 @@ def run_module():
138
161
139
162
if module .params .get ("state" ) == "present" :
140
163
# Code for checking if template name exists already
141
- if target_template :
164
+ if target_template and not module . params . get ( "force" ) :
142
165
module .logger .debug (f"Detected existing template:\n { target_template } \n " )
143
166
result .msg = (
144
167
f"Template with name { template_name } already present on vManage, skipping create template operation."
145
168
)
169
+ elif target_template :
170
+ current_template = module .get_response_safely (
171
+ module .session .api .templates .get_device_template , template_id = target_template [0 ].id
172
+ )
173
+ current_template_configuration = CiscoConfParse (current_template .template_configuration .splitlines ())
174
+
175
+ new_template = CLITemplate (
176
+ template_name = template_name ,
177
+ template_description = module .params .get ("template_description" ),
178
+ device_model = module .params .get ("device_model" ),
179
+ )
180
+ new_template_configuration = new_template .load_from_file (file = module .params .get ("config_file" ))
181
+
182
+ template_configuration_diff = CLITemplate .compare_template (
183
+ current_template_configuration , new_template_configuration
184
+ )
185
+ if template_configuration_diff :
186
+ module .logger .debug (f"Detected changes:\n { template_configuration_diff } \n Template will be updated" )
187
+ response = module .get_response_safely (module .session .api .templates .edit , template = new_template )
188
+ template_config_attached = response .dataseq (ExtendedManagerResponse )[0 ].attached_configs
189
+ all_devices : DataSequence [Device ] = module .get_response_safely (module .session .api .devices .get )
190
+ for attached_config in template_config_attached :
191
+ module .logger .debug (
192
+ f"Template is attached to device: { attached_config .uuid } \n Reattaching new version"
193
+ )
194
+ device = all_devices .filter (uuid = attached_config .uuid )[0 ]
195
+ module .get_response_safely (
196
+ module .session .api .templates .edit_before_push , name = template_name , device = device
197
+ )
198
+ response = module .session .api .templates .attach (
199
+ name = template_name ,
200
+ device = device ,
201
+ timeout_seconds = module .params .get ("timeout_seconds" ),
202
+ is_edited = True ,
203
+ )
204
+ result .changed = True
205
+ result .msg = (
206
+ f"Template with name { template_name } already present on vManage is different than provided."
207
+ " Updating and reattaching."
208
+ )
209
+ else :
210
+ module .logger .debug (f"Detected existing template:\n { target_template } \n " )
211
+ result .msg = (
212
+ f"Template with name { template_name } already present on vManage and is the same as provided."
213
+ " Skipping template update."
214
+ )
146
215
else :
147
216
cli_template = CLITemplate (
148
217
template_name = template_name ,
0 commit comments