zbxapi.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. #!/usr/bin/env python
  2. # Copyright 2015 Red Hat Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. # Purpose: An ansible module to communicate with zabbix.
  17. #
  18. import json
  19. import httplib2
  20. import sys
  21. import os
  22. import re
  23. class ZabbixAPI(object):
  24. '''
  25. ZabbixAPI class
  26. '''
  27. classes = {
  28. 'Action': ['create', 'delete', 'get', 'update'],
  29. 'Alert': ['get'],
  30. 'Application': ['create', 'delete', 'get', 'massadd', 'update'],
  31. 'Configuration': ['export', 'import'],
  32. 'Dcheck': ['get'],
  33. 'Dhost': ['get'],
  34. 'Drule': ['copy', 'create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  35. 'Dservice': ['get'],
  36. 'Event': ['acknowledge', 'get'],
  37. 'Graph': ['create', 'delete', 'get', 'update'],
  38. 'Graphitem': ['get'],
  39. 'Graphprototype': ['create', 'delete', 'get', 'update'],
  40. 'History': ['get'],
  41. 'Hostgroup': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massremove', 'massupdate', 'update'],
  42. 'Hostinterface': ['create', 'delete', 'get', 'massadd', 'massremove', 'replacehostinterfaces', 'update'],
  43. 'Host': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massremove', 'massupdate', 'update'],
  44. 'Hostprototype': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  45. 'Httptest': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  46. 'Iconmap': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  47. 'Image': ['create', 'delete', 'get', 'update'],
  48. 'Item': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  49. 'Itemprototype': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  50. 'Maintenance': ['create', 'delete', 'get', 'update'],
  51. 'Map': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  52. 'Mediatype': ['create', 'delete', 'get', 'update'],
  53. 'Proxy': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  54. 'Screen': ['create', 'delete', 'get', 'update'],
  55. 'Screenitem': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update', 'updatebyposition'],
  56. 'Script': ['create', 'delete', 'execute', 'get', 'getscriptsbyhosts', 'update'],
  57. 'Service': ['adddependencies', 'addtimes', 'create', 'delete', 'deletedependencies', 'deletetimes', 'get', 'getsla', 'isreadable', 'iswritable', 'update'],
  58. 'Template': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massremove', 'massupdate', 'update'],
  59. 'Templatescreen': ['copy', 'create', 'delete', 'get', 'isreadable', 'iswritable', 'update'],
  60. 'Templatescreenitem': ['get'],
  61. 'Trigger': ['adddependencies', 'create', 'delete', 'deletedependencies', 'get', 'isreadable', 'iswritable', 'update'],
  62. 'Triggerprototype': ['create', 'delete', 'get', 'update'],
  63. 'User': ['addmedia', 'create', 'delete', 'deletemedia', 'get', 'isreadable', 'iswritable', 'login', 'logout', 'update', 'updatemedia', 'updateprofile'],
  64. 'Usergroup': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massupdate', 'update'],
  65. 'Usermacro': ['create', 'createglobal', 'delete', 'deleteglobal', 'get', 'update', 'updateglobal'],
  66. 'Usermedia': ['get'],
  67. }
  68. def __init__(self, data={}):
  69. self.server = data['server'] or None
  70. self.username = data['user'] or None
  71. self.password = data['password'] or None
  72. if any(map(lambda value: value == None, [self.server, self.username, self.password])):
  73. print 'Please specify zabbix server url, username, and password.'
  74. sys.exit(1)
  75. self.verbose = data.has_key('verbose')
  76. self.use_ssl = data.has_key('use_ssl')
  77. self.auth = None
  78. for class_name, method_names in self.classes.items():
  79. #obj = getattr(self, class_name)(self)
  80. #obj.__dict__
  81. setattr(self, class_name.lower(), getattr(self, class_name)(self))
  82. results = self.user.login(user=self.username, password=self.password)
  83. if results[0]['status'] == '200':
  84. if results[1].has_key('result'):
  85. self.auth = results[1]['result']
  86. elif results[1].has_key('error'):
  87. print "Unable to authenticate with zabbix server. {0} ".format(results[1]['error'])
  88. sys.exit(1)
  89. else:
  90. print "Error in call to zabbix. Http status: {0}.".format(results[0]['status'])
  91. sys.exit(1)
  92. def perform(self, method, params):
  93. '''
  94. This method calls your zabbix server.
  95. It requires the following parameters in order for a proper request to be processed:
  96. jsonrpc - the version of the JSON-RPC protocol used by the API; the Zabbix API implements JSON-RPC version 2.0;
  97. method - the API method being called;
  98. params - parameters that will be passed to the API method;
  99. id - an arbitrary identifier of the request;
  100. auth - a user authentication token; since we don't have one yet, it's set to null.
  101. '''
  102. http_method = "POST"
  103. if params.has_key("http_method"):
  104. http_method = params['http_method']
  105. jsonrpc = "2.0"
  106. if params.has_key('jsonrpc'):
  107. jsonrpc = params['jsonrpc']
  108. rid = 1
  109. if params.has_key('id'):
  110. rid = params['id']
  111. http = None
  112. if self.use_ssl:
  113. http = httplib2.Http()
  114. else:
  115. http = httplib2.Http( disable_ssl_certificate_validation=True,)
  116. headers = params.get('headers', {})
  117. headers["Content-type"] = "application/json"
  118. body = {
  119. "jsonrpc": jsonrpc,
  120. "method": method,
  121. "params": params,
  122. "id": rid,
  123. 'auth': self.auth,
  124. }
  125. if method in ['user.login','api.version']:
  126. del body['auth']
  127. body = json.dumps(body)
  128. if self.verbose:
  129. print body
  130. print method
  131. print headers
  132. httplib2.debuglevel = 1
  133. response, results = http.request(self.server, http_method, body, headers)
  134. if self.verbose:
  135. print response
  136. print results
  137. try:
  138. results = json.loads(results)
  139. except ValueError as e:
  140. results = {"error": e.message}
  141. return response, results
  142. '''
  143. This bit of metaprogramming is where the ZabbixAPI subclasses are created.
  144. For each of ZabbixAPI.classes we create a class from the key and methods
  145. from the ZabbixAPI.classes values. We pass a reference to ZabbixAPI class
  146. to each subclass in order for each to be able to call the perform method.
  147. '''
  148. @staticmethod
  149. def meta(class_name, method_names):
  150. # This meta method allows a class to add methods to it.
  151. def meta_method(Class, method_name):
  152. # This template method is a stub method for each of the subclass
  153. # methods.
  154. def template_method(self, **params):
  155. return self.parent.perform(class_name.lower()+"."+method_name, params)
  156. template_method.__doc__ = "https://www.zabbix.com/documentation/2.4/manual/api/reference/%s/%s" % (class_name.lower(), method_name)
  157. template_method.__name__ = method_name
  158. # this is where the template method is placed inside of the subclass
  159. # e.g. setattr(User, "create", stub_method)
  160. setattr(Class, template_method.__name__, template_method)
  161. # This class call instantiates a subclass. e.g. User
  162. Class=type(class_name, (object,), { '__doc__': "https://www.zabbix.com/documentation/2.4/manual/api/reference/%s" % class_name.lower() })
  163. # This init method gets placed inside of the Class
  164. # to allow it to be instantiated. A reference to the parent class(ZabbixAPI)
  165. # is passed in to allow each class access to the perform method.
  166. def __init__(self, parent):
  167. self.parent = parent
  168. # This attaches the init to the subclass. e.g. Create
  169. setattr(Class, __init__.__name__, __init__)
  170. # For each of our ZabbixAPI.classes dict values
  171. # Create a method and attach it to our subclass.
  172. # e.g. 'User': ['delete', 'get', 'updatemedia', 'updateprofile',
  173. # 'update', 'iswritable', 'logout', 'addmedia', 'create',
  174. # 'login', 'deletemedia', 'isreadable'],
  175. # User.delete
  176. # User.get
  177. for method_name in method_names:
  178. meta_method(Class, method_name)
  179. # Return our subclass with all methods attached
  180. return Class
  181. # Attach all ZabbixAPI.classes to ZabbixAPI class through metaprogramming
  182. for class_name, method_names in ZabbixAPI.classes.items():
  183. setattr(ZabbixAPI, class_name, ZabbixAPI.meta(class_name, method_names))
  184. def main():
  185. module = AnsibleModule(
  186. argument_spec = dict(
  187. server=dict(default='https://localhost/zabbix/api_jsonrpc.php', type='str'),
  188. user=dict(default=None, type='str'),
  189. password=dict(default=None, type='str'),
  190. zbx_class=dict(choices=ZabbixAPI.classes.keys()),
  191. action=dict(default=None, type='str'),
  192. params=dict(),
  193. debug=dict(default=False, type='bool'),
  194. ),
  195. #supports_check_mode=True
  196. )
  197. user = module.params.get('user', None)
  198. if not user:
  199. user = os.environ['ZABBIX_USER']
  200. pw = module.params.get('password', None)
  201. if not pw:
  202. pw = os.environ['ZABBIX_PASSWORD']
  203. server = module.params['server']
  204. if module.params['debug']:
  205. options['debug'] = True
  206. api_data = {
  207. 'user': user,
  208. 'password': pw,
  209. 'server': server,
  210. }
  211. if not user or not pw or not server:
  212. module.fail_json('Please specify the user, password, and the zabbix server.')
  213. zapi = ZabbixAPI(api_data)
  214. zbx_class = module.params.get('zbx_class')
  215. action = module.params.get('action')
  216. params = module.params.get('params', {})
  217. # Get the instance we are trying to call
  218. zbx_class_inst = zapi.__getattribute__(zbx_class.lower())
  219. # Get the instance's method we are trying to call
  220. zbx_action_method = zapi.__getattribute__(zbx_class.capitalize()).__dict__[action]
  221. # Make the call with the incoming params
  222. results = zbx_action_method(zbx_class_inst, **params)
  223. # Results Section
  224. changed_state = False
  225. status = results[0]['status']
  226. if status not in ['200', '201']:
  227. #changed_state = False
  228. module.fail_json(msg="Http response: [%s] - Error: %s" % (str(results[0]), results[1]))
  229. module.exit_json(**{'results': results[1]['result']})
  230. from ansible.module_utils.basic import *
  231. main()