内容简介:如何为zabbix创建自己的的ansible module
作为一个运维狗,zabbix这个强大的 工具 肯定知道吧,因为我们大部分的时间都在zabbix上:添加监控,ack报警,我们要创建一个个的template,一个个的item,和一个个的trigger
当然,我们可以直接web页面添加,但是,如果有一天,zabbix彻底坏了,重新创建上千条监控的手动工作量你是的懂的…..
既然不向手动创建,肯定是想把这些item,template, trigger, 通过文件管理起来,然后通过github来管理版本,然后通过ansible-playbook来统一创建,下次有人告诉你zabbix挂了的时候,不用着急,ansible-palybook一跑,半个小时给你一个全新的一模一样的zabbix(数据库得自己备份)
进入正题: ansible 其实是提供监控模块的 http://docs.ansible.com/ansible/list_of_monitoring_modules.html
我们可以用这个现成的模块来做一些基本的操作:例如增加一个mantain:
- name: Create a named maintenance window for host www1 for 90 minutes zabbix_maintenance: name: Update of www1 host_name: www1.example.com state: present minutes: 90 server_url: https://monitoring.example.com login_user: ansible login_password: pAsSwOrD
或者更新一个主机信息:http://docs.ansible.com/ansible/zabbix_host_module.html
但是,毕竟,这些是有限的,例如,我们想创建一个item, 创建一个模版,官方是没有提供的,这个时候,我们就要自己写,要自己写module,首先确定了如下:
1: zabbix 提供了相应的api
2:python 可以调去api
满足以上两个条件之后,自己写module就不成问题了
首先,我们要创建一个zabbix api文件,用来负责建立连接(参见:https://github.com/openshift/openshift-tools/blob/prod/openshift_tools/zbxapi/__init__.py)
#!/usr/bin/env python ''' ZabbixAPI library if __name__ == '__main__': server = 'http://localhost/zabbix/api_jsonrpc.php' username = '' password = '' zbc = ZabbixConnection(server, username, password) zbx = ZabbixAPI(data) print zbx.get_content('user', 'get', {}) ''' # vim: expandtab:tabstop=4:shiftwidth=4 # Copyright 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Purpose: An ansible module to communicate with zabbix. # Requires Packages on python < 2.7.9: # python-pyasn1 python-ndg_httpsclient pyOpenSSL # # pylint: disable=line-too-long # Disabling line length for readability import json import requests import httplib import copy class ZabbixAPIError(Exception): ''' ZabbixAPIError Exists to propagate errors up from the api ''' pass # Disabling to have DTO # pylint: disable=too-few-public-methods # DTO needs an extra arg # pylint: disable=too-many-arguments class ZabbixConnection(object): ''' Placeholder for connection options ''' def __init__(self, server, username, password, ssl_verify=False, verbose=False): self.server = server self.username = username self.password = password self.verbose = verbose self.ssl_verify = ssl_verify class ZabbixAPI(object): ''' ZabbixAPI class ''' classes = { 'Action': ['create', 'delete', 'get', 'update'], 'Alert': ['get'], 'Application': ['create', 'delete', 'get', 'massadd', 'update'], 'Configuration': ['export', 'import'], 'Dhost': ['get'], 'Dcheck': ['get'], 'Discoveryrule': ['copy', 'create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Drule': ['copy', 'create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Dservice': ['get'], 'Event': ['acknowledge', 'get'], 'Graph': ['create', 'delete', 'get', 'update'], 'Graphitem': ['get'], 'Graphprototype': ['create', 'delete', 'get', 'update'], 'History': ['get'], 'Hostgroup': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massremove', 'massupdate', 'update'], 'Hostinterface': ['create', 'delete', 'get', 'massadd', 'massremove', 'replacehostinterfaces', 'update'], 'Host': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massremove', 'massupdate', 'update'], 'Hostprototype': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Httptest': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Iconmap': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Image': ['create', 'delete', 'get', 'update'], 'Item': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Itemprototype': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Maintenance': ['create', 'delete', 'get', 'update'], 'Map': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Mediatype': ['create', 'delete', 'get', 'update'], 'Proxy': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Screen': ['create', 'delete', 'get', 'update'], 'Screenitem': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'update', 'updatebyposition'], 'Script': ['create', 'delete', 'execute', 'get', 'getscriptsbyhosts', 'update'], 'Service': ['adddependencies', 'addtimes', 'create', 'delete', 'deletedependencies', 'deletetimes', 'get', 'getsla', 'isreadable', 'iswritable', 'update'], 'Template': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massremove', 'massupdate', 'update'], 'Templatescreen': ['copy', 'create', 'delete', 'get', 'isreadable', 'iswritable', 'update'], 'Templatescreenitem': ['get'], 'Trigger': ['adddependencies', 'create', 'delete', 'deletedependencies', 'get', 'isreadable', 'iswritable', 'update'], 'Triggerprototype': ['create', 'delete', 'get', 'update'], 'User': ['addmedia', 'create', 'delete', 'deletemedia', 'get', 'isreadable', 'iswritable', 'login', 'logout', 'update', 'updatemedia', 'updateprofile'], 'Usergroup': ['create', 'delete', 'get', 'isreadable', 'iswritable', 'massadd', 'massupdate', 'update'], 'Usermacro': ['create', 'createglobal', 'delete', 'deleteglobal', 'get', 'update', 'updateglobal'], 'Usermedia': ['get'], } def __init__(self, zabbix_connection=None): self.server = zabbix_connection.server self.username = zabbix_connection.username self.password = zabbix_connection.password if any([value == None for value in [self.server, self.username, self.password]]): raise ZabbixAPIError('Please specify zabbix server url, username, and password.') self.verbose = zabbix_connection.verbose self.ssl_verify = zabbix_connection.ssl_verify if self.verbose: httplib.HTTPSConnection.debuglevel = 1 httplib.HTTPConnection.debuglevel = 1 self.auth = None for cname, _ in self.classes.items(): setattr(self, cname.lower(), getattr(self, cname)(self)) # pylint: disable=no-member # This method does not exist until the metaprogramming executed resp, content = self.user.login(user=self.username, password=self.password) if resp.status_code == 200: if content.has_key('result'): self.auth = content['result'] elif content.has_key('error'): raise ZabbixAPIError("Unable to authenticate with zabbix server. {0} ".format(content['error'])) else: raise ZabbixAPIError("Error in call to zabbix. Http status: {0}.".format(resp.status_code)) def perform(self, method, rpc_params): ''' This method calls your zabbix server. It requires the following parameters in order for a proper request to be processed: jsonrpc - the version of the JSON-RPC protocol used by the API; the Zabbix API implements JSON-RPC version 2.0; method - the API method being called; rpc_params - parameters that will be passed to the API method; id - an arbitrary identifier of the request; auth - a user authentication token; since we don't have one yet, it's set to null. ''' jsonrpc = "2.0" rid = 1 headers = {} headers["Content-type"] = "application/json" body = { "jsonrpc": jsonrpc, "method": method, "params": rpc_params.get('params', {}), "id": rid, 'auth': self.auth, } if method in ['user.login', 'api.version']: del body['auth'] body = json.dumps(body) if self.verbose: print "BODY:", body print "METHOD:", method print "HEADERS:", headers request = requests.Request("POST", self.server, data=body, headers=headers) session = requests.Session() req_prep = session.prepare_request(request) response = session.send(req_prep, verify=self.ssl_verify) if response.status_code not in [200, 201]: raise ZabbixAPIError('Error calling zabbix. Zabbix returned %s' % response.status_code) if self.verbose: print "RESPONSE:", response.text try: content = response.json() except ValueError as err: content = {"error": err.message} return response, content @staticmethod def meta(cname, method_names): ''' This bit of metaprogramming is where the ZabbixAPI subclasses are created. For each of ZabbixAPI.classes we create a class from the key and methods from the ZabbixAPI.classes values. We pass a reference to ZabbixAPI class to each subclass in order for each to be able to call the perform method. ''' def meta_method(_class, method_name): ''' This meta method allows a class to add methods to it. ''' # This template method is a stub method for each of the subclass # methods. def template_method(self, params=None, **rpc_params): ''' This template method is a stub method for each of the subclass methods. ''' if params: rpc_params['params'] = params else: rpc_params['params'] = copy.deepcopy(rpc_params) return self.parent.perform(cname.lower()+"."+method_name, rpc_params) template_method.__doc__ = \ "https://www.zabbix.com/documentation/2.4/manual/api/reference/%s/%s" % \ (cname.lower(), method_name) template_method.__name__ = method_name # this is where the template method is placed inside of the subclass # e.g. setattr(User, "create", stub_method) setattr(_class, template_method.__name__, template_method) # This class call instantiates a subclass. e.g. User _class = type(cname, (object,), {'__doc__': \ "https://www.zabbix.com/documentation/2.4/manual/api/reference/%s" % cname.lower()}) def __init__(self, parent): ''' This init method gets placed inside of the _class to allow it to be instantiated. A reference to the parent class(ZabbixAPI) is passed in to allow each class access to the perform method. ''' self.parent = parent # This attaches the init to the subclass. e.g. Create setattr(_class, __init__.__name__, __init__) # For each of our ZabbixAPI.classes dict values # Create a method and attach it to our subclass. # e.g. 'User': ['delete', 'get', 'updatemedia', 'updateprofile', # 'update', 'iswritable', 'logout', 'addmedia', 'create', # 'login', 'deletemedia', 'isreadable'], # User.delete # User.get for method_name in method_names: meta_method(_class, method_name) # Return our subclass with all methods attached return _class def get_content(self, zbx_class_name, method, params): ''' This bit of metaprogramming takes a zabbix_class_name (e.g. 'user' ) This gets the instantiated object of type user and calls method with params as the parameters. Returns the zabbix query results ''' zbx_class_inst = self.__getattribute__(zbx_class_name.lower()) zbx_class = self.__getattribute__(zbx_class_name.capitalize()) return zbx_class.__dict__[method](zbx_class_inst, params)[1] # Attach all ZabbixAPI.classes to ZabbixAPI class through metaprogramming for _class_name, _method_names in ZabbixAPI.classes.items(): setattr(ZabbixAPI, _class_name, ZabbixAPI.meta(_class_name, _method_names))
目录结构:
$tree zbxapi/ zbxapi/ `-- __init__.py
然后就是我们的module了(这个地方要注意,module的名字,就是你的文件名)https://github.com/openshift/openshift-tools/tree/prod/ansible/roles/lib_zabbix/library
#!/usr/bin/env python ''' Ansible module for zabbix items ''' # vim: expandtab:tabstop=4:shiftwidth=4 # # Zabbix item ansible module # # # Copyright 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This is in place because each module looks similar to each other. # These need duplicate code as their behavior is very similar # but different for each zabbix class. # pylint: disable=duplicate-code # pylint: disable=import-error from openshift_tools.zbxapi import ZabbixAPI, ZabbixConnection def exists(content, key='result'): ''' Check if key exists in content or the size of content[key] > 0 ''' if not content.has_key(key): return False if not content[key]: return False return True def get_data_type(data_type): ''' Possible values: 0 - decimal; 1 - octal; 2 - hexadecimal; 3 - bool; ''' vtype = 0 if 'octal' in data_type: vtype = 1 elif 'hexadecimal' in data_type: vtype = 2 elif 'bool' in data_type: vtype = 3 return vtype def get_value_type(value_type): ''' Possible values: 0 - numeric float; 1 - character; 2 - log; 3 - numeric unsigned; 4 - text ''' vtype = 0 if 'int' in value_type: vtype = 3 elif 'log' in value_type: vtype = 2 elif 'char' in value_type: vtype = 1 elif 'str' in value_type: vtype = 4 return vtype def get_app_ids(application_names, app_name_ids): ''' get application ids from names ''' applications = [] if application_names: for app in application_names: applications.append(app_name_ids[app]) return applications def get_template_ids(zapi, template_name): ''' get related templates ''' template_ids = [] app_ids = {} # Fetch templates by name content = zapi.get_content('template', 'get', {'search': {'host': template_name}, 'selectApplications': ['applicationid', 'name']}) if content.has_key('result'): template_ids.append(content['result'][0]['templateid']) for app in content['result'][0]['applications']: app_ids[app['name']] = app['applicationid'] return template_ids, app_ids def get_host_ids(module, zapi, hostname): ''' get related host_id ''' host_ids = [] content = zapi.get_content('host', 'get', {'search': {'host': hostname}}) # Module exit attempting to catch details about the following error during # TASK [tools_roles/os_zabbix_cluster_stats : item: openshift.master.cluster.all.cpu.idle_sum]: # in get_host_ids\n host_ids.append(content['result'][0]['hostid']) # IndexError: list index out of range try: if content.has_key('result'): host_ids.append(content['result'][0]['hostid']) except IndexError as ierror: module.exit_json(failed=True, changed=False, results='Error (%s): get host ids for %s did not contain expected content format: %s' % \ (str(ierror), hostname, str(content)), state="Unknown") return host_ids def get_multiplier(inval): ''' Determine the multiplier ''' if inval == None or inval == '': return None, 0 rval = None try: rval = int(inval) except ValueError: pass if rval: return rval, 1 return rval, 0 def get_zabbix_type(ztype): ''' Determine which type of discoverrule this is ''' _types = {'agent': 0, 'SNMPv1': 1, 'trapper': 2, 'simple': 3, 'SNMPv2': 4, 'internal': 5, 'SNMPv3': 6, 'active': 7, 'aggregate': 8, 'web': 9, 'external': 10, 'database monitor': 11, 'ipmi': 12, 'ssh': 13, 'telnet': 14, 'calculated': 15, 'JMX': 16, 'SNMP trap': 17, } for typ in _types.keys(): if ztype in typ or ztype == typ: _vtype = _types[typ] break else: _vtype = 2 return _vtype # The branches are needed for CRUD and error handling # pylint: disable=too-many-branches, too-many-statements def main(): ''' ansible zabbix module for zbx_item ''' module = AnsibleModule( argument_spec=dict( zbx_server=dict(default='https://localhost/zabbix/api_jsonrpc.php', type='str'), zbx_user=dict(default=os.environ.get('ZABBIX_USER', None), type='str'), zbx_password=dict(default=os.environ.get('ZABBIX_PASSWORD', None), type='str'), zbx_debug=dict(default=False, type='bool'), name=dict(default=None, type='str'), key=dict(default=None, type='str'), template_name=dict(default=None, type='str'), hostname=dict(default=None, type='str'), zabbix_type=dict(default='trapper', type='str'), value_type=dict(default='int', type='str'), data_type=dict(default='decimal', type='str'), interval=dict(default=60, type='int'), delta=dict(default=0, type='int'), multiplier=dict(default=None, type='str'), description=dict(default=None, type='str'), units=dict(default=None, type='str'), history=dict(default=None, type='int'), trends=dict(default=None, type='int'), applications=dict(default=None, type='list'), state=dict(default='present', type='str'), params=dict(default=None, type='str'), ), #supports_check_mode=True mutually_exclusive=[ ['template_name', 'hostname'], ['applications', 'hostname'], ] ) zapi = ZabbixAPI(ZabbixConnection(module.params['zbx_server'], module.params['zbx_user'], module.params['zbx_password'], module.params['zbx_debug'])) #Set the instance and the template for the rest of the calls zbx_class_name = 'item' state = module.params['state'] template_ids = None app_name_ids = None host_ids = None content = None if module.params['template_name']: template_ids, app_name_ids = get_template_ids(zapi, module.params['template_name']) if not template_ids: module.exit_json(failed=True, changed=False, results='Error: Could not find template with name %s for item.' % \ module.params['template_name'], state="Unknown") if len(template_ids) != 1: module.exit_json(failed=True, changed=False, results='Error: Found multiple templates matching %s for item.' % \ module.params['template_name'], state="Unknown") content = zapi.get_content(zbx_class_name, 'get', {'search': {'key_': module.params['key']}, 'selectApplications': 'applicationid', 'templateids': template_ids, }) if module.params['hostname']: host_ids = get_host_ids(module, zapi, module.params['hostname']) if not host_ids: module.exit_json(failed=True, changed=False, results='Error: Could not find host with name %s for item.' % module.params['hostname'], state="Unknown") if len(host_ids) != 1: module.exit_json(failed=True, changed=False, results='Error: Found multiple hosts matching %s for item.' % module.params['hostname'], state="Unknown") content = zapi.get_content(zbx_class_name, 'get', {'search': {'key_': module.params['key']}, 'hostids': host_ids, }) #******# # GET #******# if state == 'list': module.exit_json(changed=False, results=content['result'], state="list") #******# # DELETE #******# if state == 'absent': if not exists(content): module.exit_json(changed=False, state="absent") content = zapi.get_content(zbx_class_name, 'delete', [content['result'][0]['itemid']]) module.exit_json(changed=True, results=content['result'], state="absent") # Create and Update if state == 'present': formula, use_multiplier = get_multiplier(module.params['multiplier']) params = {'name': module.params.get('name', module.params['key']), 'key_': module.params['key'], 'type': get_zabbix_type(module.params['zabbix_type']), 'value_type': get_value_type(module.params['value_type']), 'data_type': get_data_type(module.params['data_type']), 'formula': formula, 'multiplier': use_multiplier, 'description': module.params['description'], 'units': module.params['units'], 'history': module.params['history'], 'trends': module.params['trends'], 'delay': module.params['interval'], 'delta': module.params['delta'], 'params': module.params['params'], } if module.params['hostname']: params['hostid'] = host_ids[0] else: params['applications'] = get_app_ids(module.params['applications'], app_name_ids) params['hostid'] = template_ids[0] # Remove any None valued params _ = [params.pop(key, None) for key in params.keys() if params[key] is None] #******# # CREATE #******# if not exists(content): content = zapi.get_content(zbx_class_name, 'create', params) if content.has_key('error'): module.exit_json(failed=True, changed=True, results=content['error'], state="present") module.exit_json(changed=True, results=content['result'], state='present') ######## # UPDATE ######## _ = params.pop('hostid', None) differences = {} zab_results = content['result'][0] for key, value in params.items(): if key == 'applications': app_ids = [item['applicationid'] for item in zab_results[key]] if set(app_ids) != set(value): differences[key] = value elif zab_results[key] != value and zab_results[key] != str(value): differences[key] = value if not differences: module.exit_json(changed=False, results=zab_results, state="present") # We have differences and need to update differences['itemid'] = zab_results['itemid'] content = zapi.get_content(zbx_class_name, 'update', differences) if content.has_key('error'): module.exit_json(failed=True, changed=False, results=content['error'], state="present") module.exit_json(changed=True, results=content['result'], state="present") module.exit_json(failed=True, changed=False, results='Unknown state passed. %s' % state, state="unknown") # pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled # import module snippets. This are required from ansible.module_utils.basic import * main()
目录结构:
$tree lib_zabbix/ lib_zabbix/ |-- build | |-- ansible | | `-- zbx_maintenance.py | |-- doc | | `-- maintenance | |-- generate.py | |-- lib | | |-- base.py | | |-- hostgroup.py | | |-- host.py | | `-- zbxapi.py | `-- src | `-- zbx_maintenance.py |-- library | |-- __init__.py | |-- zbx_action.py | |-- zbx_application.py | |-- zbx_discoveryrule.py | |-- zbx_graphprototype.py | |-- zbx_graph.py | |-- zbx_hostgroup.py | |-- zbx_host.py | |-- zbx_httptest.py | |-- zbx_itemprototype.py | |-- zbx_item.py | |-- zbx_itservice.py | |-- zbx_maintenance.py | |-- zbx_mediatype.py | |-- zbx_template.py | |-- zbx_triggerprototype.py | |-- zbx_trigger.py | |-- zbx_usergroup.py | |-- zbx_user_media.py | `-- zbx_user.py |-- README.md `-- tasks |-- create_template.yml `-- create_user.yml
,目录结构里有很多其它方法,全是我们组在使用的module,全部在github上,大家可以自行前往(我大Redhat! 威武霸气)
然后就是具体使用了:
- name: Create Items zbx_item: zbx_server: "{{ server }}" zbx_user: "{{ user }}" zbx_password: "{{ password }}" key: "{{ item.key }}" name: "{{ item.name | default(item.key, true) }}" value_type: "{{ item.value_type | default('int') }}" data_type: "{{ item.data_type | default('decimal') }}" description: "{{ item.description | default('', True) }}" multiplier: "{{ item.multiplier | default('', True) }}" units: "{{ item.units | default('', True) }}" history: "{{ item.history | default(14, True) }}" trends: "{{ item.trends | default(omit, True) }}" template_name: "{{ template.name }}" applications: "{{ item.applications }}" zabbix_type: "{{ item.zabbix_type | default('trapper') }}" interval: "{{ item.interval | default(60, True) }}" delta: "{{ item.delta | default(0, True) }}" params: "{{ item.params | default(omit, True) }}" with_items: "{{ template.zitems | default([]) }}" register: created_items
我们将写好的变量存放到/var下的yml文件, 引入之后,通过with_items来进行循环,直接调用,zabbix就自动创建了
- name: Include Template OpenShift Cluster include: ../../lib_zabbix/tasks/create_template.yml vars: template: "{{ g_template_openshift_cluster }}" server: "{{ ozb_server }}" user: "{{ ozb_user }}" password: "{{ ozb_password }}" tags: - clusterwide
调用这个方法的时候,已经传入template变量了
具体的存储模版是
https://github.com/openshift/openshift-tools/blob/prod/ansible/roles/os_zabbix/vars/template_openshift_master.yml
以上所述就是小编给大家介绍的《如何为zabbix创建自己的的ansible module》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Sass and Compass in Action
Wynn Netherland、Nathan Weizenbaum、Chris Eppstein、Brandon Mathis / Manning Publications / 2013-8-2 / USD 44.99
Written by Sass and Compass creators * Complete Sass language reference * Covers prominent Compass community plug-ins * Innovative approach to creating stylesheets Cascading Style Sheets paint the we......一起来看看 《Sass and Compass in Action》 这本书的介绍吧!