X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fvirt.c;h=2a97df326fa508a3f85fb9cb4c61b70d04a0762e;hb=fe5167843e22b7b1ca4c7e5ba0e1d0df387b0094;hp=443e5b434eb691944ceec74fdff4a2e9fd42017f;hpb=c3a4930c24272257db478b22710471bd5f4972f7;p=collectd.git diff --git a/src/virt.c b/src/virt.c index 443e5b43..2a97df32 100644 --- a/src/virt.c +++ b/src/virt.c @@ -69,9 +69,10 @@ #endif /* - virConnectListAllDomains() appeared in 0.10.2 - Note that LIBVIR_CHECK_VERSION appeared a year later, so - in some systems which actually have virConnectListAllDomains() + virConnectListAllDomains() appeared in 0.10.2 (Sep 2012) + Note that LIBVIR_CHECK_VERSION appeared a year later (Dec 2013, + libvirt-1.2.0), + so in some systems which actually have virConnectListAllDomains() we can't detect this. */ #if LIBVIR_CHECK_VERSION(0, 10, 2) @@ -107,6 +108,9 @@ #define HAVE_DOM_REASON_POSTCOPY 1 #endif +#if LIBVIR_CHECK_VERSION(4, 10, 0) +#define HAVE_DOM_REASON_SHUTOFF_DAEMON 1 +#endif #endif /* LIBVIR_CHECK_VERSION */ /* structure used for aggregating notification-thread data*/ @@ -300,6 +304,16 @@ static int map_domain_event_detail_to_reason(int event, int detail) { switch (detail) { case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: /* Guest finished shutdown sequence */ +#ifdef LIBVIR_CHECK_VERSION +#if LIBVIR_CHECK_VERSION(3, 4, 0) + case VIR_DOMAIN_EVENT_SHUTDOWN_GUEST: /* Domain finished shutting down after + request from the guest itself (e.g. + hardware-specific action) */ + case VIR_DOMAIN_EVENT_SHUTDOWN_HOST: /* Domain finished shutting down after + request from the host (e.g. killed + by a signal) */ +#endif +#endif ret = VIR_DOMAIN_SHUTDOWN_USER; break; default: @@ -425,6 +439,10 @@ const char *domain_reasons[][DOMAIN_STATE_REASON_MAX_SIZE] = { "domain failed to start", [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_FROM_SNAPSHOT] = "restored from a snapshot which was taken while domain was shutoff", +#ifdef HAVE_DOM_REASON_SHUTOFF_DAEMON + [VIR_DOMAIN_SHUTOFF][VIR_DOMAIN_SHUTOFF_DAEMON] = + "daemon decides to kill domain during reconnection processing", +#endif [VIR_DOMAIN_CRASHED][VIR_DOMAIN_CRASHED_UNKNOWN] = "the reason is unknown", @@ -592,7 +610,8 @@ enum ex_stats { #endif ex_stats_disk_allocation = 1 << 10, ex_stats_disk_capacity = 1 << 11, - ex_stats_disk_physical = 1 << 12 + ex_stats_disk_physical = 1 << 12, + ex_stats_memory = 1 << 13 }; static unsigned int extra_stats = ex_stats_none; @@ -623,6 +642,7 @@ static const struct ex_stats_item ex_stats_table[] = { {"disk_allocation", ex_stats_disk_allocation}, {"disk_capacity", ex_stats_disk_capacity}, {"disk_physical", ex_stats_disk_physical}, + {"memory", ex_stats_memory}, {NULL, ex_stats_none}, }; @@ -635,6 +655,8 @@ static enum if_field interface_format = if_name; static time_t last_refresh = (time_t)0; static int refresh_lists(struct lv_read_instance *inst); +static int register_event_impl(void); +static int start_event_loop(virt_notif_thread_t *thread_data); struct lv_block_stats { virDomainBlockStatsStruct bi; @@ -916,10 +938,10 @@ static void memory_submit(virDomainPtr dom, gauge_t value) { static void memory_stats_submit(gauge_t value, virDomainPtr dom, int tag_index) { - static const char *tags[] = {"swap_in", "swap_out", "major_fault", - "minor_fault", "unused", "available", - "actual_balloon", "rss", "usable", - "last_update"}; + static const char *tags[] = {"swap_in", "swap_out", "major_fault", + "minor_fault", "unused", "available", + "actual_balloon", "rss", "usable", + "last_update", "disk_caches"}; if ((tag_index < 0) || (tag_index >= (int)STATIC_ARRAY_SIZE(tags))) { ERROR("virt plugin: Array index out of bounds: tag_index = %d", tag_index); @@ -1155,6 +1177,11 @@ static int lv_init_ignorelists() { /* Validates config option that may take multiple strings arguments. * Returns 0 on success, -1 otherwise */ static int check_config_multiple_string_entry(const oconfig_item_t *ci) { + if (ci == NULL) { + ERROR(PLUGIN_NAME " plugin: ci oconfig_item can't be NULL"); + return -1; + } + if (ci->values_num < 1) { ERROR(PLUGIN_NAME " plugin: the '%s' option requires at least one string argument", @@ -1184,25 +1211,19 @@ static int lv_config(oconfig_item_t *ci) { oconfig_item_t *c = ci->children + i; if (strcasecmp(c->key, "Connection") == 0) { - if (cf_util_get_string(c, &conn_string) != 0 || conn_string == NULL) { - ERROR(PLUGIN_NAME " plugin: Could not get 'Connection' parameter"); + if (cf_util_get_string(c, &conn_string) != 0 || conn_string == NULL) return -1; - } continue; } else if (strcasecmp(c->key, "RefreshInterval") == 0) { - if (cf_util_get_int(c, &interval) != 0) { - ERROR(PLUGIN_NAME " plugin: Could not get 'RefreshInterval' parameter"); + if (cf_util_get_int(c, &interval) != 0) return -1; - } continue; } else if (strcasecmp(c->key, "Domain") == 0) { char *domain_name = NULL; - if (cf_util_get_string(c, &domain_name) != 0 || domain_name == NULL) { - ERROR(PLUGIN_NAME " plugin: Could not get 'Domain' parameter"); + if (cf_util_get_string(c, &domain_name) != 0) return -1; - } if (ignorelist_add(il_domains, domain_name)) { ERROR(PLUGIN_NAME " plugin: Adding '%s' to domain-ignorelist failed", @@ -1215,10 +1236,8 @@ static int lv_config(oconfig_item_t *ci) { continue; } else if (strcasecmp(c->key, "BlockDevice") == 0) { char *device_name = NULL; - if (cf_util_get_string(c, &device_name) != 0 || device_name == NULL) { - ERROR(PLUGIN_NAME " plugin: Could not get 'BlockDevice' parameter"); + if (cf_util_get_string(c, &device_name) != 0) return -1; - } if (ignorelist_add(il_block_devices, device_name) != 0) { ERROR(PLUGIN_NAME @@ -1232,11 +1251,8 @@ static int lv_config(oconfig_item_t *ci) { continue; } else if (strcasecmp(c->key, "BlockDeviceFormat") == 0) { char *device_format = NULL; - if (cf_util_get_string(c, &device_format) != 0 || device_format == NULL) { - ERROR(PLUGIN_NAME - " plugin: Could not get 'BlockDeviceFormat' parameter"); + if (cf_util_get_string(c, &device_format) != 0) return -1; - } if (strcasecmp(device_format, "target") == 0) blockdevice_format = target; @@ -1252,20 +1268,14 @@ static int lv_config(oconfig_item_t *ci) { sfree(device_format); continue; } else if (strcasecmp(c->key, "BlockDeviceFormatBasename") == 0) { - if (cf_util_get_boolean(c, &blockdevice_format_basename) != 0) { - ERROR(PLUGIN_NAME - " plugin: Could not get 'BlockDeviceFormatBasename' parameter"); + if (cf_util_get_boolean(c, &blockdevice_format_basename) != 0) return -1; - } continue; } else if (strcasecmp(c->key, "InterfaceDevice") == 0) { char *interface_name = NULL; - if (cf_util_get_string(c, &interface_name) != 0 || - interface_name == NULL) { - ERROR(PLUGIN_NAME " plugin: Could not get 'InterfaceDevice' parameter"); + if (cf_util_get_string(c, &interface_name) != 0) return -1; - } if (ignorelist_add(il_interface_devices, interface_name)) { ERROR(PLUGIN_NAME " plugin: Adding '%s' to interface-ignorelist failed", @@ -1278,10 +1288,8 @@ static int lv_config(oconfig_item_t *ci) { continue; } else if (strcasecmp(c->key, "IgnoreSelected") == 0) { bool ignore_selected = false; - if (cf_util_get_boolean(c, &ignore_selected) != 0) { - ERROR(PLUGIN_NAME " plugin: Could not get 'IgnoreSelected' parameter"); + if (cf_util_get_boolean(c, &ignore_selected) != 0) return -1; - } if (ignore_selected) { ignorelist_set_invert(il_domains, 0); @@ -1295,25 +1303,21 @@ static int lv_config(oconfig_item_t *ci) { continue; } else if (strcasecmp(c->key, "HostnameMetadataNS") == 0) { - if (cf_util_get_string(c, &hm_ns) != 0) { - ERROR(PLUGIN_NAME - " plugin: Could not get 'HostnameMetadataNS' parameter"); + if (cf_util_get_string(c, &hm_ns) != 0) return -1; - } continue; } else if (strcasecmp(c->key, "HostnameMetadataXPath") == 0) { - if (cf_util_get_string(c, &hm_xpath) != 0) { - ERROR(PLUGIN_NAME - " plugin: Could not get 'HostnameMetadataXPath' parameter"); + if (cf_util_get_string(c, &hm_xpath) != 0) return -1; - } continue; } else if (strcasecmp(c->key, "HostnameFormat") == 0) { /* this option can take multiple strings arguments in one config line*/ - if (check_config_multiple_string_entry(c) != 0) + if (check_config_multiple_string_entry(c) != 0) { + ERROR(PLUGIN_NAME " plugin: Could not get 'HostnameFormat' parameter"); return -1; + } const int params_num = c->values_num; for (int i = 0; i < params_num; ++i) { @@ -1339,8 +1343,11 @@ static int lv_config(oconfig_item_t *ci) { continue; } else if (strcasecmp(c->key, "PluginInstanceFormat") == 0) { /* this option can handle list of string parameters in one line*/ - if (check_config_multiple_string_entry(c) != 0) + if (check_config_multiple_string_entry(c) != 0) { + ERROR(PLUGIN_NAME + " plugin: Could not get 'PluginInstanceFormat' parameter"); return -1; + } const int params_num = c->values_num; for (int i = 0; i < params_num; ++i) { @@ -1368,10 +1375,8 @@ static int lv_config(oconfig_item_t *ci) { continue; } else if (strcasecmp(c->key, "InterfaceFormat") == 0) { char *format = NULL; - if (cf_util_get_string(c, &format) != 0 || format == NULL) { - ERROR(PLUGIN_NAME " plugin: could not get 'InterfaceFormat' parameter"); + if (cf_util_get_string(c, &format) != 0) return -1; - } if (strcasecmp(format, "name") == 0) interface_format = if_name; @@ -1388,10 +1393,8 @@ static int lv_config(oconfig_item_t *ci) { sfree(format); continue; } else if (strcasecmp(c->key, "Instances") == 0) { - if (cf_util_get_int(c, &nr_instances) != 0) { - ERROR(PLUGIN_NAME " plugin: could not get 'Instances' parameter"); + if (cf_util_get_int(c, &nr_instances) != 0) return -1; - } if (nr_instances <= 0) { ERROR(PLUGIN_NAME " plugin: Instances <= 0 makes no sense."); @@ -1409,10 +1412,8 @@ static int lv_config(oconfig_item_t *ci) { } else if (strcasecmp(c->key, "ExtraStats") == 0) { char *ex_str = NULL; - if (cf_util_get_string(c, &ex_str) != 0 || ex_str == NULL) { - ERROR(PLUGIN_NAME " plugin: could not get 'ExtraStats' parameter"); + if (cf_util_get_string(c, &ex_str) != 0) return -1; - } char *exstats[EX_STATS_MAX_FIELDS]; int numexstats = strsplit(ex_str, exstats, STATIC_ARRAY_SIZE(exstats)); @@ -1433,30 +1434,21 @@ static int lv_config(oconfig_item_t *ci) { } #endif - /* ExtraStats parsed successfully*/ + /* ExtraStats parsed successfully */ continue; } else if (strcasecmp(c->key, "PersistentNotification") == 0) { - if (cf_util_get_boolean(c, &persistent_notification) != 0) { - ERROR(PLUGIN_NAME - " plugin: could not get 'PersistentNotification' parameter"); + if (cf_util_get_boolean(c, &persistent_notification) != 0) return -1; - } continue; } else if (strcasecmp(c->key, "ReportBlockDevices") == 0) { - if (cf_util_get_boolean(c, &report_block_devices) != 0) { - ERROR(PLUGIN_NAME - " plugin: could not get 'ReportBlockDevices' parameter"); + if (cf_util_get_boolean(c, &report_block_devices) != 0) return -1; - } continue; } else if (strcasecmp(c->key, "ReportNetworkInterfaces") == 0) { - if (cf_util_get_boolean(c, &report_network_interfaces) != 0) { - ERROR(PLUGIN_NAME - " plugin: could not get 'ReportNetworkInterfaces' parameter"); + if (cf_util_get_boolean(c, &report_network_interfaces) != 0) return -1; - } continue; } else { @@ -1471,6 +1463,11 @@ static int lv_config(oconfig_item_t *ci) { static int lv_connect(void) { if (conn == NULL) { + /* event implementation must be registered before connection is opened */ + if (!persistent_notification) + if (register_event_impl() != 0) + return -1; + /* `conn_string == NULL' is acceptable */ #ifdef HAVE_FS_INFO /* virDomainGetFSInfo requires full read-write access connection */ @@ -1488,8 +1485,17 @@ static int lv_connect(void) { int status = virNodeGetInfo(conn, &nodeinfo); if (status != 0) { ERROR(PLUGIN_NAME " plugin: virNodeGetInfo failed"); + virConnectClose(conn); + conn = NULL; return -1; } + + if (!persistent_notification) + if (start_event_loop(¬if_thread) != 0) { + virConnectClose(conn); + conn = NULL; + return -1; + } } c_release(LOG_NOTICE, &conn_complain, PLUGIN_NAME " plugin: Connection established."); @@ -1661,16 +1667,7 @@ static int get_pcpu_stats(virDomainPtr dom) { #endif /* HAVE_CPU_STATS */ #ifdef HAVE_DOM_REASON - -static void domain_state_submit(virDomainPtr dom, int state, int reason) { - value_t values[] = { - {.gauge = (gauge_t)state}, {.gauge = (gauge_t)reason}, - }; - - submit(dom, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values)); -} - -static int get_domain_state(virDomainPtr domain) { +static int submit_domain_state(virDomainPtr domain) { int domain_state = 0; int domain_reason = 0; @@ -1681,9 +1678,13 @@ static int get_domain_state(virDomainPtr domain) { return status; } - domain_state_submit(domain, domain_state, domain_reason); + value_t values[] = { + {.gauge = (gauge_t)domain_state}, {.gauge = (gauge_t)domain_reason}, + }; - return status; + submit(domain, "domain_state", NULL, values, STATIC_ARRAY_SIZE(values)); + + return 0; } #ifdef HAVE_LIST_ALL_DOMAINS @@ -1698,8 +1699,7 @@ static int get_domain_state_notify(virDomainPtr domain) { return status; } - if (persistent_notification) - domain_state_submit_notif(domain, domain_state, domain_reason); + domain_state_submit_notif(domain, domain_state, domain_reason); return status; } @@ -1723,8 +1723,42 @@ static int get_memory_stats(virDomainPtr domain) { return mem_stats; } - for (int i = 0; i < mem_stats; i++) - memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag); + derive_t swap_in = -1; + derive_t swap_out = -1; + derive_t min_flt = -1; + derive_t maj_flt = -1; + + for (int i = 0; i < mem_stats; i++) { + if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_IN) + swap_in = minfo[i].val; + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_SWAP_OUT) + swap_out = minfo[i].val; + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT) + min_flt = minfo[i].val; + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT) + maj_flt = minfo[i].val; +#ifdef LIBVIR_CHECK_VERSION +#if LIBVIR_CHECK_VERSION(2, 1, 0) + else if (minfo[i].tag == VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE) + /* Skip 'last_update' reporting as that is not memory but timestamp */ + continue; +#endif +#endif + else + memory_stats_submit((gauge_t)minfo[i].val * 1024, domain, minfo[i].tag); + } + + if (swap_in > 0 || swap_out > 0) { + submit(domain, "swap_io", "in", &(value_t){.gauge = swap_in}, 1); + submit(domain, "swap_io", "out", &(value_t){.gauge = swap_out}, 1); + } + + if (min_flt > 0 || maj_flt > 0) { + value_t values[] = { + {.gauge = (gauge_t)min_flt}, {.gauge = (gauge_t)maj_flt}, + }; + submit(domain, "ps_pagefaults", NULL, values, STATIC_ARRAY_SIZE(values)); + } sfree(minfo); return 0; @@ -1969,7 +2003,7 @@ static int get_domain_metrics(domain_t *domain) { * however it doesn't provide a reason for entering particular state. * We need to get it from virDomainGetState. */ - GET_STATS(get_domain_state, "domain reason", domain->ptr); + GET_STATS(submit_domain_state, "domain reason", domain->ptr); #endif } @@ -1987,7 +2021,8 @@ static int get_domain_metrics(domain_t *domain) { memory_submit(domain->ptr, (gauge_t)info.memory * 1024); GET_STATS(get_vcpu_stats, "vcpu stats", domain->ptr, info.nrVirtCpu); - GET_STATS(get_memory_stats, "memory stats", domain->ptr); + if (extra_stats & ex_stats_memory) + GET_STATS(get_memory_stats, "memory stats", domain->ptr); #ifdef HAVE_PERF_STATS if (extra_stats & ex_stats_perf) @@ -2121,10 +2156,9 @@ static void *event_loop_worker(void *arg) { } static int virt_notif_thread_init(virt_notif_thread_t *thread_data) { - int ret; - assert(thread_data != NULL); - ret = pthread_mutex_init(&thread_data->active_mutex, NULL); + + int ret = pthread_mutex_init(&thread_data->active_mutex, NULL); if (ret != 0) { ERROR(PLUGIN_NAME " plugin: Failed to initialize mutex, err %u", ret); return ret; @@ -2153,11 +2187,15 @@ static int start_event_loop(virt_notif_thread_t *thread_data) { return -1; } + DEBUG(PLUGIN_NAME " plugin: starting event loop"); + virt_notif_thread_set_active(thread_data, 1); if (pthread_create(&thread_data->event_loop_tid, NULL, event_loop_worker, thread_data)) { ERROR(PLUGIN_NAME " plugin: failed event loop thread creation"); + virt_notif_thread_set_active(thread_data, 0); virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); + thread_data->domain_event_cb_id = -1; return -1; } @@ -2166,13 +2204,21 @@ static int start_event_loop(virt_notif_thread_t *thread_data) { /* stop event loop thread and deregister callback */ static void stop_event_loop(virt_notif_thread_t *thread_data) { - /* stopping loop and de-registering event handler*/ - virt_notif_thread_set_active(thread_data, 0); - if (conn != NULL && thread_data->domain_event_cb_id != -1) - virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); - if (pthread_join(notif_thread.event_loop_tid, NULL) != 0) - ERROR(PLUGIN_NAME " plugin: stopping notification thread failed"); + DEBUG(PLUGIN_NAME " plugin: stopping event loop"); + + /* Stopping loop */ + if (virt_notif_thread_is_active(thread_data)) { + virt_notif_thread_set_active(thread_data, 0); + if (pthread_join(notif_thread.event_loop_tid, NULL) != 0) + ERROR(PLUGIN_NAME " plugin: stopping notification thread failed"); + } + + /* ... and de-registering event handler */ + if (conn != NULL && thread_data->domain_event_cb_id != -1) { + virConnectDomainEventDeregisterAny(conn, thread_data->domain_event_cb_id); + thread_data->domain_event_cb_id = -1; + } } static int persistent_domains_state_notification(void) { @@ -2248,33 +2294,26 @@ static int persistent_domains_state_notification(void) { } static int lv_read(user_data_t *ud) { - time_t t; - struct lv_read_instance *inst = NULL; - struct lv_read_state *state = NULL; - if (ud->data == NULL) { ERROR(PLUGIN_NAME " plugin: NULL userdata"); return -1; } - inst = ud->data; - state = &inst->read_state; - - bool reconnect = conn == NULL ? true : false; - /* event implementation must be registered before connection is opened */ - if (inst->id == 0) { - if (!persistent_notification && reconnect) - if (register_event_impl() != 0) - return -1; + struct lv_read_instance *inst = ud->data; + struct lv_read_state *state = &inst->read_state; + if (inst->id == 0) if (lv_connect() < 0) return -1; - if (!persistent_notification && reconnect && conn != NULL) - if (start_event_loop(¬if_thread) != 0) - return -1; + /* Wait until inst#0 establish connection */ + if (conn == NULL) { + DEBUG(PLUGIN_NAME " plugin#%s: Wait until inst#0 establish connection", + inst->tag); + return 0; } + time_t t; time(&t); /* Need to refresh domain or device lists? */ @@ -2321,8 +2360,8 @@ static int lv_read(user_data_t *ud) { if (dom->active) status = get_domain_metrics(dom); #ifdef HAVE_DOM_REASON - else - status = get_domain_state(dom->ptr); + else if (extra_stats & ex_stats_domain_state) + status = submit_domain_state(dom->ptr); #endif if (status != 0) @@ -2395,21 +2434,11 @@ static int lv_init(void) { if (lv_init_ignorelists() != 0) return -1; - /* event implementation must be registered before connection is opened */ if (!persistent_notification) - if (register_event_impl() != 0) + if (virt_notif_thread_init(¬if_thread) != 0) return -1; - if (lv_connect() != 0) - return -1; - - DEBUG(PLUGIN_NAME " plugin: starting event loop"); - - if (!persistent_notification) { - virt_notif_thread_init(¬if_thread); - if (start_event_loop(¬if_thread) != 0) - return -1; - } + lv_connect(); DEBUG(PLUGIN_NAME " plugin: starting %i instances", nr_instances); @@ -2994,8 +3023,6 @@ static int lv_shutdown(void) { lv_fini_instance(i); } - DEBUG(PLUGIN_NAME " plugin: stopping event loop"); - if (!persistent_notification) stop_event_loop(¬if_thread);