From 7d698b4890b99b721ead7c0147177c39bcdb4cfc Mon Sep 17 00:00:00 2001 From: Lucas Meneghel Rodrigues Date: Wed, 3 Jun 2015 19:02:42 -0300 Subject: [PATCH] Virt Test Compatibility layer: Initial import This is the base code for the virt test compatibility layer. We arrived at this initial version of the code after significant work and previous reviews. Basically the compatibility layer aims to let people run virt-test tests inside avocado, giving them avocado specific features, and letting them to get used to the new avocado world. Changes from v3 (v13): * Fixed Ruda's comment about imp import; * Made the entire test source/runner plugin code to fit into 80 columns. Changes from v1 (v11): * The LICENSE file was fixed to state that the files inside the plugin are GPLv2+ licensed (GPLv2 or later), per adereis's comments. Signed-off-by: Lucas Meneghel Rodrigues --- .gitignore | 17 + .travis.yml | 11 + LICENSE | 344 +++++++ MANIFEST.in | 2 + Makefile | 51 + README.md | 6 + avocado-plugins-vt.spec | 39 + avocado/core/plugins/virt_test.py | 1246 ++++++++++++++++++++++++ avocado/core/plugins/virt_test_list.py | 98 ++ debian/changelog | 6 + debian/compat | 2 + debian/control | 13 + debian/copyright | 29 + debian/pyversions | 1 + debian/rules | 11 + debian/source/format | 1 + debian/source/options | 1 + debian/watch | 3 + docs/Makefile | 154 +++ docs/build/.empty | 0 docs/source/Introduction.rst | 12 + docs/source/_static/.gitignore | 0 docs/source/_templates/.gitignore | 0 docs/source/conf.py | 70 ++ docs/source/index.rst | 22 + etc/avocado/conf.d/virt-test.conf | 74 ++ requirements-travis-python26.txt | 1 + requirements-travis.txt | 2 + selftests/checkall | 15 + setup.py | 58 ++ 30 files changed, 2289 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 avocado-plugins-vt.spec create mode 100644 avocado/core/plugins/virt_test.py create mode 100644 avocado/core/plugins/virt_test_list.py create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/pyversions create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/source/options create mode 100644 debian/watch create mode 100644 docs/Makefile create mode 100644 docs/build/.empty create mode 100644 docs/source/Introduction.rst create mode 100644 docs/source/_static/.gitignore create mode 100644 docs/source/_templates/.gitignore create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 etc/avocado/conf.d/virt-test.conf create mode 100644 requirements-travis-python26.txt create mode 100644 requirements-travis.txt create mode 100755 selftests/checkall create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..588627260e --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +.project +.pydevproject +.settings +.coverage +.idea/* +coverage.xml +xunit.xml +tmp/* +logs/* +*.pyc +*~ +tags +cscope.* +build +sysinfo-* +MANIFEST +dist diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..ab4a976b24 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "2.7" + - "2.6" + +install: + - pip install -r requirements-travis.txt + - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install -r requirements-travis-python26.txt; fi + +script: + - make check diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..88820ef5d5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,344 @@ +Unless explicitly otherwise stated, all files in the avocado-vt repository +are covered by the GPLv2+ (i.e: v2, or any later version). + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000..04f196ac78 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include README.md +include LICENSE diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..00a0700b84 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +PYTHON=`which python` +DESTDIR=/ +BUILDIR=$(CURDIR)/debian/avocado-virt +PROJECT=avocado +VERSION="0.24.0" + +all: + @echo "make source - Create source package" + @echo "make install - Install on local system" + @echo "make build-deb-src - Generate a source debian package" + @echo "make build-deb-bin - Generate a binary debian package" + @echo "make build-deb-all - Generate both source and binary debian packages" + @echo "make build-rpm-all - Generate both source and binary RPMs" + @echo "make check - Runs static checks in the source code" + @echo "make clean - Get rid of scratch and byte files" + +source: + $(PYTHON) setup.py sdist $(COMPILE) --dist-dir=SOURCES --prune + +install: + $(PYTHON) setup.py install --root $(DESTDIR) $(COMPILE) + +prepare-source: + # build the source package in the parent directory + # then rename it to project_version.orig.tar.gz + dch -D "trusty" -M -v "$(VERSION)" "Automated (make builddeb) build." + $(PYTHON) setup.py sdist $(COMPILE) --dist-dir=../ --prune + rename -f 's/$(PROJECT)-(.*)\.tar\.gz/$(PROJECT)_$$1\.orig\.tar\.gz/' ../* + +build-deb-src: prepare-source + # build the source package + dpkg-buildpackage -S -elookkas@gmail.com -rfakeroot + +build-deb-bin: prepare-source + # build binary package + dpkg-buildpackage -b -rfakeroot + +build-deb-all: prepare-source + # build both source and binary packages + dpkg-buildpackage -i -I -rfakeroot + +build-rpm-all: source + rpmbuild --define '_topdir %{getenv:PWD}' \ + -ba avocado-plugins-vt.spec +check: + selftests/checkall +clean: + $(PYTHON) setup.py clean + $(MAKE) -f $(CURDIR)/debian/rules clean || true + rm -rf build/ MANIFEST BUILD BUILDROOT SPECS RPMS SRPMS SOURCES + find . -name '*.pyc' -delete diff --git a/README.md b/README.md index 5fd0687fa9..6e07172365 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # avocado-vt Avocado Virt Test Compatibility Layer Plugin + +Avocado Virt Test Compatibility is a plugin that lets you +execute tests from the virt test suite +(http://virt-test.readthedocs.org/en/latest/), with all +the avocado convenience features, such as HTML report, +Xunit output, among others. diff --git a/avocado-plugins-vt.spec b/avocado-plugins-vt.spec new file mode 100644 index 0000000000..e9c874baab --- /dev/null +++ b/avocado-plugins-vt.spec @@ -0,0 +1,39 @@ +Summary: Avocado Virt Test Compatibility Plugin +Name: avocado-plugins-vt +Version: 0.24.0 +Release: 2%{?dist} +License: GPLv2 +Group: Development/Tools +URL: http://avocado-framework.readthedocs.org/ +Source: avocado-plugins-vt-%{version}.tar.gz +BuildRequires: python2-devel +BuildArch: noarch +Requires: python, avocado + +%description +Avocado Virt Test Compatibility is a plugin that lets you +execute tests from the virt test suite +(http://virt-test.readthedocs.org/en/latest/), with all +the avocado convenience features, such as HTML report, +Xunit output, among others. + +%prep +%setup -q + +%build +%{__python} setup.py build + +%install +%{__python} setup.py install --root %{buildroot} --skip-build + +%files +%defattr(-,root,root,-) +%dir /etc/avocado +%dir /etc/avocado/conf.d +%config(noreplace)/etc/avocado/conf.d/virt-test.conf +%doc README.md LICENSE +%{python_sitelib}/avocado* + +%changelog +* Wed Jun 3 2015 Lucas Meneghel Rodrigues - 0.24.0-2 +- First version of the compatibility layer plugin diff --git a/avocado/core/plugins/virt_test.py b/avocado/core/plugins/virt_test.py new file mode 100644 index 0000000000..a2b326cb37 --- /dev/null +++ b/avocado/core/plugins/virt_test.py @@ -0,0 +1,1246 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 LICENSE for more details. +# +# Copyright: Red Hat Inc. 2015 +# Author: Lucas Meneghel Rodrigues + +""" +Avocado virt-test compatibility wrapper +""" + +import os +import sys +import logging +import Queue +import time +import imp + +from avocado.core import result +from avocado.core import loader +from avocado.core import output +from avocado.core import exceptions +from avocado.core.plugins import plugin +from avocado import test +from avocado.settings import settings +from avocado.utils import path +from avocado import multiplexer + +# virt-test supports using autotest from a git checkout, so we'll have to +# support that as well. The code below will pick up the environment variable +# $AUTOTEST_PATH and do the import magic needed to make the autotest library +# available in the system. +AUTOTEST_PATH = None + +if 'AUTOTEST_PATH' in os.environ: + AUTOTEST_PATH = os.path.expanduser(os.environ['AUTOTEST_PATH']) + client_dir = os.path.join(os.path.abspath(AUTOTEST_PATH), 'client') + setup_modules_path = os.path.join(client_dir, 'setup_modules.py') + setup_modules = imp.load_source('autotest_setup_modules', + setup_modules_path) + setup_modules.setup(base_path=client_dir, + root_module_name="autotest.client") + +from autotest.client import utils +from autotest.client.shared import error + +# The code below is used by this plugin to find the virt test directory, +# so that it can load the virttest python lib, used by the plugin code. +# If the user doesn't provide the proper configuration, the plugin will +# fail to load. +VIRT_TEST_PATH = None + +if 'VIRT_TEST_PATH' in os.environ: + VIRT_TEST_PATH = os.environ['VIRT_TEST_PATH'] +else: + VIRT_TEST_PATH = settings.get_value(section='virt_test', + key='virt_test_path', default=None) + +if VIRT_TEST_PATH is not None: + sys.path.append(os.path.expanduser(VIRT_TEST_PATH)) + +from virttest import asset +from virttest import bootstrap +from virttest import cartesian_config +from virttest import data_dir +from virttest import defaults +from virttest import env_process +from virttest import funcatexit +from virttest import standalone_test +from virttest import utils_env +from virttest import utils_misc +from virttest import utils_params +from virttest import version +from virttest import storage + +from virttest.standalone_test import SUPPORTED_TEST_TYPES +from virttest.standalone_test import SUPPORTED_LIBVIRT_URIS +from virttest.standalone_test import SUPPORTED_LIBVIRT_DRIVERS +from virttest.standalone_test import SUPPORTED_IMAGE_TYPES +from virttest.standalone_test import SUPPORTED_DISK_BUSES +from virttest.standalone_test import SUPPORTED_NIC_MODELS +from virttest.standalone_test import SUPPORTED_NET_TYPES +from virttest.standalone_test import QEMU_DEFAULT_SET +from virttest.standalone_test import LIBVIRT_DEFAULT_SET +from virttest.standalone_test import LVSB_DEFAULT_SET +from virttest.standalone_test import OVS_DEFAULT_SET +from virttest.standalone_test import LIBVIRT_INSTALL +from virttest.standalone_test import LIBVIRT_REMOVE + + +class VirtTestResult(result.HumanTestResult): + + """ + Virt Test compatibility layer Result class. + """ + + def __init__(self, stream, args): + """ + Creates an instance of RemoteTestResult. + + :param stream: an instance of :class:`avocado.core.output.View`. + :param args: an instance of :class:`argparse.Namespace`. + """ + result.HumanTestResult.__init__(self, stream, args) + self.output = '-' + self.setup() + + def setup(self): + """ + Run the setup needed before tests start to run (restore test images). + """ + options = self.args + if options.vt_config: + parent_config_dir = os.path.dirname( + os.path.dirname(options.vt_config)) + parent_config_dir = os.path.dirname(parent_config_dir) + options.vt_type = parent_config_dir + + kwargs = {'options': options} + + failed = False + + bg = utils.InterruptedThread(bootstrap.setup, kwargs=kwargs) + t_begin = time.time() + bg.start() + + self.stream.notify(event='message', msg="SETUP : ", + skip_newline=True) + while bg.isAlive(): + self.stream.notify_progress(True) + time.sleep(0.1) + + reason = None + try: + bg.join() + except Exception, e: + failed = True + reason = e + + t_end = time.time() + t_elapsed = t_end - t_begin + state = dict() + state['time_elapsed'] = t_elapsed + if not failed: + self.stream.set_test_status(status='PASS', state=state) + else: + self.stream.set_test_status(status='FAIL', state=state) + self.stream.notify(event='error', msg="Setup error: %s" % reason) + sys.exit(-1) + + return True + + +def configure_console_logging(loglevel=logging.DEBUG): + """ + Simple helper for adding a file logger to the root logger. + """ + logger = logging.getLogger() + stream_handler = logging.StreamHandler(sys.stdout) + stream_handler.setLevel(loglevel) + + fmt = ('%(asctime)s %(module)-10.10s L%(lineno)-.4d %(' + 'levelname)-5.5s| %(message)s') + formatter = logging.Formatter(fmt=fmt, datefmt='%H:%M:%S') + + stream_handler.setFormatter(formatter) + logger.addHandler(stream_handler) + + return stream_handler + + +def configure_file_logging(logfile, loglevel=logging.DEBUG): + """ + Add a file logger to the root logger. + + This file logger contains the formatting of the avocado + job log. This way all things logged by autotest go + straight to the avocado job log. + """ + logger = logging.getLogger() + file_handler = logging.FileHandler(filename=logfile) + file_handler.setLevel(loglevel) + fmt = ('%(asctime)s %(module)-10.10s L%(lineno)-.4d %(' + 'levelname)-5.5s| %(message)s') + formatter = logging.Formatter(fmt=fmt, datefmt='%H:%M:%S') + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + return file_handler + + +def guest_listing(options, view): + term_support = output.TermSupport() + if options.vt_type == 'lvsb': + view.notify(event='error', + msg="No guest types available for lvsb testing") + sys.exit(1) + index = 0 + view.notify(event='minor', msg=("Searched %s for guest images\n" % + os.path.join(data_dir.get_data_dir(), + 'images'))) + view.notify(event='minor', msg="Available guests in config:") + view.notify(msg='') + guest_name_parser = standalone_test.get_guest_name_parser(options) + guest_name_parser.only_filter('i440fx') + for params in guest_name_parser.get_dicts(): + index += 1 + base_dir = params.get("images_base_dir", data_dir.get_data_dir()) + image_name = storage.get_image_filename(params, base_dir) + name = params['name'] + if os.path.isfile(image_name): + out = name + else: + out = (name + " " + + term_support.warn_header_str("(missing %s)" % + os.path.basename(image_name))) + view.notify(event='minor', msg=out) + + +class VirtTestListJob(object): + + """ + Mock Job class, used to provide test loaders with a Job object with enough + options data for test listing purposes. + """ + + def __init__(self, args): + self.view = output.View() + self.logfile = None + self.logdir = '.' + self.args = args + self.args.vt_config = None + self.args.vt_verbose = True + self.args.vt_log_level = 'debug' + self.args.vt_console_level = 'debug' + self.args.vt_datadir = data_dir.get_data_dir() + self.args.vt_config = None + self.args.vt_arch = None + self.args.vt_machine_type = None + self.args.vt_keep_guest_running = False + self.args.vt_keep_image_between_tests = False + self.args.vt_mem = 1024 + self.args.vt_no_filter = '' + self.args.vt_qemu_bin = None + self.args.vt_dst_qemu_bin = None + self.args.vt_nettype = 'user' + self.args.vt_only_type_specific = False + self.args.vt_tests = '' + self.args.vt_connect_uri = 'qemu:///system' + self.args.vt_accel = 'kvm' + self.args.vt_monitor = 'human' + self.args.vt_smp = 1 + self.args.vt_image_type = 'qcow2' + self.args.vt_nic_model = 'virtio_net' + self.args.vt_disk_bus = 'virtio_blk' + self.args.vt_vhost = 'off' + self.args.vt_malloc_perturb = 'yes' + self.args.vt_qemu_sandbox = 'on' + self.args.vt_tests = '' + self.args.show_job_log = False + self.args.test_lister = True + + +class VirtTestLoader(loader.TestLoader): + + def __init__(self, job=None, args=None): + if job is None: + job = VirtTestListJob(args=args) + loader.TestLoader.__init__(self, job=job) + standalone_test.reset_logging() + if self.job.args.show_job_log: + configure_console_logging() + else: + if job.logfile is not None: + configure_file_logging(logfile=job.logfile) + + def _get_parser(self): + options_processor = VirtTestOptionsProcess(self.job) + return options_processor.get_parser() + + def get_extra_listing(self, args): + if args.vt_list_guests: + use_paginator = args.paginator == 'on' + try: + view = output.View(use_paginator=use_paginator) + guest_listing(args, view) + finally: + view.cleanup() + sys.exit(0) + + def get_base_keywords(self): + """ + Get base keywords to use (when no keywords are passed to 'list'). + + Used to list all tests available in virt-test. + + :return: list with keyword strings. + """ + return ['vt_list_all'] + + def get_type_label_mapping(self): + """ + Get label mapping for display in test listing. + + :return: Dict {TestClass: 'TEST_LABEL_STRING'} + """ + return {VirtTest: 'VT'} + + def get_decorator_mapping(self): + """ + Get label mapping for display in test listing. + + :return: Dict {TestClass: decorator function} + """ + term_support = output.TermSupport() + return {VirtTest: term_support.healthy_str} + + def discover(self, params_list): + """ + Discover tests for test suite. + + :param params_list: a list of test parameters. + :type params_list: list + :return: a test suite (a list of test factories). + """ + test_suite = [] + for params in params_list: + # We want avocado to inject params coming from its multiplexer into + # the test params. This will allow users to access avocado params + # from inside virt tests. This feature would only work if the virt + # test in question is executed from inside avocado. + params['avocado_inject_params'] = True + test_name = params.get("_short_name_map_file")["subtests.cfg"] + params['id'] = test_name + test_parameters = {'name': test_name, + 'base_logdir': self.job.logdir, + 'params': params, + 'job': self.job} + test_suite.append((VirtTest, test_parameters)) + return test_suite + + def validate_ui(self, test_suite, ignore_missing=False, + ignore_not_test=False, ignore_broken_symlinks=False, + ignore_access_denied=False): + del test_suite + del ignore_missing + del ignore_not_test + del ignore_broken_symlinks + del ignore_access_denied + return [] + + def discover_url(self, url): + cartesian_parser = self._get_parser() + if url != 'vt_list_all': + cartesian_parser.only_filter(url) + params_list = [t for t in cartesian_parser.get_dicts()] + return params_list + + +class VirtTest(test.Test): + + """ + Mininal test class used to run a virt test. + """ + + env_version = utils_env.get_env_version() + + def __init__(self, methodName='runTest', name=None, params=None, + base_logdir=None, tag=None, job=None, runner_queue=None): + del name + options = job.args + + self.bindir = data_dir.get_root_dir() + self.virtdir = os.path.join(self.bindir, 'shared') + self.builddir = os.path.join(self.bindir, 'backends', + params.get("vm_type")) + self.srcdir = path.init_dir(os.path.join(self.builddir, 'src')) + self.tmpdir = path.init_dir(os.path.join(self.bindir, 'tmp')) + + self.iteration = 0 + if options.vt_config: + name = params.get("shortname") + else: + name = params.get("_short_name_map_file")["subtests.cfg"] + self.outputdir = None + self.resultsdir = None + self.logfile = None + self.file_handler = None + self.background_errors = Queue.Queue() + self.whiteboard = None + super(VirtTest, self).__init__(methodName=methodName, name=name, + params=params, base_logdir=base_logdir, + tag=tag, job=job, + runner_queue=runner_queue) + self.params = utils_params.Params(params) + # Here we turn the data the multiplexer injected into the params and + # turn it into an AvocadoParams object, that will allow users to + # access data from it. Example: + # sleep_length = test.avocado_params.get('sleep_length', default=1) + p = params.get('avocado_params', None) + if p is not None: + params, mux_entry = p[0], p[1] + else: + params, mux_entry = [], [] + self.avocado_params = multiplexer.AvocadoParams(params, self.name, + self.tag, + mux_entry, + self.default_params) + + self.debugdir = self.logdir + utils_misc.set_log_file_dir(self.logdir) + + def start_logging(self): + super(VirtTest, self).start_logging() + root_logger = logging.getLogger() + root_logger.addHandler(self.file_handler) + + def stop_logging(self): + super(VirtTest, self).stop_logging() + root_logger = logging.getLogger() + root_logger.removeHandler(self.file_handler) + + def write_test_keyval(self, d): + self.whiteboard = str(d) + + def verify_background_errors(self): + """ + Verify if there are any errors that happened on background threads. + + :raise Exception: Any exception stored on the background_errors queue. + """ + try: + exc = self.background_errors.get(block=False) + except Queue.Empty: + pass + else: + raise exc[1], None, exc[2] + + def runTest(self): + try: + self._runTest() + # This trick will give better reporting of virt tests + # being executed into avocado (skips and errors will display correctly) + except error.TestNAError, details: + raise exceptions.TestNAError(details) + except error.TestError, details: + raise exceptions.TestError(details) + + def _runTest(self): + params = self.params + + # If a dependency test prior to this test has failed, let's fail + # it right away as TestNA. + if params.get("dependency_failed") == 'yes': + raise error.TestNAError("Test dependency failed") + + # Report virt test version + logging.info(version.get_pretty_version_info()) + # Report the parameters we've received and write them as keyvals + logging.info("Starting test %s", self.tag) + logging.debug("Test parameters:") + keys = params.keys() + keys.sort() + for key in keys: + logging.debug(" %s = %s", key, params[key]) + + # Warn of this special condition in related location in output & logs + if os.getuid() == 0 and params.get('nettype', 'user') == 'user': + logging.warning("") + logging.warning("Testing with nettype='user' while running " + "as root may produce unexpected results!!!") + logging.warning("") + + # Open the environment file + env_filename = os.path.join( + data_dir.get_backend_dir(params.get("vm_type")), + params.get("env", "env")) + env = utils_env.Env(env_filename, self.env_version) + + test_passed = False + t_type = None + + try: + try: + try: + subtest_dirs = [] + test_filter = bootstrap.test_filter + + other_subtests_dirs = params.get("other_tests_dirs", "") + for d in other_subtests_dirs.split(): + d = os.path.join(*d.split("/")) + subtestdir = os.path.join(self.bindir, d, "tests") + if not os.path.isdir(subtestdir): + raise error.TestError("Directory %s does not " + "exist" % subtestdir) + subtest_dirs += data_dir.SubdirList(subtestdir, + test_filter) + + provider = params.get("provider", None) + + if provider is None: + # Verify if we have the correspondent source file for + # it + generic_subdirs = asset.get_test_provider_subdirs( + 'generic') + for generic_subdir in generic_subdirs: + subtest_dirs += data_dir.SubdirList(generic_subdir, + test_filter) + specific_subdirs = asset.get_test_provider_subdirs( + params.get("vm_type")) + for specific_subdir in specific_subdirs: + subtest_dirs += data_dir.SubdirList( + specific_subdir, bootstrap.test_filter) + else: + provider_info = asset.get_test_provider_info(provider) + for key in provider_info['backends']: + subtest_dirs += data_dir.SubdirList( + provider_info['backends'][key]['path'], + bootstrap.test_filter) + + subtest_dir = None + + # Get the test routine corresponding to the specified + # test type + logging.debug("Searching for test modules that match " + "'type = %s' and 'provider = %s' " + "on this cartesian dict", + params.get("type"), + params.get("provider", None)) + + t_types = params.get("type").split() + # Make sure we can load provider_lib in tests + for s in subtest_dirs: + if os.path.dirname(s) not in sys.path: + sys.path.insert(0, os.path.dirname(s)) + + test_modules = {} + for t_type in t_types: + for d in subtest_dirs: + module_path = os.path.join(d, "%s.py" % t_type) + if os.path.isfile(module_path): + logging.debug("Found subtest module %s", + module_path) + subtest_dir = d + break + if subtest_dir is None: + msg = ("Could not find test file %s.py on test" + "dirs %s" % (t_type, subtest_dirs)) + raise error.TestError(msg) + # Load the test module + f, p, d = imp.find_module(t_type, [subtest_dir]) + test_modules[t_type] = imp.load_module(t_type, f, p, d) + f.close() + + # Preprocess + try: + params = env_process.preprocess(self, params, env) + finally: + env.save() + + # Run the test function + for t_type in t_types: + test_module = test_modules[t_type] + run_func = utils_misc.get_test_entrypoint_func( + t_type, test_module) + try: + run_func(self, params, env) + self.verify_background_errors() + finally: + env.save() + test_passed = True + error_message = funcatexit.run_exitfuncs(env, t_type) + if error_message: + raise error.TestWarn("funcatexit failed with: %s" + % error_message) + + except Exception: + if t_type is not None: + error_message = funcatexit.run_exitfuncs(env, t_type) + if error_message: + logging.error(error_message) + try: + env_process.postprocess_on_error(self, params, env) + finally: + env.save() + raise + + finally: + # Postprocess + try: + try: + env_process.postprocess(self, params, env) + except Exception, e: + if test_passed: + raise + logging.error("Exception raised during " + "postprocessing: %s", e) + finally: + env.save() + + except Exception, e: + if params.get("abort_on_error") != "yes": + raise + # Abort on error + logging.info("Aborting job (%s)", e) + if params.get("vm_type") == "qemu": + for vm in env.get_all_vms(): + if vm.is_dead(): + continue + logging.info("VM '%s' is alive.", vm.name) + for m in vm.monitors: + logging.info("It has a %s monitor unix socket at: %s", + m.protocol, m.filename) + logging.info("The command line used to start it was:\n%s", + vm.make_qemu_command()) + raise error.JobError("Abort requested (%s)" % e) + + return test_passed + + +class VirtTestOptionsProcess(object): + + """ + Pick virt test options and parse them to get to a cartesian parser. + """ + + def __init__(self, job): + """ + Parses options and initializes attributes. + """ + self.options = job.args + # There are a few options from the original virt-test runner + # that don't quite make sense for avocado (avocado implements a + # better version of the virt-test feature). + # So let's just inject some values into options. + self.options.vt_verbose = False + self.options.vt_log_level = logging.DEBUG + self.options.vt_console_level = logging.DEBUG + self.options.vt_no_downloads = False + self.options.vt_selinux_setup = False + # These options can be done by using virt test scripts + self.options.vt_list_tests = False + self.options.vt_list_guests = False + + # Here we'll inject values from the config file. + # Doing this makes things configurable yet the number of options + # is not overwhelming. + # setup section + self.options.vt_keep_image = settings.get_value( + 'virt_test.setup', 'keep_image', key_type=bool) + self.options.vt_keep_image_between_tests = settings.get_value( + 'virt_test.setup', 'keep_image_between_tests', key_type=bool) + self.options.vt_keep_guest_running = settings.get_value( + 'virt_test.setup', 'keep_guest_running', key_type=bool) + # common section + self.options.vt_data_dir = settings.get_value( + 'virt_test.common', 'data_dir', default=None) + self.options.vt_type_specific = settings.get_value( + 'virt_test.common', 'type_specific_only', key_type=bool) + self.options.vt_mem = settings.get_value( + 'virt_test.common', 'mem', key_type=int) + self.options.vt_arch = settings.get_value( + 'virt_test.common', 'arch', default=None) + self.options.vt_machine_type = settings.get_value( + 'virt_test.common', 'machine_type', + default=defaults.DEFAULT_MACHINE_TYPE) + # qemu section + self.options.vt_accel = settings.get_value( + 'virt_test.qemu', 'accel', default='kvm') + self.options.vt_nettype = settings.get_value( + 'virt_test.qemu', 'nettype', default='user') + self.options.vt_netdst = settings.get_value( + 'virt_test.qemu', 'netdst', default='virbr0') + self.options.vt_vhost = settings.get_value( + 'virt_test.qemu', 'vhost', default='off') + self.options.vt_monitor = settings.get_value( + 'virt_test.qemu', 'monitor', default='human') + self.options.vt_smp = settings.get_value( + 'virt_test.qemu', 'smp', default='2') + self.options.vt_image_type = settings.get_value( + 'virt_test.qemu', 'image_type', default='qcow2') + self.options.vt_nic_model = settings.get_value( + 'virt_test.qemu', 'nic_model', default='virtio_net') + self.options.vt_disk_bus = settings.get_value( + 'virt_test.qemu', 'disk_bus', default='virtio_blk') + self.options.vt_qemu_sandbox = settings.get_value( + 'virt_test.qemu', 'sandbox', default='on') + self.options.vt_qemu_defconfig = settings.get_value( + 'virt_test.qemu', 'defconfig', default='yes') + self.options.vt_malloc_perturb = settings.get_value( + 'virt_test.qemu', 'malloc_perturb', default='yes') + + # libvirt section + self.options.vt_install_guest = settings.get_value( + 'virt_test.libvirt', 'install_guest', key_type=bool, + default=False) + self.options.vt_remove_guest = settings.get_value( + 'virt_test.libvirt', 'remove_guest', key_type=bool, + default=False) + + # debug section + self.options.vt_no_cleanup = settings.get_value( + 'virt_test.debug', 'no_cleanup', key_type=bool, default=False) + + self.view = job.view + self.cartesian_parser = None + + def _process_qemu_bin(self): + """ + Puts the value of the qemu bin option in the cartesian parser command. + """ + if self.options.vt_config and self.options.vt_qemu_bin is None: + logging.info("Config provided and no --vt-qemu-bin set. Not trying " + "to automatically set qemu bin.") + else: + (qemu_bin_path, qemu_img_path, qemu_io_path, + qemu_dst_bin_path) = standalone_test.find_default_qemu_paths( + self.options.vt_qemu_bin, self.options.vt_dst_qemu_bin) + self.cartesian_parser.assign("qemu_binary", qemu_bin_path) + self.cartesian_parser.assign("qemu_img_binary", qemu_img_path) + self.cartesian_parser.assign("qemu_io_binary", qemu_io_path) + if qemu_dst_bin_path is not None: + self.cartesian_parser.assign("qemu_dst_binary", + qemu_dst_bin_path) + + def _process_qemu_img(self): + """ + Puts the value of the qemu bin option in the cartesian parser command. + """ + if self.options.vt_config and self.options.vt_qemu_bin is None: + logging.info("Config provided and no --vt-qemu-bin set. Not trying " + "to automatically set qemu bin.") + else: + (_, qemu_img_path, + _, _) = standalone_test.find_default_qemu_paths( + self.options.vt_qemu_bin, self.options.vt_dst_qemu_bin) + self.cartesian_parser.assign("qemu_img_binary", qemu_img_path) + + def _process_qemu_accel(self): + """ + Puts the value of the qemu bin option in the cartesian parser command. + """ + if self.options.vt_accel == 'tcg': + self.cartesian_parser.assign("disable_kvm", "yes") + + def _process_bridge_mode(self): + if self.options.vt_nettype not in SUPPORTED_NET_TYPES: + self.view.notify(event='error', + msg="Invalid --vt-nettype option '%s'. " + "Valid options are: (%s)" % + (self.options.vt_nettype, + ", ".join(SUPPORTED_NET_TYPES))) + sys.exit(1) + if self.options.vt_nettype == 'bridge': + if os.getuid() != 0: + self.view.notify(event='error', + msg="In order to use bridge, " + "you need to be root, aborting...") + sys.exit(1) + self.cartesian_parser.assign("nettype", "bridge") + self.cartesian_parser.assign("netdst", self.options.netdst) + elif self.options.vt_nettype == 'user': + self.cartesian_parser.assign("nettype", "user") + elif self.options.vt_nettype == 'none': + self.cartesian_parser.assign("nettype", "none") + + def _process_monitor(self): + if self.options.vt_monitor == 'qmp': + self.cartesian_parser.assign("monitors", "qmp1") + self.cartesian_parser.assign("monitor_type_qmp1", "qmp") + + def _process_smp(self): + if not self.options.vt_config: + if self.options.vt_smp == '1': + self.cartesian_parser.only_filter("up") + elif self.options.vt_smp == '2': + self.cartesian_parser.only_filter("smp2") + else: + try: + self.cartesian_parser.only_filter("up") + self.cartesian_parser.assign( + "smp", int(self.options.vt_smp)) + except ValueError: + self.view.notify(event='error', + msg="Invalid option for smp: %s, " + "aborting..." % self.options.vt_smp) + sys.exit(1) + else: + logging.info("Config provided, ignoring --vt-smp option") + + def _process_arch(self): + if self.options.vt_arch is None: + pass + elif not self.options.vt_config: + self.cartesian_parser.only_filter(self.options.vt_arch) + else: + logging.info("Config provided, ignoring --vt-arch option") + + def _process_machine_type(self): + if not self.options.vt_config: + if self.options.vt_machine_type is None: + # TODO: this is x86-specific, instead we can get the default + # arch from qemu binary and run on all supported machine types + if ((self.options.vt_arch is None) and + (self.options.vt_guest_os is None)): + self.cartesian_parser.only_filter( + defaults.DEFAULT_MACHINE_TYPE) + else: + self.cartesian_parser.only_filter(self.options.vt_machine_type) + else: + logging.info("Config provided, ignoring --vt-machine-type option") + + def _process_image_type(self): + if not self.options.vt_config: + if self.options.vt_image_type in SUPPORTED_IMAGE_TYPES: + self.cartesian_parser.only_filter(self.options.vt_image_type) + else: + self.cartesian_parser.only_filter("raw") + # The actual param name is image_format. + self.cartesian_parser.assign("image_format", + self.options.vt_image_type) + else: + logging.info("Config provided, ignoring --vt-image-type option") + + def _process_nic_model(self): + if not self.options.vt_config: + if self.options.vt_nic_model in SUPPORTED_NIC_MODELS: + self.cartesian_parser.only_filter(self.options.vt_nic_model) + else: + self.cartesian_parser.only_filter("nic_custom") + self.cartesian_parser.assign( + "nic_model", self.options.vt_nic_model) + else: + logging.info("Config provided, ignoring --vt-nic-model option") + + def _process_disk_buses(self): + if not self.options.vt_config: + if self.options.vt_disk_bus in SUPPORTED_DISK_BUSES: + self.cartesian_parser.only_filter(self.options.vt_disk_bus) + else: + self.view.notify(event='error', + msg=("Option %s is not in the list %s, " + "aborting..." % (self.options.vt_disk_bus, + SUPPORTED_DISK_BUSES))) + sys.exit(1) + else: + logging.info("Config provided, ignoring --vt-disk-bus option") + + def _process_vhost(self): + if not self.options.vt_config: + if self.options.vt_nettype == "bridge": + if self.options.vt_vhost == "on": + self.cartesian_parser.assign("vhost", "on") + elif self.options.vt_vhost == "force": + self.cartesian_parser.assign("netdev_extra_params", + '",vhostforce=on"') + self.cartesian_parser.assign("vhost", "on") + else: + if self.options.vt_vhost in ["on", "force"]: + self.view.notify( + event='error', msg=("Nettype %s is incompatible " + "with vhost %s, aborting..." % + (self.options.vt_nettype, + self.options.vt_vhost))) + sys.exit(1) + else: + logging.info("Config provided, ignoring --vt-vhost option") + + def _process_qemu_sandbox(self): + if not self.options.vt_config: + if self.options.vt_qemu_sandbox == "off": + self.cartesian_parser.assign("qemu_sandbox", "off") + else: + logging.info("Config provided, " + "ignoring \"--vt-sandbox \" option") + + def _process_qemu_defconfig(self): + if not self.options.vt_config: + if self.options.vt_qemu_defconfig == "no": + self.cartesian_parser.assign("defconfig", "no") + else: + logging.info("Config provided, " + "ignoring \"--defconfig \" option") + + def _process_malloc_perturb(self): + self.cartesian_parser.assign("malloc_perturb", + self.options.vt_malloc_perturb) + + def _process_qemu_specific_options(self): + """ + Calls for processing all options specific to the qemu test. + + This method modifies the cartesian set by parsing additional lines. + """ + + self._process_qemu_bin() + self._process_qemu_accel() + self._process_monitor() + self._process_smp() + self._process_image_type() + self._process_nic_model() + self._process_disk_buses() + self._process_vhost() + self._process_malloc_perturb() + self._process_qemu_sandbox() + + def _process_lvsb_specific_options(self): + """ + Calls for processing all options specific to lvsb test + """ + self.options.no_downloads = True + + def _process_libvirt_specific_options(self): + """ + Calls for processing all options specific to libvirt test. + """ + if self.options.vt_connect_uri: + driver_found = False + for driver in SUPPORTED_LIBVIRT_DRIVERS: + if self.options.vt_connect_uri.count(driver): + driver_found = True + self.cartesian_parser.only_filter(driver) + if not driver_found: + self.view.notify(event='error', + msg=("Unsupported uri: %s." % + self.options.vt_connect_uri)) + sys.exit(1) + else: + self.cartesian_parser.only_filter("qemu") + + def _process_guest_os(self): + if not self.options.vt_config: + if len(standalone_test.get_guest_name_list(self.options)) == 0: + self.view.notify(event='error', + msg="Guest name %s is not on the " + "known guest os list " + "(see --vt-list-guests), " + "aborting..." % self.options.vt_guest_os) + sys.exit(1) + self.cartesian_parser.only_filter( + self.options.vt_guest_os or defaults.DEFAULT_GUEST_OS) + else: + logging.info("Config provided, ignoring --vt-guest-os option") + + def _process_list(self): + if self.options.vt_list_tests: + standalone_test.print_test_list(self.options, + self.cartesian_parser) + sys.exit(0) + if self.options.vt_list_guests: + standalone_test.print_guest_list(self.options) + sys.exit(0) + + def _process_tests(self): + if not self.options.vt_config: + if self.options.vt_type: + if self.options.url and self.options.dropin: + self.view.notify(event='error', + msg="Option --vt-tests and " + "--vt-run-dropin can't be set at " + "the same time") + sys.exit(1) + elif self.options.url: + tests = self.options.url + if self.options.vt_type == 'libvirt': + if self.options.install_guest: + tests.insert(0, LIBVIRT_INSTALL) + if self.options.vt_remove_guest: + tests.append(LIBVIRT_REMOVE) + self.cartesian_parser.only_filter(", ".join(tests)) + elif self.options.dropin: + dropin_tests = os.listdir( + os.path.join(data_dir.get_root_dir(), "dropin")) + if len(dropin_tests) <= 1: + self.view.notify(event='error', + msg="No drop in tests detected, " + "aborting. Make sure you have " + "scripts on the 'dropin' " + "directory") + sys.exit(1) + self.cartesian_parser.only_filter("dropin") + else: + if self.options.vt_type == 'qemu': + self.cartesian_parser.only_filter(QEMU_DEFAULT_SET) + self.cartesian_parser.no_filter("with_reboot") + elif self.options.vt_type == 'libvirt': + self.cartesian_parser.only_filter(LIBVIRT_DEFAULT_SET) + elif self.options.vt_type == 'lvsb': + self.cartesian_parser.only_filter(LVSB_DEFAULT_SET) + elif self.options.vt_type == 'openvswitch': + self.cartesian_parser.only_filter(OVS_DEFAULT_SET) + else: + logging.info("Config provided, ignoring --vt-tests option") + + def _process_restart_vm(self): + if not self.options.vt_config: + if not self.options.vt_keep_guest_running: + self.cartesian_parser.assign("kill_vm", "yes") + + def _process_restore_image_between_tests(self): + if not self.options.vt_config: + if not self.options.vt_keep_image_between_tests: + self.cartesian_parser.assign("restore_image", "yes") + + def _process_mem(self): + self.cartesian_parser.assign("mem", self.options.vt_mem) + + def _process_tcpdump(self): + """ + Verify whether we can run tcpdump. If we can't, turn it off. + """ + try: + tcpdump_path = utils_misc.find_command('tcpdump') + except ValueError: + tcpdump_path = None + + non_root = os.getuid() != 0 + + if tcpdump_path is None or non_root: + self.cartesian_parser.assign("run_tcpdump", "no") + + def _process_no_filter(self): + if not self.options.vt_config: + if self.options.vt_no_filter: + no_filter = ", ".join(self.options.vt_no_filter.split(' ')) + self.cartesian_parser.no_filter(no_filter) + + def _process_only_type_specific(self): + if not self.options.vt_config: + if self.options.vt_type_specific: + self.cartesian_parser.only_filter("(subtest=type_specific)") + + def _process_general_options(self): + """ + Calls for processing all generic options. + + This method modifies the cartesian set by parsing additional lines. + """ + self._process_guest_os() + self._process_arch() + self._process_machine_type() + self._process_restart_vm() + self._process_restore_image_between_tests() + self._process_mem() + self._process_tcpdump() + self._process_no_filter() + self._process_qemu_img() + self._process_bridge_mode() + self._process_only_type_specific() + + def _process_options(self): + """ + Process the options given in the command line. + """ + cfg = None + if (not self.options.vt_type) and (not self.options.vt_config): + self.view.notify(event='error', + msg=("No type (--vt-type) or config " + "(--vt-config) options specified, " + "aborting...")) + sys.exit(0) + + if self.options.vt_type: + if self.options.vt_type not in SUPPORTED_TEST_TYPES: + self.view.notify(event='error', + msg="Invalid test type %s. " + "Valid test types: %s. Aborting..." % + (self.options.vt_type, + " ".join(SUPPORTED_TEST_TYPES))) + sys.exit(1) + + if self.options.vt_data_dir: + data_dir.set_backing_data_dir(self.options.vt_data_dir) + + self.cartesian_parser = cartesian_config.Parser(debug=False) + + if self.options.vt_config: + cfg = os.path.abspath(self.options.vt_config) + + if not self.options.vt_config: + cfg = data_dir.get_backend_cfg_path(self.options.vt_type, + 'tests.cfg') + + self.cartesian_parser.parse_file(cfg) + if self.options.vt_type != 'lvsb': + self._process_general_options() + + if self.options.vt_type == 'qemu': + self._process_qemu_specific_options() + elif self.options.vt_type == 'lvsb': + self._process_lvsb_specific_options() + elif self.options.vt_type == 'openvswitch': + self._process_qemu_specific_options() + elif self.options.vt_type == 'libvirt': + self._process_libvirt_specific_options() + # List and tests have to be the last things to be processed + self._process_list() + # Tests won't be processed here. The code of the function will + # be utilized elsewhere. + # self._process_tests() + + def get_parser(self): + self._process_options() + return self.cartesian_parser + + +class VirtTestCompatPlugin(plugin.Plugin): + + """ + Implements the avocado virt test options + """ + + name = 'virt_test_compat_runner' + enabled = True + configured = False + parser = None + + def configure(self, parser): + """ + Add the subparser for the run action. + + :param parser: Main test runner parser. + """ + self.parser = parser + + try: + qemu_bin_path = standalone_test.find_default_qemu_paths()[0] + except ValueError: + qemu_bin_path = "Could not find one" + + qemu_nw_msg = "QEMU network option (%s). " % ", ".join( + SUPPORTED_NET_TYPES) + qemu_nw_msg += "Default: user" + + vt_compat_group_setup = parser.runner.add_argument_group( + 'Virt-Test compat layer - VM Setup options') + vt_compat_group_common = parser.runner.add_argument_group( + 'Virt-Test compat layer - Common options') + vt_compat_group_qemu = parser.runner.add_argument_group( + 'Virt-Test compat layer - QEMU options') + vt_compat_group_libvirt = parser.runner.add_argument_group( + 'Virt-Test compat layer - Libvirt options') + + current_run_setup = settings.get_value( + 'virt_test.setup', 'run_setup', key_type=bool) + + vt_compat_group_setup.add_argument("--vt-setup", action="store_true", + dest="vt_setup", + default=current_run_setup, + help="Run virt test setup actions " + "(restore JeOS image from " + "pristine). Current: %s" % + current_run_setup) + + vt_compat_group_common.add_argument("--vt-config", action="store", + dest="vt_config", + help=("Explicitly choose a " + "cartesian config. " + "When choosing this, " + "some options will be " + "ignored (see options " + "below)")) + vt_compat_group_common.add_argument("--vt-type", action="store", + dest="vt_type", + help=("Choose test type (%s). " + "Default: qemu" % + ", ".join( + SUPPORTED_TEST_TYPES)), + default='qemu') + vt_compat_group_common.add_argument("--vt-guest-os", action="store", + dest="vt_guest_os", + default=None, + help=("Select the guest OS to " + "be used. If --vt-config is " + "provided, this will be " + "ignored. Default: %s" % + defaults.DEFAULT_GUEST_OS)) + vt_compat_group_common.add_argument("--vt-no-filter", action="store", + dest="vt_no_filter", default="", + help=("List of space separated " + "'no' filters to be passed " + "to the config parser. " + "If --vt-config is " + "provided, this will be " + "ignored. Default: ''")) + qemu_bin_path_current = settings.get_value('virt_test.qemu', 'qemu_bin', + default=qemu_bin_path) + vt_compat_group_qemu.add_argument("--vt-qemu-bin", action="store", + dest="vt_qemu_bin", + default=None, + help=("Path to a custom qemu binary " + "to be tested. If --vt-config " + "is provided and this flag is " + "omitted, no attempt to set " + "the qemu binaries will be " + "made. Current: %s" % + qemu_bin_path_current)) + qemu_dst_bin_path_current = settings.get_value('virt_test.qemu', + 'qemu_dst_bin', + default=qemu_bin_path) + vt_compat_group_qemu.add_argument("--vt-qemu-dst-bin", action="store", + dest="vt_dst_qemu_bin", + default=None, + help=("Path to a custom qemu binary " + "to be tested for the " + "destination of a migration, " + "overrides --vt-qemu-bin. " + "If --vt-config is provided " + "and this flag is omitted, " + "no attempt to set the qemu " + "binaries will be made. " + "Current: %s" % + qemu_dst_bin_path_current)) + supported_uris = ", ".join(SUPPORTED_LIBVIRT_URIS) + uri_current = settings.get_value('virt_test.libvirt', 'connect_uri', + default=None) + vt_compat_group_libvirt.add_argument("--vt-connect-uri", + action="store", + dest="vt_connect_uri", + default=uri_current, + help=("Choose test connect uri " + "for libvirt (E.g: %s). " + "Current: %s" % + (supported_uris, + uri_current))) + + self.configured = True + + def activate(self, args): + """ + Run test modules or simple tests. + + :param args: Command line args received from the run subparser. + """ + if getattr(args, 'vt_setup', False): + self.parser.application.set_defaults( + vt_loader=VirtTestLoader, vt_result=VirtTestResult) + else: + self.parser.application.set_defaults(vt_loader=VirtTestLoader) diff --git a/avocado/core/plugins/virt_test_list.py b/avocado/core/plugins/virt_test_list.py new file mode 100644 index 0000000000..644b829a24 --- /dev/null +++ b/avocado/core/plugins/virt_test_list.py @@ -0,0 +1,98 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 LICENSE for more details. +# +# Copyright: Red Hat Inc. 2015 +# Author: Lucas Meneghel Rodrigues + +""" +Avocado plugin that augments 'avocado list' with virt-test related options. +""" + +import os +import sys + +from avocado.settings import settings +from avocado.core.plugins import plugin + +# virt-test supports using autotest from a git checkout, so we'll have to +# support that as well. The code below will pick up the environment variable +# $AUTOTEST_PATH and do the import magic needed to make the autotest library +# available in the system. +AUTOTEST_PATH = None + +if 'AUTOTEST_PATH' in os.environ: + AUTOTEST_PATH = os.path.expanduser(os.environ['AUTOTEST_PATH']) + client_dir = os.path.join(os.path.abspath(AUTOTEST_PATH), 'client') + setup_modules_path = os.path.join(client_dir, 'setup_modules.py') + import imp + setup_modules = imp.load_source('autotest_setup_modules', + setup_modules_path) + setup_modules.setup(base_path=client_dir, + root_module_name="autotest.client") + +# The code below is used by this plugin to find the virt test directory, +# so that it can load the virttest python lib, used by the plugin code. +# If the user doesn't provide the proper configuration, the plugin will +# fail to load. +VIRT_TEST_PATH = None + +if 'VIRT_TEST_PATH' in os.environ: + VIRT_TEST_PATH = os.environ['VIRT_TEST_PATH'] +else: + VIRT_TEST_PATH = settings.get_value(section='virt_test', + key='virt_test_path', default=None) + +if VIRT_TEST_PATH is not None: + sys.path.append(os.path.expanduser(VIRT_TEST_PATH)) + +from virttest.standalone_test import SUPPORTED_TEST_TYPES +from virttest.defaults import DEFAULT_GUEST_OS + + +class VirtTestListerPlugin(plugin.Plugin): + + """ + Implements the avocado virt test options + """ + + name = 'virt_test_compat_lister' + enabled = True + + def configure(self, parser): + """ + Add the subparser for the run action. + + :param parser: Main test runner parser. + """ + self.parser = parser + vt_compat_group_lister = parser.lister.add_argument_group( + 'Virt-Test compat layer - Lister options') + vt_compat_group_lister.add_argument("--vt-type", action="store", + dest="vt_type", + help="Choose test type (%s). " + "Default: qemu" % + ", ".join(SUPPORTED_TEST_TYPES), + default='qemu') + vt_compat_group_lister.add_argument("--vt-guest-os", action="store", + dest="vt_guest_os", + default=None, + help=("Select the guest OS to be " + "used (different guests " + "support different test " + "lists). You can list " + "available guests " + "with --vt-list-guests. " + "Default: %s" % + DEFAULT_GUEST_OS)) + vt_compat_group_lister.add_argument("--vt-list-guests", + action="store_true", + default=False, + help="List available guests") diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000000..8a29208be7 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +avocado-vt-plugins (0.24.0) trusty; urgency=low + + * Initial release. + + -- Lucas Meneghel Rodrigues (lmr) Wed, 03 Sep 2014 01:50:43 -0200 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000000..48c962ffa2 --- /dev/null +++ b/debian/compat @@ -0,0 +1,2 @@ +9 + diff --git a/debian/control b/debian/control new file mode 100644 index 0000000000..669d2f8c93 --- /dev/null +++ b/debian/control @@ -0,0 +1,13 @@ +Source: avocado-plugins-vt +Section: python +Priority: optional +Maintainer: Lucas Meneghel Rodrigues (lmr) +Build-Depends: debhelper (>=7.0.50~), python-support (>= 0.6), cdbs (>= 0.4.49) +Standards-Version: 3.8.4 + +Package: avocado-plugins-vt +Architecture: all +Homepage: https://github.com/avocado-framework/avocado-virt +XB-Python-Version: ${python:Versions} +Depends: ${misc:Depends}, ${python:Depends}, avocado +Description: Avocado virt test compatibility layer plugin. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000000..7f96b89137 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,29 @@ +Upstream Author: + + Lucas Meneghel Rodrigues + +Files: * +Copyright: + 2013-2014, Lucas Meneghel Rodrigues, Rudá Moura +License: GPL + +Files: debian/* +Copyright: + 2012, Lucas Meneghel Rodrigues (lmr) +License: GPL + +License: GPL + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 only. + + This package 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, see . + +On Debian systems, the complete text of the GNU General +Public License 2 can be found in `/usr/share/common-licenses/GPL-2'. diff --git a/debian/pyversions b/debian/pyversions new file mode 100644 index 0000000000..3ad2293ef1 --- /dev/null +++ b/debian/pyversions @@ -0,0 +1 @@ +2.7- diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000000..fb55c26ace --- /dev/null +++ b/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +DEB_PYTHON_SYSTEM := pysupport + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/python-distutils.mk + +clean:: + rm -rf build build-stamp configure-stamp build/ MANIFEST + dh_clean diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000000..89ae9db8f8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/debian/source/options b/debian/source/options new file mode 100644 index 0000000000..2059712f88 --- /dev/null +++ b/debian/source/options @@ -0,0 +1 @@ +tar-ignore = ".git/*" diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000000..c78d569454 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=3 + +https://github.com/avocado-framework/avocado-virt/tags .*/(\d[\d\.]+)\.tar\.gz diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000..fb4c4fbf96 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,154 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + -rm -rf source/api/*.rst + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/avocado.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/avocado.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/books" + @echo "# cp -r $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/books/Avocado" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/build/.empty b/docs/build/.empty new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/source/Introduction.rst b/docs/source/Introduction.rst new file mode 100644 index 0000000000..ac0c88e14f --- /dev/null +++ b/docs/source/Introduction.rst @@ -0,0 +1,12 @@ +.. _introduction: + +============ +Introduction +============ + +Avocado Virt Test Compatibility is a plugin that lets you +execute tests from the virt test suite +(http://virt-test.readthedocs.org/en/latest/), with all +the avocado convenience features, such as HTML report, +Xunit output, among others. + diff --git a/docs/source/_static/.gitignore b/docs/source/_static/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/source/_templates/.gitignore b/docs/source/_templates/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000000..570ade8acf --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,70 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 LICENSE for more details. +# +# Copyright: Red Hat Inc. 2013-2014 +# Author: Lucas Meneghel Rodrigues + +# -*- coding: utf-8 -*- + +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +root_path = os.path.abspath(os.path.join("..", "..")) + +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage'] + +master_doc = 'index' +project = u'Avocado Virt Test Compatibility Layer' +copyright = u'2014, Red Hat' + +version = '0.24.0' +release = '0.24.0' + +# on_rtd is whether we are on readthedocs.org, this line of code grabbed from +# docs.readthedocs.org +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + except ImportError: + html_theme = 'default' + +htmlhelp_basename = 'avocadodoc' + +latex_documents = [ + ('index', 'avocado.tex', u'avocado Documentation', + u'Lucas Meneghel Rodrigues', 'manual'), +] + +man_pages = [ + ('index', 'avocado', u'avocado Documentation', + [u'Lucas Meneghel Rodrigues'], 1) +] + +texinfo_documents = [ + ('index', 'avocado', u'avocado Documentation', + u'Lucas Meneghel Rodrigues', 'avocado', 'One line description of project.', + 'Miscellaneous'), +] + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None, + 'http://avocado-framework.readthedocs.org/en/latest/': None} + +autoclass_content = 'both' diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000000..fec48968a2 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,22 @@ +.. avocado documentation master file, created by + sphinx-quickstart on Wed Mar 5 14:44:57 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +====================================== +Avocado Virt Test Compatibility Plugin +====================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + Introduction + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/etc/avocado/conf.d/virt-test.conf b/etc/avocado/conf.d/virt-test.conf new file mode 100644 index 0000000000..19ac0056d6 --- /dev/null +++ b/etc/avocado/conf.d/virt-test.conf @@ -0,0 +1,74 @@ +[virt_test] +# Path to virt-test, required for the compatibility plugin to locate +# virt-test libs. You should set this locally for avocado users. +# For people working on the compatibility layer itself, you can export the +# environment variable VIRT_TEST_DIR with that location instead of using +# the config system. +virt_test_path= +[virt_test.setup] +# Run the setup procedure (uncompress JeOS' pristine image). +# The keys below help you customize the procedure +run_setup=False +# Don't restore the JeOS at the beginning of the test job (faster, but unsafe) +# This has no effect unless you set run_setup=True or --vt-setup on the cmdline +keep_image=False +# Don't restore the JeOS between tests (faster, but unsafe) +# This has no effect unless you set run_setup=True or --vt-setup on the cmdline +keep_image_between_tests=False +# Keep guest running between tests (faster, but unsafe) +keep_guest_running=False +[virt_test.common] +# Data dir path. If none specified, the default virt-test data dir will be used +data_dir = +# Enable only type specific tests. Shared tests will not be tested +type_specific_only = False +# RAM dedicated to the main VM +mem = 1024 +# Architecture under test +arch = +# Machine type under test +machine_type = +[virt_test.qemu] +# Path to a custom qemu binary to be tested +qemu_bin = +# Path to a custom qemu binary to be tested for the +# destination of a migration, overrides qemu_bin for +# that particular purpose +qemu_dst_bin = +# Accelerator used to run qemu (kvm or tcg) +accel = kvm +# Nettype (bridge, user, none) +nettype = user +# Bridge name to be used if you select bridge as a nettype +netdst = virbr0 +# Whether to enable vhost for qemu (on/off/force). Depends on nettype=bridge +vhost = off +# Monitor type (human or qmp) +monitor = human +# Number of virtual cpus to use (1 or 2) +smp = 2 +# Image format type to use (any valid qemu format) +image_type = qcow2 +# Guest network card model (any valid qemu card) +nic_model = virtio_net +# Guest disk bus for main image. One of +# ('ide', 'scsi', 'virtio_blk', 'virtio_scsi', 'lsi_scsi', 'ahci', 'usb2', 'xenblk') +disk_bus = virtio_blk +# Enable qemu sandboxing (on/off) +sandbox = on +# Prevent qemu from loading sysconfdir/qemu.conf and sysconfdir/target-ARCH.conf at startup +# (yes/no) +defconfig = yes +# Use MALLOC_PERTURB_ env variable set to 1 to help catch memory allocation problems on qemu +# (yes/no) +malloc_perturb = yes +[virt_test.libvirt] +# Test connect URI for libvirt (qemu:///system', 'lxc:///') +connect_uri = qemu:///session +# Install the guest using import method before the tests are run +install_guest = False +# Remove the guest from libvirt. This will not delete the guest's disk file +remove_guest = False +[virt_test.debug] +# Don't clean up tmp files or VM processes at the end of a virt-test execution +no_cleanup = False diff --git a/requirements-travis-python26.txt b/requirements-travis-python26.txt new file mode 100644 index 0000000000..402de794b3 --- /dev/null +++ b/requirements-travis-python26.txt @@ -0,0 +1 @@ +logutils==0.3.3 diff --git a/requirements-travis.txt b/requirements-travis.txt new file mode 100644 index 0000000000..6d848ab4b1 --- /dev/null +++ b/requirements-travis.txt @@ -0,0 +1,2 @@ +pep8==1.6.2 +inspektor==0.1.15 diff --git a/selftests/checkall b/selftests/checkall new file mode 100755 index 0000000000..6df7d35f7f --- /dev/null +++ b/selftests/checkall @@ -0,0 +1,15 @@ +#!/bin/bash +run_rc() { + echo "Running '$1'" + $1 +} +run_rc 'inspekt lint' +rc1=$? +echo "" +run_rc 'inspekt indent' +rc2=$? +echo "" +run_rc 'inspekt style' +rc3=$? +exit $rc1 || $rc2 || $rc3 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000..325fd990b6 --- /dev/null +++ b/setup.py @@ -0,0 +1,58 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 LICENSE for more details. +# +# Copyright: Red Hat Inc. 2013-2014 +# Author: Lucas Meneghel Rodrigues + +import os + +# pylint: disable=E0611 +from distutils.core import setup + +VERSION = '0.24.0' + +VIRTUAL_ENV = 'VIRTUAL_ENV' in os.environ + + +def get_dir(system_path=None, virtual_path=None): + """ + Retrieve VIRTUAL_ENV friendly path + :param system_path: Relative system path + :param virtual_path: Overrides system_path for virtual_env only + :return: VIRTUAL_ENV friendly path + """ + if virtual_path is None: + virtual_path = system_path + if VIRTUAL_ENV: + if virtual_path is None: + virtual_path = [] + return os.path.join(*virtual_path) + else: + if system_path is None: + system_path = [] + return os.path.join(*(['/'] + system_path)) + + +def get_data_files(): + data_files = [(get_dir(['etc', 'avocado', 'conf.d']), + ['etc/avocado/conf.d/virt-test.conf'])] + return data_files + +setup(name='avocado-plugins-vt', + version=VERSION, + description='Avocado Virt Test Compatibility Layer plugin', + author='Lucas Meneghel Rodrigues', + author_email='lmr@redhat.com', + url='http://github.com/avocado-framework/avocado-vt', + packages=['avocado', + 'avocado.core.plugins'], + data_files=get_data_files() + )