zbx_action.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. #!/usr/bin/env python
  2. # vim: expandtab:tabstop=4:shiftwidth=4
  3. '''
  4. Ansible module for zabbix actions
  5. '''
  6. #
  7. # Zabbix action ansible module
  8. #
  9. #
  10. # Copyright 2015 Red Hat Inc.
  11. #
  12. # Licensed under the Apache License, Version 2.0 (the "License");
  13. # you may not use this file except in compliance with the License.
  14. # You may obtain a copy of the License at
  15. #
  16. # http://www.apache.org/licenses/LICENSE-2.0
  17. #
  18. # Unless required by applicable law or agreed to in writing, software
  19. # distributed under the License is distributed on an "AS IS" BASIS,
  20. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. # See the License for the specific language governing permissions and
  22. # limitations under the License.
  23. #
  24. # This is in place because each module looks similar to each other.
  25. # These need duplicate code as their behavior is very similar
  26. # but different for each zabbix class.
  27. # pylint: disable=duplicate-code
  28. # pylint: disable=import-error
  29. from openshift_tools.monitoring.zbxapi import ZabbixAPI, ZabbixConnection, ZabbixAPIError
  30. CUSTOM_SCRIPT_ACTION = '0'
  31. IPMI_ACTION = '1'
  32. SSH_ACTION = '2'
  33. TELNET_ACTION = '3'
  34. GLOBAL_SCRIPT_ACTION = '4'
  35. EXECUTE_ON_ZABBIX_AGENT = '0'
  36. EXECUTE_ON_ZABBIX_SERVER = '1'
  37. OPERATION_REMOTE_COMMAND = '1'
  38. def exists(content, key='result'):
  39. ''' Check if key exists in content or the size of content[key] > 0
  40. '''
  41. if not content.has_key(key):
  42. return False
  43. if not content[key]:
  44. return False
  45. return True
  46. def conditions_equal(zab_conditions, user_conditions):
  47. '''Compare two lists of conditions'''
  48. c_type = 'conditiontype'
  49. _op = 'operator'
  50. val = 'value'
  51. if len(user_conditions) != len(zab_conditions):
  52. return False
  53. for zab_cond, user_cond in zip(zab_conditions, user_conditions):
  54. if zab_cond[c_type] != str(user_cond[c_type]) or zab_cond[_op] != str(user_cond[_op]) or \
  55. zab_cond[val] != str(user_cond[val]):
  56. return False
  57. return True
  58. def filter_differences(zabbix_filters, user_filters):
  59. '''Determine the differences from user and zabbix for operations'''
  60. rval = {}
  61. for key, val in user_filters.items():
  62. if key == 'conditions':
  63. if not conditions_equal(zabbix_filters[key], val):
  64. rval[key] = val
  65. elif zabbix_filters[key] != str(val):
  66. rval[key] = val
  67. return rval
  68. def host_in_zabbix(zab_hosts, usr_host):
  69. ''' Check whether a particular user host is already in the
  70. Zabbix list of hosts '''
  71. for usr_hst_key, usr_hst_val in usr_host.items():
  72. for zab_host in zab_hosts:
  73. if usr_hst_key in zab_host and \
  74. zab_host[usr_hst_key] == str(usr_hst_val):
  75. return True
  76. return False
  77. def hostlist_in_zabbix(zab_hosts, usr_hosts):
  78. ''' Check whether user-provided list of hosts are already in
  79. the Zabbix action '''
  80. if len(zab_hosts) != len(usr_hosts):
  81. return False
  82. for usr_host in usr_hosts:
  83. if not host_in_zabbix(zab_hosts, usr_host):
  84. return False
  85. return True
  86. def opcommand_diff(zab_op_cmd, usr_op_cmd):
  87. ''' Check whether user-provided opcommand matches what's already
  88. stored in Zabbix '''
  89. for usr_op_cmd_key, usr_op_cmd_val in usr_op_cmd.items():
  90. if zab_op_cmd[usr_op_cmd_key] != str(usr_op_cmd_val):
  91. return True
  92. return False
  93. # This logic is quite complex. We are comparing two lists of dictionaries.
  94. # The outer for-loops allow us to descend down into both lists at the same time
  95. # and then walk over the key,val pairs of the incoming user dict's changes
  96. # or updates. The if-statements are looking at different sub-object types and
  97. # comparing them. The other suggestion on how to write this is to write a recursive
  98. # compare function but for the time constraints and for complexity I decided to go
  99. # this route.
  100. # pylint: disable=too-many-branches
  101. def operation_differences(zabbix_ops, user_ops):
  102. '''Determine the differences from user and zabbix for operations'''
  103. # if they don't match, take the user options
  104. if len(zabbix_ops) != len(user_ops):
  105. return user_ops
  106. rval = {}
  107. for zab, user in zip(zabbix_ops, user_ops):
  108. for key, val in user.items():
  109. if key == 'opconditions':
  110. if len(zab[key]) != len(val):
  111. rval[key] = val
  112. break
  113. for z_cond, u_cond in zip(zab[key], user[key]):
  114. if not all([str(u_cond[op_key]) == z_cond[op_key] for op_key in \
  115. ['conditiontype', 'operator', 'value']]):
  116. rval[key] = val
  117. break
  118. elif key == 'opmessage':
  119. # Verify each passed param matches
  120. for op_msg_key, op_msg_val in val.items():
  121. if zab[key][op_msg_key] != str(op_msg_val):
  122. rval[key] = val
  123. break
  124. elif key == 'opmessage_grp':
  125. zab_grp_ids = set([ugrp['usrgrpid'] for ugrp in zab[key]])
  126. usr_grp_ids = set([ugrp['usrgrpid'] for ugrp in val])
  127. if usr_grp_ids != zab_grp_ids:
  128. rval[key] = val
  129. elif key == 'opmessage_usr':
  130. zab_usr_ids = set([usr['userid'] for usr in zab[key]])
  131. usr_ids = set([usr['userid'] for usr in val])
  132. if usr_ids != zab_usr_ids:
  133. rval[key] = val
  134. elif key == 'opcommand':
  135. if opcommand_diff(zab[key], val):
  136. rval[key] = val
  137. break
  138. # opcommand_grp can be treated just like opcommand_hst
  139. # as opcommand_grp[] is just a list of groups
  140. elif key == 'opcommand_hst' or key == 'opcommand_grp':
  141. if not hostlist_in_zabbix(zab[key], val):
  142. rval[key] = val
  143. break
  144. elif zab[key] != str(val):
  145. rval[key] = val
  146. return rval
  147. def get_users(zapi, users):
  148. '''get the mediatype id from the mediatype name'''
  149. rval_users = []
  150. for user in users:
  151. content = zapi.get_content('user',
  152. 'get',
  153. {'filter': {'alias': user}})
  154. rval_users.append({'userid': content['result'][0]['userid']})
  155. return rval_users
  156. def get_user_groups(zapi, groups):
  157. '''get the mediatype id from the mediatype name'''
  158. user_groups = []
  159. content = zapi.get_content('usergroup',
  160. 'get',
  161. {'search': {'name': groups}})
  162. for usr_grp in content['result']:
  163. user_groups.append({'usrgrpid': usr_grp['usrgrpid']})
  164. return user_groups
  165. def get_mediatype_id_by_name(zapi, m_name):
  166. '''get the mediatype id from the mediatype name'''
  167. content = zapi.get_content('mediatype',
  168. 'get',
  169. {'filter': {'description': m_name}})
  170. return content['result'][0]['mediatypeid']
  171. def get_priority(priority):
  172. ''' determine priority
  173. '''
  174. prior = 0
  175. if 'info' in priority:
  176. prior = 1
  177. elif 'warn' in priority:
  178. prior = 2
  179. elif 'avg' == priority or 'ave' in priority:
  180. prior = 3
  181. elif 'high' in priority:
  182. prior = 4
  183. elif 'dis' in priority:
  184. prior = 5
  185. return prior
  186. def get_event_source(from_src):
  187. '''Translate even str into value'''
  188. choices = ['trigger', 'discovery', 'auto', 'internal']
  189. rval = 0
  190. try:
  191. rval = choices.index(from_src)
  192. except ValueError as _:
  193. ZabbixAPIError('Value not found for event source [%s]' % from_src)
  194. return rval
  195. def get_status(inc_status):
  196. '''determine status for action'''
  197. rval = 1
  198. if inc_status == 'enabled':
  199. rval = 0
  200. return rval
  201. def get_condition_operator(inc_operator):
  202. ''' determine the condition operator'''
  203. vals = {'=': 0,
  204. '<>': 1,
  205. 'like': 2,
  206. 'not like': 3,
  207. 'in': 4,
  208. '>=': 5,
  209. '<=': 6,
  210. 'not in': 7,
  211. }
  212. return vals[inc_operator]
  213. def get_host_id_by_name(zapi, host_name):
  214. '''Get host id by name'''
  215. content = zapi.get_content('host',
  216. 'get',
  217. {'filter': {'name': host_name}})
  218. return content['result'][0]['hostid']
  219. def get_trigger_value(inc_trigger):
  220. '''determine the proper trigger value'''
  221. rval = 1
  222. if inc_trigger == 'PROBLEM':
  223. rval = 1
  224. else:
  225. rval = 0
  226. return rval
  227. def get_template_id_by_name(zapi, t_name):
  228. '''get the template id by name'''
  229. content = zapi.get_content('template',
  230. 'get',
  231. {'filter': {'host': t_name}})
  232. return content['result'][0]['templateid']
  233. def get_host_group_id_by_name(zapi, hg_name):
  234. '''Get hostgroup id by name'''
  235. content = zapi.get_content('hostgroup',
  236. 'get',
  237. {'filter': {'name': hg_name}})
  238. return content['result'][0]['groupid']
  239. def get_condition_type(event_source, inc_condition):
  240. '''determine the condition type'''
  241. c_types = {}
  242. if event_source == 'trigger':
  243. c_types = {'host group': 0,
  244. 'host': 1,
  245. 'trigger': 2,
  246. 'trigger name': 3,
  247. 'trigger severity': 4,
  248. 'trigger value': 5,
  249. 'time period': 6,
  250. 'host template': 13,
  251. 'application': 15,
  252. 'maintenance status': 16,
  253. }
  254. elif event_source == 'discovery':
  255. c_types = {'host IP': 7,
  256. 'discovered service type': 8,
  257. 'discovered service port': 9,
  258. 'discovery status': 10,
  259. 'uptime or downtime duration': 11,
  260. 'received value': 12,
  261. 'discovery rule': 18,
  262. 'discovery check': 19,
  263. 'proxy': 20,
  264. 'discovery object': 21,
  265. }
  266. elif event_source == 'auto':
  267. c_types = {'proxy': 20,
  268. 'host name': 22,
  269. 'host metadata': 24,
  270. }
  271. elif event_source == 'internal':
  272. c_types = {'host group': 0,
  273. 'host': 1,
  274. 'host template': 13,
  275. 'application': 15,
  276. 'event type': 23,
  277. }
  278. else:
  279. raise ZabbixAPIError('Unkown event source %s' % event_source)
  280. return c_types[inc_condition]
  281. def get_operation_type(inc_operation):
  282. ''' determine the correct operation type'''
  283. o_types = {'send message': 0,
  284. 'remote command': OPERATION_REMOTE_COMMAND,
  285. 'add host': 2,
  286. 'remove host': 3,
  287. 'add to host group': 4,
  288. 'remove from host group': 5,
  289. 'link to template': 6,
  290. 'unlink from template': 7,
  291. 'enable host': 8,
  292. 'disable host': 9,
  293. }
  294. return o_types[inc_operation]
  295. def get_opcommand_type(opcommand_type):
  296. ''' determine the opcommand type '''
  297. oc_types = {'custom script': CUSTOM_SCRIPT_ACTION,
  298. 'IPMI': IPMI_ACTION,
  299. 'SSH': SSH_ACTION,
  300. 'Telnet': TELNET_ACTION,
  301. 'global script': GLOBAL_SCRIPT_ACTION,
  302. }
  303. return oc_types[opcommand_type]
  304. def get_execute_on(execute_on):
  305. ''' determine the execution target '''
  306. e_types = {'zabbix agent': EXECUTE_ON_ZABBIX_AGENT,
  307. 'zabbix server': EXECUTE_ON_ZABBIX_SERVER,
  308. }
  309. return e_types[execute_on]
  310. def action_remote_command(ansible_module, zapi, operation):
  311. ''' Process remote command type of actions '''
  312. if 'type' not in operation['opcommand']:
  313. ansible_module.exit_json(failed=True, changed=False, state='unknown',
  314. results="No Operation Type provided")
  315. operation['opcommand']['type'] = get_opcommand_type(operation['opcommand']['type'])
  316. if operation['opcommand']['type'] == CUSTOM_SCRIPT_ACTION:
  317. if 'execute_on' in operation['opcommand']:
  318. operation['opcommand']['execute_on'] = get_execute_on(operation['opcommand']['execute_on'])
  319. # custom script still requires the target hosts/groups to be set
  320. operation['opcommand_hst'] = []
  321. operation['opcommand_grp'] = []
  322. for usr_host in operation['target_hosts']:
  323. if usr_host['target_type'] == 'zabbix server':
  324. # 0 = target host local/current host
  325. operation['opcommand_hst'].append({'hostid': 0})
  326. elif usr_host['target_type'] == 'group':
  327. group_name = usr_host['target']
  328. gid = get_host_group_id_by_name(zapi, group_name)
  329. operation['opcommand_grp'].append({'groupid': gid})
  330. elif usr_host['target_type'] == 'host':
  331. host_name = usr_host['target']
  332. hid = get_host_id_by_name(zapi, host_name)
  333. operation['opcommand_hst'].append({'hostid': hid})
  334. # 'target_hosts' is just to make it easier to build zbx_actions
  335. # not part of ZabbixAPI
  336. del operation['target_hosts']
  337. else:
  338. ansible_module.exit_json(failed=True, changed=False, state='unknown',
  339. results="Unsupported remote command type")
  340. def get_action_operations(ansible_module, zapi, inc_operations):
  341. '''Convert the operations into syntax for api'''
  342. for operation in inc_operations:
  343. operation['operationtype'] = get_operation_type(operation['operationtype'])
  344. if operation['operationtype'] == 0: # send message. Need to fix the
  345. operation['opmessage']['mediatypeid'] = \
  346. get_mediatype_id_by_name(zapi, operation['opmessage']['mediatypeid'])
  347. operation['opmessage_grp'] = get_user_groups(zapi, operation.get('opmessage_grp', []))
  348. operation['opmessage_usr'] = get_users(zapi, operation.get('opmessage_usr', []))
  349. if operation['opmessage']['default_msg']:
  350. operation['opmessage']['default_msg'] = 1
  351. else:
  352. operation['opmessage']['default_msg'] = 0
  353. elif operation['operationtype'] == OPERATION_REMOTE_COMMAND:
  354. action_remote_command(ansible_module, zapi, operation)
  355. # Handle Operation conditions:
  356. # Currently there is only 1 available which
  357. # is 'event acknowledged'. In the future
  358. # if there are any added we will need to pass this
  359. # option to a function and return the correct conditiontype
  360. if operation.has_key('opconditions'):
  361. for condition in operation['opconditions']:
  362. if condition['conditiontype'] == 'event acknowledged':
  363. condition['conditiontype'] = 14
  364. if condition['operator'] == '=':
  365. condition['operator'] = 0
  366. if condition['value'] == 'acknowledged':
  367. condition['value'] = 1
  368. else:
  369. condition['value'] = 0
  370. return inc_operations
  371. def get_operation_evaltype(inc_type):
  372. '''get the operation evaltype'''
  373. rval = 0
  374. if inc_type == 'and/or':
  375. rval = 0
  376. elif inc_type == 'and':
  377. rval = 1
  378. elif inc_type == 'or':
  379. rval = 2
  380. elif inc_type == 'custom':
  381. rval = 3
  382. return rval
  383. def get_action_conditions(zapi, event_source, inc_conditions):
  384. '''Convert the conditions into syntax for api'''
  385. calc_type = inc_conditions.pop('calculation_type')
  386. inc_conditions['evaltype'] = get_operation_evaltype(calc_type)
  387. for cond in inc_conditions['conditions']:
  388. cond['operator'] = get_condition_operator(cond['operator'])
  389. # Based on conditiontype we need to set the proper value
  390. # e.g. conditiontype = hostgroup then the value needs to be a hostgroup id
  391. # e.g. conditiontype = host the value needs to be a host id
  392. cond['conditiontype'] = get_condition_type(event_source, cond['conditiontype'])
  393. if cond['conditiontype'] == 0:
  394. cond['value'] = get_host_group_id_by_name(zapi, cond['value'])
  395. elif cond['conditiontype'] == 1:
  396. cond['value'] = get_host_id_by_name(zapi, cond['value'])
  397. elif cond['conditiontype'] == 4:
  398. cond['value'] = get_priority(cond['value'])
  399. elif cond['conditiontype'] == 5:
  400. cond['value'] = get_trigger_value(cond['value'])
  401. elif cond['conditiontype'] == 13:
  402. cond['value'] = get_template_id_by_name(zapi, cond['value'])
  403. elif cond['conditiontype'] == 16:
  404. cond['value'] = ''
  405. return inc_conditions
  406. def get_send_recovery(send_recovery):
  407. '''Get the integer value'''
  408. rval = 0
  409. if send_recovery:
  410. rval = 1
  411. return rval
  412. # The branches are needed for CRUD and error handling
  413. # pylint: disable=too-many-branches
  414. def main():
  415. '''
  416. ansible zabbix module for zbx_item
  417. '''
  418. module = AnsibleModule(
  419. argument_spec=dict(
  420. zbx_server=dict(default='https://localhost/zabbix/api_jsonrpc.php', type='str'),
  421. zbx_user=dict(default=os.environ.get('ZABBIX_USER', None), type='str'),
  422. zbx_password=dict(default=os.environ.get('ZABBIX_PASSWORD', None), type='str'),
  423. zbx_debug=dict(default=False, type='bool'),
  424. name=dict(default=None, type='str'),
  425. event_source=dict(default='trigger', choices=['trigger', 'discovery', 'auto', 'internal'], type='str'),
  426. action_subject=dict(default="{TRIGGER.NAME}: {TRIGGER.STATUS}", type='str'),
  427. action_message=dict(default="{TRIGGER.NAME}: {TRIGGER.STATUS}\r\n" +
  428. "Last value: {ITEM.LASTVALUE}\r\n\r\n{TRIGGER.URL}", type='str'),
  429. reply_subject=dict(default="{TRIGGER.NAME}: {TRIGGER.STATUS}", type='str'),
  430. reply_message=dict(default="Trigger: {TRIGGER.NAME}\r\nTrigger status: {TRIGGER.STATUS}\r\n" +
  431. "Trigger severity: {TRIGGER.SEVERITY}\r\nTrigger URL: {TRIGGER.URL}\r\n\r\n" +
  432. "Item values:\r\n\r\n1. {ITEM.NAME1} ({HOST.NAME1}:{ITEM.KEY1}): " +
  433. "{ITEM.VALUE1}\r\n2. {ITEM.NAME2} ({HOST.NAME2}:{ITEM.KEY2}): " +
  434. "{ITEM.VALUE2}\r\n3. {ITEM.NAME3} ({HOST.NAME3}:{ITEM.KEY3}): " +
  435. "{ITEM.VALUE3}", type='str'),
  436. send_recovery=dict(default=False, type='bool'),
  437. status=dict(default=None, type='str'),
  438. escalation_time=dict(default=60, type='int'),
  439. conditions_filter=dict(default=None, type='dict'),
  440. operations=dict(default=None, type='list'),
  441. state=dict(default='present', type='str'),
  442. ),
  443. #supports_check_mode=True
  444. )
  445. zapi = ZabbixAPI(ZabbixConnection(module.params['zbx_server'],
  446. module.params['zbx_user'],
  447. module.params['zbx_password'],
  448. module.params['zbx_debug']))
  449. #Set the instance and the template for the rest of the calls
  450. zbx_class_name = 'action'
  451. state = module.params['state']
  452. content = zapi.get_content(zbx_class_name,
  453. 'get',
  454. {'search': {'name': module.params['name']},
  455. 'selectFilter': 'extend',
  456. 'selectOperations': 'extend',
  457. })
  458. #******#
  459. # GET
  460. #******#
  461. if state == 'list':
  462. module.exit_json(changed=False, results=content['result'], state="list")
  463. #******#
  464. # DELETE
  465. #******#
  466. if state == 'absent':
  467. if not exists(content):
  468. module.exit_json(changed=False, state="absent")
  469. content = zapi.get_content(zbx_class_name, 'delete', [content['result'][0]['actionid']])
  470. module.exit_json(changed=True, results=content['result'], state="absent")
  471. # Create and Update
  472. if state == 'present':
  473. conditions = get_action_conditions(zapi, module.params['event_source'], module.params['conditions_filter'])
  474. operations = get_action_operations(module, zapi,
  475. module.params['operations'])
  476. params = {'name': module.params['name'],
  477. 'esc_period': module.params['escalation_time'],
  478. 'eventsource': get_event_source(module.params['event_source']),
  479. 'status': get_status(module.params['status']),
  480. 'def_shortdata': module.params['action_subject'],
  481. 'def_longdata': module.params['action_message'],
  482. 'r_shortdata': module.params['reply_subject'],
  483. 'r_longdata': module.params['reply_message'],
  484. 'recovery_msg': get_send_recovery(module.params['send_recovery']),
  485. 'filter': conditions,
  486. 'operations': operations,
  487. }
  488. # Remove any None valued params
  489. _ = [params.pop(key, None) for key in params.keys() if params[key] is None]
  490. #******#
  491. # CREATE
  492. #******#
  493. if not exists(content):
  494. content = zapi.get_content(zbx_class_name, 'create', params)
  495. if content.has_key('error'):
  496. module.exit_json(failed=True, changed=True, results=content['error'], state="present")
  497. module.exit_json(changed=True, results=content['result'], state='present')
  498. ########
  499. # UPDATE
  500. ########
  501. _ = params.pop('hostid', None)
  502. differences = {}
  503. zab_results = content['result'][0]
  504. for key, value in params.items():
  505. if key == 'operations':
  506. ops = operation_differences(zab_results[key], value)
  507. if ops:
  508. differences[key] = ops
  509. elif key == 'filter':
  510. filters = filter_differences(zab_results[key], value)
  511. if filters:
  512. differences[key] = filters
  513. elif zab_results[key] != value and zab_results[key] != str(value):
  514. differences[key] = value
  515. if not differences:
  516. module.exit_json(changed=False, results=zab_results, state="present")
  517. # We have differences and need to update.
  518. # action update requires an id, filters, and operations
  519. differences['actionid'] = zab_results['actionid']
  520. differences['operations'] = params['operations']
  521. differences['filter'] = params['filter']
  522. content = zapi.get_content(zbx_class_name, 'update', differences)
  523. if content.has_key('error'):
  524. module.exit_json(failed=True, changed=False, results=content['error'], state="present")
  525. module.exit_json(changed=True, results=content['result'], state="present")
  526. module.exit_json(failed=True,
  527. changed=False,
  528. results='Unknown state passed. %s' % state,
  529. state="unknown")
  530. # pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled
  531. # import module snippets. This are required
  532. from ansible.module_utils.basic import *
  533. main()