├── module_ubuntu ├── README.txt └── zabbix_module_lxd.so ├── Makefile ├── README.md ├── Zabbix-template-LXD.xml └── zabbix_module_lxd.c /module_ubuntu/README.txt: -------------------------------------------------------------------------------- 1 | Module tested in Ubuntu 14.04 and Ubuntu 16.04. 2 | -------------------------------------------------------------------------------- /module_ubuntu/zabbix_module_lxd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scanterog/zabbix-lxd/HEAD/module_ubuntu/zabbix_module_lxd.so -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | zabbix_module_lxd: zabbix_module_lxd.c 2 | gcc -fPIC -shared -o zabbix_module_lxd.so zabbix_module_lxd.c -I../../../include -I../../../src/libs/zbxsysinfo 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zabbix-lxd 2 | 3 | A loadable module for Zabbix Agent in order to monitor LXD containers. 4 | 5 | ## Get the source 6 | 7 | When you have Zabbix installed from the official repository: 8 | ``` 9 | # debian / ubuntu / proxmox 10 | apt install dpkg-dev 11 | apt-get source zabbix-agent 12 | cd zabbix-3.2.x 13 | 14 | # redhat / centos 15 | # TODO 16 | ``` 17 | 18 | or directly from svn: 19 | 20 | ``` 21 | mkdir zabbix3.2 22 | cd zabbix3.2 23 | svn co svn://svn.zabbix.com/branches/3.2 . 24 | ./bootstrap.sh 25 | ``` 26 | 27 | ## Compiling the module 28 | 29 | ``` 30 | ./configure --enable-agent 31 | mkdir src/modules/zabbix_module_lxd 32 | cd src/modules/zabbix_module_lxd 33 | wget https://raw.githubusercontent.com/scanterog/zabbix-lxd/master/zabbix_module_lxd.c \ 34 | https://raw.githubusercontent.com/scanterog/zabbix-lxd/master/Makefile 35 | make 36 | ``` 37 | The output should be a dynamically linked shared object library named `zabbix_module_lxd.so`. 38 | 39 | ## Install and configure the module 40 | 41 | To install the module: 42 | 43 | ``` 44 | mkdir -p /usr/lib/zabbix/modules 45 | cp zabbix_module_lxd.so /usr/lib/zabbix/modules 46 | ``` 47 | 48 | To enable the module, add in `/etc/zabbix/zabbix_agentd.conf`: 49 | ``` 50 | LoadModule=zabbix_module_lxd.so 51 | ``` 52 | 53 | Finally, restart the zabbix-agent and upload `Zabbix-template-LXD.xml` to your Zabbix server. 54 | -------------------------------------------------------------------------------- /Zabbix-template-LXD.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3.2 4 | 2016-09-28T02:48:51Z 5 | 6 | 7 | Templates 8 | 9 | 10 | 11 | 472 | 473 | 474 | 475 | Service state 476 | 477 | 478 | 0 479 | Down 480 | 481 | 482 | 1 483 | Up 484 | 485 | 486 | 487 | 488 | 489 | -------------------------------------------------------------------------------- /zabbix_module_lxd.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** Zabbix module for LXD container monitoring 3 | ** Script based on Jan Garaj from www.monitoringartist.com 4 | ** Author: Samuel Cantero 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 | 21 | #include "common.h" 22 | #include "log.h" 23 | #include "comms.h" 24 | #include "module.h" 25 | #include "sysinc.h" 26 | #include "zbxjson.h" 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | // request parameters 37 | #include "common/common.h" 38 | #include "sysinfo.c" 39 | 40 | #ifndef ZBX_MODULE_API_VERSION 41 | # define ZBX_MODULE_API_VERSION ZBX_MODULE_API_VERSION_ONE 42 | #endif 43 | 44 | struct inspect_result 45 | { 46 | char *value; 47 | int return_code; 48 | }; 49 | 50 | char *m_version = "v0.1"; 51 | char *stat_dir = NULL, *driver, *cpu_cgroup = NULL, *hostname = 0; 52 | static int item_timeout = 1, buffer_size = 1024, cid_length = 66, socket_api; 53 | int zbx_module_lxd_discovery(AGENT_REQUEST *request, AGENT_RESULT *result); 54 | int zbx_module_lxd_up(AGENT_REQUEST *request, AGENT_RESULT *result); 55 | int zbx_module_lxd_mem(AGENT_REQUEST *request, AGENT_RESULT *result); 56 | int zbx_module_lxd_cpu(AGENT_REQUEST *request, AGENT_RESULT *result); 57 | int zbx_module_lxd_dev(AGENT_REQUEST *request, AGENT_RESULT *result); 58 | 59 | 60 | static ZBX_METRIC keys[] = 61 | /* KEY FLAG FUNCTION TEST PARAMETERS */ 62 | { 63 | {"lxd.discovery", CF_HAVEPARAMS, zbx_module_lxd_discovery, ", , "}, 64 | {"lxd.up", CF_HAVEPARAMS, zbx_module_lxd_up, "container name"}, 65 | {"lxd.mem", CF_HAVEPARAMS, zbx_module_lxd_mem, "container name, memory metric name"}, 66 | {"lxd.cpu", CF_HAVEPARAMS, zbx_module_lxd_cpu, "container name, cpu metric name"}, 67 | {"lxd.dev", CF_HAVEPARAMS, zbx_module_lxd_dev, "container name, blkio file, blkio metric name"}, 68 | {NULL} 69 | }; 70 | 71 | /****************************************************************************** 72 | * * 73 | * Function: zbx_module_api_version * 74 | * * 75 | * Purpose: returns version number of the module interface * 76 | * * 77 | * Return value: ZBX_MODULE_API_VERSION_ONE - the only version supported by * 78 | * Zabbix currently * 79 | * * 80 | ******************************************************************************/ 81 | int zbx_module_api_version() 82 | { 83 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_api_version()"); 84 | return ZBX_MODULE_API_VERSION_ONE; 85 | } 86 | 87 | /****************************************************************************** 88 | * * 89 | * Function: zbx_module_item_timeout * 90 | * * 91 | * Purpose: set timeout value for processing of items * 92 | * * 93 | * Parameters: timeout - timeout in seconds, 0 - no timeout set * 94 | * * 95 | ******************************************************************************/ 96 | void zbx_module_item_timeout(int timeout) 97 | { 98 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_item_timeout()"); 99 | item_timeout = timeout; 100 | } 101 | 102 | /****************************************************************************** 103 | * * 104 | * Function: zbx_module_item_list * 105 | * * 106 | * Purpose: returns list of item keys supported by the module * 107 | * * 108 | * Return value: list of item keys * 109 | * * 110 | ******************************************************************************/ 111 | ZBX_METRIC *zbx_module_item_list() 112 | { 113 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_item_list()"); 114 | return keys; 115 | } 116 | 117 | /****************************************************************************** 118 | * * 119 | * Function: zbx_lxd_dir_detect * 120 | * * 121 | * Purpose: it should find metric folder - it depends on exec environment * 122 | * * 123 | * Return value: SYSINFO_RET_FAIL - stat folder was not found * 124 | * SYSINFO_RET_OK - stat folder was found * 125 | * * 126 | ******************************************************************************/ 127 | int zbx_lxd_dir_detect() 128 | { 129 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_lxd_dir_detect()"); 130 | 131 | char *drivers[] = { 132 | "lxc/", // LXC 133 | "" 134 | }, **tdriver; 135 | char path[512]; 136 | char *temp1, *temp2; 137 | FILE *fp; 138 | DIR *dir; 139 | 140 | if ((fp = fopen("/proc/mounts", "r")) == NULL) 141 | { 142 | zabbix_log(LOG_LEVEL_WARNING, "Cannot open /proc/mounts: %s", zbx_strerror(errno)); 143 | return SYSINFO_RET_FAIL; 144 | } 145 | 146 | while (fgets(path, 512, fp) != NULL) 147 | { 148 | if ((strstr(path, "cpuset cgroup")) != NULL) 149 | { 150 | temp1 = string_replace(path, "cgroup ", ""); 151 | temp2 = string_replace(temp1, strstr(temp1, " "), ""); 152 | free(temp1); 153 | if (stat_dir != NULL) free(stat_dir); 154 | stat_dir = string_replace(temp2, "cpuset", ""); 155 | free(temp2); 156 | zabbix_log(LOG_LEVEL_DEBUG, "Detected LXD stat directory: %s", stat_dir); 157 | 158 | 159 | pclose(fp); 160 | 161 | char *cgroup = "cpuset/"; 162 | tdriver = drivers; 163 | size_t ddir_size; 164 | char *ddir; 165 | while (*tdriver != "") 166 | { 167 | ddir_size = strlen(cgroup) + strlen(stat_dir) + strlen(*tdriver) + 1; 168 | ddir = malloc(ddir_size); 169 | zbx_strlcpy(ddir, stat_dir, ddir_size); 170 | zbx_strlcat(ddir, cgroup, ddir_size); 171 | zbx_strlcat(ddir, *tdriver, ddir_size); 172 | zabbix_log(LOG_LEVEL_DEBUG, "ddir to test: %s", ddir); 173 | 174 | if (NULL != (dir = opendir(ddir))) 175 | { 176 | closedir(dir); 177 | free(ddir); 178 | driver = *tdriver; 179 | zabbix_log(LOG_LEVEL_DEBUG, "Detected used LXD driver dir: %s", driver); 180 | 181 | // detect cpu_cgroup - JoinController cpu,cpuacct 182 | cgroup = "cpu,cpuacct/"; 183 | ddir_size = strlen(cgroup) + strlen(stat_dir) + 1; 184 | ddir = malloc(ddir_size); 185 | zbx_strlcpy(ddir, stat_dir, ddir_size); 186 | zbx_strlcat(ddir, cgroup, ddir_size); 187 | if (NULL != (dir = opendir(ddir))) 188 | { 189 | closedir(dir); 190 | cpu_cgroup = "cpu,cpuacct/"; 191 | zabbix_log(LOG_LEVEL_DEBUG, "Detected JoinController cpu,cpuacct"); 192 | } else { 193 | cpu_cgroup = "cpuacct/"; 194 | } 195 | free(ddir); 196 | return SYSINFO_RET_OK; 197 | } 198 | *tdriver++; 199 | free(ddir); 200 | } 201 | driver = ""; 202 | zabbix_log(LOG_LEVEL_DEBUG, "Cannot detect used LXD driver"); 203 | return SYSINFO_RET_FAIL; 204 | } 205 | } 206 | pclose(fp); 207 | zabbix_log(LOG_LEVEL_DEBUG, "Cannot detect LXD stat directory"); 208 | return SYSINFO_RET_FAIL; 209 | } 210 | 211 | /****************************************************************************** 212 | * * 213 | * Function: zbx_module_lxd_up * 214 | * * 215 | * Purpose: check if container is running * 216 | * * 217 | * Return value: 1 - is running, 0 - is not running * 218 | * * 219 | ******************************************************************************/ 220 | int zbx_module_lxd_up(AGENT_REQUEST *request, AGENT_RESULT *result) 221 | { 222 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_lxd_up()"); 223 | char *container; 224 | 225 | if (1 != request->nparam) 226 | { 227 | zabbix_log(LOG_LEVEL_ERR, "Invalid number of parameters: %d", request->nparam); 228 | SET_MSG_RESULT(result, strdup("Invalid number of parameters")); 229 | return SYSINFO_RET_FAIL; 230 | } 231 | 232 | if (stat_dir == NULL || driver == NULL) 233 | { 234 | zabbix_log(LOG_LEVEL_DEBUG, "up check is not available at the moment - no stat directory"); 235 | SET_MSG_RESULT(result, zbx_strdup(NULL, "up check is not available at the moment - no stat directory")); 236 | return SYSINFO_RET_FAIL; 237 | } 238 | 239 | if(cpu_cgroup == NULL) 240 | { 241 | if (zbx_lxd_dir_detect() == SYSINFO_RET_FAIL) 242 | { 243 | zabbix_log(LOG_LEVEL_DEBUG, "up check is not available at the moment - no cpu_cgroup directory"); 244 | SET_MSG_RESULT(result, zbx_strdup(NULL, "up check is not available at the moment - no cpu_cgroup directory")); 245 | return SYSINFO_RET_FAIL; 246 | } 247 | } 248 | 249 | container = zbx_strdup(NULL, get_rparam(request, 0)); 250 | char *stat_file = "/cpuacct.stat"; 251 | char *cgroup = cpu_cgroup; 252 | size_t filename_size = strlen(cgroup) + strlen(container) + strlen(stat_dir) + strlen(driver) + strlen(stat_file) + 2; 253 | char *filename = malloc(filename_size); 254 | zbx_strlcpy(filename, stat_dir, filename_size); 255 | zbx_strlcat(filename, cgroup, filename_size); 256 | zbx_strlcat(filename, driver, filename_size); 257 | zbx_strlcat(filename, container, filename_size); 258 | free(container); 259 | zbx_strlcat(filename, stat_file, filename_size); 260 | zabbix_log(LOG_LEVEL_DEBUG, "Metric source file: %s", filename); 261 | 262 | FILE *file; 263 | if (NULL == (file = fopen(filename, "r"))) 264 | { 265 | zabbix_log(LOG_LEVEL_DEBUG, "Cannot open metric file: '%s', container doesn't run", filename); 266 | free(filename); 267 | SET_UI64_RESULT(result, 0); 268 | return SYSINFO_RET_OK; 269 | } 270 | zbx_fclose(file); 271 | zabbix_log(LOG_LEVEL_DEBUG, "Can open metric file: '%s', container is running", filename); 272 | free(filename); 273 | SET_UI64_RESULT(result, 1); 274 | return SYSINFO_RET_OK; 275 | } 276 | 277 | /****************************************************************************** 278 | * * 279 | * Function: zbx_module_lxd_mem * 280 | * * 281 | * Purpose: container memory metrics * 282 | * * 283 | * Return value: SYSINFO_RET_FAIL - function failed, item will be marked * 284 | * as not supported by zabbix * 285 | * SYSINFO_RET_OK - success * 286 | * * 287 | * Notes: https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt * 288 | ******************************************************************************/ 289 | int zbx_module_lxd_mem(AGENT_REQUEST *request, AGENT_RESULT *result) 290 | { 291 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_lxd_mem()"); 292 | char *container, *metric; 293 | int ret = SYSINFO_RET_FAIL; 294 | 295 | if (2 != request->nparam) 296 | { 297 | zabbix_log(LOG_LEVEL_ERR, "Invalid number of parameters: %d", request->nparam); 298 | SET_MSG_RESULT(result, strdup("Invalid number of parameters")); 299 | return SYSINFO_RET_FAIL; 300 | } 301 | 302 | if (stat_dir == NULL || driver == NULL) 303 | { 304 | zabbix_log(LOG_LEVEL_DEBUG, "mem metrics are not available at the moment - no stat directory"); 305 | SET_MSG_RESULT(result, zbx_strdup(NULL, "mem metrics are not available at the moment - no stat directory")); 306 | return SYSINFO_RET_FAIL; 307 | } 308 | 309 | container = zbx_strdup(NULL, get_rparam(request, 0)); 310 | metric = get_rparam(request, 1); 311 | char *stat_file = "/memory.stat"; 312 | char *cgroup = "memory/"; 313 | size_t filename_size = strlen(cgroup) + strlen(container) + strlen(stat_dir) + strlen(driver) + strlen(stat_file) + 2; 314 | char *filename = malloc(filename_size); 315 | zbx_strlcpy(filename, stat_dir, filename_size); 316 | zbx_strlcat(filename, cgroup, filename_size); 317 | zbx_strlcat(filename, driver, filename_size); 318 | zbx_strlcat(filename, container, filename_size); 319 | zbx_strlcat(filename, stat_file, filename_size); 320 | zabbix_log(LOG_LEVEL_DEBUG, "Metric source file: %s", filename); 321 | 322 | FILE *file; 323 | if (NULL == (file = fopen(filename, "r"))) 324 | { 325 | zabbix_log(LOG_LEVEL_ERR, "Cannot open metric file: '%s'", filename); 326 | free(container); 327 | free(filename); 328 | SET_MSG_RESULT(result, strdup("Cannot open memory.stat file")); 329 | return SYSINFO_RET_FAIL; 330 | } 331 | 332 | char line[MAX_STRING_LEN]; 333 | char *metric2 = malloc(strlen(metric)+3); 334 | memcpy(metric2, metric, strlen(metric)); 335 | memcpy(metric2 + strlen(metric), " ", 2); 336 | zbx_uint64_t value = 0; 337 | zabbix_log(LOG_LEVEL_DEBUG, "Looking metric %s in memory.stat file", metric); 338 | while (NULL != fgets(line, sizeof(line), file)) 339 | { 340 | if (0 != strncmp(line, metric2, strlen(metric2))) 341 | continue; 342 | if (1 != sscanf(line, "%*s " ZBX_FS_UI64, &value)) 343 | { 344 | zabbix_log(LOG_LEVEL_ERR, "sscanf failed for matched metric line"); 345 | continue; 346 | } 347 | zabbix_log(LOG_LEVEL_DEBUG, "Id: %s; metric: %s; value: %d", container, metric, value); 348 | SET_UI64_RESULT(result, value); 349 | ret = SYSINFO_RET_OK; 350 | break; 351 | } 352 | zbx_fclose(file); 353 | 354 | free(container); 355 | free(filename); 356 | free(metric2); 357 | 358 | if (SYSINFO_RET_FAIL == ret) 359 | SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot find a line with requested metric in memory.stat file")); 360 | 361 | return ret; 362 | } 363 | 364 | /****************************************************************************** 365 | * * 366 | * Function: zbx_module_lxd_cpu * 367 | * * 368 | * Purpose: container CPU metrics * 369 | * * 370 | * Return value: SYSINFO_RET_FAIL - function failed, item will be marked * 371 | * as not supported by zabbix * 372 | * SYSINFO_RET_OK - success * 373 | * * 374 | * Notes: https://www.kernel.org/doc/Documentation/cgroup-v1/cpuacct.txt * 375 | ******************************************************************************/ 376 | int zbx_module_lxd_cpu(AGENT_REQUEST *request, AGENT_RESULT *result) 377 | { 378 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_lxd_cpu()"); 379 | 380 | char *container, *metric; 381 | int ret = SYSINFO_RET_FAIL; 382 | 383 | if (2 != request->nparam) 384 | { 385 | zabbix_log(LOG_LEVEL_ERR, "Invalid number of parameters: %d", request->nparam); 386 | SET_MSG_RESULT(result, strdup("Invalid number of parameters")); 387 | return SYSINFO_RET_FAIL; 388 | } 389 | 390 | if (stat_dir == NULL || driver == NULL) 391 | { 392 | zabbix_log(LOG_LEVEL_DEBUG, "cpu metrics are not available at the moment - no stat directory"); 393 | SET_MSG_RESULT(result, zbx_strdup(NULL, "cpu metrics are not available at the moment - no stat directory")); 394 | return SYSINFO_RET_FAIL; 395 | } 396 | 397 | if(cpu_cgroup == NULL) 398 | { 399 | if (zbx_lxd_dir_detect() == SYSINFO_RET_FAIL) 400 | { 401 | zabbix_log(LOG_LEVEL_DEBUG, "cpu check is not available at the moment - no cpu_cgroup directory"); 402 | SET_MSG_RESULT(result, zbx_strdup(NULL, "cpu check is not available at the moment - no cpu_cgroup directory")); 403 | return SYSINFO_RET_FAIL; 404 | } 405 | } 406 | 407 | container = zbx_strdup(NULL, get_rparam(request, 0)); 408 | metric = get_rparam(request, 1); 409 | char *cgroup = NULL, *stat_file = NULL; 410 | if(strcmp(metric, "user") == 0 || strcmp(metric, "system") == 0) { 411 | stat_file = "/cpuacct.stat"; 412 | cgroup = cpu_cgroup; 413 | } else { 414 | stat_file = "/cpu.stat"; 415 | if (strchr(cpu_cgroup, ',') != NULL) { 416 | cgroup = cpu_cgroup; 417 | } else { 418 | cgroup = "cpu/"; 419 | } 420 | } 421 | 422 | zabbix_log(LOG_LEVEL_DEBUG, "cpu_cgroup: %s, cgroup: %s, stat_file: %s, metric: %s, container: %s", cpu_cgroup, cgroup, stat_file, metric, container); 423 | size_t filename_size = strlen(cgroup) + strlen(container) + strlen(stat_dir) + strlen(driver) + strlen(stat_file) + 2; 424 | char *filename = malloc(filename_size); 425 | 426 | zbx_strlcpy(filename, stat_dir, filename_size); 427 | zbx_strlcat(filename, cgroup, filename_size); 428 | zbx_strlcat(filename, driver, filename_size); 429 | zbx_strlcat(filename, container, filename_size); 430 | zbx_strlcat(filename, stat_file, filename_size); 431 | zabbix_log(LOG_LEVEL_DEBUG, "Metric source file: %s", filename); 432 | 433 | FILE *file; 434 | if (NULL == (file = fopen(filename, "r"))) 435 | { 436 | zabbix_log(LOG_LEVEL_ERR, "Cannot open metric file: '%s'", filename); 437 | free(filename); 438 | free(container); 439 | SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open %s file", ++stat_file)); 440 | return SYSINFO_RET_FAIL; 441 | } 442 | 443 | char line[MAX_STRING_LEN]; 444 | char *metric2 = malloc(strlen(metric)+3); 445 | zbx_uint64_t cpu_num; 446 | memcpy(metric2, metric, strlen(metric)); 447 | memcpy(metric2 + strlen(metric), " ", 2); 448 | zbx_uint64_t value = 0; 449 | zabbix_log(LOG_LEVEL_DEBUG, "Looking metric %s in cpuacct.stat file", metric); 450 | while (NULL != fgets(line, sizeof(line), file)) 451 | { 452 | 453 | if (0 != strncmp(line, metric2, strlen(metric2))) 454 | continue; 455 | if (1 != sscanf(line, "%*s " ZBX_FS_UI64, &value)) 456 | { 457 | zabbix_log(LOG_LEVEL_ERR, "sscanf failed for matched metric line"); 458 | continue; 459 | } 460 | // normalize CPU usage by using number of online CPUs 461 | if (1 < (cpu_num = sysconf(_SC_NPROCESSORS_ONLN))) 462 | { 463 | value /= cpu_num; 464 | } 465 | zabbix_log(LOG_LEVEL_DEBUG, "Id: %s; metric: %s; value: %d", container, metric, value); 466 | SET_UI64_RESULT(result, value); 467 | ret = SYSINFO_RET_OK; 468 | break; 469 | } 470 | zbx_fclose(file); 471 | 472 | free(container); 473 | free(filename); 474 | free(metric2); 475 | 476 | if (SYSINFO_RET_FAIL == ret) 477 | SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot find a line with requested metric in cpuacct.stat file")); 478 | 479 | return ret; 480 | } 481 | 482 | /****************************************************************************** 483 | * * 484 | * Function: zbx_module_lxd_dev * 485 | * * 486 | * Purpose: container device blkio metrics * 487 | * * 488 | * Return value: SYSINFO_RET_FAIL - function failed, item will be marked * 489 | * as not supported by zabbix * 490 | * SYSINFO_RET_OK - success * 491 | * * 492 | * Notes: https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt 493 | ******************************************************************************/ 494 | int zbx_module_lxd_dev(AGENT_REQUEST *request, AGENT_RESULT *result) 495 | { 496 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_lxd_dev()"); 497 | char *container, *metric; 498 | int ret = SYSINFO_RET_FAIL; 499 | 500 | if (3 != request->nparam) 501 | { 502 | zabbix_log(LOG_LEVEL_ERR, "Invalid number of parameters: %d", request->nparam); 503 | SET_MSG_RESULT(result, strdup("Invalid number of parameters")); 504 | return SYSINFO_RET_FAIL; 505 | } 506 | 507 | if (stat_dir == NULL || driver == NULL) 508 | { 509 | zabbix_log(LOG_LEVEL_DEBUG, "dev metrics are not available at the moment - no stat directory"); 510 | SET_MSG_RESULT(result, zbx_strdup(NULL, "dev metrics are not available at the moment - no stat directory")); 511 | return SYSINFO_RET_FAIL; 512 | } 513 | 514 | container = zbx_strdup(NULL, get_rparam(request, 0)); 515 | char *stat_file = malloc(strlen(get_rparam(request, 1)) + 2); 516 | zbx_strlcpy(stat_file, "/", strlen(get_rparam(request, 1)) + 2); 517 | zbx_strlcat(stat_file, get_rparam(request, 1), strlen(get_rparam(request, 1)) + 2); 518 | metric = get_rparam(request, 2); 519 | 520 | char *cgroup = "blkio/"; 521 | size_t filename_size = strlen(cgroup) + strlen(container) + strlen(stat_dir) + strlen(driver) + strlen(stat_file) + 2; 522 | char *filename = malloc(filename_size); 523 | zbx_strlcpy(filename, stat_dir, filename_size); 524 | zbx_strlcat(filename, cgroup, filename_size); 525 | zbx_strlcat(filename, driver, filename_size); 526 | zbx_strlcat(filename, container, filename_size); 527 | zbx_strlcat(filename, stat_file, filename_size); 528 | zabbix_log(LOG_LEVEL_DEBUG, "Metric source file: %s", filename); 529 | 530 | FILE *file; 531 | if (NULL == (file = fopen(filename, "r"))) 532 | { 533 | zabbix_log(LOG_LEVEL_ERR, "Cannot open metric file: '%s'", filename); 534 | free(container); 535 | free(stat_file); 536 | free(filename); 537 | SET_MSG_RESULT(result, strdup("Cannot open stat file, maybe CONFIG_DEBUG_BLK_CGROUP is not enabled")); 538 | zabbix_log(LOG_LEVEL_ERR, "Cannot open stat file, maybe CONFIG_DEBUG_BLK_CGROUP is not enabled"); 539 | return SYSINFO_RET_FAIL; 540 | } 541 | 542 | char line[MAX_STRING_LEN]; 543 | char *metric2 = malloc(strlen(metric)+3); 544 | memcpy(metric2, metric, strlen(metric)); 545 | memcpy(metric2 + strlen(metric), " ", 2); 546 | zbx_uint64_t value = 0; 547 | zabbix_log(LOG_LEVEL_DEBUG, "Looking metric %s in blkio file", metric); 548 | while (NULL != fgets(line, sizeof(line), file)) 549 | { 550 | if (0 != strncmp(line, metric2, strlen(metric2))) 551 | continue; 552 | if (1 != sscanf(line, "%*s " ZBX_FS_UI64, &value)) 553 | { 554 | // maybe per blk device metric, e.g. '8:0 Read' 555 | if (1 != sscanf(line, "%*s %*s " ZBX_FS_UI64, &value)) 556 | { 557 | zabbix_log(LOG_LEVEL_ERR, "sscanf failed for matched metric line"); 558 | break; 559 | } 560 | } 561 | zabbix_log(LOG_LEVEL_DEBUG, "Id: %s; stat file: %s, metric: %s; value: %d", container, stat_file, metric, value); 562 | SET_UI64_RESULT(result, value); 563 | ret = SYSINFO_RET_OK; 564 | break; 565 | } 566 | zbx_fclose(file); 567 | 568 | free(container); 569 | free(stat_file); 570 | free(filename); 571 | free(metric2); 572 | 573 | if (SYSINFO_RET_FAIL == ret){ 574 | SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot find a line with requested metric in blkio file")); 575 | zabbix_log(LOG_LEVEL_ERR, "Cannot find a line with requested metric in blkio file"); 576 | } 577 | 578 | return ret; 579 | } 580 | 581 | 582 | /****************************************************************************** 583 | * * 584 | * Function: zbx_module_uninit * 585 | * * 586 | * Purpose: the function is called on agent shutdown * 587 | * It should be used to cleanup used resources if there are any * 588 | * * 589 | * Return value: ZBX_MODULE_OK - success * 590 | * ZBX_MODULE_FAIL - function failed * 591 | * * 592 | ******************************************************************************/ 593 | int zbx_module_uninit() 594 | { 595 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_uninit()"); 596 | 597 | const char* znetns_prefix = "zabbix_module_lxd_"; 598 | DIR *dir; 599 | struct dirent *d; 600 | 601 | if (NULL == (dir = opendir("/var/run/netns"))) 602 | { 603 | zabbix_log(LOG_LEVEL_DEBUG, "/var/run/netns: %s", zbx_strerror(errno)); 604 | return ZBX_MODULE_OK; 605 | } 606 | char *file = NULL; 607 | while (NULL != (d = readdir(dir))) 608 | { 609 | if(0 == strcmp(d->d_name, ".") || 0 == strcmp(d->d_name, "..")) 610 | continue; 611 | 612 | // delete zabbix netns 613 | if ((strstr(d->d_name, znetns_prefix)) != NULL) 614 | { 615 | file = NULL; 616 | file = zbx_dsprintf(file, "/var/run/netns/%s", d->d_name); 617 | if(unlink(file) != 0) 618 | { 619 | zabbix_log(LOG_LEVEL_WARNING, "%s: %s", d->d_name, zbx_strerror(errno)); 620 | } 621 | } 622 | } 623 | if(0 != closedir(dir)) 624 | { 625 | zabbix_log(LOG_LEVEL_WARNING, "/var/run/netns/: %s", zbx_strerror(errno)); 626 | } 627 | 628 | free(stat_dir); 629 | 630 | return ZBX_MODULE_OK; 631 | } 632 | 633 | /****************************************************************************** 634 | * * 635 | * Function: zbx_module_init * 636 | * * 637 | * Purpose: the function is called on agent startup * 638 | * It should be used to call any initialization routines * 639 | * * 640 | * Return value: ZBX_MODULE_OK - success * 641 | * ZBX_MODULE_FAIL - module initialization failed * 642 | * * 643 | * Comment: the module won't be loaded in case of ZBX_MODULE_FAIL * 644 | * * 645 | ******************************************************************************/ 646 | int zbx_module_init() 647 | { 648 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_init()"); 649 | zabbix_log(LOG_LEVEL_DEBUG, "zabbix_module_lxd %s, compilation time: %s %s", m_version, __DATE__, __TIME__); 650 | zbx_lxd_dir_detect(); 651 | return ZBX_MODULE_OK; 652 | } 653 | 654 | 655 | /****************************************************************************** 656 | * * 657 | * Function: zbx_module_lxd_discovery * 658 | * * 659 | * Purpose: container discovery * 660 | * * 661 | * Return value: SYSINFO_RET_FAIL - function failed, item will be marked * 662 | * as not supported by zabbix * 663 | * SYSINFO_RET_OK - success * 664 | * * 665 | ******************************************************************************/ 666 | int zbx_module_lxd_discovery(AGENT_REQUEST *request, AGENT_RESULT *result) 667 | { 668 | zabbix_log(LOG_LEVEL_DEBUG, "In zbx_module_lxd_discovery()"); 669 | 670 | struct zbx_json j; 671 | if(stat_dir == NULL && zbx_lxd_dir_detect() == SYSINFO_RET_FAIL) 672 | { 673 | zabbix_log(LOG_LEVEL_DEBUG, "lxd.discovery is not available at the moment - no stat directory - empty discovery"); 674 | zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN); 675 | zbx_json_addarray(&j, ZBX_PROTO_TAG_DATA); 676 | zbx_json_close(&j); 677 | SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer)); 678 | zbx_json_free(&j); 679 | return SYSINFO_RET_FAIL; 680 | } 681 | 682 | DIR *dir; 683 | zbx_stat_t sb; 684 | char *file = NULL, *containerid; 685 | struct dirent *d; 686 | char *cgroup = "cpuset/"; 687 | size_t ddir_size = strlen(cgroup) + strlen(stat_dir) + strlen(driver) + 2; 688 | char *ddir = malloc(ddir_size); 689 | zbx_strlcpy(ddir, stat_dir, ddir_size); 690 | zbx_strlcat(ddir, cgroup, ddir_size); 691 | zbx_strlcat(ddir, driver, ddir_size); 692 | zabbix_log(LOG_LEVEL_DEBUG, "lxd.discovery-> ddir: %s", ddir); 693 | 694 | if (NULL == (dir = opendir(ddir))) 695 | { 696 | zabbix_log(LOG_LEVEL_WARNING, "%s: %s", ddir, zbx_strerror(errno)); 697 | free(ddir); 698 | return SYSINFO_RET_FAIL; 699 | } 700 | 701 | zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN); 702 | zbx_json_addarray(&j, ZBX_PROTO_TAG_DATA); 703 | 704 | size_t hostname_len = 128; 705 | while (1) { 706 | char* realloc_hostname = realloc(hostname, hostname_len); 707 | if (realloc_hostname == 0) { 708 | free(hostname); 709 | } 710 | hostname = realloc_hostname; 711 | hostname[hostname_len-1] = 0; 712 | if (gethostname(hostname, hostname_len-1) == 0) { 713 | size_t count = strlen(hostname); 714 | if (count < hostname_len-2) { 715 | break; 716 | } 717 | } 718 | hostname_len *= 2; 719 | } 720 | zabbix_log(LOG_LEVEL_WARNING, "hostname: %s, dir: %s", hostname, ddir); 721 | while (NULL != (d = readdir(dir))) 722 | { 723 | if(0 == strcmp(d->d_name, ".") || 0 == strcmp(d->d_name, "..")) 724 | continue; 725 | 726 | file = zbx_dsprintf(file, "%s/%s", ddir, d->d_name); 727 | 728 | if (0 != zbx_stat(file, &sb) || 0 == S_ISDIR(sb.st_mode)) 729 | continue; 730 | 731 | containerid = d->d_name; 732 | 733 | zbx_json_addobject(&j, NULL); 734 | zbx_json_addstring(&j, "{#HCONTAINERID}", containerid, ZBX_JSON_TYPE_STRING); 735 | zbx_json_addstring(&j, "{#SYSTEM.HOSTNAME}", hostname, ZBX_JSON_TYPE_STRING); 736 | zbx_json_close(&j); 737 | 738 | } 739 | 740 | if(0 != closedir(dir)) 741 | { 742 | zabbix_log(LOG_LEVEL_WARNING, "%s: %s\n", ddir, zbx_strerror(errno)); 743 | } 744 | 745 | zbx_json_close(&j); 746 | 747 | SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer)); 748 | 749 | zbx_json_free(&j); 750 | 751 | free(ddir); 752 | 753 | return SYSINFO_RET_OK; 754 | } 755 | 756 | --------------------------------------------------------------------------------