Setting C<BlockDeviceFormatBasename true> will cause the I<type instance> to be
set to C<image1.qcow2>.
-=item B<HostnameFormat> B<name|uuid|hostname|...>
+=item B<HostnameFormat> B<name|uuid|hostname|metadata...>
When the virt plugin logs data, it sets the hostname of the collected data
according to this setting. The default is to use the guest name as provided by
useful on its own because all guests will appear to have the same name. This is
useful in conjunction with B<PluginInstanceFormat> though.
+B<metadata> means use information from guest's metadata. Use
+B<HostnameMetadataNS> and B<HostnameMetadataXPath> to localize this information.
+
You can also specify combinations of these fields. For example B<name uuid>
means to concatenate the guest name and UUID (with a literal colon character
between, thus I<"foo:1234-1234-1234-1234">).
B<address> means use the interface's mac address. This is useful since the
interface path might change between reboots of a guest or across migrations.
-=item B<PluginInstanceFormat> B<name|uuid|none>
+=item B<PluginInstanceFormat> B<name|uuid|metadata|none>
When the virt plugin logs data, it sets the plugin_instance of the collected
data according to this setting. The default is to not set the plugin_instance.
B<name> means use the guest's name as provided by the hypervisor.
B<uuid> means use the guest's UUID.
+B<metadata> means use information from guest's metadata.
You can also specify combinations of the B<name> and B<uuid> fields.
For example B<name uuid> means to concatenate the guest name and UUID
(with a literal colon character between, thus I<"foo:1234-1234-1234-1234">).
-=item B<Instances> B<integer>
+=item B<HostnameMetadataNS> B<string>
-How many read instances you want to use for this plugin. The default is one,
-and the sensible setting is a multiple of the B<ReadThreads> value.
-If you are not sure, just use the default setting.
+When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+selects in which metadata namespace we will pick the hostname. The default is
+I<http://openstack.org/xmlns/libvirt/nova/1.0>.
+
+=item B<HostnameMetadataXPath> B<string>
+
+When B<metadata> is used in B<HostnameFormat> or B<PluginInstanceFormat>, this
+describes where the hostname is located in the libvirt metadata. The default is
+I</instance/name/text()>.
=item B<ExtraStats> B<string>
=back
=item B<PersistentNotification> B<true>|B<false>
+
Override default configuration to only send notifications when there is a change
in the lifecycle state of a domain. When set to true notifications will be sent
for every read cycle. Default is false. Does not affect the stats being
dispatched.
+=item B<Instances> B<integer>
+
+How many read instances you want to use for this plugin. The default is one,
+and the sensible setting is a multiple of the B<ReadThreads> value.
+
+This option is only useful then domains are specially tagged.
+If you are not sure, just use the default setting.
+
+The reader instance will only query the domains with attached a matching tag.
+Tags should have the form of 'virt-X' where X is reader instance number,
+starting from 0.
+
+The special-purpose reader instance #0, guaranteed to be always present,
+will query all the domains with missing or unrecognized tag, so no domain will
+ever left out.
+
+Domain tagging is done with custom attribute in the libvirt domain metadata
+section. Value is taken by XPath I</domain/metadata/ovirtmap/tag/text()>
+expression in I<http://ovirt.org/ovirtmap/tag/1.0> namespace.
+(XPath and namespace values are not configurable yet).
+
+Tagging could be used by management application to evenly spread the
+load among the reader threads, or to pin on the same threads all
+the libvirt domains which use the same shared storage, to minimize
+the disruption in presence of storage outages.
+
=back
=head2 Plugin C<vmem>
"IgnoreSelected",
"HostnameFormat",
+ "HostnameMetadataNS",
+ "HostnameMetadataXPath",
"InterfaceFormat",
"PluginInstanceFormat",
static struct lv_user_data lv_read_user_data[NR_INSTANCES_MAX];
/* HostnameFormat. */
-#define HF_MAX_FIELDS 3
+#define HF_MAX_FIELDS 4
-enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid };
+enum hf_field { hf_none = 0, hf_hostname, hf_name, hf_uuid, hf_metadata };
static enum hf_field hostname_format[HF_MAX_FIELDS] = {hf_name};
/* PluginInstanceFormat */
-#define PLGINST_MAX_FIELDS 2
+#define PLGINST_MAX_FIELDS 3
-enum plginst_field { plginst_none = 0, plginst_name, plginst_uuid };
+enum plginst_field {
+ plginst_none = 0,
+ plginst_name,
+ plginst_uuid,
+ plginst_metadata
+};
static enum plginst_field plugin_instance_format[PLGINST_MAX_FIELDS] = {
plginst_none};
+/* HostnameMetadataNS && HostnameMetadataXPath */
+static char *hm_xpath;
+static char *hm_ns;
+
/* BlockDeviceFormat */
enum bd_field { target, source };
ERROR(PLUGIN_NAME " plugin: %s failed: %s", (s), err->message); \
} while (0)
+char *metadata_get_hostname(virDomainPtr dom) {
+ const char *xpath_str = NULL;
+ if (hm_xpath == NULL)
+ xpath_str = "/instance/name/text()";
+ else
+ xpath_str = hm_xpath;
+
+ const char *namespace = NULL;
+ if (hm_ns == NULL) {
+ namespace = "http://openstack.org/xmlns/libvirt/nova/1.0";
+ } else {
+ namespace = hm_ns;
+ }
+
+ char *metadata_str = virDomainGetMetadata(
+ dom, VIR_DOMAIN_METADATA_ELEMENT, namespace, VIR_DOMAIN_AFFECT_CURRENT);
+ if (metadata_str == NULL) {
+ return NULL;
+ }
+
+ char *hostname = NULL;
+ xmlXPathContextPtr xpath_ctx = NULL;
+ xmlXPathObjectPtr xpath_obj = NULL;
+ xmlNodePtr xml_node = NULL;
+
+ xmlDocPtr xml_doc =
+ xmlReadDoc((xmlChar *)metadata_str, NULL, NULL, XML_PARSE_NONET);
+ if (xml_doc == NULL) {
+ ERROR(PLUGIN_NAME " plugin: xmlReadDoc failed to read metadata");
+ goto metadata_end;
+ }
+
+ xpath_ctx = xmlXPathNewContext(xml_doc);
+ if (xpath_ctx == NULL) {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathNewContext(%s) failed for metadata",
+ metadata_str);
+ goto metadata_end;
+ }
+ xpath_obj = xmlXPathEval((xmlChar *)xpath_str, xpath_ctx);
+ if (xpath_obj == NULL) {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) failed for metadata",
+ xpath_str);
+ goto metadata_end;
+ }
+
+ if (xpath_obj->type != XPATH_NODESET) {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unexpected return type %d "
+ "(wanted %d) for metadata",
+ xpath_str, xpath_obj->type, XPATH_NODESET);
+ goto metadata_end;
+ }
+
+ // TODO(sileht): We can support || operator by looping on nodes here
+ if (xpath_obj->nodesetval == NULL || xpath_obj->nodesetval->nodeNr != 1) {
+ WARNING(PLUGIN_NAME " plugin: xmlXPathEval(%s) return nodeset size=%i "
+ "expected=1 for metadata",
+ xpath_str,
+ (xpath_obj->nodesetval == NULL) ? 0
+ : xpath_obj->nodesetval->nodeNr);
+ goto metadata_end;
+ }
+
+ xml_node = xpath_obj->nodesetval->nodeTab[0];
+ if (xml_node->type == XML_TEXT_NODE) {
+ hostname = strdup((const char *)xml_node->content);
+ } else if (xml_node->type == XML_ATTRIBUTE_NODE) {
+ hostname = strdup((const char *)xml_node->children->content);
+ } else {
+ ERROR(PLUGIN_NAME " plugin: xmlXPathEval(%s) unsupported node type %d",
+ xpath_str, xml_node->type);
+ goto metadata_end;
+ }
+
+ if (hostname == NULL) {
+ ERROR(PLUGIN_NAME " plugin: strdup(%s) hostname failed", xpath_str);
+ goto metadata_end;
+ }
+
+metadata_end:
+ if (xpath_obj)
+ xmlXPathFreeObject(xpath_obj);
+ if (xpath_ctx)
+ xmlXPathFreeContext(xpath_ctx);
+ if (xml_doc)
+ xmlFreeDoc(xml_doc);
+ sfree(metadata_str);
+ return hostname;
+}
+
static void init_value_list(value_list_t *vl, virDomainPtr dom) {
const char *name;
char uuid[VIR_UUID_STRING_BUFLEN];
if (virDomainGetUUIDString(dom, uuid) == 0)
SSTRNCAT(vl->host, uuid, sizeof(vl->host));
break;
+ case hf_metadata:
+ name = metadata_get_hostname(dom);
+ if (name)
+ SSTRNCAT(vl->host, name, sizeof(vl->host));
+ break;
}
}
if (virDomainGetUUIDString(dom, uuid) == 0)
SSTRNCAT(vl->plugin_instance, uuid, sizeof(vl->plugin_instance));
break;
+ case plginst_metadata:
+ name = metadata_get_hostname(dom);
+ if (name)
+ SSTRNCAT(vl->plugin_instance, name, sizeof(vl->plugin_instance));
+ break;
}
}
return 0;
}
+ if (strcasecmp(key, "HostnameMetadataNS") == 0) {
+ char *tmp = strdup(value);
+ if (tmp == NULL) {
+ ERROR(PLUGIN_NAME " plugin: HostnameMetadataNS strdup failed.");
+ return 1;
+ }
+ sfree(hm_ns);
+ hm_ns = tmp;
+ return 0;
+ }
+
+ if (strcasecmp(key, "HostnameMetadataXPath") == 0) {
+ char *tmp = strdup(value);
+ if (tmp == NULL) {
+ ERROR(PLUGIN_NAME " plugin: HostnameMetadataXPath strdup failed.");
+ return 1;
+ }
+ sfree(hm_xpath);
+ hm_xpath = tmp;
+ return 0;
+ }
+
if (strcasecmp(key, "HostnameFormat") == 0) {
char *value_copy = strdup(value);
if (value_copy == NULL) {
hostname_format[i] = hf_name;
else if (strcasecmp(fields[i], "uuid") == 0)
hostname_format[i] = hf_uuid;
+ else if (strcasecmp(fields[i], "metadata") == 0)
+ hostname_format[i] = hf_metadata;
else {
ERROR(PLUGIN_NAME " plugin: unknown HostnameFormat field: %s",
fields[i]);
plugin_instance_format[i] = plginst_name;
else if (strcasecmp(fields[i], "uuid") == 0)
plugin_instance_format[i] = plginst_uuid;
+ else if (strcasecmp(fields[i], "metadata") == 0)
+ plugin_instance_format[i] = plginst_metadata;
else {
ERROR(PLUGIN_NAME " plugin: unknown PluginInstanceFormat field: %s",
fields[i]);