Merge ~dmzoneill/charm-prometheus-libvirt-exporter:dev/q2-20 into charm-prometheus-libvirt-exporter:master

Proposed by David O Neill
Status: Merged
Merge reported by: Alvaro Uria
Merged at revision: 78cbf7a6c3109fa232a083f9dbd7d9db317008ea
Proposed branch: ~dmzoneill/charm-prometheus-libvirt-exporter:dev/q2-20
Merge into: charm-prometheus-libvirt-exporter:master
Diff against target: 2440 lines (+2225/-49)
23 files modified
.gitignore (+24/-0)
Makefile (+52/-0)
README.md (+43/-0)
config.yaml (+14/-0)
dev/null (+0/-46)
files/grafana-dashboards/libvirt.json (+1374/-0)
icon.svg (+279/-0)
interfaces/.empty (+0/-0)
layer.yaml (+10/-3)
layers/.empty (+0/-0)
metadata.yaml (+12/-0)
reactive/prometheus-libvirt-exporter.py (+185/-0)
requirements.txt (+1/-0)
tests/__init__.py (+1/-0)
tests/bundles/bionic.yaml (+22/-0)
tests/bundles/focal.yaml (+22/-0)
tests/bundles/overlays/local-charm-overlay.yaml.j2 (+3/-0)
tests/bundles/xenial.yaml (+22/-0)
tests/requirements.txt (+1/-0)
tests/test_prometheus_libvirt_exporter.py (+78/-0)
tests/tests.yaml (+11/-0)
tests/unit_tests/requirements.txt (+6/-0)
tox.ini (+65/-0)
Reviewer Review Type Date Requested Status
Alvaro Uria (community) Approve
Giuseppe Petralia Needs Fixing
Chris MacNaughton Pending
Andrew McLeod Pending
Review via email: mp+380729@code.launchpad.net
To post a comment you must log in.
78cbf7a... by David O Neill

Zaza, nrpe, prometheus updates

- makefile updates for CAHRM_BUILD_DIR
- removed pytest artefacts
- added support for pushing dashboard upstream to grafana when leader
- provided resource for attaching or updateing additional dashboards
- fix layer and metadata

Revision history for this message
Chris MacNaughton (chris.macnaughton) wrote :

General code review
===================

See inline comments for an overall code review.

The code has lint checking which it passes

Unit testing coverage
=====================

This charm change has no unit test coverage. As such, any new functionality introduced, and the potential impact of this change to existing behavior has not been assessed by the reviewer.

Functional testing coverage
===========================

The cham has basic functional coverage of all series that the charm supports

Revision history for this message
James Troup (elmo) wrote :

Some drive by comments

Revision history for this message
Giuseppe Petralia (peppepetra) wrote :

Some more comments in line

Revision history for this message
Giuseppe Petralia (peppepetra) wrote :

Added one more comment

review: Needs Fixing
Revision history for this message
Giuseppe Petralia (peppepetra) wrote :

Comment inline

Revision history for this message
Alvaro Uria (aluria) wrote :

I've added more comments inline. Other comments:
* The COMMIT_MSG has a typo (CHARM_BUILD_DIR)
* After cloning the repo, "make lint" failed because "mkdir report" is needed (I think it should be included by adding ./report/.keep or the extra flags in the flake8 command removed). Unit tests would not fail because of this, because pytest creates the folder if it does not exist.
* Unit tests should be added, and functional tests moved to a new folder ./tests/functional/
* If this MP won't add unit tests, I'd suggest removing all references to unit tests.

In general, the change looks good. I need to review a full deployment because the Zaza test does not include nova-compute to verify that metrics are retrieved from a running libvirt service.

review: Needs Fixing
Revision history for this message
Alvaro Uria (aluria) wrote :

BTW, this MP should be rebased. OTOH, I've found another MP [1] that should be merged with this one (I'll add a similar comment on the other MP).

1. https://code.launchpad.net/~woutervb/charm-prometheus-libvirt-exporter/+git/prometheus-libvirt-exporter-charm/+merge/382466

Revision history for this message
Alvaro Uria (aluria) wrote :

There is a dashboard that was initially included in charm-grafana [1]. However, the approach followed in this MP (share dashboards via relations) is the end goal. Please have a look to the mentioned charm-grafana MP because it uses the "_input" attribute to rewrite the datasources. Currently, it is hardcoded to "prometheus - Juju generated datasource". However, the Juju application may be named "prometheus2". In such case, the datasource name should be "prometheus2 - Juju generated datasource".

1. https://code.launchpad.net/~llama-charmers/charm-grafana/+git/charm-grafana/+merge/380965

Revision history for this message
Alvaro Uria (aluria) wrote :

This dashboard uses the "stat" plugin which is only available from v6.6 onwards. The snap is v5.0.

Revision history for this message
Alvaro Uria (aluria) wrote :

The snap v6.7.3 is going to be released as stable. Besides this fix, there is another one of top of this one that removes unit tests template (there are no unit tests in this charm) as well as it fixes the dashboard's title ("[juju] Libvirt metrics"). Next fix also removes the uid from the dashboard template.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.coverage b/.coverage
2index 834f8fb..e5c6f66 100644
3Binary files a/.coverage and b/.coverage differ
4diff --git a/.gitignore b/.gitignore
5new file mode 100644
6index 0000000..2dc89c9
7--- /dev/null
8+++ b/.gitignore
9@@ -0,0 +1,24 @@
10+# Byte-compiled / optimized / DLL files
11+__pycache__/
12+*.py[cod]
13+*$py.class
14+
15+# Log files
16+*.log
17+.tox/
18+.coverage
19+
20+# vi
21+.*.swp
22+
23+# pycharm
24+.idea/
25+
26+# version data
27+repo-info
28+
29+# reports
30+report/*
31+
32+# layers
33+layers/*
34\ No newline at end of file
35diff --git a/Makefile b/Makefile
36new file mode 100644
37index 0000000..a4e48ca
38--- /dev/null
39+++ b/Makefile
40@@ -0,0 +1,52 @@
41+ifndef CHARM_BUILD_DIR
42+ CHARM_BUILD_DIR=/tmp/builds
43+endif
44+
45+help:
46+ @echo "This project supports the following targets"
47+ @echo ""
48+ @echo " make help - show this text"
49+ @echo " make submodules - make sure that the submodules are up-to-date"
50+ @echo " make lint - run flake8"
51+ @echo " make test - run the unittests and lint"
52+ @echo " make unittest - run the tests defined in the unittest subdirectory"
53+ @echo " make functional - run the tests defined in the functional subdirectory"
54+ @echo " make release - build the charm"
55+ @echo " make clean - remove unneeded files"
56+ @echo ""
57+
58+submodules:
59+ @echo "Cloning submodules"
60+ @git submodule update --init --recursive
61+
62+lint:
63+ @echo "Running flake8"
64+ @tox -e lint
65+
66+#test: lint unittest functional
67+test: lint functional
68+
69+unittest:
70+ @tox -e unit
71+
72+functional: build
73+ @echo Executing with: CHARM_BUILD_DIR=$(CHARM_BUILD_DIR) tox -e func
74+ @CHARM_BUILD_DIR=$(CHARM_BUILD_DIR) tox -e func
75+
76+build:
77+ @echo "Building charm to base directory $(CHARM_BUILD_DIR)"
78+ @-git describe --tags > ./repo-info
79+ @CHARM_LAYERS_DIR=./layers CHARM_INTERFACES_DIR=./interfaces TERM=linux \
80+ CHARM_BUILD_DIR=$(CHARM_BUILD_DIR) charm build . --force
81+
82+release: clean build
83+ @echo "Charm is built at $(CHARM_BUILD_DIR)/prometheus-libvirt-exporter"
84+
85+clean:
86+ @echo "Cleaning files"
87+ @if [ -d .tox ] ; then rm -r .tox ; fi
88+ @if [ -d .pytest_cache ] ; then rm -r .pytest_cache ; fi
89+ @find . -iname __pycache__ -exec rm -r {} +
90+
91+# The targets below don't depend on a file
92+.PHONY: lint test unittest functional build release clean help submodules
93diff --git a/README.md b/README.md
94index 1bb3648..fda45af 100644
95--- a/README.md
96+++ b/README.md
97@@ -1,3 +1,46 @@
98 # Juju prometheus libvirt exporter charm
99
100 This charm provides the [Prometheus libvirt exporter](https://github.com/kumina/libvirt_exporter)
101+
102+## Testing
103+
104+# This directory needs to be create in the charm path prior to testing
105+```
106+mkdir -p report/lint
107+```
108+
109+## Deployment
110+
111+# A typical deployment with nova and libvirt is as follows:
112+# The metrics will be at http://nova-compute:9177
113+```
114+juju deploy nova-compute
115+juju deploy prometheus-libvirt-exporter
116+juju add-relation nova-compute prometheus-libvirt-exporter
117+```
118+
119+# To avail of the metrics in grafana the following steps can be used
120+```
121+juju deploy grafana
122+juju deploy prometheus2
123+juju add-relation prometheus-libvirt-exporter:scrape prometheus2:target
124+juju add-relation prometheus-libvirt-exporter:dashboards grafana:dashboards
125+```
126+
127+# To setup reporting with nagios
128+```
129+juju deploy nrpe
130+juju add-relation nova-compute nrpe
131+juju add-relation prometheus-libvirt-exporter:nrpe-external-master nrpe:nrpe-external-master
132+```
133+
134+# Change or update dashboards
135+```
136+# The exporter is distributed with a standard dashboard
137+# To provide your own dashboards, create a zip file and attach it as a resource
138+zip grafana-dashboards.zip libvirt-simple.json libvirtadvanced.json
139+juju attach-resource prometheus-libvirt-exporter dashboards=./grafana-dashboards.zip
140+```
141+
142+# Contact Information
143+- Charm bugs: https://bugs.launchpad.net/charm-prometheus-libvirt-exporter
144diff --git a/config.yaml b/config.yaml
145index 678e69b..b253652 100644
146--- a/config.yaml
147+++ b/config.yaml
148@@ -5,3 +5,17 @@ options:
149 description: |
150 If install_method is set to "snap" this option controlls channel name.
151 Supported values are: "stable", "candidate", "beta" and "edge"
152+ nagios_context:
153+ default: "juju"
154+ type: string
155+ description: |
156+ A string that will be prepended to instance name to set the host name
157+ in nagios. So for instance the hostname would be something like:
158+ juju-myservice-0
159+ If you're running multiple environments with the same services in them
160+ this allows you to differentiate between them.
161+ nagios_servicegroups:
162+ default: "juju"
163+ type: string
164+ description: |
165+ Comma separated list of nagios servicegroups
166\ No newline at end of file
167diff --git a/files/grafana-dashboards/libvirt.json b/files/grafana-dashboards/libvirt.json
168new file mode 100644
169index 0000000..6954193
170--- /dev/null
171+++ b/files/grafana-dashboards/libvirt.json
172@@ -0,0 +1,1374 @@
173+{
174+ "dashboard": {
175+ "annotations": {
176+ "list": [
177+ {
178+ "builtIn": 1,
179+ "datasource": "-- Grafana --",
180+ "enable": true,
181+ "hide": true,
182+ "iconColor": "rgba(0, 211, 255, 1)",
183+ "name": "Annotations & Alerts",
184+ "type": "dashboard"
185+ }
186+ ]
187+ },
188+ "description": "Libvirt Cluster overview.",
189+ "editable": true,
190+ "gnetId": null,
191+ "graphTooltip": 0,
192+ "id": null,
193+ "iteration": 1584700907234,
194+ "links": [],
195+ "panels": [
196+ {
197+ "collapsed": false,
198+ "datasource": null,
199+ "gridPos": {
200+ "h": 1,
201+ "w": 24,
202+ "x": 0,
203+ "y": 0
204+ },
205+ "id": 24,
206+ "panels": [],
207+ "title": "Info",
208+ "type": "row"
209+ },
210+ {
211+ "content": "\n# Important info\nIf you match very high number of hosts you may kill your browser and/or prometheus. See \"Matching targets\"\n\n\n",
212+ "datasource": null,
213+ "gridPos": {
214+ "h": 4,
215+ "w": 12,
216+ "x": 0,
217+ "y": 1
218+ },
219+ "id": 20,
220+ "mode": "markdown",
221+ "timeFrom": null,
222+ "timeShift": null,
223+ "title": "Panel Title",
224+ "type": "text"
225+ },
226+ {
227+ "cacheTimeout": null,
228+ "colorBackground": true,
229+ "colorValue": false,
230+ "colors": [
231+ "#73BF69",
232+ "rgba(237, 129, 40, 0.89)",
233+ "#d44a3a"
234+ ],
235+ "datasource": "prometheus - Juju generated source",
236+ "format": "none",
237+ "gauge": {
238+ "maxValue": 100,
239+ "minValue": 0,
240+ "show": false,
241+ "thresholdLabels": false,
242+ "thresholdMarkers": true
243+ },
244+ "gridPos": {
245+ "h": 4,
246+ "w": 12,
247+ "x": 12,
248+ "y": 1
249+ },
250+ "id": 22,
251+ "interval": null,
252+ "links": [],
253+ "mappingType": 1,
254+ "mappingTypes": [
255+ {
256+ "name": "value to text",
257+ "value": 1
258+ },
259+ {
260+ "name": "range to text",
261+ "value": 2
262+ }
263+ ],
264+ "maxDataPoints": 100,
265+ "nullPointMode": "connected",
266+ "nullText": null,
267+ "postfix": "",
268+ "postfixFontSize": "50%",
269+ "prefix": "",
270+ "prefixFontSize": "50%",
271+ "rangeMaps": [
272+ {
273+ "from": "null",
274+ "text": "N/A",
275+ "to": "null"
276+ }
277+ ],
278+ "sparkline": {
279+ "fillColor": "rgba(31, 118, 189, 0.18)",
280+ "full": false,
281+ "lineColor": "rgb(31, 120, 193)",
282+ "show": false,
283+ "ymax": null,
284+ "ymin": null
285+ },
286+ "tableColumn": "",
287+ "targets": [
288+ {
289+ "expr": "count(libvirt_up{instance=~\"$host\"})+count(libvirt_domain_info_virtual_cpus{domain=~\"$domain\"})",
290+ "interval": "",
291+ "legendFormat": "",
292+ "refId": "A"
293+ }
294+ ],
295+ "thresholds": "5,10",
296+ "timeFrom": null,
297+ "timeShift": null,
298+ "title": "Matching targets",
299+ "type": "singlestat",
300+ "valueFontSize": "200%",
301+ "valueMaps": [
302+ {
303+ "op": "=",
304+ "text": "N/A",
305+ "value": "null"
306+ }
307+ ],
308+ "valueName": "current"
309+ },
310+ {
311+ "collapsed": true,
312+ "datasource": null,
313+ "gridPos": {
314+ "h": 1,
315+ "w": 24,
316+ "x": 0,
317+ "y": 5
318+ },
319+ "id": 28,
320+ "panels": [
321+ {
322+ "cacheTimeout": null,
323+ "datasource": "prometheus - Juju generated source",
324+ "gridPos": {
325+ "h": 5,
326+ "w": 12,
327+ "x": 0,
328+ "y": 6
329+ },
330+ "id": 10,
331+ "links": [],
332+ "options": {
333+ "colorMode": "value",
334+ "fieldOptions": {
335+ "calcs": [
336+ "last"
337+ ],
338+ "defaults": {
339+ "mappings": [],
340+ "thresholds": {
341+ "mode": "absolute",
342+ "steps": [
343+ {
344+ "color": "green",
345+ "value": null
346+ },
347+ {
348+ "color": "red",
349+ "value": 80
350+ }
351+ ]
352+ }
353+ },
354+ "overrides": [],
355+ "values": false
356+ },
357+ "graphMode": "area",
358+ "justifyMode": "auto",
359+ "orientation": "auto"
360+ },
361+ "pluginVersion": "6.7.0",
362+ "targets": [
363+ {
364+ "expr": "sum(libvirt_up{instance=~\"$host\"})",
365+ "interval": "",
366+ "legendFormat": "",
367+ "refId": "A"
368+ }
369+ ],
370+ "timeFrom": null,
371+ "timeShift": null,
372+ "title": "Virt Hosts",
373+ "type": "stat"
374+ },
375+ {
376+ "cacheTimeout": null,
377+ "datasource": "prometheus - Juju generated source",
378+ "gridPos": {
379+ "h": 5,
380+ "w": 12,
381+ "x": 12,
382+ "y": 6
383+ },
384+ "id": 8,
385+ "links": [],
386+ "options": {
387+ "colorMode": "value",
388+ "fieldOptions": {
389+ "calcs": [
390+ "mean"
391+ ],
392+ "defaults": {
393+ "mappings": [],
394+ "thresholds": {
395+ "mode": "absolute",
396+ "steps": [
397+ {
398+ "color": "green",
399+ "value": null
400+ },
401+ {
402+ "color": "red",
403+ "value": 80
404+ }
405+ ]
406+ }
407+ },
408+ "overrides": [],
409+ "values": false
410+ },
411+ "graphMode": "area",
412+ "justifyMode": "auto",
413+ "orientation": "auto"
414+ },
415+ "pluginVersion": "6.7.0",
416+ "targets": [
417+ {
418+ "expr": "sum(libvirt_domain_info_virtual_cpus{domain=~\"$domain\"})",
419+ "interval": "",
420+ "legendFormat": "",
421+ "refId": "A"
422+ }
423+ ],
424+ "timeFrom": null,
425+ "timeShift": null,
426+ "title": "Vcpus",
427+ "type": "stat"
428+ }
429+ ],
430+ "title": "Host & CPU",
431+ "type": "row"
432+ },
433+ {
434+ "collapsed": true,
435+ "datasource": null,
436+ "gridPos": {
437+ "h": 1,
438+ "w": 24,
439+ "x": 0,
440+ "y": 6
441+ },
442+ "id": 26,
443+ "panels": [
444+ {
445+ "aliasColors": {},
446+ "bars": true,
447+ "cacheTimeout": null,
448+ "dashLength": 10,
449+ "dashes": false,
450+ "datasource": "prometheus - Juju generated source",
451+ "fill": 1,
452+ "fillGradient": 0,
453+ "gridPos": {
454+ "h": 7,
455+ "w": 12,
456+ "x": 0,
457+ "y": 7
458+ },
459+ "hiddenSeries": false,
460+ "id": 6,
461+ "legend": {
462+ "avg": false,
463+ "current": false,
464+ "max": false,
465+ "min": false,
466+ "show": true,
467+ "total": false,
468+ "values": false
469+ },
470+ "lines": false,
471+ "linewidth": 1,
472+ "links": [],
473+ "nullPointMode": "null",
474+ "options": {
475+ "dataLinks": []
476+ },
477+ "percentage": false,
478+ "pluginVersion": "6.6.2",
479+ "pointradius": 2,
480+ "points": false,
481+ "renderer": "flot",
482+ "seriesOverrides": [],
483+ "spaceLength": 10,
484+ "stack": false,
485+ "steppedLine": false,
486+ "targets": [
487+ {
488+ "expr": "libvirt_domain_info_maximum_memory_bytes{domain=~\"$domain\"}/1024/1024",
489+ "interval": "",
490+ "legendFormat": "{{host}} - {{domain}}",
491+ "refId": "A"
492+ }
493+ ],
494+ "thresholds": [],
495+ "timeFrom": null,
496+ "timeRegions": [],
497+ "timeShift": null,
498+ "title": "Memory Usage",
499+ "tooltip": {
500+ "shared": true,
501+ "sort": 0,
502+ "value_type": "individual"
503+ },
504+ "type": "graph",
505+ "xaxis": {
506+ "buckets": null,
507+ "mode": "time",
508+ "name": null,
509+ "show": true,
510+ "values": []
511+ },
512+ "yaxes": [
513+ {
514+ "format": "short",
515+ "label": null,
516+ "logBase": 1,
517+ "max": null,
518+ "min": null,
519+ "show": true
520+ },
521+ {
522+ "format": "short",
523+ "label": null,
524+ "logBase": 1,
525+ "max": null,
526+ "min": null,
527+ "show": true
528+ }
529+ ],
530+ "yaxis": {
531+ "align": false,
532+ "alignLevel": null
533+ }
534+ },
535+ {
536+ "cacheTimeout": null,
537+ "colorBackground": false,
538+ "colorValue": false,
539+ "colors": [
540+ "#299c46",
541+ "rgba(237, 129, 40, 0.89)",
542+ "#d44a3a"
543+ ],
544+ "datasource": "prometheus - Juju generated source",
545+ "description": "",
546+ "format": "none",
547+ "gauge": {
548+ "maxValue": 100,
549+ "minValue": 0,
550+ "show": false,
551+ "thresholdLabels": false,
552+ "thresholdMarkers": true
553+ },
554+ "gridPos": {
555+ "h": 7,
556+ "w": 12,
557+ "x": 12,
558+ "y": 7
559+ },
560+ "id": 38,
561+ "interval": null,
562+ "links": [],
563+ "mappingType": 1,
564+ "mappingTypes": [
565+ {
566+ "name": "value to text",
567+ "value": 1
568+ },
569+ {
570+ "name": "range to text",
571+ "value": 2
572+ }
573+ ],
574+ "maxDataPoints": 100,
575+ "nullPointMode": "connected",
576+ "nullText": null,
577+ "postfix": "",
578+ "postfixFontSize": "50%",
579+ "prefix": "",
580+ "prefixFontSize": "50%",
581+ "rangeMaps": [
582+ {
583+ "from": "null",
584+ "text": "N/A",
585+ "to": "null"
586+ }
587+ ],
588+ "sparkline": {
589+ "fillColor": "rgba(31, 118, 189, 0.18)",
590+ "full": false,
591+ "lineColor": "rgb(31, 120, 193)",
592+ "show": false,
593+ "ymax": null,
594+ "ymin": null
595+ },
596+ "tableColumn": "",
597+ "targets": [
598+ {
599+ "expr": "sum(libvirt_domain_info_memory_usage_bytes)/1024/1024/1024",
600+ "interval": "",
601+ "legendFormat": "Total Memory Usage",
602+ "refId": "A"
603+ }
604+ ],
605+ "thresholds": "",
606+ "timeFrom": null,
607+ "timeShift": null,
608+ "title": "Total Memory Usage GB",
609+ "type": "singlestat",
610+ "valueFontSize": "200%",
611+ "valueMaps": [
612+ {
613+ "op": "=",
614+ "text": "N/A",
615+ "value": "null"
616+ }
617+ ],
618+ "valueName": "avg"
619+ }
620+ ],
621+ "title": "Memory",
622+ "type": "row"
623+ },
624+ {
625+ "collapsed": true,
626+ "datasource": null,
627+ "gridPos": {
628+ "h": 1,
629+ "w": 24,
630+ "x": 0,
631+ "y": 7
632+ },
633+ "id": 30,
634+ "panels": [
635+ {
636+ "aliasColors": {},
637+ "bars": false,
638+ "cacheTimeout": null,
639+ "dashLength": 10,
640+ "dashes": false,
641+ "datasource": "prometheus - Juju generated source",
642+ "fill": 2,
643+ "fillGradient": 0,
644+ "gridPos": {
645+ "h": 7,
646+ "w": 12,
647+ "x": 0,
648+ "y": 8
649+ },
650+ "hiddenSeries": false,
651+ "id": 12,
652+ "legend": {
653+ "alignAsTable": false,
654+ "avg": false,
655+ "current": false,
656+ "max": false,
657+ "min": false,
658+ "show": true,
659+ "total": false,
660+ "values": false
661+ },
662+ "lines": true,
663+ "linewidth": 1,
664+ "links": [],
665+ "nullPointMode": "null",
666+ "options": {
667+ "dataLinks": []
668+ },
669+ "percentage": false,
670+ "pluginVersion": "6.6.2",
671+ "pointradius": 2,
672+ "points": false,
673+ "renderer": "flot",
674+ "seriesOverrides": [],
675+ "spaceLength": 10,
676+ "stack": true,
677+ "steppedLine": false,
678+ "targets": [
679+ {
680+ "expr": "sum(libvirt_domain_interface_stats_transmit_bytes_total{domain=~\"$domain\"})",
681+ "format": "time_series",
682+ "interval": "",
683+ "intervalFactor": 2,
684+ "legendFormat": "TX",
685+ "refId": "A"
686+ },
687+ {
688+ "expr": "sum(libvirt_domain_interface_stats_receive_bytes_total{domain=~\"$domain\"})",
689+ "interval": "",
690+ "intervalFactor": 2,
691+ "legendFormat": "RX",
692+ "refId": "B"
693+ }
694+ ],
695+ "thresholds": [],
696+ "timeFrom": null,
697+ "timeRegions": [],
698+ "timeShift": null,
699+ "title": "Network RX/TX Throughput",
700+ "tooltip": {
701+ "shared": true,
702+ "sort": 0,
703+ "value_type": "individual"
704+ },
705+ "type": "graph",
706+ "xaxis": {
707+ "buckets": null,
708+ "mode": "time",
709+ "name": null,
710+ "show": true,
711+ "values": []
712+ },
713+ "yaxes": [
714+ {
715+ "format": "short",
716+ "label": "KiloByes",
717+ "logBase": 1,
718+ "max": null,
719+ "min": null,
720+ "show": true
721+ },
722+ {
723+ "format": "short",
724+ "label": "",
725+ "logBase": 1,
726+ "max": null,
727+ "min": null,
728+ "show": true
729+ }
730+ ],
731+ "yaxis": {
732+ "align": false,
733+ "alignLevel": null
734+ }
735+ },
736+ {
737+ "aliasColors": {},
738+ "bars": false,
739+ "dashLength": 10,
740+ "dashes": false,
741+ "datasource": "prometheus - Juju generated source",
742+ "fill": 1,
743+ "fillGradient": 0,
744+ "gridPos": {
745+ "h": 7,
746+ "w": 12,
747+ "x": 12,
748+ "y": 8
749+ },
750+ "hiddenSeries": false,
751+ "id": 16,
752+ "legend": {
753+ "avg": false,
754+ "current": false,
755+ "max": false,
756+ "min": false,
757+ "show": true,
758+ "total": false,
759+ "values": false
760+ },
761+ "lines": true,
762+ "linewidth": 1,
763+ "nullPointMode": "null",
764+ "options": {
765+ "dataLinks": []
766+ },
767+ "percentage": false,
768+ "pointradius": 2,
769+ "points": false,
770+ "renderer": "flot",
771+ "seriesOverrides": [],
772+ "spaceLength": 10,
773+ "stack": false,
774+ "steppedLine": false,
775+ "targets": [
776+ {
777+ "expr": "sum(libvirt_domain_interface_stats_receive_drops_total{domain=~\"$domain\"})",
778+ "interval": "",
779+ "legendFormat": "RX drops",
780+ "refId": "A"
781+ },
782+ {
783+ "expr": "sum(libvirt_domain_interface_stats_receive_errors_total{domain=~\"$domain\"})",
784+ "interval": "",
785+ "legendFormat": "RX errors",
786+ "refId": "B"
787+ }
788+ ],
789+ "thresholds": [],
790+ "timeFrom": null,
791+ "timeRegions": [],
792+ "timeShift": null,
793+ "title": "Network Receive Errors/Drops",
794+ "tooltip": {
795+ "shared": true,
796+ "sort": 0,
797+ "value_type": "individual"
798+ },
799+ "type": "graph",
800+ "xaxis": {
801+ "buckets": null,
802+ "mode": "time",
803+ "name": null,
804+ "show": true,
805+ "values": []
806+ },
807+ "yaxes": [
808+ {
809+ "format": "short",
810+ "label": "Total",
811+ "logBase": 1,
812+ "max": null,
813+ "min": null,
814+ "show": true
815+ },
816+ {
817+ "format": "short",
818+ "label": null,
819+ "logBase": 1,
820+ "max": null,
821+ "min": null,
822+ "show": true
823+ }
824+ ],
825+ "yaxis": {
826+ "align": false,
827+ "alignLevel": null
828+ }
829+ },
830+ {
831+ "columns": [],
832+ "datasource": "prometheus - Juju generated source",
833+ "fontSize": "100%",
834+ "gridPos": {
835+ "h": 7,
836+ "w": 12,
837+ "x": 0,
838+ "y": 15
839+ },
840+ "id": 34,
841+ "pageSize": null,
842+ "showHeader": true,
843+ "sort": {
844+ "col": 0,
845+ "desc": true
846+ },
847+ "styles": [
848+ {
849+ "alias": "",
850+ "align": "auto",
851+ "colorMode": null,
852+ "colors": [
853+ "rgba(245, 54, 54, 0.9)",
854+ "rgba(237, 129, 40, 0.89)",
855+ "rgba(50, 172, 45, 0.97)"
856+ ],
857+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
858+ "decimals": 2,
859+ "mappingType": 1,
860+ "pattern": "Time",
861+ "thresholds": [],
862+ "type": "hidden",
863+ "unit": "short"
864+ },
865+ {
866+ "alias": "Source",
867+ "align": "auto",
868+ "colorMode": null,
869+ "colors": [
870+ "rgba(245, 54, 54, 0.9)",
871+ "rgba(237, 129, 40, 0.89)",
872+ "rgba(50, 172, 45, 0.97)"
873+ ],
874+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
875+ "decimals": 2,
876+ "mappingType": 1,
877+ "pattern": "Metric",
878+ "thresholds": [],
879+ "type": "number",
880+ "unit": "short"
881+ },
882+ {
883+ "alias": "Read bytes total",
884+ "align": "auto",
885+ "colorMode": null,
886+ "colors": [
887+ "rgba(245, 54, 54, 0.9)",
888+ "rgba(237, 129, 40, 0.89)",
889+ "rgba(50, 172, 45, 0.97)"
890+ ],
891+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
892+ "decimals": 2,
893+ "mappingType": 1,
894+ "pattern": "Value",
895+ "thresholds": [],
896+ "type": "number",
897+ "unit": "short"
898+ },
899+ {
900+ "alias": "",
901+ "align": "right",
902+ "colorMode": null,
903+ "colors": [
904+ "rgba(245, 54, 54, 0.9)",
905+ "rgba(237, 129, 40, 0.89)",
906+ "rgba(50, 172, 45, 0.97)"
907+ ],
908+ "decimals": 2,
909+ "pattern": "/.*/",
910+ "thresholds": [],
911+ "type": "number",
912+ "unit": "short"
913+ }
914+ ],
915+ "targets": [
916+ {
917+ "expr": "libvirt_domain_block_stats_read_bytes_total{instance=~\"$host\"}",
918+ "interval": "",
919+ "legendFormat": "{{instance}} - {{domain}}",
920+ "refId": "A"
921+ }
922+ ],
923+ "timeFrom": null,
924+ "timeShift": null,
925+ "title": "Instance Read Bytes Total",
926+ "transform": "timeseries_to_rows",
927+ "type": "table"
928+ },
929+ {
930+ "aliasColors": {},
931+ "bars": false,
932+ "dashLength": 10,
933+ "dashes": false,
934+ "datasource": "prometheus - Juju generated source",
935+ "fill": 1,
936+ "fillGradient": 0,
937+ "gridPos": {
938+ "h": 7,
939+ "w": 12,
940+ "x": 12,
941+ "y": 15
942+ },
943+ "hiddenSeries": false,
944+ "id": 14,
945+ "legend": {
946+ "avg": false,
947+ "current": false,
948+ "max": false,
949+ "min": false,
950+ "show": true,
951+ "total": false,
952+ "values": false
953+ },
954+ "lines": true,
955+ "linewidth": 1,
956+ "nullPointMode": "null",
957+ "options": {
958+ "dataLinks": []
959+ },
960+ "percentage": false,
961+ "pointradius": 2,
962+ "points": false,
963+ "renderer": "flot",
964+ "seriesOverrides": [],
965+ "spaceLength": 10,
966+ "stack": false,
967+ "steppedLine": false,
968+ "targets": [
969+ {
970+ "expr": "sum(libvirt_domain_interface_stats_transmit_drops_total{domain=~\"$domain\"})",
971+ "interval": "",
972+ "legendFormat": "TX drops",
973+ "refId": "A"
974+ },
975+ {
976+ "expr": "sum(libvirt_domain_interface_stats_transmit_errors_total{domain=~\"$domain\"})",
977+ "interval": "",
978+ "legendFormat": "RX drops",
979+ "refId": "B"
980+ }
981+ ],
982+ "thresholds": [],
983+ "timeFrom": null,
984+ "timeRegions": [],
985+ "timeShift": null,
986+ "title": "Network Transmit Errors/Drops",
987+ "tooltip": {
988+ "shared": true,
989+ "sort": 0,
990+ "value_type": "individual"
991+ },
992+ "type": "graph",
993+ "xaxis": {
994+ "buckets": null,
995+ "mode": "time",
996+ "name": null,
997+ "show": true,
998+ "values": []
999+ },
1000+ "yaxes": [
1001+ {
1002+ "format": "short",
1003+ "label": "Total",
1004+ "logBase": 1,
1005+ "max": null,
1006+ "min": null,
1007+ "show": true
1008+ },
1009+ {
1010+ "format": "short",
1011+ "label": null,
1012+ "logBase": 1,
1013+ "max": null,
1014+ "min": null,
1015+ "show": true
1016+ }
1017+ ],
1018+ "yaxis": {
1019+ "align": false,
1020+ "alignLevel": null
1021+ }
1022+ },
1023+ {
1024+ "columns": [],
1025+ "datasource": "prometheus - Juju generated source",
1026+ "fontSize": "100%",
1027+ "gridPos": {
1028+ "h": 7,
1029+ "w": 12,
1030+ "x": 0,
1031+ "y": 22
1032+ },
1033+ "id": 36,
1034+ "pageSize": null,
1035+ "showHeader": true,
1036+ "sort": {
1037+ "col": 0,
1038+ "desc": true
1039+ },
1040+ "styles": [
1041+ {
1042+ "alias": "",
1043+ "align": "auto",
1044+ "colorMode": null,
1045+ "colors": [
1046+ "rgba(245, 54, 54, 0.9)",
1047+ "rgba(237, 129, 40, 0.89)",
1048+ "rgba(50, 172, 45, 0.97)"
1049+ ],
1050+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
1051+ "decimals": 2,
1052+ "mappingType": 1,
1053+ "pattern": "Time",
1054+ "thresholds": [],
1055+ "type": "hidden",
1056+ "unit": "short"
1057+ },
1058+ {
1059+ "alias": "Source",
1060+ "align": "auto",
1061+ "colorMode": null,
1062+ "colors": [
1063+ "rgba(245, 54, 54, 0.9)",
1064+ "rgba(237, 129, 40, 0.89)",
1065+ "rgba(50, 172, 45, 0.97)"
1066+ ],
1067+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
1068+ "decimals": 2,
1069+ "mappingType": 1,
1070+ "pattern": "Metric",
1071+ "thresholds": [],
1072+ "type": "number",
1073+ "unit": "short"
1074+ },
1075+ {
1076+ "alias": "Write bytes total",
1077+ "align": "auto",
1078+ "colorMode": null,
1079+ "colors": [
1080+ "rgba(245, 54, 54, 0.9)",
1081+ "rgba(237, 129, 40, 0.89)",
1082+ "rgba(50, 172, 45, 0.97)"
1083+ ],
1084+ "dateFormat": "YYYY-MM-DD HH:mm:ss",
1085+ "decimals": 2,
1086+ "mappingType": 1,
1087+ "pattern": "Value",
1088+ "thresholds": [],
1089+ "type": "number",
1090+ "unit": "short"
1091+ },
1092+ {
1093+ "alias": "",
1094+ "align": "right",
1095+ "colorMode": null,
1096+ "colors": [
1097+ "rgba(245, 54, 54, 0.9)",
1098+ "rgba(237, 129, 40, 0.89)",
1099+ "rgba(50, 172, 45, 0.97)"
1100+ ],
1101+ "decimals": 2,
1102+ "pattern": "/.*/",
1103+ "thresholds": [],
1104+ "type": "number",
1105+ "unit": "short"
1106+ }
1107+ ],
1108+ "targets": [
1109+ {
1110+ "expr": "libvirt_domain_block_stats_write_bytes_total{instance=~\"$host\"}",
1111+ "interval": "",
1112+ "legendFormat": "{{instance}} - {{domain}}",
1113+ "refId": "A"
1114+ }
1115+ ],
1116+ "timeFrom": null,
1117+ "timeShift": null,
1118+ "title": "Instance Write Bytes Total",
1119+ "transform": "timeseries_to_rows",
1120+ "type": "table"
1121+ }
1122+ ],
1123+ "title": "Network",
1124+ "type": "row"
1125+ },
1126+ {
1127+ "collapsed": true,
1128+ "datasource": null,
1129+ "gridPos": {
1130+ "h": 1,
1131+ "w": 24,
1132+ "x": 0,
1133+ "y": 8
1134+ },
1135+ "id": 32,
1136+ "panels": [
1137+ {
1138+ "aliasColors": {},
1139+ "bars": false,
1140+ "dashLength": 10,
1141+ "dashes": false,
1142+ "datasource": "prometheus - Juju generated source",
1143+ "fill": 5,
1144+ "fillGradient": 5,
1145+ "gridPos": {
1146+ "h": 7,
1147+ "w": 12,
1148+ "x": 0,
1149+ "y": 9
1150+ },
1151+ "hiddenSeries": false,
1152+ "id": 4,
1153+ "legend": {
1154+ "avg": false,
1155+ "current": false,
1156+ "max": false,
1157+ "min": false,
1158+ "show": true,
1159+ "total": false,
1160+ "values": false
1161+ },
1162+ "lines": true,
1163+ "linewidth": 2,
1164+ "nullPointMode": "null",
1165+ "options": {
1166+ "dataLinks": []
1167+ },
1168+ "percentage": false,
1169+ "pluginVersion": "6.7.0",
1170+ "pointradius": 2,
1171+ "points": false,
1172+ "renderer": "flot",
1173+ "seriesOverrides": [],
1174+ "spaceLength": 10,
1175+ "stack": false,
1176+ "steppedLine": false,
1177+ "targets": [
1178+ {
1179+ "expr": "libvirt_domain_block_stats_write_bytes_total{domain=~\"$domain\"}/1024/1024",
1180+ "interval": "",
1181+ "legendFormat": "{{instance}} - {{domain}}",
1182+ "refId": "A"
1183+ }
1184+ ],
1185+ "thresholds": [],
1186+ "timeFrom": null,
1187+ "timeRegions": [],
1188+ "timeShift": null,
1189+ "title": "Disk Write",
1190+ "tooltip": {
1191+ "shared": true,
1192+ "sort": 0,
1193+ "value_type": "individual"
1194+ },
1195+ "type": "graph",
1196+ "xaxis": {
1197+ "buckets": null,
1198+ "mode": "time",
1199+ "name": null,
1200+ "show": true,
1201+ "values": []
1202+ },
1203+ "yaxes": [
1204+ {
1205+ "format": "short",
1206+ "label": null,
1207+ "logBase": 1,
1208+ "max": null,
1209+ "min": null,
1210+ "show": true
1211+ },
1212+ {
1213+ "format": "short",
1214+ "label": null,
1215+ "logBase": 1,
1216+ "max": null,
1217+ "min": null,
1218+ "show": true
1219+ }
1220+ ],
1221+ "yaxis": {
1222+ "align": false,
1223+ "alignLevel": null
1224+ }
1225+ },
1226+ {
1227+ "aliasColors": {},
1228+ "bars": false,
1229+ "dashLength": 10,
1230+ "dashes": false,
1231+ "datasource": "prometheus - Juju generated source",
1232+ "fill": 5,
1233+ "fillGradient": 5,
1234+ "gridPos": {
1235+ "h": 7,
1236+ "w": 12,
1237+ "x": 12,
1238+ "y": 9
1239+ },
1240+ "hiddenSeries": false,
1241+ "id": 40,
1242+ "legend": {
1243+ "avg": false,
1244+ "current": false,
1245+ "max": false,
1246+ "min": false,
1247+ "show": true,
1248+ "total": false,
1249+ "values": false
1250+ },
1251+ "lines": true,
1252+ "linewidth": 2,
1253+ "nullPointMode": "null",
1254+ "options": {
1255+ "dataLinks": []
1256+ },
1257+ "percentage": false,
1258+ "pluginVersion": "6.7.0",
1259+ "pointradius": 2,
1260+ "points": false,
1261+ "renderer": "flot",
1262+ "seriesOverrides": [],
1263+ "spaceLength": 10,
1264+ "stack": false,
1265+ "steppedLine": false,
1266+ "targets": [
1267+ {
1268+ "expr": "libvirt_domain_block_stats_read_bytes_total{domain=~\"$domain\"}/1024/1024",
1269+ "interval": "",
1270+ "legendFormat": "{{instance}} - {{domain}}",
1271+ "refId": "A"
1272+ }
1273+ ],
1274+ "thresholds": [],
1275+ "timeFrom": null,
1276+ "timeRegions": [],
1277+ "timeShift": null,
1278+ "title": "Disk Read",
1279+ "tooltip": {
1280+ "shared": true,
1281+ "sort": 0,
1282+ "value_type": "individual"
1283+ },
1284+ "type": "graph",
1285+ "xaxis": {
1286+ "buckets": null,
1287+ "mode": "time",
1288+ "name": null,
1289+ "show": true,
1290+ "values": []
1291+ },
1292+ "yaxes": [
1293+ {
1294+ "format": "short",
1295+ "label": null,
1296+ "logBase": 1,
1297+ "max": null,
1298+ "min": null,
1299+ "show": true
1300+ },
1301+ {
1302+ "format": "short",
1303+ "label": null,
1304+ "logBase": 1,
1305+ "max": null,
1306+ "min": null,
1307+ "show": true
1308+ }
1309+ ],
1310+ "yaxis": {
1311+ "align": false,
1312+ "alignLevel": null
1313+ }
1314+ },
1315+ {
1316+ "cacheTimeout": null,
1317+ "datasource": "prometheus - Juju generated source",
1318+ "gridPos": {
1319+ "h": 7,
1320+ "w": 12,
1321+ "x": 0,
1322+ "y": 16
1323+ },
1324+ "id": 44,
1325+ "links": [],
1326+ "options": {
1327+ "fieldOptions": {
1328+ "calcs": [
1329+ "last"
1330+ ],
1331+ "defaults": {
1332+ "mappings": [
1333+ {
1334+ "id": 0,
1335+ "op": "=",
1336+ "text": "N/A",
1337+ "type": 1,
1338+ "value": "null"
1339+ }
1340+ ],
1341+ "nullValueMode": "connected",
1342+ "thresholds": {
1343+ "mode": "absolute",
1344+ "steps": [
1345+ {
1346+ "color": "green",
1347+ "value": null
1348+ },
1349+ {
1350+ "color": "red",
1351+ "value": 80
1352+ }
1353+ ]
1354+ },
1355+ "title": "MB / sec",
1356+ "unit": "none"
1357+ },
1358+ "overrides": [],
1359+ "values": false
1360+ },
1361+ "orientation": "horizontal",
1362+ "showThresholdLabels": false,
1363+ "showThresholdMarkers": true
1364+ },
1365+ "pluginVersion": "6.7.0",
1366+ "targets": [
1367+ {
1368+ "expr": "sum(libvirt_domain_block_stats_write_bytes_total)/1024/1024",
1369+ "format": "heatmap",
1370+ "hide": false,
1371+ "instant": false,
1372+ "interval": "",
1373+ "intervalFactor": 1,
1374+ "legendFormat": "",
1375+ "refId": "A"
1376+ }
1377+ ],
1378+ "timeFrom": null,
1379+ "timeShift": null,
1380+ "title": "Disk Cumulative Write",
1381+ "type": "gauge"
1382+ },
1383+ {
1384+ "cacheTimeout": null,
1385+ "datasource": "prometheus - Juju generated source",
1386+ "gridPos": {
1387+ "h": 7,
1388+ "w": 12,
1389+ "x": 12,
1390+ "y": 16
1391+ },
1392+ "id": 2,
1393+ "links": [],
1394+ "options": {
1395+ "fieldOptions": {
1396+ "calcs": [
1397+ "mean"
1398+ ],
1399+ "defaults": {
1400+ "mappings": [
1401+ {
1402+ "id": 0,
1403+ "op": "=",
1404+ "text": "N/A",
1405+ "type": 1,
1406+ "value": "null"
1407+ }
1408+ ],
1409+ "nullValueMode": "connected",
1410+ "thresholds": {
1411+ "mode": "absolute",
1412+ "steps": [
1413+ {
1414+ "color": "green",
1415+ "value": null
1416+ },
1417+ {
1418+ "color": "red",
1419+ "value": 80
1420+ }
1421+ ]
1422+ },
1423+ "title": "MB",
1424+ "unit": "none"
1425+ },
1426+ "overrides": [],
1427+ "values": false
1428+ },
1429+ "orientation": "horizontal",
1430+ "showThresholdLabels": false,
1431+ "showThresholdMarkers": true
1432+ },
1433+ "pluginVersion": "6.7.0",
1434+ "targets": [
1435+ {
1436+ "expr": "sum(libvirt_domain_block_stats_read_bytes_total)/1024/1024",
1437+ "format": "heatmap",
1438+ "hide": false,
1439+ "instant": false,
1440+ "intervalFactor": 1,
1441+ "legendFormat": "Read {{instance}}",
1442+ "refId": "A"
1443+ }
1444+ ],
1445+ "timeFrom": null,
1446+ "timeShift": null,
1447+ "title": "Disk Cumulative Read",
1448+ "type": "gauge"
1449+ }
1450+ ],
1451+ "title": "Disk",
1452+ "type": "row"
1453+ }
1454+ ],
1455+ "refresh": "5s",
1456+ "schemaVersion": 22,
1457+ "style": "dark",
1458+ "tags": [],
1459+ "templating": {
1460+ "list": [
1461+ {
1462+ "allValue": null,
1463+ "current": {
1464+ "text": "All",
1465+ "value": [
1466+ "$__all"
1467+ ]
1468+ },
1469+ "datasource": "prometheus - Juju generated source",
1470+ "definition": "libvirt_up",
1471+ "hide": 0,
1472+ "includeAll": true,
1473+ "index": -1,
1474+ "label": "libvirt daemon",
1475+ "multi": true,
1476+ "name": "host",
1477+ "options": [],
1478+ "query": "libvirt_up",
1479+ "refresh": 1,
1480+ "regex": ".*instance=\"([^\"]+).*?\".*$",
1481+ "skipUrlSync": false,
1482+ "sort": 0,
1483+ "tagValuesQuery": "",
1484+ "tags": [],
1485+ "tagsQuery": "",
1486+ "type": "query",
1487+ "useTags": false
1488+ },
1489+ {
1490+ "allValue": null,
1491+ "current": {
1492+ "tags": [],
1493+ "text": "All",
1494+ "value": [
1495+ "$__all"
1496+ ]
1497+ },
1498+ "datasource": "prometheus - Juju generated source",
1499+ "definition": "libvirt_domain_info_virtual_cpus",
1500+ "hide": 0,
1501+ "includeAll": true,
1502+ "index": -1,
1503+ "label": null,
1504+ "multi": true,
1505+ "name": "domain",
1506+ "options": [],
1507+ "query": "libvirt_domain_info_virtual_cpus",
1508+ "refresh": 1,
1509+ "regex": ".*domain=\"([^\"]+)\".*$",
1510+ "skipUrlSync": false,
1511+ "sort": 1,
1512+ "tagValuesQuery": "",
1513+ "tags": [],
1514+ "tagsQuery": "",
1515+ "type": "query",
1516+ "useTags": false
1517+ }
1518+ ]
1519+ },
1520+ "time": {
1521+ "from": "now-1h",
1522+ "to": "now"
1523+ },
1524+ "timepicker": {
1525+ "refresh_intervals": [
1526+ "5s",
1527+ "10s",
1528+ "30s",
1529+ "1m",
1530+ "5m",
1531+ "15m",
1532+ "30m",
1533+ "1h",
1534+ "2h",
1535+ "1d"
1536+ ]
1537+ },
1538+ "timezone": "",
1539+ "title": "libvirt",
1540+ "uid": "u_Zk_wuZk",
1541+ "variables": {
1542+ "list": []
1543+ },
1544+ "version": 288
1545+ }
1546+}
1547diff --git a/icon.svg b/icon.svg
1548new file mode 100644
1549index 0000000..96a5d0c
1550--- /dev/null
1551+++ b/icon.svg
1552@@ -0,0 +1,279 @@
1553+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
1554+<!-- Created with Inkscape (http://www.inkscape.org/) -->
1555+
1556+<svg
1557+ xmlns:dc="http://purl.org/dc/elements/1.1/"
1558+ xmlns:cc="http://creativecommons.org/ns#"
1559+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
1560+ xmlns:svg="http://www.w3.org/2000/svg"
1561+ xmlns="http://www.w3.org/2000/svg"
1562+ xmlns:xlink="http://www.w3.org/1999/xlink"
1563+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
1564+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
1565+ width="96"
1566+ height="96"
1567+ id="svg6517"
1568+ version="1.1"
1569+ inkscape:version="0.48+devel r12274"
1570+ sodipodi:docname="Juju_charm_icon_template.svg">
1571+ <defs
1572+ id="defs6519">
1573+ <linearGradient
1574+ inkscape:collect="always"
1575+ xlink:href="#Background"
1576+ id="linearGradient6461"
1577+ gradientUnits="userSpaceOnUse"
1578+ x1="0"
1579+ y1="970.29498"
1580+ x2="144"
1581+ y2="970.29498"
1582+ gradientTransform="matrix(0,-0.66666669,0.6660448,0,-866.25992,731.29077)" />
1583+ <linearGradient
1584+ id="Background">
1585+ <stop
1586+ id="stop4178"
1587+ offset="0"
1588+ style="stop-color:#b8b8b8;stop-opacity:1" />
1589+ <stop
1590+ id="stop4180"
1591+ offset="1"
1592+ style="stop-color:#c9c9c9;stop-opacity:1" />
1593+ </linearGradient>
1594+ <filter
1595+ style="color-interpolation-filters:sRGB;"
1596+ inkscape:label="Inner Shadow"
1597+ id="filter1121">
1598+ <feFlood
1599+ flood-opacity="0.59999999999999998"
1600+ flood-color="rgb(0,0,0)"
1601+ result="flood"
1602+ id="feFlood1123" />
1603+ <feComposite
1604+ in="flood"
1605+ in2="SourceGraphic"
1606+ operator="out"
1607+ result="composite1"
1608+ id="feComposite1125" />
1609+ <feGaussianBlur
1610+ in="composite1"
1611+ stdDeviation="1"
1612+ result="blur"
1613+ id="feGaussianBlur1127" />
1614+ <feOffset
1615+ dx="0"
1616+ dy="2"
1617+ result="offset"
1618+ id="feOffset1129" />
1619+ <feComposite
1620+ in="offset"
1621+ in2="SourceGraphic"
1622+ operator="atop"
1623+ result="composite2"
1624+ id="feComposite1131" />
1625+ </filter>
1626+ <filter
1627+ style="color-interpolation-filters:sRGB;"
1628+ inkscape:label="Drop Shadow"
1629+ id="filter950">
1630+ <feFlood
1631+ flood-opacity="0.25"
1632+ flood-color="rgb(0,0,0)"
1633+ result="flood"
1634+ id="feFlood952" />
1635+ <feComposite
1636+ in="flood"
1637+ in2="SourceGraphic"
1638+ operator="in"
1639+ result="composite1"
1640+ id="feComposite954" />
1641+ <feGaussianBlur
1642+ in="composite1"
1643+ stdDeviation="1"
1644+ result="blur"
1645+ id="feGaussianBlur956" />
1646+ <feOffset
1647+ dx="0"
1648+ dy="1"
1649+ result="offset"
1650+ id="feOffset958" />
1651+ <feComposite
1652+ in="SourceGraphic"
1653+ in2="offset"
1654+ operator="over"
1655+ result="composite2"
1656+ id="feComposite960" />
1657+ </filter>
1658+ <clipPath
1659+ clipPathUnits="userSpaceOnUse"
1660+ id="clipPath873">
1661+ <g
1662+ transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
1663+ id="g875"
1664+ inkscape:label="Layer 1"
1665+ style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
1666+ <path
1667+ style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
1668+ d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 -46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"
1669+ id="path877"
1670+ inkscape:connector-curvature="0"
1671+ sodipodi:nodetypes="sssssssss" />
1672+ </g>
1673+ </clipPath>
1674+ <filter
1675+ inkscape:collect="always"
1676+ id="filter891"
1677+ inkscape:label="Badge Shadow">
1678+ <feGaussianBlur
1679+ inkscape:collect="always"
1680+ stdDeviation="0.71999962"
1681+ id="feGaussianBlur893" />
1682+ </filter>
1683+ </defs>
1684+ <sodipodi:namedview
1685+ id="base"
1686+ pagecolor="#ffffff"
1687+ bordercolor="#666666"
1688+ borderopacity="1.0"
1689+ inkscape:pageopacity="0.0"
1690+ inkscape:pageshadow="2"
1691+ inkscape:zoom="4.0745362"
1692+ inkscape:cx="18.514671"
1693+ inkscape:cy="49.018169"
1694+ inkscape:document-units="px"
1695+ inkscape:current-layer="layer1"
1696+ showgrid="true"
1697+ fit-margin-top="0"
1698+ fit-margin-left="0"
1699+ fit-margin-right="0"
1700+ fit-margin-bottom="0"
1701+ inkscape:window-width="1920"
1702+ inkscape:window-height="1029"
1703+ inkscape:window-x="0"
1704+ inkscape:window-y="24"
1705+ inkscape:window-maximized="1"
1706+ showborder="true"
1707+ showguides="true"
1708+ inkscape:guide-bbox="true"
1709+ inkscape:showpageshadow="false">
1710+ <inkscape:grid
1711+ type="xygrid"
1712+ id="grid821" />
1713+ <sodipodi:guide
1714+ orientation="1,0"
1715+ position="16,48"
1716+ id="guide823" />
1717+ <sodipodi:guide
1718+ orientation="0,1"
1719+ position="64,80"
1720+ id="guide825" />
1721+ <sodipodi:guide
1722+ orientation="1,0"
1723+ position="80,40"
1724+ id="guide827" />
1725+ <sodipodi:guide
1726+ orientation="0,1"
1727+ position="64,16"
1728+ id="guide829" />
1729+ </sodipodi:namedview>
1730+ <metadata
1731+ id="metadata6522">
1732+ <rdf:RDF>
1733+ <cc:Work
1734+ rdf:about="">
1735+ <dc:format>image/svg+xml</dc:format>
1736+ <dc:type
1737+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
1738+ <dc:title></dc:title>
1739+ </cc:Work>
1740+ </rdf:RDF>
1741+ </metadata>
1742+ <g
1743+ inkscape:label="BACKGROUND"
1744+ inkscape:groupmode="layer"
1745+ id="layer1"
1746+ transform="translate(268,-635.29076)"
1747+ style="display:inline">
1748+ <path
1749+ style="fill:url(#linearGradient6461);fill-opacity:1;stroke:none;display:inline;filter:url(#filter1121)"
1750+ d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 C -264.11215,731.29077 -268,727.39888 -268,700.15563 Z"
1751+ id="path6455"
1752+ inkscape:connector-curvature="0"
1753+ sodipodi:nodetypes="sssssssss" />
1754+ </g>
1755+ <g
1756+ inkscape:groupmode="layer"
1757+ id="layer3"
1758+ inkscape:label="PLACE YOUR PICTOGRAM HERE"
1759+ style="display:inline" />
1760+ <g
1761+ inkscape:groupmode="layer"
1762+ id="layer2"
1763+ inkscape:label="BADGE"
1764+ style="display:none"
1765+ sodipodi:insensitive="true">
1766+ <g
1767+ style="display:inline"
1768+ transform="translate(-340.00001,-581)"
1769+ id="g4394"
1770+ clip-path="none">
1771+ <g
1772+ id="g855">
1773+ <g
1774+ inkscape:groupmode="maskhelper"
1775+ id="g870"
1776+ clip-path="url(#clipPath873)"
1777+ style="opacity:0.6;filter:url(#filter891)">
1778+ <path
1779+ transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
1780+ d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
1781+ sodipodi:ry="12"
1782+ sodipodi:rx="12"
1783+ sodipodi:cy="552.36218"
1784+ sodipodi:cx="252"
1785+ id="path844"
1786+ style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1787+ sodipodi:type="arc" />
1788+ </g>
1789+ <g
1790+ id="g862">
1791+ <path
1792+ sodipodi:type="arc"
1793+ style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1794+ id="path4398"
1795+ sodipodi:cx="252"
1796+ sodipodi:cy="552.36218"
1797+ sodipodi:rx="12"
1798+ sodipodi:ry="12"
1799+ d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
1800+ transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" />
1801+ <path
1802+ transform="matrix(1.25,0,0,1.25,33,-100.45273)"
1803+ d="m 264,552.36218 a 12,12 0 1 1 -24,0 A 12,12 0 1 1 264,552.36218 Z"
1804+ sodipodi:ry="12"
1805+ sodipodi:rx="12"
1806+ sodipodi:cy="552.36218"
1807+ sodipodi:cx="252"
1808+ id="path4400"
1809+ style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1810+ sodipodi:type="arc" />
1811+ <path
1812+ sodipodi:type="star"
1813+ style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
1814+ id="path4459"
1815+ sodipodi:sides="5"
1816+ sodipodi:cx="666.19574"
1817+ sodipodi:cy="589.50385"
1818+ sodipodi:r1="7.2431178"
1819+ sodipodi:r2="4.3458705"
1820+ sodipodi:arg1="1.0471976"
1821+ sodipodi:arg2="1.6755161"
1822+ inkscape:flatsided="false"
1823+ inkscape:rounded="0.1"
1824+ inkscape:randomized="0"
1825+ d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 -4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 -0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 -2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 C 669.9821,591.68426 670.20862,595.55064 669.8173,595.77657 Z"
1826+ transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)" />
1827+ </g>
1828+ </g>
1829+ </g>
1830+ </g>
1831+</svg>
1832diff --git a/interfaces/.empty b/interfaces/.empty
1833new file mode 100644
1834index 0000000..e69de29
1835--- /dev/null
1836+++ b/interfaces/.empty
1837diff --git a/layer.yaml b/layer.yaml
1838index b289219..8617d82 100644
1839--- a/layer.yaml
1840+++ b/layer.yaml
1841@@ -1,6 +1,13 @@
1842-includes: ['layer:basic', 'interface:http', 'layer:snap']
1843+includes:
1844+ - layer:basic
1845+ - interface:http
1846+ - layer:snap
1847+ - interface:juju-info
1848+ - interface:nrpe-external-master
1849+ - interface:grafana-dashboard
1850+ - layer:leadership
1851 ignore: ['.*.swp' ]
1852 options:
1853- basic:
1854- use_venv: true
1855+ basic:
1856+ use_venv: true
1857 repo: https://git.launchpad.net/prometheus-libvirt-exporter-charm
1858diff --git a/layers/.empty b/layers/.empty
1859new file mode 100644
1860index 0000000..e69de29
1861--- /dev/null
1862+++ b/layers/.empty
1863diff --git a/metadata.yaml b/metadata.yaml
1864index 1d2bfb4..3c8e44b 100644
1865--- a/metadata.yaml
1866+++ b/metadata.yaml
1867@@ -8,6 +8,7 @@ description: |
1868 series:
1869 - xenial
1870 - bionic
1871+ - focal
1872 tags:
1873 - monitoring
1874 subordinate: true
1875@@ -18,8 +19,19 @@ requires:
1876 provides:
1877 scrape:
1878 interface: http
1879+ nrpe-external-master:
1880+ interface: nrpe-external-master
1881+ scope: container
1882+ dashboards:
1883+ interface: grafana-dashboard
1884 resources:
1885 prometheus-libvirt-exporter:
1886 type: file
1887 filename: prometheus-libvirt-exporter.snap
1888 description: Prometheus-libvirt-exporter snap
1889+ dashboards:
1890+ type: file
1891+ filename: grafana-dashboards.zip
1892+ description:
1893+ By default the charm is distributed with a dashboard that gets pushed to grafana.
1894+ You can override this dashboard with your own
1895diff --git a/reactive/libvirt-exporter.py b/reactive/libvirt-exporter.py
1896deleted file mode 100644
1897index 8727119..0000000
1898--- a/reactive/libvirt-exporter.py
1899+++ /dev/null
1900@@ -1,46 +0,0 @@
1901-from charmhelpers.core import hookenv, host
1902-from charms.reactive import (
1903- when, when_not, set_state, remove_state
1904-)
1905-from charms.layer import snap
1906-import subprocess
1907-
1908-
1909-SNAP_NAME = 'prometheus-libvirt-exporter'
1910-SVC_NAME = 'snap.prometheus-libvirt-exporter.daemon'
1911-PORT_NUMBER = '9177'
1912-
1913-
1914-@when_not('libvirt-exporter.installed')
1915-def install_packages():
1916- hookenv.status_set('maintenance', 'Installing software')
1917- config = hookenv.config()
1918- channel = config.get('snap_channel', 'stable')
1919- snap.install(SNAP_NAME, channel=channel, force_dangerous=False)
1920- subprocess.check_call(['snap', 'connect', 'prometheus-libvirt-exporter:libvirt'])
1921- hookenv.status_set('active', 'Exporter installed and connected to libvirt slot')
1922- hookenv.open_port(PORT_NUMBER)
1923- set_state('libvirt-exporter.installed')
1924-
1925-
1926-@when('libvirt-exporter.installed')
1927-def check_status():
1928- config = hookenv.config()
1929- if config.changed('snap_channel'):
1930- remove_state('libvirt-exporter.installed')
1931- return
1932- if not host.service_running(SVC_NAME):
1933- hookenv.status_set('maintenance', 'Service is down, starting')
1934- hookenv.log('Service {} is down, starting...'.format(SVC_NAME))
1935- host.service_start(SVC_NAME)
1936- hookenv.status_set('active', 'Service started')
1937- else:
1938- hookenv.status_set('active', 'Service is up')
1939- set_state('libvirt-exporter.started')
1940-
1941-
1942-# Relations
1943-@when('libvirt-exporter.started')
1944-@when('scrape.available')
1945-def configure_scrape_relation(scrape_service):
1946- scrape_service.configure(PORT_NUMBER)
1947diff --git a/reactive/prometheus-libvirt-exporter.py b/reactive/prometheus-libvirt-exporter.py
1948new file mode 100644
1949index 0000000..1660481
1950--- /dev/null
1951+++ b/reactive/prometheus-libvirt-exporter.py
1952@@ -0,0 +1,185 @@
1953+#!/usr/bin/python3
1954+"""Installs and configures prometheus-libvirt-exporter."""
1955+
1956+import os
1957+from pathlib import Path
1958+import shutil
1959+import subprocess
1960+from zipfile import BadZipFile, ZipFile
1961+
1962+from charmhelpers.contrib.charmsupport import nrpe
1963+from charmhelpers.core import hookenv, host
1964+from charms.layer import snap
1965+from charms.reactive import (
1966+ endpoint_from_flag,
1967+ hook,
1968+ remove_state,
1969+ set_state,
1970+ when,
1971+ when_all,
1972+ when_any,
1973+ when_not,
1974+ when_not_all
1975+)
1976+
1977+
1978+DASHBOARD_PATH = os.getcwd() + '/files/grafana-dashboards'
1979+SNAP_NAME = 'prometheus-libvirt-exporter'
1980+SVC_NAME = 'snap.prometheus-libvirt-exporter.daemon'
1981+PORT_NUMBER = '9177'
1982+
1983+
1984+@when('juju-info.connected')
1985+@when_not_all('libvirt-exporter.installed', 'libvirt-exporter.started')
1986+def install_packages():
1987+ """Installs the prometheus-libvirt-exporter snap."""
1988+ hookenv.status_set('maintenance', 'Installing software')
1989+ config = hookenv.config()
1990+ channel = config.get('snap_channel')
1991+ snap.install(SNAP_NAME, channel=channel, force_dangerous=False)
1992+ subprocess.check_call(['snap', 'connect', 'prometheus-libvirt-exporter:libvirt'])
1993+ hookenv.status_set('active', 'Exporter installed and connected to libvirt slot')
1994+ hookenv.open_port(PORT_NUMBER)
1995+ set_state('libvirt-exporter.installed')
1996+
1997+
1998+@hook('upgrade-charm')
1999+def upgrade():
2000+ """Reset the install state on upgrade, to ensure resource extraction."""
2001+ hookenv.status_set('maintenance', 'Charm upgrade in progress')
2002+ remove_state('libvirt-exporter.installed')
2003+ remove_state('libvirt-exporter.started')
2004+ update_dashboards_from_resource()
2005+
2006+
2007+@when_not('libvirt-exporter.started')
2008+@when_any('libvirt-exporter.installed', 'config.changed')
2009+def start_snap():
2010+ """Configure snap.prometheus-libvirt-exporter.daemon service."""
2011+ if not host.service_running(SVC_NAME):
2012+ hookenv.status_set('maintenance', 'Service is down, starting')
2013+ hookenv.log('Service {} is down, starting...'.format(SVC_NAME))
2014+ host.service_start(SVC_NAME)
2015+ hookenv.status_set('active', 'Service started')
2016+ hookenv.log('start_snap() Service started')
2017+ else:
2018+ hookenv.status_set('active', 'Ready')
2019+ set_state('libvirt-exporter.started')
2020+ hookenv.log('start_snap() libvirt-exporter.started')
2021+
2022+ update_dashboards_from_resource()
2023+
2024+ hookenv.log('Installed and set flag libvirt-exporter.started')
2025+
2026+
2027+@when('config.changed.snap_channel')
2028+def snap_channel_changed():
2029+ """Remove the state libvirt.exporter.installed if the snap channel changes."""
2030+ remove_state('libvirt-exporter.installed')
2031+ remove_state('libvirt-exporter.started')
2032+
2033+
2034+@when_all('libvirt-exporter.started', 'scrape.available')
2035+def configure_scrape_relation(scrape_service):
2036+ """Connect prometheus to the the exporter for consumption."""
2037+ scrape_service.configure(PORT_NUMBER)
2038+ remove_state('libvirt-exporter.configured')
2039+
2040+
2041+@when('nrpe-external-master.changed')
2042+def nrpe_changed():
2043+ """Trigger nrpe update."""
2044+ remove_state('libvirt-exporter.configured')
2045+
2046+
2047+@when('libvirt-exporter.changed')
2048+def prometheus_changed():
2049+ """Trigger prometheus update."""
2050+ remove_state('libvirt-exporter.prometheus_relation_configured')
2051+ remove_state('libvirt-exporter.configured')
2052+
2053+
2054+@when('nrpe-external-master.available')
2055+@when_not('libvirt-exporter.configured')
2056+def update_nrpe_config(svc):
2057+ """Configure the nrpe check for the service."""
2058+ if not os.path.exists('/var/lib/nagios'):
2059+ hookenv.status_set('blocked', 'Waiting for nrpe package installation')
2060+ return
2061+
2062+ hookenv.status_set('maintenance', 'Configuring nrpe checks')
2063+
2064+ hostname = nrpe.get_nagios_hostname()
2065+ nrpe_setup = nrpe.NRPE(hostname=hostname)
2066+ nrpe_setup.add_check(shortname='prometheus_libvirt_exporter_http',
2067+ check_cmd='check_http -I 127.0.0.1 -p {} -u /metrics'.format(PORT_NUMBER),
2068+ description='Prometheus Libvirt Exporter HTTP check')
2069+ nrpe_setup.write()
2070+ hookenv.status_set('active', 'ready')
2071+ set_state('libvirt-exporter.configured')
2072+
2073+
2074+@when('libvirt-exporter.installed')
2075+@when_not('juju-info.available')
2076+def remove_libvirt_exporter():
2077+ """Uninstall the snap."""
2078+ remove_state('libvirt-exporter.installed')
2079+ remove_state('libvirt-exporter.started')
2080+ snap.remove(SNAP_NAME)
2081+
2082+
2083+@when('libvirt-exporter.configured')
2084+@when_not('nrpe-external-master.available')
2085+def remove_nrpe_check():
2086+ """Remove the nrpe check."""
2087+ hostname = nrpe.get_nagios_hostname()
2088+ nrpe_setup = nrpe.NRPE(hostname=hostname)
2089+ nrpe_setup.remove_check(shortname="prometheus_libvirt_exporter_http")
2090+ remove_state('libvirt-exporter.configured')
2091+
2092+
2093+@when_all('leadership.is_leader', 'endpoint.dashboards.joined')
2094+def register_grafana_dashboards():
2095+ """After joining to grafana, push the dashboard."""
2096+ grafana_endpoint = endpoint_from_flag('endpoint.dashboards.joined')
2097+
2098+ if grafana_endpoint is None:
2099+ return
2100+
2101+ hookenv.log('Grafana relation joined, push dashboard')
2102+
2103+ # load pre-distributed dashboards, that may have been overwritten by resource
2104+ dash_dir = Path(DASHBOARD_PATH)
2105+ for dash_file in dash_dir.glob('*.json'):
2106+ dashboard = dash_file.read_text()
2107+ grafana_endpoint.register_dashboard(dash_file.stem, dashboard)
2108+ hookenv.log('Pushed {}'.format(dash_file))
2109+
2110+
2111+def update_dashboards_from_resource():
2112+ """Extract resource zip file into templates directory."""
2113+ dashboards_zip_resource = hookenv.resource_get('dashboards')
2114+ if not dashboards_zip_resource:
2115+ hookenv.log('No dashboards resource found', hookenv.DEBUG)
2116+ # no dashboards zip found, go with the default distributed dashboard
2117+ return
2118+
2119+ hookenv.log('Installing dashboards from resource', hookenv.DEBUG)
2120+ try:
2121+ shutil.copy(dashboards_zip_resource, DASHBOARD_PATH)
2122+ except IOError as error:
2123+ hookenv.log('Problem copying resource: {}'.format(error), hookenv.ERROR)
2124+ return
2125+
2126+ try:
2127+ with ZipFile(dashboards_zip_resource, 'r') as zipfile:
2128+ zipfile.extractall(path=DASHBOARD_PATH)
2129+ hookenv.log('Extracted dashboards from resource', hookenv.DEBUG)
2130+ except BadZipFile as error:
2131+ hookenv.log('BadZipFile: {}'.format(error), hookenv.ERROR)
2132+ return
2133+ except PermissionError as error:
2134+ hookenv.log('Unable to unzip the provided resource: {}'.format(error), hookenv.ERROR)
2135+ return
2136+
2137+ register_grafana_dashboards()
2138diff --git a/requirements.txt b/requirements.txt
2139new file mode 100644
2140index 0000000..1713f68
2141--- /dev/null
2142+++ b/requirements.txt
2143@@ -0,0 +1 @@
2144+# Include python requirements here
2145\ No newline at end of file
2146diff --git a/tests/__init__.py b/tests/__init__.py
2147new file mode 100644
2148index 0000000..d236d73
2149--- /dev/null
2150+++ b/tests/__init__.py
2151@@ -0,0 +1 @@
2152+"""Zaza tests."""
2153diff --git a/tests/bundles/bionic.yaml b/tests/bundles/bionic.yaml
2154new file mode 100644
2155index 0000000..dcff425
2156--- /dev/null
2157+++ b/tests/bundles/bionic.yaml
2158@@ -0,0 +1,22 @@
2159+series: bionic
2160+applications:
2161+ ubuntu:
2162+ charm: cs:ubuntu
2163+ num_units: 1
2164+ prometheus-libvirt-exporter:
2165+ options:
2166+ snap_channel: stable
2167+ nrpe:
2168+ charm: cs:nrpe
2169+ prometheus:
2170+ charm: cs:prometheus2
2171+ num_units: 1
2172+relations:
2173+ - - ubuntu
2174+ - prometheus-libvirt-exporter
2175+ - - ubuntu:juju-info
2176+ - nrpe:general-info
2177+ - - prometheus-libvirt-exporter:nrpe-external-master
2178+ - nrpe:nrpe-external-master
2179+ - - prometheus-libvirt-exporter:scrape
2180+ - prometheus:target
2181\ No newline at end of file
2182diff --git a/tests/bundles/focal.yaml b/tests/bundles/focal.yaml
2183new file mode 100644
2184index 0000000..13bb440
2185--- /dev/null
2186+++ b/tests/bundles/focal.yaml
2187@@ -0,0 +1,22 @@
2188+series: focal
2189+applications:
2190+ ubuntu:
2191+ charm: cs:ubuntu
2192+ num_units: 1
2193+ prometheus-libvirt-exporter:
2194+ options:
2195+ snap_channel: stable
2196+ nrpe:
2197+ charm: cs:nrpe
2198+ prometheus:
2199+ charm: cs:prometheus2
2200+ num_units: 1
2201+relations:
2202+ - - ubuntu
2203+ - prometheus-libvirt-exporter
2204+ - - ubuntu:juju-info
2205+ - nrpe:general-info
2206+ - - prometheus-libvirt-exporter:nrpe-external-master
2207+ - nrpe:nrpe-external-master
2208+ - - prometheus-libvirt-exporter:scrape
2209+ - prometheus:target
2210\ No newline at end of file
2211diff --git a/tests/bundles/overlays/local-charm-overlay.yaml.j2 b/tests/bundles/overlays/local-charm-overlay.yaml.j2
2212new file mode 100644
2213index 0000000..bfaaa2d
2214--- /dev/null
2215+++ b/tests/bundles/overlays/local-charm-overlay.yaml.j2
2216@@ -0,0 +1,3 @@
2217+applications:
2218+ prometheus-libvirt-exporter:
2219+ charm: "{{ CHARM_BUILD_DIR }}/{{ charm_name }}"
2220diff --git a/tests/bundles/xenial.yaml b/tests/bundles/xenial.yaml
2221new file mode 100644
2222index 0000000..aff59d8
2223--- /dev/null
2224+++ b/tests/bundles/xenial.yaml
2225@@ -0,0 +1,22 @@
2226+series: xenial
2227+applications:
2228+ ubuntu:
2229+ charm: cs:ubuntu
2230+ num_units: 1
2231+ prometheus-libvirt-exporter:
2232+ options:
2233+ snap_channel: stable
2234+ nrpe:
2235+ charm: cs:nrpe
2236+ prometheus:
2237+ charm: cs:prometheus2
2238+ num_units: 1
2239+relations:
2240+ - - ubuntu
2241+ - prometheus-libvirt-exporter
2242+ - - ubuntu:juju-info
2243+ - nrpe:general-info
2244+ - - prometheus-libvirt-exporter:nrpe-external-master
2245+ - nrpe:nrpe-external-master
2246+ - - prometheus-libvirt-exporter:scrape
2247+ - prometheus:target
2248\ No newline at end of file
2249diff --git a/tests/requirements.txt b/tests/requirements.txt
2250new file mode 100644
2251index 0000000..b7c9112
2252--- /dev/null
2253+++ b/tests/requirements.txt
2254@@ -0,0 +1 @@
2255+git+https://github.com/openstack-charmers/zaza.git#egg=zaza
2256diff --git a/tests/test_prometheus_libvirt_exporter.py b/tests/test_prometheus_libvirt_exporter.py
2257new file mode 100644
2258index 0000000..1cb865f
2259--- /dev/null
2260+++ b/tests/test_prometheus_libvirt_exporter.py
2261@@ -0,0 +1,78 @@
2262+"""Encapsulate prometheus-libvirt-exporter testing."""
2263+import logging
2264+import time
2265+import unittest
2266+
2267+import zaza.model as model
2268+
2269+
2270+CURL_TIMEOUT = 180
2271+REQ_TIMEOUT = 12
2272+DEFAULT_API_PORT = "9177"
2273+DEFAULT_API_URL = "/metrics"
2274+
2275+
2276+class BasePrometheusLibvirtExporterTest(unittest.TestCase):
2277+ """Base for Prometheus-libvirt-exporter charm tests."""
2278+
2279+ @classmethod
2280+ def setUpClass(cls):
2281+ """Set up tests."""
2282+ super(BasePrometheusLibvirtExporterTest, cls).setUpClass()
2283+ cls.model_name = model.get_juju_model()
2284+ cls.application_name = "prometheus-libvirt-exporter"
2285+ cls.lead_unit_name = model.get_lead_unit_name(
2286+ cls.application_name, model_name=cls.model_name
2287+ )
2288+ cls.units = model.get_units(
2289+ cls.application_name, model_name=cls.model_name
2290+ )
2291+ cls.prometheus_libvirt_exporter_ip = model.get_app_ips(cls.application_name)[0]
2292+
2293+
2294+class CharmOperationTest(BasePrometheusLibvirtExporterTest):
2295+ """Verify operations."""
2296+
2297+ def test_01_api_ready(self):
2298+ """Verify if the API is ready.
2299+
2300+ Curl the api endpoint.
2301+ We'll retry until the CURL_TIMEOUT.
2302+ """
2303+ curl_command = "curl http://localhost:{}/metrics".format(DEFAULT_API_PORT)
2304+ timeout = time.time() + CURL_TIMEOUT
2305+ while time.time() < timeout:
2306+ response = model.run_on_unit(self.lead_unit_name, curl_command)
2307+ if response["Code"] == "0":
2308+ return
2309+ logging.warning(
2310+ "Unexpected curl response: {}. Retrying in 30s.".format(
2311+ response
2312+ )
2313+ )
2314+ time.sleep(30)
2315+
2316+ # we didn't get rc=0 in the allowed time, fail the test
2317+ self.fail(
2318+ "Prometheus-libvirt-exporter didn't respond to the command \n"
2319+ "'{curl_command}' as expected.\n"
2320+ "Result: {result}".format(
2321+ curl_command=curl_command, result=response
2322+ )
2323+ )
2324+
2325+ def test_02_nrpe_http_check(self):
2326+ """Verify nrpe check exists."""
2327+ expected_nrpe_check = "command[check_prometheus_libvirt_exporter_http]={} -I 127.0.0.1 -p {} -u {}".format(
2328+ "/usr/lib/nagios/plugins/check_http",
2329+ DEFAULT_API_PORT,
2330+ DEFAULT_API_URL
2331+ )
2332+ logging.debug('Verify the nrpe check is created and has the required content...')
2333+ cmd = "cat /etc/nagios/nrpe.d/check_prometheus_libvirt_exporter_http.cfg"
2334+ result = model.run_on_unit(self.lead_unit_name, cmd)
2335+ code = result.get('Code')
2336+ if code != '0':
2337+ raise model.CommandRunFailed(cmd, result)
2338+ content = result.get('Stdout')
2339+ self.assertTrue(expected_nrpe_check in content)
2340diff --git a/tests/tests.yaml b/tests/tests.yaml
2341new file mode 100644
2342index 0000000..24ca291
2343--- /dev/null
2344+++ b/tests/tests.yaml
2345@@ -0,0 +1,11 @@
2346+charm_name: prometheus-libvirt-exporter
2347+gate_bundles:
2348+ - xenial
2349+ - bionic
2350+ #- focal # Uncomment when nrpe and prometheus2 become available for focal
2351+smoke_bundles:
2352+ - bionic
2353+dev_bundles:
2354+ - bionic
2355+tests:
2356+ - tests.test_prometheus_libvirt_exporter.CharmOperationTest
2357diff --git a/tests/unit_tests/requirements.txt b/tests/unit_tests/requirements.txt
2358new file mode 100644
2359index 0000000..8e404fb
2360--- /dev/null
2361+++ b/tests/unit_tests/requirements.txt
2362@@ -0,0 +1,6 @@
2363+charmhelpers
2364+charms.reactive
2365+mock
2366+pytest
2367+pytest-cov
2368+pytest-html
2369\ No newline at end of file
2370diff --git a/tox.ini b/tox.ini
2371new file mode 100644
2372index 0000000..b50e481
2373--- /dev/null
2374+++ b/tox.ini
2375@@ -0,0 +1,65 @@
2376+[tox]
2377+skipsdist=True
2378+envlist = unit, functional
2379+skip_missing_interpreters = True
2380+
2381+[testenv]
2382+basepython = python3
2383+setenv =
2384+ PYTHONPATH = .
2385+
2386+[testenv:unit]
2387+commands = pytest -v \
2388+ --ignore {toxinidir}/tests/ \
2389+ --ignore {toxinidir}/interfaces \
2390+ --ignore {toxinidir}/layers \
2391+ --cov=lib \
2392+ --cov=actions \
2393+ --cov-report=term \
2394+ --cov-report=annotate:report/unit/coverage-annotated \
2395+ --cov-report=html:report/unit/coverage-html \
2396+ --html=report/unit/tests/index.html \
2397+ --junitxml=report/unit/junit.xml
2398+deps = -r{toxinidir}/tests/unit_tests/requirements.txt
2399+ -r{toxinidir}/tests/requirements.txt
2400+setenv = PYTHONPATH={toxinidir}/lib
2401+
2402+[testenv:func]
2403+commands = functest-run-suite --keep-model
2404+deps = -r{toxinidir}/tests/requirements.txt
2405+passenv =
2406+ HOME
2407+ CHARM_BUILD_DIR
2408+ MODEL_SETTINGS
2409+
2410+[testenv:lint]
2411+commands = flake8 --format=html --htmldir=report/lint/ --tee
2412+deps =
2413+ flake8
2414+ flake8-colors
2415+ flake8-docstrings
2416+ flake8-html
2417+ flake8-import-order
2418+ pep8-naming
2419+
2420+[flake8]
2421+exclude =
2422+ .git,
2423+ __pycache__,
2424+ .tox,
2425+ layers,
2426+ interfaces,
2427+max-line-length = 120
2428+max-complexity = 10
2429+import-order-style = google
2430+
2431+[isort]
2432+order_by_type = true
2433+from_first = true
2434+line_length = 120
2435+
2436+[pytest]
2437+markers =
2438+ deploy: mark deployment tests to allow running w/o redeploy
2439+filterwarnings =
2440+ ignore::DeprecationWarning

Subscribers

People subscribed via source and target branches

to all changes: