pax_global_header 0000666 0000000 0000000 00000000064 13676652661 0014534 g ustar 00root root 0000000 0000000 52 comment=df45f3148212994e19ef74fcb159f5fe8752faf3 libvirt_exporter-0.2.0/ 0000775 0000000 0000000 00000000000 13676652661 0015136 5 ustar 00root root 0000000 0000000 libvirt_exporter-0.2.0/.gitignore 0000664 0000000 0000000 00000000030 13676652661 0017117 0 ustar 00root root 0000000 0000000 .idea /libvirt_exporter libvirt_exporter-0.2.0/Dockerfile 0000664 0000000 0000000 00000002451 13676652661 0017132 0 ustar 00root root 0000000 0000000 # Stage 1: Build libvirt exporter FROM golang:alpine # Install dependencies RUN apk add --update git gcc g++ make libc-dev portablexdr-dev linux-headers libnl-dev perl libtirpc-dev pkgconfig wget RUN wget ftp://xmlsoft.org/libxml2/libxml2-2.9.4.tar.gz -P /tmp && \ tar -xf /tmp/libxml2-2.9.4.tar.gz -C /tmp WORKDIR /tmp/libxml2-2.9.4 RUN ./configure --disable-shared --enable-static && \ make -j2 && \ make install RUN wget https://libvirt.org/sources/libvirt-3.2.0.tar.xz -P /tmp && \ tar -xf /tmp/libvirt-3.2.0.tar.xz -C /tmp WORKDIR /tmp/libvirt-3.2.0 RUN ./configure --disable-shared --enable-static --localstatedir=/var --without-storage-mpath && \ make -j2 && \ make install && \ sed -i 's/^Libs:.*/& -lnl -ltirpc -lxml2/' /usr/local/lib/pkgconfig/libvirt.pc # Prepare working directory ENV LIBVIRT_EXPORTER_PATH=/go/src/github.com/kumina/libvirt_exporter RUN mkdir -p $LIBVIRT_EXPORTER_PATH WORKDIR $LIBVIRT_EXPORTER_PATH COPY . . # Build and strip exporter RUN go get -d ./... && \ go build --ldflags '-extldflags "-static"' && \ strip libvirt_exporter # Stage 2: Prepare final image FROM scratch # Copy binary from Stage 1 COPY --from=0 /go/src/github.com/kumina/libvirt_exporter/libvirt_exporter . # Entrypoint for starting exporter ENTRYPOINT [ "./libvirt_exporter" ] libvirt_exporter-0.2.0/LICENSE 0000664 0000000 0000000 00000026135 13676652661 0016152 0 ustar 00root root 0000000 0000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. libvirt_exporter-0.2.0/README.md 0000664 0000000 0000000 00000005404 13676652661 0016420 0 ustar 00root root 0000000 0000000 # Prometheus libvirt exporter This repository provides code for a Prometheus metrics exporter for [libvirt](https://libvirt.org/). This exporter connects to any libvirt daemon and exports per-domain metrics related to CPU, memory, disk and network usage. By default, this exporter listens on TCP port 9177. This exporter makes use of [libvirt-go](https://github.com/libvirt/libvirt-go), the official Go bindings for libvirt. Ideally, this exporter should make use of the `GetAllDomainStats()` API call to extract all relevant metrics. Unfortunately, we at Kumina still need this exporter to be compatible with older versions of libvirt that don't support this API call. The following metrics/labels are being exported: ``` libvirt_domain_block_stats_read_bytes_total{domain="...",uuid="...",source_file="...",target_device="..."} libvirt_domain_block_stats_read_requests_total{domain="...",uuid="...",source_file="...",target_device="..."} libvirt_domain_block_stats_write_bytes_total{domain="...",uuid="...",source_file="...",target_device="..."} libvirt_domain_block_stats_write_requests_total{domain="...",uuid="...",source_file="...",target_device="..."} libvirt_domain_info_cpu_time_seconds_total{domain="...",uuid="..."} libvirt_domain_info_maximum_memory_bytes{domain="...",uuid="..."} libvirt_domain_info_memory_usage_bytes{domain="...",uuid="..."} libvirt_domain_info_virtual_cpus{domain="...",uuid="..."} libvirt_domain_interface_stats_receive_bytes_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_receive_drops_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_receive_errors_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_receive_packets_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_transmit_bytes_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_transmit_drops_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_transmit_errors_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_domain_interface_stats_transmit_packets_total{domain="...",uuid="...",source_bridge="...",target_device="..."} libvirt_up ``` With the `--libvirt.export-nova-metadata` flag, it will export the following additional OpenStack-specific labels for every domain: - name - flavor - project_name At Kumina we want to perform a single build of this exporter, deploying it to a variety of Linux distribution versions. This is why this repository contains a shell script, `build_static.sh`, that builds a statically linked copy of this exporter in an Alpine Linux based container. libvirt_exporter-0.2.0/build_static.sh 0000775 0000000 0000000 00000002117 13676652661 0020144 0 ustar 00root root 0000000 0000000 #!/bin/sh docker run -i -v `pwd`:/gopath/src/github.com/kumina/libvirt_exporter alpine:3.8 /bin/sh << 'EOF' set -ex # Install prerequisites for the build process. apk update apk add ca-certificates g++ git go libnl-dev linux-headers make perl pkgconf libtirpc-dev wget update-ca-certificates # Install libxml2. Alpine's version does not ship with a static library. cd /tmp wget ftp://xmlsoft.org/libxml2/libxml2-2.9.4.tar.gz tar -xf libxml2-2.9.4.tar.gz cd libxml2-2.9.4 ./configure --disable-shared --enable-static make -j2 make install # Install libvirt. Alpine's version does not ship with a static library. cd /tmp wget https://libvirt.org/sources/libvirt-3.2.0.tar.xz tar -xf libvirt-3.2.0.tar.xz cd libvirt-3.2.0 ./configure --disable-shared --enable-static --localstatedir=/var --without-storage-mpath make -j2 make install sed -i 's/^Libs:.*/& -lnl -ltirpc -lxml2/' /usr/local/lib/pkgconfig/libvirt.pc # Build the libvirt_exporter. cd /gopath/src/github.com/kumina/libvirt_exporter export GOPATH=/gopath go get -d ./... go build --ldflags '-extldflags "-static"' strip libvirt_exporter EOF libvirt_exporter-0.2.0/go.mod 0000664 0000000 0000000 00000000306 13676652661 0016243 0 ustar 00root root 0000000 0000000 module github.com/kumina/libvirt_exporter go 1.12 require ( github.com/libvirt/libvirt-go v5.5.0+incompatible github.com/prometheus/client_golang v1.0.0 gopkg.in/alecthomas/kingpin.v2 v2.2.6 ) libvirt_exporter-0.2.0/go.sum 0000664 0000000 0000000 00000013252 13676652661 0016274 0 ustar 00root root 0000000 0000000 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/libvirt/libvirt-go v5.5.0+incompatible h1:O+SnmyiG4qDIAZQRHAeehC7uN2Zu0BZv74BQoj4Ic6o= github.com/libvirt/libvirt-go v5.5.0+incompatible/go.mod h1:34zsnB4iGeOv7Byj6qotuW8Ya4v4Tr43ttjz/F0wjLE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5 h1:mzjBh+S5frKOsOBobWIMAbXavqjmgO17k/2puhcFR94= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= libvirt_exporter-0.2.0/libvirt_exporter.go 0000664 0000000 0000000 00000043146 13676652661 0021100 0 ustar 00root root 0000000 0000000 // Copyright 2017 Kumina, https://kumina.nl/ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "encoding/xml" "log" "net/http" "os" "github.com/kumina/libvirt_exporter/libvirt_schema" "github.com/libvirt/libvirt-go" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "gopkg.in/alecthomas/kingpin.v2" ) // LibvirtExporter implements a Prometheus exporter for libvirt state. type LibvirtExporter struct { uri string exportNovaMetadata bool libvirtUpDesc *prometheus.Desc libvirtDomainsNumberDesc *prometheus.Desc libvirtDomainStateCode *prometheus.Desc libvirtDomainInfoMaxMemDesc *prometheus.Desc libvirtDomainInfoMemoryDesc *prometheus.Desc libvirtDomainInfoNrVirtCpuDesc *prometheus.Desc libvirtDomainInfoCpuTimeDesc *prometheus.Desc libvirtDomainBlockRdBytesDesc *prometheus.Desc libvirtDomainBlockRdReqDesc *prometheus.Desc libvirtDomainBlockRdTotalTimesDesc *prometheus.Desc libvirtDomainBlockWrBytesDesc *prometheus.Desc libvirtDomainBlockWrReqDesc *prometheus.Desc libvirtDomainBlockWrTotalTimesDesc *prometheus.Desc libvirtDomainBlockFlushReqDesc *prometheus.Desc libvirtDomainBlockFlushTotalTimesDesc *prometheus.Desc libvirtDomainInterfaceRxBytesDesc *prometheus.Desc libvirtDomainInterfaceRxPacketsDesc *prometheus.Desc libvirtDomainInterfaceRxErrsDesc *prometheus.Desc libvirtDomainInterfaceRxDropDesc *prometheus.Desc libvirtDomainInterfaceTxBytesDesc *prometheus.Desc libvirtDomainInterfaceTxPacketsDesc *prometheus.Desc libvirtDomainInterfaceTxErrsDesc *prometheus.Desc libvirtDomainInterfaceTxDropDesc *prometheus.Desc } // NewLibvirtExporter creates a new Prometheus exporter for libvirt. func NewLibvirtExporter(uri string, exportNovaMetadata bool) (*LibvirtExporter, error) { var domainLabels []string if exportNovaMetadata { domainLabels = []string{"domain", "uuid", "name", "flavor", "project_name"} } else { domainLabels = []string{"domain", "uuid"} } return &LibvirtExporter{ uri: uri, exportNovaMetadata: exportNovaMetadata, libvirtUpDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "", "up"), "Whether scraping libvirt's metrics was successful.", nil, nil), libvirtDomainsNumberDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "", "domains_number"), "Number of domains.", nil, nil), libvirtDomainStateCode: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "", "domain_state_code"), "State of the domain.", domainLabels, nil), libvirtDomainInfoMaxMemDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_info", "maximum_memory_bytes"), "Maximum allowed memory of the domain, in bytes.", domainLabels, nil), libvirtDomainInfoMemoryDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_info", "memory_usage_bytes"), "Memory usage of the domain, in bytes.", domainLabels, nil), libvirtDomainInfoNrVirtCpuDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_info", "virtual_cpus"), "Number of virtual CPUs for the domain.", domainLabels, nil), libvirtDomainInfoCpuTimeDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_info", "cpu_time_seconds_total"), "Amount of CPU time used by the domain, in seconds.", domainLabels, nil), libvirtDomainBlockRdBytesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "read_bytes_total"), "Number of bytes read from a block device, in bytes.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockRdReqDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "read_requests_total"), "Number of read requests from a block device.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockRdTotalTimesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "read_seconds_total"), "Amount of time spent reading from a block device, in seconds.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockWrBytesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "write_bytes_total"), "Number of bytes written from a block device, in bytes.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockWrReqDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "write_requests_total"), "Number of write requests from a block device.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockWrTotalTimesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "write_seconds_total"), "Amount of time spent writing from a block device, in seconds.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockFlushReqDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "flush_requests_total"), "Number of flush requests from a block device.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainBlockFlushTotalTimesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_block_stats", "flush_seconds_total"), "Amount of time spent flushing of a block device, in seconds.", append(domainLabels, "source_file", "target_device"), nil), libvirtDomainInterfaceRxBytesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "receive_bytes_total"), "Number of bytes received on a network interface, in bytes.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceRxPacketsDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "receive_packets_total"), "Number of packets received on a network interface.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceRxErrsDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "receive_errors_total"), "Number of packet receive errors on a network interface.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceRxDropDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "receive_drops_total"), "Number of packet receive drops on a network interface.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceTxBytesDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "transmit_bytes_total"), "Number of bytes transmitted on a network interface, in bytes.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceTxPacketsDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "transmit_packets_total"), "Number of packets transmitted on a network interface.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceTxErrsDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "transmit_errors_total"), "Number of packet transmit errors on a network interface.", append(domainLabels, "source_bridge", "target_device"), nil), libvirtDomainInterfaceTxDropDesc: prometheus.NewDesc( prometheus.BuildFQName("libvirt", "domain_interface_stats", "transmit_drops_total"), "Number of packet transmit drops on a network interface.", append(domainLabels, "source_bridge", "target_device"), nil), }, nil } // Describe returns metadata for all Prometheus metrics that may be exported. func (e *LibvirtExporter) Describe(ch chan<- *prometheus.Desc) { ch <- e.libvirtUpDesc ch <- e.libvirtDomainsNumberDesc ch <- e.libvirtDomainStateCode ch <- e.libvirtDomainInfoMaxMemDesc ch <- e.libvirtDomainInfoMemoryDesc ch <- e.libvirtDomainInfoNrVirtCpuDesc ch <- e.libvirtDomainInfoCpuTimeDesc ch <- e.libvirtDomainBlockRdBytesDesc ch <- e.libvirtDomainBlockRdReqDesc ch <- e.libvirtDomainBlockRdTotalTimesDesc ch <- e.libvirtDomainBlockWrBytesDesc ch <- e.libvirtDomainBlockWrReqDesc ch <- e.libvirtDomainBlockWrTotalTimesDesc ch <- e.libvirtDomainBlockFlushReqDesc ch <- e.libvirtDomainBlockFlushTotalTimesDesc } // Collect scrapes Prometheus metrics from libvirt. func (e *LibvirtExporter) Collect(ch chan<- prometheus.Metric) { err := e.CollectFromLibvirt(ch) if err == nil { ch <- prometheus.MustNewConstMetric( e.libvirtUpDesc, prometheus.GaugeValue, 1.0) } else { log.Printf("Failed to scrape metrics: %s", err) ch <- prometheus.MustNewConstMetric( e.libvirtUpDesc, prometheus.GaugeValue, 0.0) } } // CollectFromLibvirt obtains Prometheus metrics from all domains in a // libvirt setup. func (e *LibvirtExporter) CollectFromLibvirt(ch chan<- prometheus.Metric) error { conn, err := libvirt.NewConnect(e.uri) if err != nil { return err } defer conn.Close() // Use ListDomains() as opposed to using ListAllDomains(), as // the latter is unsupported when talking to a system using // libvirt 0.9.12 or older. domainIds, err := conn.ListDomains() if err != nil { return err } // number of domains ch <- prometheus.MustNewConstMetric( e.libvirtDomainsNumberDesc, prometheus.GaugeValue, float64(len(domainIds))) for _, id := range domainIds { domain, err := conn.LookupDomainById(id) if err == nil { err = e.CollectDomain(ch, domain) domain.Free() if err != nil { return err } } } return nil } // CollectDomain extracts Prometheus metrics from a libvirt domain. func (e *LibvirtExporter) CollectDomain(ch chan<- prometheus.Metric, domain *libvirt.Domain) error { // Decode XML description of domain to get block device names, etc. xmlDesc, err := domain.GetXMLDesc(0) if err != nil { return err } var desc libvirt_schema.Domain err = xml.Unmarshal([]byte(xmlDesc), &desc) if err != nil { return err } domainName, err := domain.GetName() if err != nil { return err } var domainUUID = desc.UUID // Extract domain label valuies var domainLabelValues []string if e.exportNovaMetadata { var ( novaName = desc.Metadata.NovaInstance.Name novaFlavor = desc.Metadata.NovaInstance.Flavor.Name novaProjectName = desc.Metadata.NovaInstance.Owner.ProjectName ) domainLabelValues = []string{domainName, domainUUID, novaName, novaFlavor, novaProjectName} } else { domainLabelValues = []string{domainName, domainUUID} } // Report domain info. info, err := domain.GetInfo() if err != nil { return err } ch <- prometheus.MustNewConstMetric( e.libvirtDomainStateCode, prometheus.GaugeValue, float64(info.State), domainLabelValues...) ch <- prometheus.MustNewConstMetric( e.libvirtDomainInfoMaxMemDesc, prometheus.GaugeValue, float64(info.MaxMem)*1024, domainLabelValues...) ch <- prometheus.MustNewConstMetric( e.libvirtDomainInfoMemoryDesc, prometheus.GaugeValue, float64(info.Memory)*1024, domainLabelValues...) ch <- prometheus.MustNewConstMetric( e.libvirtDomainInfoNrVirtCpuDesc, prometheus.GaugeValue, float64(info.NrVirtCpu), domainLabelValues...) ch <- prometheus.MustNewConstMetric( e.libvirtDomainInfoCpuTimeDesc, prometheus.CounterValue, float64(info.CpuTime)/1e9, domainLabelValues...) // Report block device statistics. for _, disk := range desc.Devices.Disks { if disk.Device == "cdrom" || disk.Device == "fd" { continue } blockStats, err := domain.BlockStats(disk.Target.Device) if err != nil { return err } if blockStats.RdBytesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockRdBytesDesc, prometheus.CounterValue, float64(blockStats.RdBytes), append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.RdReqSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockRdReqDesc, prometheus.CounterValue, float64(blockStats.RdReq), append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.RdTotalTimesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockRdTotalTimesDesc, prometheus.CounterValue, float64(blockStats.RdTotalTimes)/1e9, append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.WrBytesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockWrBytesDesc, prometheus.CounterValue, float64(blockStats.WrBytes), append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.WrReqSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockWrReqDesc, prometheus.CounterValue, float64(blockStats.WrReq), append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.WrTotalTimesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockWrTotalTimesDesc, prometheus.CounterValue, float64(blockStats.WrTotalTimes)/1e9, append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.FlushReqSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockFlushReqDesc, prometheus.CounterValue, float64(blockStats.FlushReq), append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } if blockStats.FlushTotalTimesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainBlockFlushTotalTimesDesc, prometheus.CounterValue, float64(blockStats.FlushTotalTimes)/1e9, append(domainLabelValues, disk.Source.File, disk.Target.Device)...) } // Skip "Errs", as the documentation does not clearly // explain what this means. } // Report network interface statistics. for _, iface := range desc.Devices.Interfaces { if iface.Target.Device == "" { continue } interfaceStats, err := domain.InterfaceStats(iface.Target.Device) if err != nil { return err } if interfaceStats.RxBytesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceRxBytesDesc, prometheus.CounterValue, float64(interfaceStats.RxBytes), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.RxPacketsSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceRxPacketsDesc, prometheus.CounterValue, float64(interfaceStats.RxPackets), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.RxErrsSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceRxErrsDesc, prometheus.CounterValue, float64(interfaceStats.RxErrs), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.RxDropSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceRxDropDesc, prometheus.CounterValue, float64(interfaceStats.RxDrop), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.TxBytesSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceTxBytesDesc, prometheus.CounterValue, float64(interfaceStats.TxBytes), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.TxPacketsSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceTxPacketsDesc, prometheus.CounterValue, float64(interfaceStats.TxPackets), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.TxErrsSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceTxErrsDesc, prometheus.CounterValue, float64(interfaceStats.TxErrs), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } if interfaceStats.TxDropSet { ch <- prometheus.MustNewConstMetric( e.libvirtDomainInterfaceTxDropDesc, prometheus.CounterValue, float64(interfaceStats.TxDrop), append(domainLabelValues, iface.Source.Bridge, iface.Target.Device)...) } } return nil } func main() { var ( app = kingpin.New("libvirt_exporter", "Prometheus metrics exporter for libvirt") listenAddress = app.Flag("web.listen-address", "Address to listen on for web interface and telemetry.").Default(":9177").String() metricsPath = app.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() libvirtURI = app.Flag("libvirt.uri", "Libvirt URI from which to extract metrics.").Default("qemu:///system").String() libvirtExportNovaMetadata = app.Flag("libvirt.export-nova-metadata", "Export OpenStack Nova specific labels from libvirt domain xml").Default("false").Bool() ) kingpin.MustParse(app.Parse(os.Args[1:])) exporter, err := NewLibvirtExporter(*libvirtURI, *libvirtExportNovaMetadata) if err != nil { panic(err) } prometheus.MustRegister(exporter) http.Handle(*metricsPath, promhttp.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`