From nobody Mon Feb 9 19:31:44 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1625073150; cv=none; d=zohomail.com; s=zohoarc; b=dQrgrzk70BCuVxWO3tL7UTRNlsUNiXpiNtBCTtnpp2dDGhBMaGCeY/P5aWnhFcpGSEEpXXnif0l8YUKIeCXY7vpuClF64ar8x2SvvQcorWhV+u0Izxqgyv+S+jJoyeSNnyqvPZ3rRZxjfDLfKJ8ul/AeX//Z8BYn2GhOqpmZPGA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1625073150; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=dnVHAjjD+hrf0VY1woBC5OwILcgU9CKidsf3FVMWO5o=; b=Rs8eWFSeEoMNl8rv0OmOzkUHB8SdIPn78wEy6o1JFjZsnBtSHpHdU+swNkSXcgNun5DDVNyjcfBdLd75J3+jXrNLK8FCXWFdZOyIzO3hlwVDcVB3bTq1SGiFkfSvM1MhXmU+gFuBTDRUAQqBmbkUDwsU9+A+ZSfg+EIbebp0Gg8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 1625073150116888.796123912991; Wed, 30 Jun 2021 10:12:30 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-484-0Jhx1Bv9N0ex5LRMZ2tIGQ-1; Wed, 30 Jun 2021 13:11:53 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3BE6BCC648; Wed, 30 Jun 2021 17:11:26 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D8DA15D6AD; Wed, 30 Jun 2021 17:11:25 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id A7C2B4EA2A; Wed, 30 Jun 2021 17:11:25 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 15UGannO025790 for ; Wed, 30 Jun 2021 12:36:49 -0400 Received: by smtp.corp.redhat.com (Postfix) id 02C982026D46; Wed, 30 Jun 2021 16:36:49 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id F2EB8207B2C3 for ; Wed, 30 Jun 2021 16:36:43 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8F69718A01A0 for ; Wed, 30 Jun 2021 16:36:43 +0000 (UTC) Received: from mail-pj1-f70.google.com (mail-pj1-f70.google.com [209.85.216.70]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-531-D3vLZDY7MCmmEHk4inaNPA-1; Wed, 30 Jun 2021 12:36:42 -0400 Received: by mail-pj1-f70.google.com with SMTP id u12-20020a17090abb0cb029016ee12ec9a1so1758464pjr.3 for ; Wed, 30 Jun 2021 09:36:41 -0700 (PDT) Received: from localhost ([181.191.236.153]) by smtp.gmail.com with ESMTPSA id 10sm22479979pfh.174.2021.06.30.09.36.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 09:36:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1625073149; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=dnVHAjjD+hrf0VY1woBC5OwILcgU9CKidsf3FVMWO5o=; b=bWbHvbfbGT4mIiqi5SeK1kHDcT+SJ4VnXK+MRFOZczObVOmM4NDo30rBUlyiEK5DrbuZ8c nUcrkbduOr2B+yEfcYYiVQPq5ElFhQJgBhnEXCvf+fhuamUISMWO4hXlD3jEC/MNaW++my iaBsmxxlFuslxmCgjrcrmsjuMxX8yxw= X-MC-Unique: 0Jhx1Bv9N0ex5LRMZ2tIGQ-1 X-MC-Unique: D3vLZDY7MCmmEHk4inaNPA-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dnVHAjjD+hrf0VY1woBC5OwILcgU9CKidsf3FVMWO5o=; b=nQuX2/lxrDChPfxZ1HHz0SLz6D1QuCQREJ2MGrRjI9NMnkXzK6QBI5E21rOi1Z3Yct wtjBDPXnAbDNeN7j5tIC2h+WC2OeEhUpL/svQX6KTYPoEeaTs6g2rDssM1OK0nU/q2DZ ktAXtO4fh+Zd2CPupXr5pvlnMje9LfCu/ki3kPOEoXjNJLQAJHbyaINzWxqQdpSQ3VvQ enP7r3XHxFIDn/17lEPOyXy+TAMQXM7402Xbz8o7EGT4Jt/yo5rC9+bxqQGT7Wq34vcX 629pXUUZgKneDJxTk00j9EpKNYwRyfUBj3qfXMhk/BV+76Bm9dpS9P6r9wR+8bikKX7E hQ1g== X-Gm-Message-State: AOAM531Y8+aHJstwtwVE50aQ3CQ4ad09wKjZBTQS1HZSpSCSlBTaf8DC 2BVyQJka6bjylCzAqbz/olPqSq/ziJ7N1hS1oX8egi2ytT2dKfmENKh0dLYdk2mDnFz8DUgLBZJ D3LmTNzbQ6HeeGxmAvY2GL6/Fr1uz/kBy/oxFCpaUNickZDR/870bLwHndSsG0ksEGaU= X-Received: by 2002:a05:6a00:23d0:b029:2de:c1a2:f1e with SMTP id g16-20020a056a0023d0b02902dec1a20f1emr37054491pfc.60.1625071000566; Wed, 30 Jun 2021 09:36:40 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzU+O4M2FC69ptVQWan9uxTxsCLD8oPxW/SdColaeqGOalHAKRBkNpPV3+uIRlNSMyPXUH7mQ== X-Received: by 2002:a05:6a00:23d0:b029:2de:c1a2:f1e with SMTP id g16-20020a056a0023d0b02902dec1a20f1emr37054458pfc.60.1625071000115; Wed, 30 Jun 2021 09:36:40 -0700 (PDT) From: Beraldo Leal To: libvir-list@redhat.com Subject: [libvirt PATCH 1/4] tests: introduce lavocado: initial code structure Date: Wed, 30 Jun 2021 13:36:31 -0300 Message-Id: <20210630163634.2801636-2-bleal@redhat.com> In-Reply-To: <20210630163634.2801636-1-bleal@redhat.com> References: <20210630163634.2801636-1-bleal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-loop: libvir-list@redhat.com Cc: Beraldo Leal X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" lavocado aims to be an alternative test framework for the libvirt project using Python, python-libvirt and Avocado. This can be used to write unit, functional and integration tests and it is inspired by the libvirt-tck framework. Documentation, helper classes and templates will be provided to speed up common test writing scenarios. Signed-off-by: Beraldo Leal --- tests/lavocado/lavocado/__init__.py | 0 tests/lavocado/lavocado/defaults.py | 11 ++ tests/lavocado/lavocado/exceptions.py | 20 +++ tests/lavocado/lavocado/helpers/__init__.py | 0 tests/lavocado/lavocado/helpers/domains.py | 75 ++++++++++ tests/lavocado/lavocado/test.py | 144 ++++++++++++++++++++ tests/lavocado/requirements.txt | 3 + tests/lavocado/templates/domain.xml.jinja | 20 +++ 8 files changed, 273 insertions(+) create mode 100644 tests/lavocado/lavocado/__init__.py create mode 100644 tests/lavocado/lavocado/defaults.py create mode 100644 tests/lavocado/lavocado/exceptions.py create mode 100644 tests/lavocado/lavocado/helpers/__init__.py create mode 100644 tests/lavocado/lavocado/helpers/domains.py create mode 100644 tests/lavocado/lavocado/test.py create mode 100644 tests/lavocado/requirements.txt create mode 100644 tests/lavocado/templates/domain.xml.jinja diff --git a/tests/lavocado/lavocado/__init__.py b/tests/lavocado/lavocado/= __init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lavocado/lavocado/defaults.py b/tests/lavocado/lavocado/= defaults.py new file mode 100644 index 0000000000..47f1299cf4 --- /dev/null +++ b/tests/lavocado/lavocado/defaults.py @@ -0,0 +1,11 @@ +LIBVIRT_URI =3D "qemu:///system" + +TEMPLATE_PATH =3D "./templates/domain.xml.jinja" + +VMIMAGE =3D { + 'provider': 'fedora', + 'version': '33', + 'checksum': '67daa956d8c82ef799f8b0a191c1753c9bda3bca' + } + +CACHE_DIR =3D '/tmp/lavocado-cache' diff --git a/tests/lavocado/lavocado/exceptions.py b/tests/lavocado/lavocad= o/exceptions.py new file mode 100644 index 0000000000..d89cbb3eef --- /dev/null +++ b/tests/lavocado/lavocado/exceptions.py @@ -0,0 +1,20 @@ +# Copyright (C) 2021 Red Hat, Inc. +# Author: Beraldo Leal +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . + + +class TestSetupException(Exception): + pass diff --git a/tests/lavocado/lavocado/helpers/__init__.py b/tests/lavocado/l= avocado/helpers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/lavocado/lavocado/helpers/domains.py b/tests/lavocado/la= vocado/helpers/domains.py new file mode 100644 index 0000000000..cddee1b4b7 --- /dev/null +++ b/tests/lavocado/lavocado/helpers/domains.py @@ -0,0 +1,75 @@ +# Copyright (C) 2021 Red Hat, Inc. +# Author: Beraldo Leal +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . + + +import os + +from avocado.utils.genio import read_file +from jinja2 import Template + +from lavocado import defaults +from lavocado.exceptions import TestSetupException + + +class Domain: + @classmethod + def from_xml_path(cls, conn, xml_path): + """Create a new domain from a XML file. + + :param conn: a connection object to the Hypervisor. + :type conn: libvirt.virConnect + :param xml_path: XML file path. + :type xml_path: str + :returns: : the created domain object + :rtype: libvirt.virDomain + """ + xml_content =3D read_file(xml_path) + return conn.createXML(xml_content) + + @classmethod + def from_xml_template(cls, conn, suffix, arguments=3DNone): + """Create a new domain from the default XML template. + + This will use the `defaults.TEMPLATE_PATH` file, parsing some argu= ments + defined there. + + :param conn: a connection object to the Hypervisor. + :type conn: libvirt.virConnect + :param suffix: A suffix string to be added to the domain domain. + :type suffix: str + :param arguments: a key/value dict to be used during + template parse. currently supported keys are: na= me, + memory, vcpus, arch, machine and image. Visit the + template file for details. + :rtype arguments: dict + :returns: : the created domain object + :rtype: libvirt.virDomain + """ + template_path =3D defaults.TEMPLATE_PATH + arguments =3D arguments or {} + + if not os.path.isfile(template_path): + error =3D f"Template {template_path} not found." + raise TestSetupException(error) + + # Adding a suffix to the name + name =3D arguments.get('name', 'lavocado-test') + arguments['name'] =3D f"{name}-{suffix}" + + template =3D Template(read_file(template_path)) + xml_content =3D template.render(**arguments) + return conn.createXML(xml_content) diff --git a/tests/lavocado/lavocado/test.py b/tests/lavocado/lavocado/test= .py new file mode 100644 index 0000000000..b77ecaed58 --- /dev/null +++ b/tests/lavocado/lavocado/test.py @@ -0,0 +1,144 @@ +# Copyright (C) 2021 Red Hat, Inc. +# Author: Beraldo Leal +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . + +"""Basic test helper module to avoid code redundancy.""" + +import os +import libvirt + +from avocado import Test +from avocado.utils import vmimage + +from lavocado import defaults +from lavocado.exceptions import TestSetupException +from lavocado.helpers.domains import Domain + + +class LibvirtTest(Test): + """Main class helper for tests. + + Any test that inherits from this class, will have some methods and + properties to assist on their jobs. + """ + def setUp(self): + """Setup to be executed before each test. + + Currently, this method is creating just a basic hypervisor connect= ion. + Please, extend this method when writing your tests for your own ne= eds. + + Any error that happens here *will not* flag the test as "FAIL", in= stead + tests will be flagged as "ERROR", meaning that some bootstrap erro= r has + happened. + """ + self.defaults =3D defaults + self.conn =3D self.connect() + + def connect(self): + """Try to open a new connection with the hypervisor. + + This method uses the value defined at `defaults.LIBVIRT_URI` as UR= I. + + :returns: a libvirt connection. + :rtype: libvirt.virConnect + """ + try: + return libvirt.open(self.defaults.LIBVIRT_URI) + except libvirt.libvirtError: + msg =3D ("Failed to open connection with the hypervisor using " + + self.defaults.LIBVIRT_URI) + self.cancel(msg) + + def create_domain(self, arguments=3DNone): + """Creates a libvirt domain based on the default template. + + This will receive some arguments that will be rendered on the + template. For more information about the arguments, see + templates/domain.xml.jinja. For now, at least the 'image' argument= must + be informed, with the path for the image to boot. + + If you are using this method from a test method (different from + setUp()), AND you would like to count its call as a "setup/bootstr= ap" + stage, consider using the following Avocado decorator: + + from avocado.core.decorators import cancel_on + + @cancel_on(TestSetupException) + def test_foo(self): + ... + + In that way, your test will not FAIL, instead it will be cancelled= in + case of any problem during this bootstrap. + + :param dict arguments: A key,value dictionary with the arguments + to be replaced on the template. If + any missing argument, template will be + rendered with default values. + """ + try: + return Domain.from_xml_template(self.conn, self.id(), argument= s) + # This will catch any avocado exception plus any os error + except Exception as ex: + msg =3D f"Failed to create domain: {ex}" + raise TestSetupException(msg) from ex + + def get_generic_image(self): + """Ask Avocado to fetch an VM image snapshot. + + Avocado will handle if image is already downloaded into the + cache dir and also will make sure the checksum is matching. + + This will return an Image object pointing to a snapshot file. So + multiple calls of this method will never return the same object. + + If you are using this method from a test method (different from + setUp()), AND you would like to count its call as a "setup/bootstr= ap" + stage, consider using the following Avocado decorator: + + from avocado.core.decorators import cancel_on + + @cancel_on(TestSetupException) + def test_foo(self): + ... + + In that way, your test will not FAIL, instead it will be cancelled= in + case of any problem during this bootstrap. + """ + image =3D self.defaults.VMIMAGE + try: + return vmimage.get(name=3Dimage.get('provider'), + version=3Dimage.get('version'), + cache_dir=3Dself.defaults.CACHE_DIR, + checksum=3Dimage.get('checksum')) + # This will catch any error, including avocado exceptions + OS err= ors + except Exception as ex: + msg =3D f"Failed to get a generic image: {ex}" + raise TestSetupException(msg) from ex + + def tearDown(self): + """Shutdown after each test. + + This will destroy all previously created domains by this test, and + remove any image snapshot if created. + """ + for domain in self.conn.listAllDomains(): + if domain.name().endswith(self.id()): + domain.destroy() + + if hasattr(self, 'image') and isinstance(self.image, vmimage.Image= ): + if os.path.exists(self.image.path): + os.remove(self.image.path) + self.conn.close() diff --git a/tests/lavocado/requirements.txt b/tests/lavocado/requirements.= txt new file mode 100644 index 0000000000..6927528323 --- /dev/null +++ b/tests/lavocado/requirements.txt @@ -0,0 +1,3 @@ +git+https://github.com/avocado-framework/avocado@8c87bfe5e8a1895d772260644= 33453f158a3ce56#egg=3Davocado_framework +libvirt-python +Jinja2 diff --git a/tests/lavocado/templates/domain.xml.jinja b/tests/lavocado/tem= plates/domain.xml.jinja new file mode 100644 index 0000000000..a7bd57e5b0 --- /dev/null +++ b/tests/lavocado/templates/domain.xml.jinja @@ -0,0 +1,20 @@ + + {{name|default("lavocado-test")}} + {{memory|default(4194304)}} + {{vcpus|default(4)}} + + hvm + + + + /usr/bin/qemu-system-x86_64 + + + + + + + + + + --=20 2.26.3