/** * collectd-tg - src/collectd-tg.c * Copyright (C) 2010-2012 Florian octo Forster * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Florian Forster **/ #if HAVE_CONFIG_H #include "config.h" #endif #if !__GNUC__ #define __attribute__(x) /**/ #endif #include #include #include #include #include #include #include #include #include #include #include "utils/heap/heap.h" #include "collectd/client.h" #include "collectd/network.h" #define DEF_NUM_HOSTS 1000 #define DEF_NUM_PLUGINS 20 #define DEF_NUM_VALUES 100000 #define DEF_INTERVAL 10.0 static int conf_num_hosts = DEF_NUM_HOSTS; static int conf_num_plugins = DEF_NUM_PLUGINS; static int conf_num_values = DEF_NUM_VALUES; static double conf_interval = DEF_INTERVAL; static const char *conf_destination = NET_DEFAULT_V6_ADDR; static const char *conf_service = NET_DEFAULT_PORT; static lcc_network_t *net; static c_heap_t *values_heap; static struct sigaction sigint_action; static struct sigaction sigterm_action; static bool loop = true; __attribute__((noreturn)) static void exit_usage(int exit_status) /* {{{ */ { fprintf( (exit_status == EXIT_FAILURE) ? stderr : stdout, "collectd-tg -- collectd traffic generator\n" "\n" " Usage: collectd-ng [OPTION]\n" "\n" " Valid options:\n" " -n Number of value lists. (Default: %i)\n" " -H Number of hosts to emulate. (Default: %i)\n" " -p Number of plugins to emulate. (Default: %i)\n" " -i Interval of each value in seconds. (Default: %.3f)\n" " -d Destination address of the network packets.\n" " (Default: %s)\n" " -D Destination port of the network packets.\n" " (Default: %s)\n" " -h Print usage information (this output).\n" "\n" "Copyright (C) 2010-2012 Florian Forster\n" "Licensed under the MIT license.\n", DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS, DEF_INTERVAL, NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT); exit(exit_status); } /* }}} void exit_usage */ static void signal_handler(int __attribute__((unused)) signal) /* {{{ */ { loop = false; } /* }}} void signal_handler */ #if HAVE_CLOCK_GETTIME static double dtime(void) /* {{{ */ { struct timespec ts = {0}; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) perror("clock_gettime"); return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; } /* }}} double dtime */ #else /* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */ static double dtime(void) /* {{{ */ { struct timeval tv = {0}; if (gettimeofday(&tv, /* timezone = */ NULL) != 0) perror("gettimeofday"); return (double)tv.tv_sec + ((double)tv.tv_usec) / 1e6; } /* }}} double dtime */ #endif static int compare_time(const void *v0, const void *v1) /* {{{ */ { const lcc_value_list_t *vl0 = v0; const lcc_value_list_t *vl1 = v1; if (vl0->time < vl1->time) return -1; else if (vl0->time > vl1->time) return 1; else return 0; } /* }}} int compare_time */ static int get_boundet_random(int min, int max) /* {{{ */ { int range; if (min >= max) return -1; if (min == (max - 1)) return min; range = max - min; return min + ((int)(((double)range) * ((double)random()) / (((double)RAND_MAX) + 1.0))); } /* }}} int get_boundet_random */ static lcc_value_list_t *create_value_list(void) /* {{{ */ { lcc_value_list_t *vl; int host_num; vl = calloc(1, sizeof(*vl)); if (vl == NULL) { fprintf(stderr, "calloc failed.\n"); return NULL; } vl->values = calloc(/* nmemb = */ 1, sizeof(*vl->values)); if (vl->values == NULL) { fprintf(stderr, "calloc failed.\n"); free(vl); return NULL; } vl->values_types = calloc(/* nmemb = */ 1, sizeof(*vl->values_types)); if (vl->values_types == NULL) { fprintf(stderr, "calloc failed.\n"); free(vl->values); free(vl); return NULL; } vl->values_len = 1; host_num = get_boundet_random(0, conf_num_hosts); vl->interval = conf_interval; vl->time = 1.0 + dtime() + (host_num % (1 + (int)vl->interval)); if (get_boundet_random(0, 2) == 0) vl->values_types[0] = LCC_TYPE_GAUGE; else vl->values_types[0] = LCC_TYPE_DERIVE; snprintf(vl->identifier.host, sizeof(vl->identifier.host), "host%04i", host_num); snprintf(vl->identifier.plugin, sizeof(vl->identifier.plugin), "plugin%03i", get_boundet_random(0, conf_num_plugins)); strncpy(vl->identifier.type, (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive", sizeof(vl->identifier.type)); vl->identifier.type[sizeof(vl->identifier.type) - 1] = '\0'; snprintf(vl->identifier.type_instance, sizeof(vl->identifier.type_instance), "ti%li", random()); return vl; } /* }}} int create_value_list */ static void destroy_value_list(lcc_value_list_t *vl) /* {{{ */ { if (vl == NULL) return; free(vl->values); free(vl->values_types); free(vl); } /* }}} void destroy_value_list */ static int send_value(lcc_value_list_t *vl) /* {{{ */ { int status; if (vl->values_types[0] == LCC_TYPE_GAUGE) vl->values[0].gauge = 100.0 * ((gauge_t)random()) / (((gauge_t)RAND_MAX) + 1.0); else vl->values[0].derive += (derive_t)get_boundet_random(0, 100); status = lcc_network_values_send(net, vl); if (status != 0) fprintf(stderr, "lcc_network_values_send failed with status %i.\n", status); vl->time += vl->interval; return 0; } /* }}} int send_value */ static int get_integer_opt(const char *str, int *ret_value) /* {{{ */ { char *endptr; int tmp; errno = 0; endptr = NULL; tmp = (int)strtol(str, &endptr, /* base = */ 0); if (errno != 0) { fprintf(stderr, "Unable to parse option as a number: \"%s\": %s\n", str, strerror(errno)); exit(EXIT_FAILURE); } else if (endptr == str) { fprintf(stderr, "Unable to parse option as a number: \"%s\"\n", str); exit(EXIT_FAILURE); } else if (*endptr != 0) { fprintf(stderr, "Garbage after end of value: \"%s\"\n", str); exit(EXIT_FAILURE); } *ret_value = tmp; return 0; } /* }}} int get_integer_opt */ static int get_double_opt(const char *str, double *ret_value) /* {{{ */ { char *endptr; double tmp; errno = 0; endptr = NULL; tmp = strtod(str, &endptr); if (errno != 0) { fprintf(stderr, "Unable to parse option as a number: \"%s\": %s\n", str, strerror(errno)); exit(EXIT_FAILURE); } else if (endptr == str) { fprintf(stderr, "Unable to parse option as a number: \"%s\"\n", str); exit(EXIT_FAILURE); } else if (*endptr != 0) { fprintf(stderr, "Garbage after end of value: \"%s\"\n", str); exit(EXIT_FAILURE); } *ret_value = tmp; return 0; } /* }}} int get_double_opt */ static int read_options(int argc, char **argv) /* {{{ */ { int opt; while ((opt = getopt(argc, argv, "n:H:p:i:d:D:h")) != -1) { switch (opt) { case 'n': get_integer_opt(optarg, &conf_num_values); break; case 'H': get_integer_opt(optarg, &conf_num_hosts); break; case 'p': get_integer_opt(optarg, &conf_num_plugins); break; case 'i': get_double_opt(optarg, &conf_interval); break; case 'd': conf_destination = optarg; break; case 'D': conf_service = optarg; break; case 'h': exit_usage(EXIT_SUCCESS); default: exit_usage(EXIT_FAILURE); } /* switch (opt) */ } /* while (getopt) */ return 0; } /* }}} int read_options */ int main(int argc, char **argv) /* {{{ */ { double last_time; int values_sent = 0; read_options(argc, argv); sigint_action.sa_handler = signal_handler; sigaction(SIGINT, &sigint_action, /* old = */ NULL); sigterm_action.sa_handler = signal_handler; sigaction(SIGTERM, &sigterm_action, /* old = */ NULL); values_heap = c_heap_create(compare_time); if (values_heap == NULL) { fprintf(stderr, "c_heap_create failed.\n"); exit(EXIT_FAILURE); } net = lcc_network_create(); if (net == NULL) { fprintf(stderr, "lcc_network_create failed.\n"); exit(EXIT_FAILURE); } else { lcc_server_t *srv; srv = lcc_server_create(net, conf_destination, conf_service); if (srv == NULL) { fprintf(stderr, "lcc_server_create failed.\n"); exit(EXIT_FAILURE); } lcc_server_set_ttl(srv, 42); #if 0 lcc_server_set_security_level (srv, ENCRYPT, "admin", "password1"); #endif } fprintf(stdout, "Creating %i values ... ", conf_num_values); fflush(stdout); for (int i = 0; i < conf_num_values; i++) { lcc_value_list_t *vl; vl = create_value_list(); if (vl == NULL) { fprintf(stderr, "create_value_list failed.\n"); exit(EXIT_FAILURE); } c_heap_insert(values_heap, vl); } fprintf(stdout, "done\n"); last_time = 0; while (loop) { lcc_value_list_t *vl = c_heap_get_root(values_heap); if (vl == NULL) break; if (vl->time != last_time) { printf("%i values have been sent.\n", values_sent); /* Check if we need to sleep */ double now = dtime(); while (now < vl->time) { double diff = vl->time - now; struct timespec ts = { .tv_sec = (time_t)diff, }; ts.tv_nsec = (long)((diff - ((double)ts.tv_sec)) * 1e9); nanosleep(&ts, /* remaining = */ NULL); now = dtime(); if (!loop) break; } last_time = vl->time; } send_value(vl); values_sent++; c_heap_insert(values_heap, vl); } fprintf(stdout, "Shutting down.\n"); fflush(stdout); while (42) { lcc_value_list_t *vl = c_heap_get_root(values_heap); if (vl == NULL) break; destroy_value_list(vl); } c_heap_destroy(values_heap); lcc_network_destroy(net); exit(EXIT_SUCCESS); } /* }}} int main */