zbxapi.py 11 KB

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