diff --git a/.gitignore b/.gitignore
index e73c109..8f1f772 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.idea
/libvirt_exporter
libvirt-exporter
+.venv
diff --git a/ansible/README.md b/ansible/README.md
new file mode 100644
index 0000000..e2f078e
--- /dev/null
+++ b/ansible/README.md
@@ -0,0 +1,47 @@
+END 2 END tests
+---------------
+
+Tested on:
+- macOS: 12.3.1
+- Vagrant: 2.2.19
+- VMware Fusion: 12.2.3
+
+Installation
+============
+
+Prerequisites (Instructions for macOS):
+- VMware Fusion (personal license): https://customerconnect.vmware.com/web/vmware/evalcenter?p=fusion-player-personal
+- Vagrant: https://www.vagrantup.com/downloads
+- VMware Fusion provider for vagrant: https://www.vagrantup.com/docs/providers/vmware/installation
+- Vagrant VMware Utility: https://www.vagrantup.com/docs/providers/vmware/vagrant-vmware-utility
+
+Ansible, molecule installation:
+- create venv: ```cd ansible; mkdir .venv; python3 -m venv .venv```
+- update pip: ```source .venv/bin/activate; pip3 install -U pip```
+- install pip packages: ```pip install -r requirements.txt```
+
+Ansible roles, collection installation:
+- enter venv: ```source .venv/bin/activate```
+- install roles,collections: ```ansible-galaxy role install -r requirements.yml```
+- install collections: ```ansible-galaxy collection install -r requirements.yml```
+
+Usage
+=====
+
+All commands must be executed inside venv.
+
+Create test environment: ```molecule converge```
+Run tests: ```molecule verify```
+
+Contribute
+==========
+
+All tests located in verify playbook inside molecule tree. You can use variable ```libvirt_exporter_metrics``` to querry specific metrics and their values. Example:
+
+```yaml
+ - name: Assert that 'libvirt_up' in metrics
+ assert:
+ that: "libvirt_exporter_metrics | selectattr('name', 'equalto', 'libvirt_up') | first"
+```
+
+In this example, we use assert ansible module (https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assert_module.html) to verify that there is a element in ```libvirt_exporter_metrics``` array with property ```name``` equals to ```libvirt_up```. ```libvirt_exporter_metrics``` array is a parsed json response from libvirt-exporter.
diff --git a/ansible/molecule/default/converge.yml b/ansible/molecule/default/converge.yml
new file mode 100644
index 0000000..d7a032c
--- /dev/null
+++ b/ansible/molecule/default/converge.yml
@@ -0,0 +1,36 @@
+---
+
+- name: Converge
+ hosts: all
+ become: True
+ vars:
+ image_name: "localhost/alekseizakharov/libvirt-exporter:latest"
+ sources_remote_path: "/srv/sources"
+ sources_local_path: "{{ playbook_dir }}/../../../"
+ tasks:
+ - name: Copy local sources to remote dir
+ ansible.posix.synchronize:
+ src: "{{ sources_local_path }}"
+ dest: "{{ sources_remote_path }}"
+ rsync_opts:
+ - "--no-motd"
+ - "--exclude=.venv"
+
+ - name: Build libvirt-exporter image
+ containers.podman.podman_image:
+ name: "{{ image_name }}"
+ path: "{{ sources_remote_path }}"
+ build:
+ format: docker
+ annotation:
+ app: libvirt-exporter
+ info: metrics exporter for libvirt
+
+ - name: Run libvirt-exporter container
+ containers.podman.podman_container:
+ name: libvirt_exporter
+ image: "{{ image_name }}"
+ state: started
+ recreate: true
+ publish: "9177:9177"
+ volume: "/var/run/libvirt:/var/run/libvirt"
diff --git a/ansible/molecule/default/metrics.yml b/ansible/molecule/default/metrics.yml
new file mode 100644
index 0000000..7172ec2
--- /dev/null
+++ b/ansible/molecule/default/metrics.yml
@@ -0,0 +1,205 @@
+---
+
+metrics:
+ - name: go_gc_duration_seconds
+ type: SUMMARY
+ - name: go_goroutines
+ type: GAUGE
+ - name: go_info
+ type: GAUGE
+ - name: go_memstats_alloc_bytes
+ type: GAUGE
+ - name: go_memstats_alloc_bytes_total
+ type: COUNTER
+ - name: go_memstats_buck_hash_sys_bytes
+ type: GAUGE
+ - name: go_memstats_frees_total
+ type: COUNTER
+ - name: go_memstats_gc_cpu_fraction
+ type: GAUGE
+ - name: go_memstats_gc_sys_bytes
+ type: GAUGE
+ - name: go_memstats_heap_alloc_bytes
+ type: GAUGE
+ - name: go_memstats_heap_idle_bytes
+ type: GAUGE
+ - name: go_memstats_heap_inuse_bytes
+ type: GAUGE
+ - name: go_memstats_heap_objects
+ type: GAUGE
+ - name: go_memstats_heap_released_bytes
+ type: GAUGE
+ - name: go_memstats_heap_sys_bytes
+ type: GAUGE
+ - name: go_memstats_last_gc_time_seconds
+ type: GAUGE
+ - name: go_memstats_lookups_total
+ type: COUNTER
+ - name: go_memstats_mallocs_total
+ type: COUNTER
+ - name: go_memstats_mcache_inuse_bytes
+ type: GAUGE
+ - name: go_memstats_mcache_sys_bytes
+ type: GAUGE
+ - name: go_memstats_mspan_inuse_bytes
+ type: GAUGE
+ - name: go_memstats_mspan_sys_bytes
+ type: GAUGE
+ - name: go_memstats_next_gc_bytes
+ type: GAUGE
+ - name: go_memstats_other_sys_bytes
+ type: GAUGE
+ - name: go_memstats_stack_inuse_bytes
+ type: GAUGE
+ - name: go_memstats_stack_sys_bytes
+ type: GAUGE
+ - name: go_memstats_sys_bytes
+ type: GAUGE
+ - name: go_threads
+ type: GAUGE
+ - name: libvirt_domain_block_meta
+ type: GAUGE
+ - name: libvirt_domain_block_stats_allocation
+ type: GAUGE
+ - name: libvirt_domain_block_stats_capacity_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_flush_requests_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_flush_time_seconds_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_limit_burst_length_read_requests_seconds
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_length_total_requests_seconds
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_length_write_requests_seconds
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_read_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_read_bytes_length_seconds
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_read_requests
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_total_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_total_bytes_length_seconds
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_total_requests
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_write_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_write_bytes_length_seconds
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_burst_write_requests
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_read_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_read_requests
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_total_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_total_requests
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_write_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_limit_write_requests
+ type: GAUGE
+ - name: libvirt_domain_block_stats_physicalsize_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_read_bytes_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_read_requests_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_read_time_seconds_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_size_iops_bytes
+ type: GAUGE
+ - name: libvirt_domain_block_stats_write_bytes_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_write_requests_total
+ type: COUNTER
+ - name: libvirt_domain_block_stats_write_time_seconds_total
+ type: COUNTER
+ - name: libvirt_domain_info_cpu_time_seconds_total
+ type: COUNTER
+ - name: libvirt_domain_info_maximum_memory_bytes
+ type: GAUGE
+ - name: libvirt_domain_info_memory_usage_bytes
+ type: GAUGE
+ - name: libvirt_domain_info_meta
+ type: GAUGE
+ - name: libvirt_domain_info_virtual_cpus
+ type: GAUGE
+ - name: libvirt_domain_info_vstate
+ type: GAUGE
+ - name: libvirt_domain_interface_meta
+ type: GAUGE
+ - name: libvirt_domain_interface_stats_receive_bytes_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_receive_drops_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_receive_errors_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_receive_packets_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_transmit_bytes_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_transmit_drops_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_transmit_errors_total
+ type: COUNTER
+ - name: libvirt_domain_interface_stats_transmit_packets_total
+ type: COUNTER
+ - name: libvirt_domain_memory_stats_actual_balloon_bytes
+ type: GAUGE
+ - name: libvirt_domain_memory_stats_available_bytes
+ type: GAUGE
+ - name: libvirt_domain_memory_stats_disk_cache_bytes
+ type: GAUGE
+ - name: libvirt_domain_memory_stats_major_fault_total
+ type: COUNTER
+ - name: libvirt_domain_memory_stats_minor_fault_total
+ type: COUNTER
+ - name: libvirt_domain_memory_stats_rss_bytes
+ type: GAUGE
+ - name: libvirt_domain_memory_stats_unused_bytes
+ type: GAUGE
+ - name: libvirt_domain_memory_stats_usable_bytes
+ type: GAUGE
+ - name: libvirt_domain_memory_stats_used_percent
+ type: GAUGE
+ - name: libvirt_domain_vcpu_cpu
+ type: GAUGE
+ - name: libvirt_domain_vcpu_state
+ type: GAUGE
+ - name: libvirt_domain_vcpu_time_seconds_total
+ type: COUNTER
+ - name: libvirt_domain_vcpu_wait_seconds_total
+ type: COUNTER
+ - name: libvirt_pool_info_allocation_bytes
+ type: GAUGE
+ - name: libvirt_pool_info_available_bytes
+ type: GAUGE
+ - name: libvirt_pool_info_capacity_bytes
+ type: GAUGE
+ - name: libvirt_up
+ type: GAUGE
+ - name: libvirt_versions_info
+ type: GAUGE
+ - name: process_cpu_seconds_total
+ type: COUNTER
+ - name: process_max_fds
+ type: GAUGE
+ - name: process_open_fds
+ type: GAUGE
+ - name: process_resident_memory_bytes
+ type: GAUGE
+ - name: process_start_time_seconds
+ type: GAUGE
+ - name: process_virtual_memory_bytes
+ type: GAUGE
+ - name: process_virtual_memory_max_bytes
+ type: GAUGE
+ - name: promhttp_metric_handler_requests_in_flight
+ type: GAUGE
+ - name: promhttp_metric_handler_requests_total
+ type: COUNTER
diff --git a/ansible/molecule/default/molecule.yml b/ansible/molecule/default/molecule.yml
new file mode 100644
index 0000000..350ece3
--- /dev/null
+++ b/ansible/molecule/default/molecule.yml
@@ -0,0 +1,15 @@
+---
+
+prerun: False
+driver:
+ name: vagrant
+platforms:
+ - name: ubuntu
+ box: generic/ubuntu2010
+ config_options:
+ ssh.keep_alive: True
+ ssh.remote_user: 'vagrant'
+provisioner:
+ name: ansible
+verifier:
+ name: ansible
diff --git a/ansible/molecule/default/pool.xml.j2 b/ansible/molecule/default/pool.xml.j2
new file mode 100644
index 0000000..1299ad6
--- /dev/null
+++ b/ansible/molecule/default/pool.xml.j2
@@ -0,0 +1,17 @@
+
+ vms
+ 28954164-9810-47bc-88f7-ab3d847c38df
+ 66844176384
+ 6587465728
+ 60256710656
+
+
+
+ /var/lib/libvirt/images
+
+ 0711
+ 0
+ 0
+
+
+
diff --git a/ansible/molecule/default/prepare.yml b/ansible/molecule/default/prepare.yml
new file mode 100644
index 0000000..d067008
--- /dev/null
+++ b/ansible/molecule/default/prepare.yml
@@ -0,0 +1,91 @@
+---
+
+- name: Configure test vm
+ hosts: all
+ become: True
+ gather_facts: True
+ tasks:
+ - name: Install libvirtd
+ apt:
+ name: "{{ item.name }}={{ item.version }}"
+ update_cache: True
+ loop:
+ - name: libvirt-daemon
+ version: "6.6.0-1ubuntu3.5"
+ - name: libvirt-daemon-system
+ version: "6.6.0-1ubuntu3.5"
+ - name: libguestfs-tools
+ version: "1:1.42.0-9ubuntu1"
+ - name: virtinst
+ version: "1:2.2.1-4ubuntu2"
+ - name: python3-libvirt
+ version: "6.1.0-1"
+ - name: python3-lxml
+ version: "4.5.2-1ubuntu0.4"
+ - name: podman
+ version: "2.0.6+dfsg1-1ubuntu1.2"
+ - name: runc
+ version: "1.0.0~rc95-0ubuntu1~20.10.1"
+
+ - name: Download prom2json
+ get_url:
+ url: 'https://github.com/prometheus/prom2json/releases/download/v1.3.0/prom2json-1.3.0.linux-amd64.tar.gz'
+ dest: /home/vagrant/prom2json-1.3.0.linux-amd64.tar.gz
+
+ - name: Unpack prom2json
+ ansible.builtin.unarchive:
+ remote_src: True
+ src: /home/vagrant/prom2json-1.3.0.linux-amd64.tar.gz
+ dest: /home/vagrant
+
+ - name: Install prom2json
+ copy:
+ remote_src: True
+ mode: '777'
+ src: /home/vagrant/prom2json-1.3.0.linux-amd64/prom2json
+ dest: /usr/local/bin/prom2json
+
+ - name: List libvirt volume pools
+ community.libvirt.virt_pool:
+ command: list_pools
+ register: libvirt_pools
+
+ - name: Create default libvirtd volume pool
+ community.libvirt.virt_pool:
+ command: define
+ name: vms
+ xml: '{{ lookup("template", "pool.xml.j2") }}'
+ when: "'default' not in libvirt_pools.list_pools"
+
+ - name: Build vms pool
+ community.libvirt.virt_pool:
+ command: build
+ name: vms
+ when: "'vms' not in libvirt_pools.list_pools"
+
+ - name: Create vms pool
+ community.libvirt.virt_pool:
+ state: active
+ name: vms
+ when: "'vms' not in libvirt_pools.list_pools"
+
+ - name: Download qcow2 vm
+ get_url:
+ url: http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img
+ dest: /home/vagrant/cirros-0.4.0-x86_64-disk.qcow2
+
+ - name: Create volume
+ command: virsh vol-create-as vms cirros-image.qcow2 128M --format qcow2
+
+ - name: Upload image contents to volume
+ command: virsh vol-upload cirros-image.qcow2 /home/vagrant/cirros-0.4.0-x86_64-disk.qcow2 --pool vms
+
+ - name: Define vm
+ community.libvirt.virt:
+ command: define
+ xml: "{{ lookup('template', 'vm.xml.j2') }}"
+
+ - name: Create vm
+ community.libvirt.virt:
+ name: test-instance
+ state: running
diff --git a/ansible/molecule/default/verify.yml b/ansible/molecule/default/verify.yml
new file mode 100644
index 0000000..6e2e567
--- /dev/null
+++ b/ansible/molecule/default/verify.yml
@@ -0,0 +1,35 @@
+---
+
+- name: Verify
+ hosts: all
+ become: True
+ gather_facts: True
+ vars_files:
+ - metrics.yml
+ vars:
+ libvirt_exporter_url: "http://localhost:9177/metrics"
+ tasks:
+ - name: Assert that libvirt_exporter is serving
+ uri:
+ url: "{{ libvirt_exporter_url }}"
+ status_code: 200
+
+ - name: Get /metrics content from libvirt_eporter
+ command: "prom2json {{ libvirt_exporter_url }}"
+ register: libvirt_exporter_response
+
+ - name: Prettify libvirt_exporter_result
+ set_fact:
+ libvirt_exporter_metrics: "{{ libvirt_exporter_response.stdout | from_json }}"
+
+ - name: Assert that all metrics are present
+ assert:
+ that: "libvirt_exporter_metrics | selectattr('name', 'equalto', item.name) | length"
+ loop: "{{ metrics }}"
+
+ - name: Assert that all metrics metric have necessary type
+ vars:
+ libvirt_up_metric: "{{ libvirt_exporter_metrics | selectattr('name', 'equalto', item.name) | first }}"
+ assert:
+ that: "libvirt_up_metric.type == item.type"
+ loop: "{{ metrics }}"
diff --git a/ansible/molecule/default/vm.xml.j2 b/ansible/molecule/default/vm.xml.j2
new file mode 100644
index 0000000..3131354
--- /dev/null
+++ b/ansible/molecule/default/vm.xml.j2
@@ -0,0 +1,109 @@
+
+ test-instance
+ 524288
+ 524288
+ 1
+
+ /machine
+
+
+ hvm
+
+
+
+
+
+
+
+ qemu64
+
+
+
+
+
+
+
+
+ destroy
+ restart
+ destroy
+
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ansible/requirements.txt b/ansible/requirements.txt
new file mode 100644
index 0000000..c0d5b68
--- /dev/null
+++ b/ansible/requirements.txt
@@ -0,0 +1,70 @@
+ansible==4.7.0
+ansible-compat==0.5.0
+ansible-core==2.11.6
+ansible-lint==5.2.1
+arrow==1.2.0
+backports.entry-points-selectable==1.1.0
+bcrypt==3.2.0
+binaryornot==0.4.4
+bracex==2.2
+Cerberus==1.3.2
+certifi==2021.10.8
+cffi==1.15.0
+cfgv==3.3.1
+chardet==4.0.0
+charset-normalizer==2.0.7
+click==8.0.3
+click-help-colors==0.9.1
+colorama==0.4.4
+commonmark==0.9.1
+cookiecutter==1.7.3
+cryptography==35.0.0
+distlib==0.3.3
+distro==1.6.0
+docker==5.0.3
+enrich==1.2.6
+filelock==3.3.1
+flake8==4.0.1
+identify==2.3.0
+idna==3.3
+Jinja2==3.0.2
+jinja2-time==0.2.0
+MarkupSafe==2.0.1
+mccabe==0.6.1
+molecule==3.5.2
+molecule-docker==1.0.2
+molecule-vagrant==0.6.3
+nodeenv==1.6.0
+packaging==21.0
+paramiko==2.8.0
+pathspec==0.9.0
+platformdirs==2.4.0
+pluggy==1.0.0
+poyo==0.5.0
+pre-commit==2.15.0
+pycodestyle==2.8.0
+pycparser==2.20
+pyflakes==2.4.0
+Pygments==2.10.0
+PyNaCl==1.4.0
+pyparsing==2.4.7
+python-dateutil==2.8.2
+python-slugify==5.0.2
+python-vagrant==0.5.15
+PyYAML==5.4.1
+requests==2.26.0
+resolvelib==0.5.4
+rich==10.12.0
+ruamel.yaml==0.17.16
+ruamel.yaml.clib==0.2.6
+selinux==0.2.1
+six==1.16.0
+subprocess-tee==0.3.5
+tenacity==8.0.1
+text-unidecode==1.3
+toml==0.10.2
+urllib3==1.26.7
+virtualenv==20.8.1
+wcmatch==8.2
+websocket-client==1.2.1
+yamllint==1.26.3
diff --git a/ansible/requirements.yml b/ansible/requirements.yml
new file mode 100644
index 0000000..af9cce7
--- /dev/null
+++ b/ansible/requirements.yml
@@ -0,0 +1,8 @@
+---
+collections:
+
+- name: ansible.posix
+ version: 1.3.0
+
+- name: community.libvirt
+ version: 1.0.2