Browse Source

tests: implement infrastructure_driver libvirt

Fixes: main/infrastructure#268
keep-around/68a3c3746a6b9a97ade4ac05826340644ed27ed2
Loïc Dachary 10 months ago
committed by some
parent
commit
fd8cc4cdd9
Signed by: dachary GPG Key ID: 992D23B392F9E4F2
  1. 7
      docs/community/contribute.rst
  2. 140
      enough/common/libvirt.py
  3. 1
      inventory/group_vars/all/infrastructure.yml
  4. 9
      tests/conftest.py
  5. 19
      tests/enough/common/test_libvirt.py
  6. 2
      tests/run-tests.sh
  7. 2
      tests/tox.dockerfile

7
docs/community/contribute.rst

@ -56,6 +56,13 @@ Debian GNU/Linux buster. The following volumes are bind-mounted:
The working directory, in the container, is the root of the
`infrastructure` repository.
Installing libvirt
~~~~~~~~~~~~~~~~~~
Manually run commands similar to
`playbooks/gitlab/roles/gitlab-ci/tasks/gitlab-ci.yml` (it could be a
playbook running on localhost with sudo sudo ?)
Running tests that do not require OpenStack
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

140
enough/common/libvirt.py

@ -0,0 +1,140 @@
import json
import libvirt
import logging
import os
import sh
import textwrap
from enough import settings
log = logging.getLogger(__name__)
class Libvirt(object):
EXTERNAL_NETWORK = 'enough-external'
EXTERNAL_NETWORK_PREFIX = '10.23.10'
INTERNAL_NETWORK = 'enough-internal'
INTERNAL_NETWORK_PREFIX = '10.23.90'
def __init__(self, config_dir, **kwargs):
self.args = kwargs
self.config_dir = config_dir
self.lv = libvirt.open('qemu:///system')
def get_stack_definitions(self, share_dir=settings.SHARE_DIR):
args = ['-i', f'{share_dir}/inventory',
'-i', f'{self.config_dir}/inventory']
if self.args.get('inventory'):
args.extend([f'--inventory={i}' for i in self.args['inventory']])
args.extend(['--vars', '--list'])
password_file = f'{self.config_dir}.pass'
if os.path.exists(password_file):
args.extend(['--vault-password-file', password_file])
r = sh.ansible_inventory(*args)
inventory = json.loads(r.stdout)
return inventory['_meta']['hostvars']
def get_stack_definition(self, host):
h = self.get_stack_definitions()[host]
if h.get('network_internal_only', False):
network_interface_unconfigured = h.get('network_primary_interface', 'eth0')
network_interface_routed = h.get('network_secondary_interface', 'eth1')
network_interface_not_routed = 'noname'
else:
network_interface_unconfigured = 'noname'
network_interface_routed = h.get('network_primary_interface', 'eth0')
network_interface_not_routed = h.get('network_secondary_interface', 'eth1')
definition = {
'name': host,
'port': h.get('ansible_port', '22'),
'network': h.get('libvirt_network', Libvirt.EXTERNAL_NETWORK),
'network_prefix': h.get('libvirt_network_prefix',
Libvirt.EXTERNAL_NETWORK_PREFIX),
'network_internal_only': h.get('network_internal_only', False),
'network_interface_unconfigured': network_interface_unconfigured,
'network_interface_routed': network_interface_routed,
'network_interface_not_routed': network_interface_not_routed,
'internal_network': h.get('libvirt_network_internal', Libvirt.INTERNAL_NETWORK),
'internal_network_prefix': h.get('libvirt_network_internal_prefix',
Libvirt.INTERNAL_NETWORK_PREFIX),
}
if 'openstack_volumes' in h:
definition['volumes'] = h['openstack_volumes']
return definition
def virt_builder(self):
image = f'{self.config_dir}/debian-10.qcow2'
if self.path.exists(image):
return False
sh.virt_builder(
'debian-10'
'--output', image,
'--format', 'qcow2',
'--size', '20G',
'--install', 'sudo',
'--run-command', 'dpkg-reconfigure --frontend=noninteractive openssh-server',
'--run-command', ('useradd -s /bin/bash -m debian || true ; '
'echo "debian ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-debian'),
'--edit', '/etc/network/interfaces: s/ens2/enp1s0/')
return True
def network_host_definition(self, host, mac, ip):
return f"<host mac='{mac}' name='{host}' ip='{ip}'/>"
def network_host_set(self, name, host, mac, ip):
network = self.lv.networkLookupByName(name)
xml = self.network_host_definition(host, mac, ip)
if xml in network.XMLDesc():
return False
network.update(libvirt.VIR_NETWORK_UPDATE_COMMAND_ADD_LAST,
libvirt.VIR_NETWORK_SECTION_IP_DHCP_HOST,
-1,
xml,
libvirt.VIR_NETWORK_UPDATE_AFFECT_CURRENT)
return True
def network_host_unset(self, name, host, mac, ip):
network = self.lv.networkLookupByName(name)
xml = self.network_host_definition(host, mac, ip)
if xml not in network.XMLDesc():
return False
network.update(libvirt.VIR_NETWORK_UPDATE_COMMAND_DELETE,
libvirt.VIR_NETWORK_SECTION_IP_DHCP_HOST,
-1,
xml,
libvirt.VIR_NETWORK_UPDATE_AFFECT_CURRENT)
return True
def network_create(self, name, prefix):
if name not in self.lv.listNetworks():
network = textwrap.dedent(f"""
<network>
<name>{name}</name>
<forward mode='nat'/>
<bridge name='virbr{name}' stp='on' delay='0'/>
<ip address='{prefix}.1' netmask='255.255.255.0'>
<dhcp>
<range start='{prefix}.100' end='{prefix}.254'/>
</dhcp>
</ip>
</network>
""")
network = self.lv.networkDefineXML(network)
network.create()
network.autostart()
else:
network = self.lv.networkLookupByName(name)
return network
def network_destroy(self, name):
if name in self.lv.listNetworks():
self.lv.networkLookupByName(name).destroy()
return True
else:
return False
def destroy_everything(self, prefix):
for network in self.lv.listNetworks():
if prefix in network:
self.network_destroy(network)

1
inventory/group_vars/all/infrastructure.yml

@ -2,4 +2,5 @@
#
# Server provisioning infrastructure
#
# infrastructure_driver: libvirt
infrastructure_driver: openstack

9
tests/conftest.py

@ -8,6 +8,7 @@ from enough import settings
from enough.common import tcp
from enough.common.retry import retry
from enough.common.openstack import OpenStack
from enough.common import libvirt
from tests import prepare_config_dir
@ -89,6 +90,14 @@ def openstack_name():
o.destroy_everything(prefix)
@pytest.fixture
def libvirt_name():
prefix = 'et' + str(int(time.time()))[3:]
yield prefix
lv = libvirt.Libvirt('.')
lv.destroy_everything(prefix)
@pytest.fixture
def tmp_config_dir(monkeypatch, tmpdir):
enough_dot_dir = str(tmpdir)

19
tests/enough/common/test_libvirt.py

@ -0,0 +1,19 @@
from enough.common import libvirt
def test_libvirt_network(libvirt_name):
lv = libvirt.Libvirt('.')
prefix = '10.2.3'
network = lv.network_create(libvirt_name, prefix)
assert network.name() == libvirt_name
assert network.name() == lv.network_create(libvirt_name, prefix).name()
n = '10'
ip = f'{prefix}.{n}'
mac = f'52:54:00:00:00:{n}'
host = 'sample-host'
assert lv.network_host_set(libvirt_name, host, mac, ip) is True
assert lv.network_host_set(libvirt_name, host, mac, ip) is False
assert lv.network_host_unset(libvirt_name, host, mac, ip) is True
assert lv.network_host_unset(libvirt_name, host, mac, ip) is False
assert lv.network_destroy(libvirt_name) is True
assert lv.network_destroy(libvirt_name) is False

2
tests/run-tests.sh

@ -98,7 +98,7 @@ function run_tests() {
local interactive=-ti
fi
container_name="$name-$(date +%s)"
docker run $interactive --rm --device=/dev/net/tun --name $container_name --user "${USER:-root}" -e PYTEST_ADDOPTS="${PYTEST_ADDOPTS-}" -e ENOUGH_API_TOKEN=$ENOUGH_API_TOKEN ${PASS_GITLAB_CI} --cap-add=NET_ADMIN $args --volume /var/run/docker.sock:/var/run/docker.sock $name "${@:-tox}"
docker run $interactive --rm --device=/dev/net/tun --name $container_name --user "${USER:-root}" -e PYTEST_ADDOPTS="${PYTEST_ADDOPTS-}" -e ENOUGH_API_TOKEN=$ENOUGH_API_TOKEN ${PASS_GITLAB_CI} --cap-add=NET_ADMIN $args --volume /run/libvirt/libvirt-sock:/run/libvirt/libvirt-sock --volume /var/run/docker.sock:/var/run/docker.sock $name "${@:-tox}"
}
function main() {

2
tests/tox.dockerfile

@ -6,6 +6,8 @@ RUN apt-get install -y python
# BEGIN dependencies of test/ssh
RUN pip install tox yq
RUN apt-get install -y jq
RUN apt-get install -y libguestfs-tools virtinst python-libvirt python-lxml pkg-config libvirt-dev # redundant with playbooks/gitlab/roles/gitlab-ci-libvirt/tasks/main.yml to speed up the run
RUN apt-get install -y python3-apt python3-libvirt python3-lxml # required because python3 is used not python2
# END dependencies of test/ssh
RUN git init
COPY requirements.txt requirements-dev.txt tox.ini setup.cfg setup.py README.md /opt/

Loading…
Cancel
Save