Skip to content

Commit a06f06a

Browse files
evgenyfedorukgundalow
authored andcommitted
Module for committing pending configuration on Radware devices (ansible#31776)
With this module, pending configurations can be commited on Radware ADC devices.
1 parent 760ea4b commit a06f06a

File tree

2 files changed

+558
-0
lines changed

2 files changed

+558
-0
lines changed
Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright 2017 Radware LTD.
5+
#
6+
# This file is part of Ansible
7+
#
8+
# Ansible is free software: you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation, either version 3 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# Ansible is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
20+
21+
from __future__ import absolute_import, division, print_function
22+
23+
__metaclass__ = type
24+
25+
ANSIBLE_METADATA = {'status': ['preview'],
26+
'supported_by': 'community',
27+
'metadata_version': '1.1'}
28+
29+
DOCUMENTATION = '''
30+
module: vdirect_commit
31+
author: Evgeny Fedoruk @ Radware LTD (@evgenyfedoruk)
32+
short_description: Commits pending configuration changes on Radware devices
33+
description:
34+
- Commits pending configuration changes on one or more Radware devices via vDirect server.
35+
- For Alteon ADC device, apply, sync and save actions will be performed by default.
36+
Skipping of an action is possible by explicit parameter specifying.
37+
- For Alteon VX Container device, no sync operation will be performed
38+
since sync action is only relevant for Alteon ADC devices.
39+
- For DefensePro and AppWall devices, a bulk commit action will be performed.
40+
Explicit apply, sync and save actions specifying is not relevant.
41+
notes:
42+
- Requires the Radware vdirect-client Python package on the host. This is as easy as
43+
C(pip install vdirect-client)
44+
version_added: "2.5"
45+
options:
46+
vdirect_ip:
47+
description:
48+
- Primary vDirect server IP address, may be set as C(VDIRECT_IP) environment variable.
49+
required: true
50+
vdirect_user:
51+
description:
52+
- vDirect server username, may be set as C(VDIRECT_USER) environment variable.
53+
required: true
54+
default: None
55+
vdirect_password:
56+
description:
57+
- vDirect server password, may be set as C(VDIRECT_PASSWORD) environment variable.
58+
required: true
59+
default: None
60+
vdirect_secondary_ip:
61+
description:
62+
- Secondary vDirect server IP address, may be set as C(VDIRECT_SECONDARY_IP) environment variable.
63+
required: false
64+
default: None
65+
vdirect_wait:
66+
description:
67+
- Wait for async operation to complete, may be set as C(VDIRECT_WAIT) environment variable.
68+
required: false
69+
type: bool
70+
default: 'yes'
71+
vdirect_https_port:
72+
description:
73+
- vDirect server HTTPS port number, may be set as C(VDIRECT_HTTPS_PORT) environment variable.
74+
required: false
75+
default: 2189
76+
vdirect_http_port:
77+
description:
78+
- vDirect server HTTP port number, may be set as C(VDIRECT_HTTP_PORT) environment variable.
79+
required: false
80+
default: 2188
81+
vdirect_timeout:
82+
description:
83+
- Amount of time to wait for async operation completion [seconds],
84+
- may be set as C(VDIRECT_TIMEOUT) environment variable.
85+
required: false
86+
default: 60
87+
vdirect_use_ssl:
88+
description:
89+
- If C(no), an HTTP connection will be used instead of the default HTTPS connection,
90+
- may be set as C(VDIRECT_HTTPS) or C(VDIRECT_USE_SSL) environment variable.
91+
required: false
92+
type: bool
93+
default: 'yes'
94+
vdirect_validate_certs:
95+
description:
96+
- If C(no), SSL certificates will not be validated,
97+
- may be set as C(VDIRECT_VALIDATE_CERTS) or C(VDIRECT_VERIFY) environment variable.
98+
- This should only set to C(no) used on personally controlled sites using self-signed certificates.
99+
required: false
100+
type: bool
101+
default: 'yes'
102+
devices:
103+
description:
104+
- List of Radware Alteon device names for commit operations.
105+
required: true
106+
apply:
107+
description:
108+
- If C(no), apply action will not be performed. Relevant for ADC devices only.
109+
required: false
110+
type: bool
111+
default: 'yes'
112+
save:
113+
description:
114+
- If C(no), save action will not be performed. Relevant for ADC devices only.
115+
required: false
116+
type: bool
117+
default: 'yes'
118+
sync:
119+
description:
120+
- If C(no), sync action will not be performed. Relevant for ADC devices only.
121+
required: false
122+
type: bool
123+
default: 'yes'
124+
125+
requirements:
126+
- "vdirect-client >= 4.1.1"
127+
'''
128+
129+
EXAMPLES = '''
130+
- name: vdirect_commit
131+
vdirect_commit:
132+
vdirect_primary_ip: 10.10.10.10
133+
vdirect_user: vDirect
134+
vdirect_password: radware
135+
devices: ['dev1', 'dev2']
136+
sync: no
137+
'''
138+
139+
RETURN = '''
140+
result:
141+
description: Message detailing actions result
142+
returned: success
143+
type: string
144+
sample: "Requested actions were successfully performed on all devices."
145+
'''
146+
147+
from ansible.module_utils.basic import AnsibleModule
148+
from ansible.module_utils.basic import env_fallback
149+
150+
try:
151+
from vdirect_client import rest_client
152+
HAS_REST_CLIENT = True
153+
except ImportError:
154+
HAS_REST_CLIENT = False
155+
156+
157+
SUCCESS = 'Requested actions were successfully performed on all devices.'
158+
FAILURE = 'Failure occurred while performing requested actions on devices. See details'
159+
160+
ADC_DEVICE_TYPE = 'Adc'
161+
CONTAINER_DEVICE_TYPE = 'Container'
162+
PARTITIONED_CONTAINER_DEVICE_TYPE = 'AlteonPartitioned'
163+
APPWALL_DEVICE_TYPE = 'AppWall'
164+
DP_DEVICE_TYPE = 'DefensePro'
165+
166+
SUCCEEDED = 'succeeded'
167+
FAILED = 'failed'
168+
NOT_PERFORMED = 'not performed'
169+
170+
meta_args = dict(
171+
vdirect_ip=dict(
172+
required=True, fallback=(env_fallback, ['VDIRECT_IP']),
173+
default=None),
174+
vdirect_user=dict(
175+
required=True, fallback=(env_fallback, ['VDIRECT_USER']),
176+
default=None),
177+
vdirect_password=dict(
178+
required=True, fallback=(env_fallback, ['VDIRECT_PASSWORD']),
179+
default=None, no_log=True, type='str'),
180+
vdirect_secondary_ip=dict(
181+
required=False, fallback=(env_fallback, ['VDIRECT_SECONDARY_IP']),
182+
default=None),
183+
vdirect_use_ssl=dict(
184+
required=False, fallback=(env_fallback, ['VDIRECT_HTTPS', 'VDIRECT_USE_SSL']),
185+
default=True, type='bool'),
186+
vdirect_wait=dict(
187+
required=False, fallback=(env_fallback, ['VDIRECT_WAIT']),
188+
default=True, type='bool'),
189+
vdirect_timeout=dict(
190+
required=False, fallback=(env_fallback, ['VDIRECT_TIMEOUT']),
191+
default=60, type='int'),
192+
vdirect_validate_certs=dict(
193+
required=False, fallback=(env_fallback, ['VDIRECT_VERIFY', 'VDIRECT_VALIDATE_CERTS']),
194+
default=True, type='bool'),
195+
vdirect_https_port=dict(
196+
required=False, fallback=(env_fallback, ['VDIRECT_HTTPS_PORT']),
197+
default=2189, type='int'),
198+
vdirect_http_port=dict(
199+
required=False, fallback=(env_fallback, ['VDIRECT_HTTP_PORT']),
200+
default=2188, type='int'),
201+
devices=dict(
202+
required=True, type='list'),
203+
apply=dict(
204+
required=False, default=True, type='bool'),
205+
save=dict(
206+
required=False, default=True, type='bool'),
207+
sync=dict(
208+
required=False, default=True, type='bool'),
209+
)
210+
211+
212+
class CommitException(Exception):
213+
def __init__(self, reason, details):
214+
self.reason = reason
215+
self.details = details
216+
217+
def __str__(self):
218+
return 'Reason: {0}. Details:{1}.'.format(self.reason, self.details)
219+
220+
221+
class MissingDeviceException(CommitException):
222+
def __init__(self, device_name):
223+
super(MissingDeviceException, self).__init__(
224+
'Device missing',
225+
'Device ' + repr(device_name) + ' does not exist')
226+
227+
228+
class VdirectCommit(object):
229+
def __init__(self, params):
230+
self.client = rest_client.RestClient(params['vdirect_ip'],
231+
params['vdirect_user'],
232+
params['vdirect_password'],
233+
wait=params['vdirect_wait'],
234+
secondary_vdirect_ip=params['vdirect_secondary_ip'],
235+
https_port=params['vdirect_https_port'],
236+
http_port=params['vdirect_http_port'],
237+
timeout=params['vdirect_timeout'],
238+
https=params['vdirect_use_ssl'],
239+
verify=params['vdirect_validate_certs'])
240+
self.devices = params['devices']
241+
self.apply = params['apply']
242+
self.save = params['save']
243+
self.sync = params['sync']
244+
self.devicesMap = {}
245+
246+
def _validate_devices(self):
247+
for device in self.devices:
248+
try:
249+
res = self.client.adc.get(device)
250+
if res[rest_client.RESP_STATUS] == 200:
251+
self.devicesMap.update({device: ADC_DEVICE_TYPE})
252+
continue
253+
res = self.client.container.get(device)
254+
if res[rest_client.RESP_STATUS] == 200:
255+
if res[rest_client.RESP_DATA]['type'] == PARTITIONED_CONTAINER_DEVICE_TYPE:
256+
self.devicesMap.update({device: CONTAINER_DEVICE_TYPE})
257+
continue
258+
res = self.client.appWall.get(device)
259+
if res[rest_client.RESP_STATUS] == 200:
260+
self.devicesMap.update({device: APPWALL_DEVICE_TYPE})
261+
continue
262+
res = self.client.defensePro.get(device)
263+
if res[rest_client.RESP_STATUS] == 200:
264+
self.devicesMap.update({device: DP_DEVICE_TYPE})
265+
continue
266+
267+
except Exception as e:
268+
raise CommitException('Failed to communicate with device ' + device, str(e))
269+
270+
raise MissingDeviceException(device)
271+
272+
def _perform_action_and_update_result(self, device, action, perform, failure_occurred, actions_result):
273+
274+
if not perform or failure_occurred:
275+
actions_result[action] = NOT_PERFORMED
276+
return True
277+
278+
try:
279+
if self.devicesMap[device] == ADC_DEVICE_TYPE:
280+
res = self.client.adc.control_device(device, action)
281+
elif self.devicesMap[device] == CONTAINER_DEVICE_TYPE:
282+
res = self.client.container.control(device, action)
283+
elif self.devicesMap[device] == APPWALL_DEVICE_TYPE:
284+
res = self.client.appWall.control_device(device, action)
285+
elif self.devicesMap[device] == DP_DEVICE_TYPE:
286+
res = self.client.defensePro.control_device(device, action)
287+
288+
if res[rest_client.RESP_STATUS] in [200, 204]:
289+
actions_result[action] = SUCCEEDED
290+
else:
291+
actions_result[action] = FAILED
292+
actions_result['failure_description'] = res[rest_client.RESP_STR]
293+
return False
294+
except Exception as e:
295+
actions_result[action] = FAILED
296+
actions_result['failure_description'] = 'Exception occurred while performing '\
297+
+ action + ' action. Exception: ' + str(e)
298+
return False
299+
300+
return True
301+
302+
def commit(self):
303+
self._validate_devices()
304+
305+
result_to_return = dict()
306+
result_to_return['details'] = list()
307+
308+
for device in self.devices:
309+
failure_occurred = False
310+
device_type = self.devicesMap[device]
311+
actions_result = dict()
312+
actions_result['device_name'] = device
313+
actions_result['device_type'] = device_type
314+
315+
if device_type in [DP_DEVICE_TYPE, APPWALL_DEVICE_TYPE]:
316+
failure_occurred = not self._perform_action_and_update_result(
317+
device, 'commit', True, failure_occurred, actions_result)\
318+
or failure_occurred
319+
else:
320+
failure_occurred = not self._perform_action_and_update_result(
321+
device, 'apply', self.apply, failure_occurred, actions_result)\
322+
or failure_occurred
323+
if device_type != CONTAINER_DEVICE_TYPE:
324+
failure_occurred = not self._perform_action_and_update_result(
325+
device, 'sync', self.sync, failure_occurred, actions_result)\
326+
or failure_occurred
327+
failure_occurred = not self._perform_action_and_update_result(
328+
device, 'save', self.save, failure_occurred, actions_result)\
329+
or failure_occurred
330+
331+
result_to_return['details'].extend([actions_result])
332+
333+
if failure_occurred:
334+
result_to_return['msg'] = FAILURE
335+
336+
if 'msg' not in result_to_return:
337+
result_to_return['msg'] = SUCCESS
338+
339+
return result_to_return
340+
341+
342+
def main():
343+
344+
if not HAS_REST_CLIENT:
345+
raise ImportError("The python vdirect-client module is required")
346+
347+
module = AnsibleModule(argument_spec=meta_args)
348+
349+
try:
350+
vdirect_commit = VdirectCommit(module.params)
351+
result = vdirect_commit.commit()
352+
result = dict(result=result)
353+
module.exit_json(**result)
354+
except Exception as e:
355+
module.fail_json(msg=str(e))
356+
357+
if __name__ == '__main__':
358+
main()

0 commit comments

Comments
 (0)