内容简介:如何为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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
测出转化率:营销优化的科学与艺术
【美】高尔德(Goward,C.) / 谭磊、唐捷译 / 电子工业出版社 / 2014-10-1 / 68.00元
本书作者通过已成功实现大幅提升转化率的案例,展示了大量以营销为核心的电子商务网站的测试设计方法及转化优化方案。书中作者强调了测试及优化思维的重要性,并就实现方法做了详细讲解。 通过本书,读者将学到如何能够在网站遇到发展和收入瓶颈时,测试出存在的问题并找到解决方案;如何可以深入地了解客户需求,并以此为基础优化网站,使其达到提升转化率的目的;如何提升网站的竞争优势,把在线营销渠道变成高效的转化通......一起来看看 《测出转化率:营销优化的科学与艺术》 这本书的介绍吧!
XML 在线格式化
在线 XML 格式化压缩工具
UNIX 时间戳转换
UNIX 时间戳转换