ansible-collections / community.libvirt

Manage libvirt with Ansible
http://galaxy.ansible.com/community/libvirt
GNU General Public License v3.0
61 stars 42 forks source link

Make the connection plugin optional and add the option to filter domains / uuids using regex. #101

Open yorick1989 opened 2 years ago

yorick1989 commented 2 years ago

…ins / uuids using regex.

SUMMARY

I ran into issues that the connection plugin was not working for out-of-the-box Red Hat servers. Red Hat has disabled the required Qemu Guest Agent commands for the libvirt connection plugin by default:

[root@test01 ~]# cat /etc/redhat-release 
Red Hat Enterprise Linux release 8.5 (Ootpa)
[root@test01 ~]# dnf download --downloadonly --destdir=/tmp qemu-guest-agent
Updating Subscription Management repositories.
Last metadata expiration check: 2:05:11 ago on Thu 06 Jan 2022 07:05:05 PM CET.
qemu-guest-agent-4.2.0-59.module+el8.5.0+13495+8166cdf8.1.x86_64.rpm                                                                    601 kB/s | 257 kB     00:00    
[root@test01 ~]# rpm2cpio /tmp/qemu-guest-agent-4.2.0-59.module+el8.5.0+13495+8166cdf8.1.x86_64.rpm | cpio -i --to-stdout ./etc/sysconfig/qemu-ga | grep BLACKLIST
BLACKLIST_RPC=guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush,guest-exec,guest-exec-status
741 blocks

Therefor I require to pull the IP addresses for each domain and set the first IP address found as the ansible_host variable.

I also added the ansible_libvirt_ifaces variable which contains all the network interfaces information of the domain. It's not necessary, but I was already working on it to get ansible_host to work anyway :) .

ISSUE TYPE
COMPONENT NAME

no_connection_plugin

ADDITIONAL INFORMATION

Connection plugin:

Before:

$ ansible-inventory --host test01
{
    "ansible_connection": "community.libvirt.libvirt_qemu",
    "ansible_libvirt_uri": "qemu:///system"
}

After (set: use_connection_plugin: False):

$ ansible-inventory --host test01
{
    "ansible_host": "192.168.122.182",
    "ansible_libvirt_ifaces": {
        "vnet39": {
            "addrs": [
                {
                    "addr": "192.168.122.182",
                    "prefix": 24,
                    "type": 0
                }
            ],
            "hwaddr": "52:54:00:12:54:a2"
        }
    }
}

Filter

Before:

$ ansible-inventory --graph | grep -v -- '-@'
@all:
  |  |--test01
  |  |--test03
  |  |--test01
  |  |--test01
  |  |--test03
  |  |--kali
  |  |--test02
  |  |--test03

After (set: filter: "test0(1|3)+"):

$ ansible-inventory --graph | grep -v -- '-@' | sort -n | uniq
@all:
  |  |--test01
  |  |--test03
  |  |--test01
  |  |--test01
  |  |--test03
  |  |--test03
odyssey4me commented 2 years ago

It seems to me that there is already functionality to not use the connection plugin, by simply removing the connection plugin configuration file?

Also, the resolving of libvirt domains to IP addresses is the sort of thing that belongs in an inventory plugin, surely?

If you're wanting to connect via SSH then the inventory plugin should resolve libvirt domains to IP addresses, then the normal SSH connection plugin does the usual from there.

yorick1989 commented 2 years ago

Thank you for your feedback.

How do I remove the 'connection plugin configuration file'? I only use the inventory filename that contains:

plugin: community.libvirt.libvirt
uri: 'qemu:///system'
use_connection_plugin: False
filter: "test0(1|3)+"

By the way, my changes are done in the inventory code, not in the connection plugin code. There was no option to skip the connection plugin within the libvirt inventory file, that is why I added the option to skip the connection plugin by adding the use_connection_plugin option to use an alternative way which pulls the IP address and adds the first found IP address to the host variables (as ansible_host); so this IP address can used for SSH connections. There was also a comment in the inventory code to make the connection plugin optional. This is why I made these changes and I would like to give it back to the upstream; if it is a beneficial addition.

If there is a better alternative, please let me know. I just tried to create a way to get this inventory plugin to work without using the connection plugin; since that's by default not working for Red Hat servers (since the 'guest-file-open,guest-file-close,guest-file-read,guest-file-write,guest-file-seek,guest-file-flush,guest-exec,guest-exec-status' options are blocked within the configuration of the qemu-guest-agent package / installation).

Thanks!

csmart commented 2 years ago

Hi @yorick1989 thanks for this contribution, I will test it out. i can see that on RHEL machines qemu guest agent does not support the required options so I think another option is useful :+1: I may have confused @odyssey4me when I was talking to him about this, as I incorrectly said it was in the connection not the inventory code. Sorry!

If we are going to rely on getting the IP from libvirt then I feel like it has to be very robust. Also, how will we handle failure scenarios? I'm wondering how this might work in edge cases, such as when an IP is not available yet, or perhaps only an IPv6 (still waiting for IPv4), or guests which have multiple network interfaces, etc. Also, how will it work with multiple inventories, or where one has specified ansible_host already elsewhere? Have you had a chance to test any of those scenarios?

Also, are there any additional requirements for this to work? Thanks!

csmart commented 2 years ago

I've done some basic testing with an inventory file like so:

---
plugin: community.libvirt.libvirt
uri: 'qemu:///system'
use_connection_plugin: False
filter: "ansible-fedora-35"

it works as expected, SSHing into the guest:

(3.9) [09:40 csmart@dev ~/.../community/libvirt (connection_filter $%)]$ date
Tue 01 Feb 2022 09:40:05 AEDT

(3.9) [09:40 csmart@dev ~/.../community/libvirt (connection_filter $%)]$ ansible -i ../../kvm.yml all --limit ansible-fedora-35 -m command -a 'whoami'
ansible-fedora-35 | CHANGED | rc=0 >>
csmart

(3.9) [09:40 csmart@dev ~/.../community/libvirt (connection_filter $%)]$ ssh ansible-fedora-35 last |head -1
csmart   pts/0        192.168.112.1    Tue Feb  1 09:40 - 09:40  (00:00)

I have tested with a filter applied but use_connection_plugin set to false so that it uses guest agent rather than SSH:

---
plugin: community.libvirt.libvirt
uri: 'qemu:///system'
#use_connection_plugin: False
filter: "ansible-fedora-35"

It works as expected.

(3.9) [09:45 csmart@dev ~/.../community/libvirt (connection_filter $%)]$ ansible -i ../../kvm.yml all -m command -a 'whoami'
ansible-fedora-35 | CHANGED | rc=0 >>
root

I have tested using SSH but when there is no filter specified in the inventory:

---
plugin: community.libvirt.libvirt
uri: 'qemu:///system'
use_connection_plugin: False
#filter: "ansible-fedora-35"

However Ansible is not able to parse the inventory. I think we would need to make sure that by default, an empty or missing filter resulted in a complete list of all VMs (the default without this code).

(3.9) [09:42 csmart@dev ~/.../community/libvirt (connection_filter $%)]$ ansible -i ../../kvm.yml all -m command -a 'whoami'
libvirt: Domain Config error : Requested operation is not valid: domain is not running
[WARNING]:  * Failed to parse /home/csmart/ansible_collections/kvm.yml with
ansible_collections.community.libvirt.plugins.inventory.libvirt plugin: Requested operation is not valid: domain is not running
[WARNING]: Unable to parse /home/csmart/ansible_collections/kvm.yml as an inventory source
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

I've also tested with use_connection_plugin: True and it works as expected.

Cheers!

Andersson007 commented 2 years ago

@csmart thanks for testing and the great report! @yorick1989 @odyssey4me any thoughts / related questions/updates?

odyssey4me commented 2 years ago

Aha, I get it now. I love the addition of the filter... that's a great idea! Then the ability to enable/disable the connection plugin seems like it makes sense. My apologies for the previous misunderstanding!

yorick1989 commented 2 years ago

Thank you all for the feedback!

@csmart Thank you for testing! I also tested it myself and it works for me:

[yorick@yorick-pc ansible]$ cat libvirt_qemu.yml 
plugin: community.libvirt.libvirt
uri: 'qemu:///system'
use_connection_plugin: False
#filter: "test-4[0-9]+"
[yorick@yorick-pc ansible]$ ansible -i libvirt_qemu.yml all -m ping
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
kali | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ansible@192.168.122.129: Permission denied (publickey,password).",
    "unreachable": true
}
test04 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
test01 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
test03 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

So, it's not clear to me why it failed on your side. The filter uses regex and by default the filter value is set to .*; so that should match every host by default.

Also thanks for the feedback in your first reply. Here my replies to your questions:

As you can see, I'm pretty new with this kind of modifications. I needed this modification to deploy / destroy my servers on a quickly manner using terraform. So any help to improve my changes is very welcome!

csmart commented 2 years ago

There's an issue with a request for filtering by VM state, for example, power off. I think if we're adding filtering here, it would be great to have this in mind so that we can filter by name (current) but also work in states at some point.