zfs_arc plugin: Use the more generic "cache_*" types …
[collectd.git] / src / libvirt.c
1 /**
2  * collectd - src/libvirt.c
3  * Copyright (C) 2006-2008  Red Hat Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the license is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Richard W.M. Jones <rjones@redhat.com>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26 #include "utils_ignorelist.h"
27 #include "utils_complain.h"
28
29 #include <libvirt/libvirt.h>
30 #include <libvirt/virterror.h>
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #include <libxml/xpath.h>
34
35 static const char *config_keys[] = {
36     "Connection",
37
38     "RefreshInterval",
39
40     "Domain",
41     "BlockDevice",
42     "InterfaceDevice",
43     "IgnoreSelected",
44
45     "HostnameFormat",
46     "InterfaceFormat",
47
48     NULL
49 };
50 #define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
51
52 /* Connection. */
53 static virConnectPtr conn = 0;
54 static char *conn_string = NULL;
55 static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
56
57 /* Seconds between list refreshes, 0 disables completely. */
58 static int interval = 60;
59
60 /* List of domains, if specified. */
61 static ignorelist_t *il_domains = NULL;
62 /* List of block devices, if specified. */
63 static ignorelist_t *il_block_devices = NULL;
64 /* List of network interface devices, if specified. */
65 static ignorelist_t *il_interface_devices = NULL;
66
67 static int ignore_device_match (ignorelist_t *,
68                                 const char *domname, const char *devpath);
69
70 /* Actual list of domains found on last refresh. */
71 static virDomainPtr *domains = NULL;
72 static int nr_domains = 0;
73
74 static void free_domains (void);
75 static int add_domain (virDomainPtr dom);
76
77 /* Actual list of block devices found on last refresh. */
78 struct block_device {
79     virDomainPtr dom;           /* domain */
80     char *path;                 /* name of block device */
81 };
82
83 static struct block_device *block_devices = NULL;
84 static int nr_block_devices = 0;
85
86 static void free_block_devices (void);
87 static int add_block_device (virDomainPtr dom, const char *path);
88
89 /* Actual list of network interfaces found on last refresh. */
90 struct interface_device {
91     virDomainPtr dom;           /* domain */
92     char *path;                 /* name of interface device */
93     char *address;              /* mac address of interface device */
94 };
95
96 static struct interface_device *interface_devices = NULL;
97 static int nr_interface_devices = 0;
98
99 static void free_interface_devices (void);
100 static int add_interface_device (virDomainPtr dom, const char *path, const char *address);
101
102 /* HostnameFormat. */
103 #define HF_MAX_FIELDS 3
104
105 enum hf_field {
106     hf_none = 0,
107     hf_hostname,
108     hf_name,
109     hf_uuid
110 };
111
112 static enum hf_field hostname_format[HF_MAX_FIELDS] =
113     { hf_name };
114
115 /* InterfaceFormat. */
116 enum if_field {
117     if_address,
118     if_name
119 };
120
121 static enum if_field interface_format = if_name;
122
123 /* Time that we last refreshed. */
124 static time_t last_refresh = (time_t) 0;
125
126 static int refresh_lists (void);
127
128 /* Submit functions. */
129 static void cpu_submit (unsigned long long cpu_time,
130                         time_t t,
131                         virDomainPtr dom, const char *type);
132 static void vcpu_submit (unsigned long long cpu_time,
133                          time_t t,
134                          virDomainPtr dom, int vcpu_nr, const char *type);
135 static void submit_counter2 (const char *type, counter_t v0, counter_t v1,
136              time_t t,
137              virDomainPtr dom, const char *devname);
138
139 /* ERROR(...) macro for virterrors. */
140 #define VIRT_ERROR(conn,s) do {                 \
141         virErrorPtr err;                        \
142         err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \
143         if (err) ERROR ("%s: %s", (s), err->message);                   \
144     } while(0)
145
146 static int
147 lv_init (void)
148 {
149     if (virInitialize () != 0)
150         return -1;
151
152         return 0;
153 }
154
155 static int
156 lv_config (const char *key, const char *value)
157 {
158     if (virInitialize () != 0)
159         return 1;
160
161     if (il_domains == NULL)
162         il_domains = ignorelist_create (1);
163     if (il_block_devices == NULL)
164         il_block_devices = ignorelist_create (1);
165     if (il_interface_devices == NULL)
166         il_interface_devices = ignorelist_create (1);
167
168     if (strcasecmp (key, "Connection") == 0) {
169         char *tmp = strdup (value);
170         if (tmp == NULL) {
171             ERROR ("libvirt plugin: Connection strdup failed.");
172             return 1;
173         }
174         sfree (conn_string);
175         conn_string = tmp;
176         return 0;
177     }
178
179     if (strcasecmp (key, "RefreshInterval") == 0) {
180         char *eptr = NULL;
181         interval = strtol (value, &eptr, 10);
182         if (eptr == NULL || *eptr != '\0') return 1;
183         return 0;
184     }
185
186     if (strcasecmp (key, "Domain") == 0) {
187         if (ignorelist_add (il_domains, value)) return 1;
188         return 0;
189     }
190     if (strcasecmp (key, "BlockDevice") == 0) {
191         if (ignorelist_add (il_block_devices, value)) return 1;
192         return 0;
193     }
194     if (strcasecmp (key, "InterfaceDevice") == 0) {
195         if (ignorelist_add (il_interface_devices, value)) return 1;
196         return 0;
197     }
198
199     if (strcasecmp (key, "IgnoreSelected") == 0) {
200         if (IS_TRUE (value))
201         {
202             ignorelist_set_invert (il_domains, 0);
203             ignorelist_set_invert (il_block_devices, 0);
204             ignorelist_set_invert (il_interface_devices, 0);
205         }
206         else
207         {
208             ignorelist_set_invert (il_domains, 1);
209             ignorelist_set_invert (il_block_devices, 1);
210             ignorelist_set_invert (il_interface_devices, 1);
211         }
212         return 0;
213     }
214
215     if (strcasecmp (key, "HostnameFormat") == 0) {
216         char *value_copy;
217         char *fields[HF_MAX_FIELDS];
218         int i, n;
219
220         value_copy = strdup (value);
221         if (value_copy == NULL) {
222             ERROR ("libvirt plugin: strdup failed.");
223             return -1;
224         }
225
226         n = strsplit (value_copy, fields, HF_MAX_FIELDS);
227         if (n < 1) {
228             sfree (value_copy);
229             ERROR ("HostnameFormat: no fields");
230             return -1;
231         }
232
233         for (i = 0; i < n; ++i) {
234             if (strcasecmp (fields[i], "hostname") == 0)
235                 hostname_format[i] = hf_hostname;
236             else if (strcasecmp (fields[i], "name") == 0)
237                 hostname_format[i] = hf_name;
238             else if (strcasecmp (fields[i], "uuid") == 0)
239                 hostname_format[i] = hf_uuid;
240             else {
241                 sfree (value_copy);
242                 ERROR ("unknown HostnameFormat field: %s", fields[i]);
243                 return -1;
244             }
245         }
246         sfree (value_copy);
247
248         for (i = n; i < HF_MAX_FIELDS; ++i)
249             hostname_format[i] = hf_none;
250
251         return 0;
252     }
253
254     if (strcasecmp (key, "InterfaceFormat") == 0) {
255         if (strcasecmp (value, "name") == 0)
256             interface_format = if_name;
257         else if (strcasecmp (value, "address") == 0)
258             interface_format = if_address;
259         else {
260             ERROR ("unknown InterfaceFormat: %s", value);
261             return -1;
262         }
263         return 0;
264     }
265
266     /* Unrecognised option. */
267     return -1;
268 }
269
270 static int
271 lv_read (void)
272 {
273     time_t t;
274     int i;
275
276     if (conn == NULL) {
277         /* `conn_string == NULL' is acceptable. */
278         conn = virConnectOpenReadOnly (conn_string);
279         if (conn == NULL) {
280             c_complain (LOG_ERR, &conn_complain,
281                     "libvirt plugin: Unable to connect: "
282                     "virConnectOpenReadOnly failed.");
283             return -1;
284         }
285     }
286     c_release (LOG_NOTICE, &conn_complain,
287             "libvirt plugin: Connection established.");
288
289     time (&t);
290
291     /* Need to refresh domain or device lists? */
292     if ((last_refresh == (time_t) 0) ||
293             ((interval > 0) && ((last_refresh + interval) <= t))) {
294         if (refresh_lists () != 0) {
295             if (conn != NULL)
296                 virConnectClose (conn);
297             conn = NULL;
298             return -1;
299         }
300         last_refresh = t;
301     }
302
303 #if 0
304     for (i = 0; i < nr_domains; ++i)
305         fprintf (stderr, "domain %s\n", virDomainGetName (domains[i]));
306     for (i = 0; i < nr_block_devices; ++i)
307         fprintf  (stderr, "block device %d %s:%s\n",
308                   i, virDomainGetName (block_devices[i].dom),
309                   block_devices[i].path);
310     for (i = 0; i < nr_interface_devices; ++i)
311         fprintf (stderr, "interface device %d %s:%s\n",
312                  i, virDomainGetName (interface_devices[i].dom),
313                  interface_devices[i].path);
314 #endif
315
316     /* Get CPU usage, VCPU usage for each domain. */
317     for (i = 0; i < nr_domains; ++i) {
318         virDomainInfo info;
319         virVcpuInfoPtr vinfo = NULL;
320         int j;
321
322         if (virDomainGetInfo (domains[i], &info) != 0)
323             continue;
324
325         cpu_submit (info.cpuTime, t, domains[i], "virt_cpu_total");
326
327         vinfo = malloc (info.nrVirtCpu * sizeof vinfo[0]);
328         if (vinfo == NULL) {
329             ERROR ("libvirt plugin: malloc failed.");
330             continue;
331         }
332
333         if (virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
334                     NULL, 0) != 0) {
335             sfree (vinfo);
336             continue;
337         }
338
339         for (j = 0; j < info.nrVirtCpu; ++j)
340             vcpu_submit (vinfo[j].cpuTime,
341                     t, domains[i], vinfo[j].number, "virt_vcpu");
342
343         sfree (vinfo);
344     }
345
346     /* Get block device stats for each domain. */
347     for (i = 0; i < nr_block_devices; ++i) {
348         struct _virDomainBlockStats stats;
349
350         if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path,
351                     &stats, sizeof stats) != 0)
352             continue;
353
354         if ((stats.rd_req != -1) && (stats.wr_req != -1))
355             submit_counter2 ("disk_ops",
356                     (counter_t) stats.rd_req, (counter_t) stats.wr_req,
357                     t, block_devices[i].dom, block_devices[i].path);
358
359         if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1))
360             submit_counter2 ("disk_octets",
361                     (counter_t) stats.rd_bytes, (counter_t) stats.wr_bytes,
362                     t, block_devices[i].dom, block_devices[i].path);
363     } /* for (nr_block_devices) */
364
365     /* Get interface stats for each domain. */
366     for (i = 0; i < nr_interface_devices; ++i) {
367         struct _virDomainInterfaceStats stats;
368         char *display_name = interface_devices[i].path;
369
370         if (interface_format == if_address)
371             display_name = interface_devices[i].address;
372
373         if (virDomainInterfaceStats (interface_devices[i].dom,
374                     interface_devices[i].path,
375                     &stats, sizeof stats) != 0)
376             continue;
377
378         if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
379             submit_counter2 ("if_octets",
380                     (counter_t) stats.rx_bytes, (counter_t) stats.tx_bytes,
381                     t, interface_devices[i].dom, display_name);
382
383         if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
384             submit_counter2 ("if_packets",
385                     (counter_t) stats.rx_packets, (counter_t) stats.tx_packets,
386                     t, interface_devices[i].dom, display_name);
387
388         if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
389             submit_counter2 ("if_errors",
390                     (counter_t) stats.rx_errs, (counter_t) stats.tx_errs,
391                     t, interface_devices[i].dom, display_name);
392
393         if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
394             submit_counter2 ("if_dropped",
395                     (counter_t) stats.rx_drop, (counter_t) stats.tx_drop,
396                     t, interface_devices[i].dom, display_name);
397     } /* for (nr_interface_devices) */
398
399     return 0;
400 }
401
402 static int
403 refresh_lists (void)
404 {
405     int n;
406
407     n = virConnectNumOfDomains (conn);
408     if (n < 0) {
409         VIRT_ERROR (conn, "reading number of domains");
410         return -1;
411     }
412
413     if (n > 0) {
414         int i;
415         int *domids;
416
417         /* Get list of domains. */
418         domids = malloc (sizeof (int) * n);
419         if (domids == 0) {
420             ERROR ("libvirt plugin: malloc failed.");
421             return -1;
422         }
423
424         n = virConnectListDomains (conn, domids, n);
425         if (n < 0) {
426             VIRT_ERROR (conn, "reading list of domains");
427             sfree (domids);
428             return -1;
429         }
430
431         free_block_devices ();
432         free_interface_devices ();
433         free_domains ();
434
435         /* Fetch each domain and add it to the list, unless ignore. */
436         for (i = 0; i < n; ++i) {
437             virDomainPtr dom = NULL;
438             const char *name;
439             char *xml = NULL;
440             xmlDocPtr xml_doc = NULL;
441             xmlXPathContextPtr xpath_ctx = NULL;
442             xmlXPathObjectPtr xpath_obj = NULL;
443             int j;
444
445             dom = virDomainLookupByID (conn, domids[i]);
446             if (dom == NULL) {
447                 VIRT_ERROR (conn, "virDomainLookupByID");
448                 /* Could be that the domain went away -- ignore it anyway. */
449                 continue;
450             }
451
452             name = virDomainGetName (dom);
453             if (name == NULL) {
454                 VIRT_ERROR (conn, "virDomainGetName");
455                 goto cont;
456             }
457
458             if (il_domains && ignorelist_match (il_domains, name) != 0)
459                 goto cont;
460
461             if (add_domain (dom) < 0) {
462                 ERROR ("libvirt plugin: malloc failed.");
463                 goto cont;
464             }
465
466             /* Get a list of devices for this domain. */
467             xml = virDomainGetXMLDesc (dom, 0);
468             if (!xml) {
469                 VIRT_ERROR (conn, "virDomainGetXMLDesc");
470                 goto cont;
471             }
472
473             /* Yuck, XML.  Parse out the devices. */
474             xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET);
475             if (xml_doc == NULL) {
476                 VIRT_ERROR (conn, "xmlReadDoc");
477                 goto cont;
478             }
479
480             xpath_ctx = xmlXPathNewContext (xml_doc);
481
482             /* Block devices. */
483             xpath_obj = xmlXPathEval
484                 ((xmlChar *) "/domain/devices/disk/target[@dev]",
485                  xpath_ctx);
486             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
487                 xpath_obj->nodesetval == NULL)
488                 goto cont;
489
490             for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
491                 xmlNodePtr node;
492                 char *path = NULL;
493
494                 node = xpath_obj->nodesetval->nodeTab[j];
495                 if (!node) continue;
496                 path = (char *) xmlGetProp (node, (xmlChar *) "dev");
497                 if (!path) continue;
498
499                 if (il_block_devices &&
500                     ignore_device_match (il_block_devices, name, path) != 0)
501                     goto cont2;
502
503                 add_block_device (dom, path);
504             cont2:
505                 if (path) xmlFree (path);
506             }
507             xmlXPathFreeObject (xpath_obj);
508
509             /* Network interfaces. */
510             xpath_obj = xmlXPathEval
511                 ((xmlChar *) "/domain/devices/interface[target[@dev]]",
512                  xpath_ctx);
513             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
514                 xpath_obj->nodesetval == NULL)
515                 goto cont;
516
517             xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
518
519             for (j = 0; j < xml_interfaces->nodeNr; ++j) {
520                 char *path = NULL;
521                 char *address = NULL;
522                 xmlNodePtr xml_interface;
523
524                 xml_interface = xml_interfaces->nodeTab[j];
525                 if (!xml_interface) continue;
526                 xmlNodePtr child = NULL;
527
528                 for (child = xml_interface->children; child; child = child->next) {
529                     if (child->type != XML_ELEMENT_NODE) continue;
530
531                     if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
532                         path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
533                         if (!path) continue;
534                     } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
535                         address = (char *) xmlGetProp (child, (const xmlChar *) "address");
536                         if (!address) continue;
537                     }
538                 }
539
540                 if (il_interface_devices &&
541                     (ignore_device_match (il_interface_devices, name, path) != 0 ||
542                      ignore_device_match (il_interface_devices, name, address) != 0))
543                     goto cont3;
544
545                 add_interface_device (dom, path, address);
546                 cont3:
547                     if (path) xmlFree (path);
548                     if (address) xmlFree (address);
549             }
550
551         cont:
552             if (xpath_obj) xmlXPathFreeObject (xpath_obj);
553             if (xpath_ctx) xmlXPathFreeContext (xpath_ctx);
554             if (xml_doc) xmlFreeDoc (xml_doc);
555             sfree (xml);
556         }
557
558         sfree (domids);
559     }
560
561     return 0;
562 }
563
564 static void
565 free_domains ()
566 {
567     int i;
568
569     if (domains) {
570         for (i = 0; i < nr_domains; ++i)
571             virDomainFree (domains[i]);
572         sfree (domains);
573     }
574     domains = NULL;
575     nr_domains = 0;
576 }
577
578 static int
579 add_domain (virDomainPtr dom)
580 {
581     virDomainPtr *new_ptr;
582     int new_size = sizeof (domains[0]) * (nr_domains+1);
583
584     if (domains)
585         new_ptr = realloc (domains, new_size);
586     else
587         new_ptr = malloc (new_size);
588
589     if (new_ptr == NULL)
590         return -1;
591
592     domains = new_ptr;
593     domains[nr_domains] = dom;
594     return nr_domains++;
595 }
596
597 static void
598 free_block_devices ()
599 {
600     int i;
601
602     if (block_devices) {
603         for (i = 0; i < nr_block_devices; ++i)
604             sfree (block_devices[i].path);
605         sfree (block_devices);
606     }
607     block_devices = NULL;
608     nr_block_devices = 0;
609 }
610
611 static int
612 add_block_device (virDomainPtr dom, const char *path)
613 {
614     struct block_device *new_ptr;
615     int new_size = sizeof (block_devices[0]) * (nr_block_devices+1);
616     char *path_copy;
617
618     path_copy = strdup (path);
619     if (!path_copy)
620         return -1;
621
622     if (block_devices)
623         new_ptr = realloc (block_devices, new_size);
624     else
625         new_ptr = malloc (new_size);
626
627     if (new_ptr == NULL) {
628         sfree (path_copy);
629         return -1;
630     }
631     block_devices = new_ptr;
632     block_devices[nr_block_devices].dom = dom;
633     block_devices[nr_block_devices].path = path_copy;
634     return nr_block_devices++;
635 }
636
637 static void
638 free_interface_devices ()
639 {
640     int i;
641
642     if (interface_devices) {
643         for (i = 0; i < nr_interface_devices; ++i) {
644             sfree (interface_devices[i].path);
645             sfree (interface_devices[i].address);
646         }
647         sfree (interface_devices);
648     }
649     interface_devices = NULL;
650     nr_interface_devices = 0;
651 }
652
653 static int
654 add_interface_device (virDomainPtr dom, const char *path, const char *address)
655 {
656     struct interface_device *new_ptr;
657     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
658     char *path_copy, *address_copy;
659
660     path_copy = strdup (path);
661     if (!path_copy) return -1;
662
663     address_copy = strdup (address);
664     if (!address_copy) return -1;
665
666     if (interface_devices)
667         new_ptr = realloc (interface_devices, new_size);
668     else
669         new_ptr = malloc (new_size);
670
671     if (new_ptr == NULL) {
672         sfree (path_copy);
673         sfree (address_copy);
674         return -1;
675     }
676     interface_devices = new_ptr;
677     interface_devices[nr_interface_devices].dom = dom;
678     interface_devices[nr_interface_devices].path = path_copy;
679     interface_devices[nr_interface_devices].address = address_copy;
680     return nr_interface_devices++;
681 }
682
683 static int
684 ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath)
685 {
686     char *name;
687     int n, r;
688
689     n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2);
690     name = malloc (n);
691     if (name == NULL) {
692         ERROR ("libvirt plugin: malloc failed.");
693         return 0;
694     }
695     ssnprintf (name, n, "%s:%s", domname, devpath);
696     r = ignorelist_match (il, name);
697     sfree (name);
698     return r;
699 }
700
701 static void
702 init_value_list (value_list_t *vl, time_t t, virDomainPtr dom)
703 {
704     int i, n;
705     const char *name;
706     char uuid[VIR_UUID_STRING_BUFLEN];
707     char  *host_ptr;
708     size_t host_len;
709
710     vl->time = t;
711     vl->interval = interval_g;
712
713     sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
714
715     vl->host[0] = '\0';
716     host_ptr = vl->host;
717     host_len = sizeof (vl->host);
718
719     /* Construct the hostname field according to HostnameFormat. */
720     for (i = 0; i < HF_MAX_FIELDS; ++i) {
721         if (hostname_format[i] == hf_none)
722             continue;
723
724         n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2;
725
726         if (i > 0 && n >= 1) {
727             strncat (vl->host, ":", 1);
728             n--;
729         }
730
731         switch (hostname_format[i]) {
732         case hf_none: break;
733         case hf_hostname:
734             strncat (vl->host, hostname_g, n);
735             break;
736         case hf_name:
737             name = virDomainGetName (dom);
738             if (name)
739                 strncat (vl->host, name, n);
740             break;
741         case hf_uuid:
742             if (virDomainGetUUIDString (dom, uuid) == 0)
743                 strncat (vl->host, uuid, n);
744             break;
745         }
746     }
747
748     vl->host[sizeof (vl->host) - 1] = '\0';
749 } /* void init_value_list */
750
751 static void
752 cpu_submit (unsigned long long cpu_time,
753             time_t t,
754             virDomainPtr dom, const char *type)
755 {
756     value_t values[1];
757     value_list_t vl = VALUE_LIST_INIT;
758
759     init_value_list (&vl, t, dom);
760
761     values[0].counter = cpu_time;
762
763     vl.values = values;
764     vl.values_len = 1;
765
766     sstrncpy (vl.type, type, sizeof (vl.type));
767
768     plugin_dispatch_values (&vl);
769 }
770
771 static void
772 vcpu_submit (counter_t cpu_time,
773              time_t t,
774              virDomainPtr dom, int vcpu_nr, const char *type)
775 {
776     value_t values[1];
777     value_list_t vl = VALUE_LIST_INIT;
778
779     init_value_list (&vl, t, dom);
780
781     values[0].counter = cpu_time;
782     vl.values = values;
783     vl.values_len = 1;
784
785     sstrncpy (vl.type, type, sizeof (vl.type));
786     ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr);
787
788     plugin_dispatch_values (&vl);
789 }
790
791 static void
792 submit_counter2 (const char *type, counter_t v0, counter_t v1,
793              time_t t,
794              virDomainPtr dom, const char *devname)
795 {
796     value_t values[2];
797     value_list_t vl = VALUE_LIST_INIT;
798
799     init_value_list (&vl, t, dom);
800
801     values[0].counter = v0;
802     values[1].counter = v1;
803     vl.values = values;
804     vl.values_len = 2;
805
806     sstrncpy (vl.type, type, sizeof (vl.type));
807     sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance));
808
809     plugin_dispatch_values (&vl);
810 } /* void submit_counter2 */
811
812 static int
813 lv_shutdown (void)
814 {
815     free_block_devices ();
816     free_interface_devices ();
817     free_domains ();
818
819     if (conn != NULL)
820         virConnectClose (conn);
821     conn = NULL;
822
823     ignorelist_free (il_domains);
824     il_domains = NULL;
825     ignorelist_free (il_block_devices);
826     il_block_devices = NULL;
827     ignorelist_free (il_interface_devices);
828     il_interface_devices = NULL;
829
830     return 0;
831 }
832
833 void
834 module_register (void)
835 {
836     plugin_register_config ("libvirt",
837             lv_config,
838             config_keys, NR_CONFIG_KEYS);
839     plugin_register_init ("libvirt", lv_init);
840     plugin_register_read ("libvirt", lv_read);
841     plugin_register_shutdown ("libvirt", lv_shutdown);
842 }
843
844 /*
845  * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker
846  */