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() + )