├── .gitignore ├── module ├── .gitigore ├── Makefile └── libzbxovirt.c ├── libzbxovirt.conf.sample ├── python ├── ovirt.sh └── ovirt.py ├── install.sh ├── COPYING ├── ovirt.conf └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /module/.gitigore: -------------------------------------------------------------------------------- 1 | *.so 2 | -------------------------------------------------------------------------------- /libzbxovirt.conf.sample: -------------------------------------------------------------------------------- 1 | ovirt = { 2 | engine = { 3 | user = "admin@internal"; 4 | pass = "PASSWORD"; 5 | server = "https://ENGINE HOST:"; 6 | uri = "/ovirt-engine/api"; 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /python/ovirt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -f /opt/rh/rh-python35/enable ] && . /opt/rh/rh-python35/enable 4 | /etc/zabbix/ovirt/bin/python /etc/zabbix/scripts/ovirt.py $@ 5 | 6 | RESULT=$? 7 | exit $RESULT 8 | -------------------------------------------------------------------------------- /module/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRC = libzbxovirt.c 3 | 4 | TARGET = libzbxovirt.so 5 | MODULEPATH = /usr/lib/zabbix/modules 6 | 7 | LDFLAGS = -lcurl -lconfig 8 | CFLAGS = -Wall -O3 -I../../zabbix-3.4.7/include 9 | 10 | $(TARGET): $(SRC) 11 | $(CC) -shared -fPIC -o $@ $(OBJ) $(SRC) $(LDFLAGS) $(CFLAGS) 12 | clean: 13 | rm -f $(TARGET) $(OBJ) 14 | 15 | install:$(TARGET) 16 | service zabbix-agent stop 17 | install -d $(MODULEPATH) 18 | install -C $(TARGET) $(MODULEPATH) 19 | service zabbix-agent start 20 | service zabbix-agent status 21 | tail /var/log/zabbix/zabbix_agentd.log 22 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ZABBIX_DIR_ETC=/etc/zabbix 4 | 5 | # create directories and copy files 6 | mkdir -p ${ZABBIX_DIR_ETC}/scripts 7 | mkdir -p ${ZABBIX_DIR_ETC}/zabbix_agentd.d 8 | cp ovirt.conf ${ZABBIX_DIR_ETC}/zabbix_agentd.d 9 | cp python/ovirt.py ${ZABBIX_DIR_ETC}/scripts 10 | cp python/ovirt.sh ${ZABBIX_DIR_ETC}/scripts 11 | 12 | if [ ! -f ${ZABBIX_DIR_ETC}/libzbxovirt.conf ]; then 13 | cp libzbxovirt.conf.sample ${ZABBIX_DIR_ETC}/libzbxovirt.conf 14 | fi 15 | 16 | # create virtualenv 17 | virtualenv ${ZABBIX_DIR_ETC}/ovirt 18 | . ${ZABBIX_DIR_ETC}/ovirt/bin/activate 19 | pip install -U pip setuptools 20 | pip install urllib3 requests libconf 21 | deactivate 22 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | /* 2 | ** libzbxovirt - A oVirt/RHEV monitoring module for Zabbix 3 | ** Copyright (C) 2018 - Peter Hudec 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | **/ 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ovirt.conf: -------------------------------------------------------------------------------- 1 | LoadModule=libzbxovirt.so 2 | 3 | # Discover calls 4 | UserParameter=ovirt.host.discover,/etc/zabbix/scripts/ovirt.sh --list hosts 5 | UserParameter=ovirt.vm.discover,/etc/zabbix/scripts/ovirt.sh --list vms 6 | 7 | # simple methods 8 | UserParameter=ovirt.simple.get[*],/etc/zabbix/scripts/ovirt.sh --get $1 --service $2 9 | UserParameter=ovirt.simple.list[*],/etc/zabbix/scripts/ovirt.sh --list $1 --service $2 10 | # moved to libzbxovirt.so 11 | #UserParameter=ovirt.simple.stats[*],/etc/zabbix/scripts/ovirt.sh --stats --service $1 12 | #UserParameter=ovirt.simple.data[*],/etc/zabbix/scripts/ovirt.sh --data --service $1 13 | 14 | # host calls 15 | UserParameter=ovirt.host.get[*],/etc/zabbix/scripts/ovirt.sh --get $1 --service hosts --uuid $2 16 | UserParameter=ovirt.host.discovery[*],/etc/zabbix/scripts/ovirt.sh --list $1 --uuid $2 17 | # moved to libzbxovirt.so 18 | #UserParameter=ovirt.host.stats[*],/etc/zabbix/scripts/ovirt.sh --stats --service hosts --uuid $1 19 | #UserParameter=ovirt.host.data[*],/etc/zabbix/scripts/ovirt.sh --data --service hosts --uuid $1 20 | 21 | # vm calls 22 | UserParameter=ovirt.vm.get[*],/etc/zabbix/scripts/ovirt.sh --get $1 --service vms --uuid $2 23 | UserParameter=ovirt.vm.discovery[*],/etc/zabbix/scripts/ovirt.sh --list $1 --uuid $2 24 | # moved to libzbxovirt.so 25 | #UserParameter=ovirt.vm.stats[*],/etc/zabbix/scripts/ovirt.sh --stats --service vms --uuid $1 26 | #UserParameter=ovirt.vm.data[*],/etc/zabbix/scripts/ovirt.sh --data --service vms --uuid $1 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libzbxovirt 2 | 3 | ## Overview 4 | Zabbix have native support to monitor **VmWare** infrastructure. The goal is to add support for **oVirt/RHEV** into zabbix too. 5 | 6 | This is at stage of **proof-of-concept**, any contributing is welcome. 7 | 8 | ## Requirements 9 | 10 | ### Zabbix sources 11 | 12 | To compile native zabbix module you need zabbix source. You could get them from `https://www.zabbix.com/download_sources` or `https://sourceforge.net/projects/zabbix/files/ZABBIX%20Latest%20Stable/` page. I'm testing against version **3.4.7**, but any **3.4.** is good. 13 | 14 | Follow the **instruction** on `https://www.zabbix.com/documentation/3.4/manual/config/items/loadablemodules`, part **2.5 Building modules, run `./configure`. 15 | 16 | 17 | ### Zabbix version 18 | You need zabbix version at least **3.4** to get it all work. In this release was introduces **Dependent items**, see `https://www.zabbix.com/documentation/3.4/manual/config/items/itemtypes/dependent_items` 19 | 20 | ### Python 21 | Discovery rules are written in python for simplicity. I'm using **python 2**, but maybe also **python 3** would work. 22 | 23 | ``` 24 | virtualenv ovirt 25 | pip install -U pip setuptools 26 | pip install urllib3 27 | pip install requests 28 | pip install libconf 29 | ``` 30 | I'm not using **ovirt-engine-sdk-python** library, since it's too big overhead for monitoring purposes. 31 | 32 | ### C libraries 33 | 34 | To compile the module you will need **curl** and **config** libraries 35 | 36 | #### Debian 9 37 | ``` 38 | apt-get install libcurl4-openssl-dev libconfig-dev 39 | ``` 40 | #### CentOS 41 | ``` 42 | yum install -y libcurl-devel libconfig-devel 43 | ``` 44 | 45 | ## Instalation 46 | 47 | ### Files, Directories, Python 48 | Review and run `./install.sh`. Check if you have in your **/etc/zabbix/zabbix_agentd.conf** file line 49 | 50 | Include=/etc/zabbix/zabbix_agentd.d 51 | 52 | ### Module 53 | In **module/Makefile** 54 | 55 | - tweak path to your zabbix sources 56 | - if you set **LoadModulePath** in **/etc/zabbix/zabbix_agentd.conf**, update **MODULEPATH** here 57 | 58 | ``` 59 | cd modules 60 | make 61 | make install 62 | ``` 63 | 64 | ### Configuration 65 | Update the **/etc/zabbix/libzbxovirt.conf** 66 | 67 | ## How it works 68 | 69 | It uses oVirt API, see `http://ovirt.github.io/ovirt-engine-api-model/`. 70 | 71 | ### Data types 72 | There are 2 type of functions 73 | 74 | - **data**: it will return full unmodified json output fro the API 75 | - **stats**: it will parse `/statistics` endpoint and return just **key/value** result 76 | 77 | On zabbix site, the data are populates on these two type of calls using the **Dependent Item** type. This offloads an engine side from queries. 78 | 79 | ### Zabbix template 80 | 81 | Import zabbix template and associate it with the host running this module. It not needs to be engine itself. 82 | 83 | ### Object types 84 | There are 3 object** types: **simple**, **vm**, **host**. 85 | 86 | The **simple** input is full api endpoint, it looks like `/ovirt-engine/api/vms`. 87 | The **host** input is host uid, it will call `/ovirt-engine/api/hosts/`. 88 | The **vm** input is vm uid, it will call `/ovirt-engine/api/vms/`. 89 | 90 | ## Notes 91 | 92 | ### Goal 93 | My idea is to build production ready oVirt as next service we provide on VmWare an Hyper-V. The VmWare has a lot of monitoring features and nice DWH reports and native zabbix integration. 94 | 95 | ### Thoughts 96 | I'm testing on 97 | 98 | - Low powered (1xCPU, 1GB RAM) zabbix server, all in one 99 | - oVirt instance with about 50 VM and 3x hosts, engine (4xCPU, 6GB RAM) 100 | 101 | The zabbix module is the only way how to offload the zabbix agent part. When using python olny implementation with **UserParameter**, the load was to high due the process forking. 102 | With native module I was able to keep load very low. 103 | 104 | To offload the engine part, the **Dependent Item** is used. But the load goes very high, about 8-10 ;( 105 | 106 | This really not the way how to monitor the oVirts VM. In my case all hosts and VMs are monitored by zabbix agent and this is preferred way how to do that. 107 | 108 | ### What's next 109 | 110 | There are following possibilities. 111 | 112 | To get nice DHW graphs try to look at **oVirt Mertics**. 113 | 114 | This still could be used to monitor basic oVirt parameters like **DataCenter**, **Cluster**, **Storage**, but it need future work on this. 115 | 116 | Instead of the **API** the direct access to the **DWH** database could be used. 117 | 118 | Instead of **API** we could try to hook on VDSM API, see **vdsm-client** on `https://www.ovirt.org/develop/developer-guide/vdsm/vdsm-client/`. 119 | 120 | Use **zabbix-sender** with any previously mentioned any data source 121 | 122 | ### TODO 123 | The module is using the **basic http auth**. The engine for each query call internally SSO so there are internal 3 requests. The code should use regular login call and reuse the bearer token. This should offload the engine a little bit. 124 | 125 | The module is not reusing http connections, aka keep alive. The **SSL handshake** is done for each query and it's expensive. 126 | -------------------------------------------------------------------------------- /python/ovirt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # libzbxovirt - A oVirt/RHEV monitoring module for Zabbix 4 | # Copyright (C) 2018 - Peter Hudec 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | import json 21 | import requests 22 | from requests.auth import HTTPBasicAuth 23 | from requests import Request, Session 24 | 25 | try: 26 | from urllib.parse import urlencode, urlparse 27 | except ImportError: 28 | from urllib import urlencode 29 | from urlparse import urlparse 30 | 31 | class ApiOvirt: 32 | def __init__(self, url, username, password): 33 | self.auth = HTTPBasicAuth(username, password) 34 | self.url = url 35 | 36 | def __headers(self): 37 | return { 'Accept': 'application/json' } 38 | 39 | def __get(self, uri, follow=None): 40 | prefix = urlparse(self.url).path 41 | if not prefix.endswith('/'): 42 | prefix += '/' 43 | if uri.startswith(prefix): 44 | uri = uri[len(prefix):] 45 | uri = "%s/%s" % (self.url, uri) 46 | if follow: 47 | uri = "%s?follow=%s" % (uri, ','.join(follow)) 48 | r = requests.get(uri, headers=self.__headers(), auth=self.auth, verify=False) 49 | return r.json() 50 | 51 | # specific service 52 | def get(self, service, follow=None): 53 | return self.__get(service, follow=follow) 54 | 55 | class ZabbixService: 56 | DISCOVERY_RULES = { 57 | 'hosts': { 58 | 'base_path': None, 59 | 'service': 'hosts', 60 | 'key': 'host', 61 | 'prefix': 'HOST', 62 | }, 63 | 'vms': { 64 | 'base_path': None, 65 | 'service': 'vms', 66 | 'key': 'vm', 67 | 'prefix': 'VM', 68 | }, 69 | 'datacenters': { 70 | 'base_path': None, 71 | 'service': 'datacenters', 72 | 'key': 'data_center', 73 | 'prefix': 'DC', 74 | }, 75 | 'clusters': { 76 | 'base_path': None, 77 | 'service': 'clusters', 78 | 'key': 'cluster', 79 | 'prefix': 'CLUSTER', 80 | }, 81 | 'host_nics': { 82 | 'base_path': 'hosts', 83 | 'service': 'nics', 84 | 'key': 'host_nic', 85 | 'prefix': 'NIC', 86 | }, 87 | 'vm_nics': { 88 | 'base_path': 'vms', 89 | 'service': 'nics', 90 | 'key': 'nic', 91 | 'prefix': 'NIC', 92 | }, 93 | 'vm_disks': { 94 | 'base_path': 'vms', 95 | 'service': 'diskattachments', 96 | 'key': 'disk_attachment', 97 | 'subkey': 'disk', 98 | 'prefix': 'DISK', 99 | 'follow': ['disk'] 100 | }, 101 | 102 | } 103 | 104 | def __init__(self, api): 105 | self.api = api 106 | 107 | def discovery(self, service, uuid): 108 | service_map = self.DISCOVERY_RULES.get(service, None) 109 | if service_map is None: 110 | return None 111 | 112 | uri = service_map['service'] 113 | if service_map.get('base_path', None) and uuid is not None: 114 | uri = "%s/%s/%s" % (service_map.get('base_path'), uuid, uri) 115 | 116 | list = self.api.get(uri, follow=service_map.get('follow', None)) 117 | result = { 118 | 'data': [] 119 | } 120 | for item in list[service_map['key']]: 121 | if service_map.get('subkey', None): 122 | item = item[service_map.get('subkey')] 123 | result_item = dict() 124 | result_item["{#%s_ID}" % service_map['prefix']] = item['id'] 125 | result_item["{#%s_HREF}" % service_map['prefix']] = item['href'] 126 | result_item["{#%s_NAME}" % service_map['prefix']] = item['name'] 127 | result['data'].append(result_item) 128 | print json.dumps(result, indent=4) 129 | 130 | def service_get(self, service, uuid, key, follow=None): 131 | if uuid is not None: 132 | service = "%s/%s" % (service, uuid) 133 | data = self.api.get(service, follow=follow) 134 | result = data 135 | for path in key.split('.'): 136 | result = result.get(path, None) 137 | if result is None: 138 | break 139 | return result 140 | 141 | def service_stats(self, service, uuid, follow=None): 142 | if uuid is not None: 143 | service = "%s/%s" % (service, uuid) 144 | service = "%s/statistics" % (service,) 145 | data = self.api.get(service, follow=follow) 146 | result = dict() 147 | 148 | for stat in data['statistic']: 149 | result[stat['name']] = stat['values']['value'][0]['datum'] 150 | return result 151 | 152 | def service_data(self, service, uuid, follow=None): 153 | if uuid is not None: 154 | service = "%s/%s" % (service, uuid) 155 | data = self.api.get(service, follow=follow) 156 | return data 157 | 158 | 159 | def service_show(self, service, follow=None): 160 | data = self.api.get(service, follow=follow) 161 | print json.dumps(data, indent=4) 162 | 163 | def main(args): 164 | api = ApiOvirt( 165 | url="%s/%s" % (config['ovirt']['engine']['server'], config['ovirt']['engine']['uri']), 166 | username=config['ovirt']['engine']['user'], 167 | password=config['ovirt']['engine']['pass']) 168 | 169 | zabbix = ZabbixService(api) 170 | if args.list is not None: 171 | zabbix.discovery(args.list, args.uuid) 172 | if args.get is not None: 173 | result = zabbix.service_get(args.service, args.uuid, args.get, follow=args.follow) 174 | print '' if result is None else result 175 | if args.stats: 176 | result = zabbix.service_stats(args.service, args.uuid, follow=args.follow) 177 | print '' if result is None else json.dumps(result, indent=2) 178 | if args.data: 179 | result = zabbix.service_data(args.service, args.uuid, follow=args.follow) 180 | print '' if result is None else json.dumps(result, indent=2) 181 | 182 | if args.show: 183 | zabbix.service_show(args.service, follow=args.follow) 184 | 185 | if __name__ == "__main__": 186 | import urllib3 187 | urllib3.disable_warnings() 188 | 189 | import argparse 190 | parser = argparse.ArgumentParser(description = 'oVirt Hosts Wrapper') 191 | parser.add_argument('--list', dest='list', action='store', help='discovery method') 192 | parser.add_argument('--get', dest='get', action='store', help='get service value') 193 | parser.add_argument('--show', dest='show', action='store_true', help='get service value') 194 | parser.add_argument('--service', dest='service', action='store', help='service uri to list/get') 195 | parser.add_argument('--uuid', dest='uuid', action='store', help='uuid for the service') 196 | parser.add_argument('--stats', dest='stats', action='store_true', help='get stats value') 197 | parser.add_argument('--data', dest='data', action='store_true', help='get data value') 198 | parser.add_argument('--follow', dest='follow', action='append', help='follow links') 199 | parser.add_argument('--config', dest='config', action='store', default='/etc/zabbix/libzbxovirt.conf', help='configuration file') 200 | 201 | args = parser.parse_args() 202 | 203 | import io,libconf 204 | with io.open(args.config) as f: 205 | config = libconf.load(f) 206 | 207 | main(args) 208 | -------------------------------------------------------------------------------- /module/libzbxovirt.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** libzbxovirt - A oVirt/RHEV monitoring module for Zabbix 3 | ** Copyright (C) 2018 - Peter Hudec 4 | ** 5 | ** This program is free software; you can redistribute it and/or modify 6 | ** it under the terms of the GNU General Public License as published by 7 | ** the Free Software Foundation; either version 2 of the License, or 8 | ** (at your option) any later version. 9 | ** 10 | ** This program is distributed in the hope that it will be useful, 11 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ** GNU General Public License for more details. 14 | ** 15 | ** You should have received a copy of the GNU General Public License 16 | ** along with this program; if not, write to the Free Software 17 | ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | **/ 19 | 20 | 21 | // Zabbix source headers 22 | #include "sysinc.h" 23 | #include "module.h" 24 | #include "common.h" 25 | #include "log.h" 26 | #include "zbxjson.h" 27 | #include "version.h" 28 | 29 | #include 30 | #include 31 | 32 | #define JSON_TAG_STATISTICS "statistic" 33 | #define JSON_PATH_STATISTICS_NAME "$.name" 34 | #define JSON_PATH_STATISTICS_VALUE "$.values.value[0].datum" 35 | 36 | #define MODULE_CFG "/etc/zabbix/libzbxovirt.conf" 37 | #define MODULE_NAME "libzbxovirt.so" 38 | #define ovirt_log(LEVEL, FMT, ...) zabbix_log(LEVEL, "[%s;%s:%s:%d] " FMT, MODULE_NAME, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) 39 | 40 | struct curl_fetch_st { 41 | char *payload; 42 | size_t size; 43 | struct zbx_json_parse json; 44 | }; 45 | 46 | static int item_timeout = 0; 47 | 48 | config_t cfg; 49 | const char *cfg_ovirt_user; 50 | const char *cfg_ovirt_pass; 51 | const char *cfg_ovirt_server; 52 | const char *cfg_ovirt_uri; 53 | 54 | int zbx_module_ovirt_simple_stats(AGENT_REQUEST *request, AGENT_RESULT *result); 55 | int zbx_module_ovirt_simple_data(AGENT_REQUEST *request, AGENT_RESULT *result); 56 | 57 | int zbx_module_ovirt_host_stats(AGENT_REQUEST *request, AGENT_RESULT *result); 58 | int zbx_module_ovirt_host_data(AGENT_REQUEST *request, AGENT_RESULT *result); 59 | 60 | int zbx_module_ovirt_vm_stats(AGENT_REQUEST *request, AGENT_RESULT *result); 61 | int zbx_module_ovirt_vm_data(AGENT_REQUEST *request, AGENT_RESULT *result); 62 | 63 | int ovirt_config_init(config_t *cfg); 64 | int ovirt_config_uninit(config_t *cfg); 65 | int ovirt_config_get(config_t *cfgi, const char* name); 66 | 67 | /* module SHOULD define internal functions as static and use a naming pattern different from Zabbix internal */ 68 | /* symbols (zbx_*) and loadable module API functions (zbx_module_*) to avoid conflicts */ 69 | static ZBX_METRIC keys[] = 70 | /* KEY FLAG FUNCTION TEST PARAMETERS */ 71 | { 72 | {"ovirt.simple.stats", CF_HAVEPARAMS, zbx_module_ovirt_simple_stats, "vms/123"}, 73 | {"ovirt.simple.data", CF_HAVEPARAMS, zbx_module_ovirt_simple_data, "vms/123"}, 74 | {"ovirt.host.data", CF_HAVEPARAMS, zbx_module_ovirt_host_data, "123"}, 75 | {"ovirt.host.stats", CF_HAVEPARAMS, zbx_module_ovirt_host_stats, "123"}, 76 | {"ovirt.vm.data", CF_HAVEPARAMS, zbx_module_ovirt_vm_data, "123"}, 77 | {"ovirt.vm.stats", CF_HAVEPARAMS, zbx_module_ovirt_vm_stats, "123"}, 78 | {NULL} 79 | }; 80 | 81 | /****************************************************************************** 82 | * * 83 | * Function: zbx_module_api_version * 84 | * * 85 | * Purpose: returns version number of the module interface * 86 | * * 87 | * Return value: ZBX_MODULE_API_VERSION - version of module.h module is * 88 | * compiled with, in order to load module successfully Zabbix * 89 | * MUST be compiled with the same version of this header file * 90 | * * 91 | ******************************************************************************/ 92 | int zbx_module_api_version(void) { 93 | return ZBX_MODULE_API_VERSION; 94 | } 95 | 96 | /****************************************************************************** 97 | * * 98 | * Function: zbx_module_item_timeout * 99 | * * 100 | * Purpose: set timeout value for processing of items * 101 | * * 102 | * Parameters: timeout - timeout in seconds, 0 - no timeout set * 103 | * * 104 | ******************************************************************************/ 105 | void zbx_module_item_timeout(int timeout) { 106 | item_timeout = timeout; 107 | } 108 | 109 | /****************************************************************************** 110 | * * 111 | * Function: zbx_module_item_list * 112 | * * 113 | * Purpose: returns list of item keys supported by the module * 114 | * * 115 | * Return value: list of item keys * 116 | * * 117 | ******************************************************************************/ 118 | ZBX_METRIC *zbx_module_item_list(void) { 119 | return keys; 120 | } 121 | 122 | /****************************************************************************** 123 | * * 124 | * Function: zbx_module_init * 125 | * * 126 | * Purpose: the function is called on agent startup * 127 | * It should be used to call any initialization routines * 128 | * * 129 | * Return value: ZBX_MODULE_OK - success * 130 | * ZBX_MODULE_FAIL - module initialization failed * 131 | * * 132 | * Comment: the module won't be loaded in case of ZBX_MODULE_FAIL * 133 | * * 134 | ******************************************************************************/ 135 | int zbx_module_init(void) { 136 | int ret = ZBX_MODULE_OK; 137 | ovirt_log(LOG_LEVEL_INFORMATION, "compile time %s %s", 138 | __DATE__, __TIME__); 139 | ovirt_log(LOG_LEVEL_INFORMATION, "starting agent module %s", 140 | PACKAGE_STRING); 141 | 142 | curl_global_init(CURL_GLOBAL_DEFAULT); 143 | ret = ovirt_config_init(&cfg); 144 | return ret; 145 | } 146 | 147 | /****************************************************************************** 148 | * * 149 | * Function: zbx_module_uninit * 150 | * * 151 | * Purpose: the function is called on agent shutdown * 152 | * It should be used to cleanup used resources if there are any * 153 | * * 154 | * Return value: ZBX_MODULE_OK - success * 155 | * ZBX_MODULE_FAIL - function failed * 156 | * * 157 | ******************************************************************************/ 158 | int zbx_module_uninit(void) { 159 | int ret = ZBX_MODULE_OK; 160 | curl_global_cleanup(); 161 | ret = ovirt_config_uninit(&cfg); 162 | return ret; 163 | } 164 | /****************************************************************************** 165 | * * 166 | * Function: zbx_module_history_write_cbs * 167 | * * 168 | * Purpose: returns a set of module functions Zabbix will call to export * 169 | * different types of historical data * 170 | * * 171 | * Return value: structure with callback function pointers (can be NULL if * 172 | * module is not interested in data of certain types) * 173 | * * 174 | ******************************************************************************/ 175 | ZBX_HISTORY_WRITE_CBS zbx_module_history_write_cbs(void) { 176 | static ZBX_HISTORY_WRITE_CBS callbacks = { 177 | NULL, 178 | NULL, 179 | NULL, 180 | NULL, 181 | NULL, 182 | }; 183 | return callbacks; 184 | } 185 | 186 | int ovirt_config_init(config_t *cfg) { 187 | ovirt_log(LOG_LEVEL_INFORMATION, "loading configiration from %s", MODULE_CFG); 188 | config_init(cfg); 189 | if(! config_read_file(cfg, MODULE_CFG)) { 190 | ovirt_log(LOG_LEVEL_ERR, "error reafing config file %s:%d - %s", 191 | config_error_file(cfg), config_error_line(cfg), config_error_text(cfg)); 192 | config_destroy(cfg); 193 | return(ZBX_MODULE_FAIL); 194 | } 195 | 196 | if(!config_lookup_string(cfg, "ovirt.engine.user", &cfg_ovirt_user)) { 197 | ovirt_log(LOG_LEVEL_ERR, "ovirt.engine.user not found"); 198 | config_destroy(cfg); 199 | return(ZBX_MODULE_FAIL); 200 | } 201 | if(!config_lookup_string(cfg, "ovirt.engine.pass", &cfg_ovirt_pass)) { 202 | ovirt_log(LOG_LEVEL_ERR, "ovirt.engine.pass not found"); 203 | config_destroy(cfg); 204 | return(ZBX_MODULE_FAIL); 205 | } 206 | if(!config_lookup_string(cfg, "ovirt.engine.server", &cfg_ovirt_server)) { 207 | ovirt_log(LOG_LEVEL_ERR, "ovirt.engine.server not found"); 208 | config_destroy(cfg); 209 | return(ZBX_MODULE_FAIL); 210 | } 211 | if(!config_lookup_string(cfg, "ovirt.engine.uri", &cfg_ovirt_uri)) { 212 | ovirt_log(LOG_LEVEL_ERR, "ovirt.engine.uri not found"); 213 | config_destroy(cfg); 214 | return(ZBX_MODULE_FAIL); 215 | } 216 | 217 | return ZBX_MODULE_OK; 218 | } 219 | 220 | int ovirt_config_uninit(config_t *cfg) { 221 | config_destroy(cfg); 222 | return ZBX_MODULE_OK; 223 | } 224 | 225 | /* callback for curl fetch */ 226 | size_t __curl_write_callback (void *contents, size_t size, size_t nmemb, void *userdata) { 227 | size_t realsize = size * nmemb; /* calculate buffer size */ 228 | struct curl_fetch_st *p = (struct curl_fetch_st *) userdata; /* cast pointer to fetch struct */ 229 | 230 | /* expand buffer */ 231 | p->payload = (char *) realloc(p->payload, p->size + realsize + 1); 232 | /* check buffer */ 233 | if (p->payload == NULL) { 234 | /* this isn't good */ 235 | ovirt_log(LOG_LEVEL_ERR, "Failed to expand buffer in curl_callback"); 236 | /* free buffer */ 237 | free(p->payload); 238 | p->payload = NULL; 239 | p->size = 0; 240 | /* return */ 241 | return 0; 242 | } 243 | 244 | /* copy contents to buffer */ 245 | memcpy(&(p->payload[p->size]), contents, realsize); 246 | 247 | /* set new buffer size */ 248 | p->size += realsize; 249 | 250 | /* ensure null termination */ 251 | p->payload[p->size] = 0; 252 | 253 | /* return size */ 254 | return realsize; 255 | } 256 | 257 | void __curl_perform_query(char* url, struct curl_fetch_st *response) { 258 | CURL *curl = NULL; 259 | CURLcode res; 260 | ovirt_log(LOG_LEVEL_DEBUG, "performing query for %s",url); 261 | curl = curl_easy_init(); 262 | if (curl) { 263 | struct curl_slist *headers = NULL; 264 | headers = curl_slist_append(headers, "Accept: application/json"); 265 | headers = curl_slist_append(headers, "Content-Type: application/json"); 266 | curl_easy_setopt(curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); 267 | curl_easy_setopt(curl, CURLOPT_URL, url); 268 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); 269 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 270 | curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 271 | curl_easy_setopt(curl, CURLOPT_USERNAME, cfg_ovirt_user); 272 | curl_easy_setopt(curl, CURLOPT_PASSWORD, cfg_ovirt_pass); 273 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 274 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, __curl_write_callback); 275 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response); 276 | curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); 277 | res = curl_easy_perform(curl); 278 | if(res != CURLE_OK) { 279 | ovirt_log(LOG_LEVEL_ERR, "curl_easy_perform failed(): %s", 280 | curl_easy_strerror(res)); 281 | } 282 | curl_slist_free_all(headers); 283 | } 284 | /* always cleanup */ 285 | curl_easy_cleanup(curl); 286 | 287 | if (response->payload) { 288 | int ret = zbx_json_open(response->payload, &(response->json)); 289 | if (ret != SUCCEED) { 290 | ovirt_log(LOG_LEVEL_ERR, "Failed to parse json object"); 291 | free(response->payload); 292 | response->payload = NULL; 293 | response->size = 0; 294 | } 295 | } else { 296 | ovirt_log(LOG_LEVEL_DEBUG, "did not received payload"); 297 | } 298 | } 299 | 300 | int __get_data(char *url, AGENT_RESULT *result) { 301 | struct curl_fetch_st response; 302 | int ret; 303 | 304 | ret = SYSINFO_RET_OK; 305 | response.payload = NULL; 306 | response.size = 0; 307 | 308 | __curl_perform_query(url, &response); 309 | 310 | if (!response.payload) { 311 | SET_STR_RESULT(result, strdup("incorrect data received")); 312 | ret = SYSINFO_RET_FAIL; 313 | goto exit; 314 | } 315 | 316 | SET_TEXT_RESULT(result, strdup(response.payload)); 317 | exit: 318 | free(response.payload); 319 | return ret; 320 | } 321 | 322 | int __get_statistics(char *url, AGENT_RESULT *result) { 323 | struct curl_fetch_st response; 324 | struct zbx_json j; 325 | int ret = SYSINFO_RET_OK; 326 | 327 | // initialize variables 328 | response.payload = NULL; 329 | response.size = 0; 330 | zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN); 331 | 332 | __curl_perform_query(url, &response); 333 | 334 | if (!response.payload) { 335 | SET_STR_RESULT(result, strdup("incorrect data received")); 336 | ret = SYSINFO_RET_FAIL; 337 | goto exit; 338 | } 339 | 340 | struct zbx_json_parse jp_data, jp_row, jp_path; 341 | const char *p = NULL; 342 | 343 | if (FAIL == zbx_json_brackets_by_name(&(response.json), JSON_TAG_STATISTICS, &jp_data)) { 344 | ovirt_log(LOG_LEVEL_ERR, "unable to find '%s' array", JSON_TAG_STATISTICS); 345 | ret = SYSINFO_RET_FAIL; 346 | SET_STR_RESULT(result, strdup("incorrect data received")); 347 | goto exit; 348 | } 349 | 350 | do { 351 | p = zbx_json_next(&jp_data, p); 352 | if (p == NULL) 353 | break; 354 | 355 | if (FAIL == zbx_json_brackets_open(p, &jp_row)) { 356 | SET_STR_RESULT(result, strdup("Cannot open value object in received JSON")); 357 | ret = SYSINFO_RET_FAIL; 358 | goto exit; 359 | } 360 | 361 | char *stats_name = NULL; 362 | char *stats_value = NULL; 363 | size_t stats_alloc; 364 | 365 | if (FAIL == zbx_json_path_open(&jp_row, JSON_PATH_STATISTICS_NAME, &jp_path)) { 366 | SET_STR_RESULT(result, strdup(zbx_json_strerror())); 367 | ret = SYSINFO_RET_FAIL; 368 | goto exit; 369 | } 370 | stats_alloc = 0; 371 | zbx_json_value_dyn(&jp_path, &stats_name, &stats_alloc); 372 | 373 | if (FAIL == zbx_json_path_open(&jp_row, JSON_PATH_STATISTICS_VALUE, &jp_path)) { 374 | SET_STR_RESULT(result, strdup(zbx_json_strerror())); 375 | ret = SYSINFO_RET_FAIL; 376 | goto exit; 377 | } 378 | stats_alloc = 0; 379 | zbx_json_value_dyn(&jp_path, &stats_value, &stats_alloc); 380 | 381 | zbx_json_addstring(&j, stats_name, stats_value, ZBX_JSON_TYPE_INT); 382 | 383 | zbx_free(stats_name); 384 | stats_name = NULL; 385 | zbx_free(stats_value); 386 | stats_value = NULL; 387 | } while(p != NULL); 388 | 389 | zbx_json_close(&j); 390 | SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer)); 391 | 392 | exit: 393 | zbx_json_free(&j); 394 | free(response.payload); 395 | return ret; 396 | } 397 | 398 | 399 | int zbx_module_ovirt_simple_stats(AGENT_REQUEST *request, AGENT_RESULT *result) { 400 | char *service; 401 | int ret; 402 | 403 | ret = SYSINFO_RET_OK; 404 | 405 | ovirt_log(LOG_LEVEL_DEBUG, "param num [%d]", request->nparam); 406 | if (1 != request->nparam) { 407 | SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); 408 | ret = SYSINFO_RET_FAIL; 409 | goto exit; 410 | } 411 | 412 | service = get_rparam(request, 0); 413 | 414 | char url[MAX_STRING_LEN]; 415 | strscpy(url, cfg_ovirt_server); 416 | strscat(url, service); 417 | strscat(url, "/statistics"); 418 | 419 | ret = __get_statistics(url, result); 420 | exit: 421 | return ret; 422 | } 423 | 424 | int zbx_module_ovirt_simple_data(AGENT_REQUEST *request, AGENT_RESULT *result) { 425 | char *service; 426 | int ret; 427 | 428 | ret = SYSINFO_RET_OK; 429 | 430 | ovirt_log(LOG_LEVEL_DEBUG, "param num [%d]", request->nparam); 431 | if (1 != request->nparam) { 432 | SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); 433 | ret = SYSINFO_RET_FAIL; 434 | goto exit; 435 | } 436 | 437 | service = get_rparam(request, 0); 438 | 439 | char url[MAX_STRING_LEN]; 440 | strscpy(url, cfg_ovirt_server); 441 | strscat(url, service); 442 | 443 | ret = __get_data(url, result); 444 | 445 | exit: 446 | return ret; 447 | } 448 | 449 | int zbx_module_ovirt_host_stats(AGENT_REQUEST *request, AGENT_RESULT *result) { 450 | char *service; 451 | int ret; 452 | 453 | ret = SYSINFO_RET_OK; 454 | 455 | ovirt_log(LOG_LEVEL_DEBUG, "param num [%d]", request->nparam); 456 | if (1 != request->nparam) { 457 | SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); 458 | ret = SYSINFO_RET_FAIL; 459 | goto exit; 460 | } 461 | 462 | service = get_rparam(request, 0); 463 | 464 | char url[MAX_STRING_LEN]; 465 | strscpy(url, cfg_ovirt_server); 466 | strscat(url, cfg_ovirt_uri); 467 | strscat(url, "/hosts/"); 468 | strscat(url, service); 469 | strscat(url, "/statistics"); 470 | 471 | ret = __get_statistics(url, result); 472 | exit: 473 | return ret; 474 | } 475 | 476 | int zbx_module_ovirt_host_data(AGENT_REQUEST *request, AGENT_RESULT *result) { 477 | char *service; 478 | int ret; 479 | 480 | ret = SYSINFO_RET_OK; 481 | 482 | ovirt_log(LOG_LEVEL_DEBUG, "param num [%d]", request->nparam); 483 | if (1 != request->nparam) { 484 | SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); 485 | ret = SYSINFO_RET_FAIL; 486 | goto exit; 487 | } 488 | 489 | service = get_rparam(request, 0); 490 | 491 | char url[MAX_STRING_LEN]; 492 | strscpy(url, cfg_ovirt_server); 493 | strscat(url, cfg_ovirt_uri); 494 | strscat(url, "/hosts/"); 495 | strscat(url, service); 496 | 497 | ret = __get_data(url, result); 498 | 499 | exit: 500 | return ret; 501 | } 502 | 503 | 504 | int zbx_module_ovirt_vm_stats(AGENT_REQUEST *request, AGENT_RESULT *result) { 505 | char *service; 506 | int ret; 507 | 508 | ret = SYSINFO_RET_OK; 509 | 510 | ovirt_log(LOG_LEVEL_DEBUG, "param num [%d]", request->nparam); 511 | if (1 != request->nparam) { 512 | SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); 513 | ret = SYSINFO_RET_FAIL; 514 | goto exit; 515 | } 516 | 517 | service = get_rparam(request, 0); 518 | 519 | char url[MAX_STRING_LEN]; 520 | strscpy(url, cfg_ovirt_server); 521 | strscat(url, cfg_ovirt_uri); 522 | strscat(url, "/vms/"); 523 | strscat(url, service); 524 | strscat(url, "/statistics"); 525 | 526 | ret = __get_statistics(url, result); 527 | exit: 528 | return ret; 529 | } 530 | 531 | int zbx_module_ovirt_vm_data(AGENT_REQUEST *request, AGENT_RESULT *result) { 532 | char *service; 533 | int ret; 534 | 535 | ret = SYSINFO_RET_OK; 536 | 537 | ovirt_log(LOG_LEVEL_DEBUG, "param num [%d]", request->nparam); 538 | if (1 != request->nparam) { 539 | SET_MSG_RESULT(result, strdup("Invalid number of parameters.")); 540 | ret = SYSINFO_RET_FAIL; 541 | goto exit; 542 | } 543 | 544 | service = get_rparam(request, 0); 545 | 546 | char url[MAX_STRING_LEN]; 547 | strscpy(url, cfg_ovirt_server); 548 | strscat(url, cfg_ovirt_uri); 549 | strscat(url, "/vms/"); 550 | strscat(url, service); 551 | 552 | ret = __get_data(url, result); 553 | 554 | exit: 555 | return ret; 556 | } 557 | --------------------------------------------------------------------------------