diff options
36 files changed, 675 insertions, 140 deletions
diff --git a/.gitignore b/.gitignore index b66b772551..77522561b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.doctrees /config-devices.* /config-all-devices.* /config-all-disas.* diff --git a/MAINTAINERS b/MAINTAINERS index 5040d9dfb1..074ad46d47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2567,3 +2567,9 @@ GIT submodules M: Daniel P. Berrange <berrange@redhat.com> S: Odd Fixes F: scripts/git-submodule.sh + +Sphinx documentation configuration and build machinery +M: Peter Maydell <peter.maydell@linaro.org> +S: Maintained +F: docs/conf.py +F: docs/*/conf.py diff --git a/Makefile b/Makefile index 2208bde419..cad585b4d6 100644 --- a/Makefile +++ b/Makefile @@ -87,6 +87,20 @@ endif include $(SRC_PATH)/rules.mak +# Create QEMU_PKGVERSION and FULL_VERSION strings +# If PKGVERSION is set, use that; otherwise get version and -dirty status from git +QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \ + cd $(SRC_PATH); \ + if test -e .git; then \ + git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ + if ! git diff-index --quiet HEAD &>/dev/null; then \ + echo "-dirty"; \ + fi; \ + fi)) + +# Either "version (pkgversion)", or just "version" if pkgversion not set +FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION)) + GENERATED_FILES = qemu-version.h config-host.h qemu-options.def GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c @@ -388,27 +402,12 @@ dummy := $(call unnest-vars,, \ include $(SRC_PATH)/tests/Makefile.include -all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules +all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules qemu-version.h: FORCE $(call quiet-command, \ - (cd $(SRC_PATH); \ - if test -n "$(PKGVERSION)"; then \ - pkgvers="$(PKGVERSION)"; \ - else \ - if test -d .git; then \ - pkgvers=$$(git describe --match 'v*' 2>/dev/null | tr -d '\n');\ - if ! git diff-index --quiet HEAD &>/dev/null; then \ - pkgvers="$${pkgvers}-dirty"; \ - fi; \ - fi; \ - fi; \ - printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \ - if test -n "$${pkgvers}"; then \ - printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \ - else \ - printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \ - fi; \ + (printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \ + printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \ ) > $@.tmp) $(call quiet-command, if ! cmp -s $@ $@.tmp; then \ mv $@.tmp $@; \ @@ -637,6 +636,14 @@ dist: qemu-$(VERSION).tar.bz2 qemu-%.tar.bz2: $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" +# Note that these commands assume that there are no HTML files in +# the docs subdir in the source tree! If there are then this will +# blow them away for an in-source-tree 'make clean'. +define clean-manual = +rm -rf docs/$1/_static +rm -f docs/$1/objects.inv docs/$1/searchindex.js docs/$1/*.html +endef + distclean: clean rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi rm -f config-all-devices.mak config-all-disas.mak config.status @@ -657,6 +664,9 @@ distclean: clean rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html rm -f docs/qemu-block-drivers.7 rm -f docs/qemu-cpu-models.7 + rm -f .doctrees + $(call clean-manual,devel) + $(call clean-manual,interop) for d in $(TARGET_DIRS); do \ rm -rf $$d || exit 1 ; \ done @@ -690,7 +700,18 @@ else BLOBS= endif -install-doc: $(DOCS) +define install-manual = +for d in $$(cd docs && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done +for f in $$(cd docs && find $1 -type f); do $(INSTALL_DATA) "docs/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done +endef + +# Note that we deliberately do not install the "devel" manual: it is +# for QEMU developers, and not interesting to our users. +.PHONY: install-sphinxdocs +install-sphinxdocs: sphinxdocs + $(call install-manual,interop) + +install-doc: $(DOCS) install-sphinxdocs $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)" @@ -841,6 +862,23 @@ docs/version.texi: $(SRC_PATH)/VERSION %.pdf: %.texi docs/version.texi $(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@") +# Sphinx builds all its documentation at once in one invocation +# and handles "don't rebuild things unless necessary" itself. +# The '.doctrees' files are cached information to speed this up. +.PHONY: sphinxdocs +sphinxdocs: docs/devel/index.html docs/interop/index.html + +# Canned command to build a single manual +build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 docs/$1 ,"SPHINX","docs/$1") +# We assume all RST files in the manual's directory are used in it +manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py + +docs/devel/index.html: $(call manual-deps,devel) + $(call build-manual,devel) + +docs/interop/index.html: $(call manual-deps,interop) + $(call build-manual,interop) + qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") @@ -869,7 +907,7 @@ docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi -html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html +html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt diff --git a/configure b/configure index cefeb8fcce..5921d08cb3 100755 --- a/configure +++ b/configure @@ -4589,13 +4589,24 @@ if compile_prog "" "" ; then syncfs=yes fi +# Check we have a new enough version of sphinx-build +has_sphinx_build() { + # This is a bit awkward but works: create a trivial document and + # try to run it with our configuration file (which enforces a + # version requirement). This will fail if either + # sphinx-build doesn't exist at all or if it is too old. + mkdir -p "$TMPDIR1/sphinx" + touch "$TMPDIR1/sphinx/index.rst" + sphinx-build -c "$source_path/docs" -b html "$TMPDIR1/sphinx" "$TMPDIR1/sphinx/out" >/dev/null 2>&1 +} + # Check if tools are available to build documentation. if test "$docs" != "no" ; then - if has makeinfo && has pod2man; then + if has makeinfo && has pod2man && has_sphinx_build; then docs=yes else if test "$docs" = "yes" ; then - feature_not_found "docs" "Install texinfo and Perl/perl-podlators" + feature_not_found "docs" "Install texinfo, Perl/perl-podlators and python-sphinx" fi docs=no fi @@ -7663,6 +7674,7 @@ LINKS="$LINKS pc-bios/qemu-icon.bmp" LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit LINKS="$LINKS tests/acceptance tests/data" LINKS="$LINKS tests/qemu-iotests/check" +LINKS="$LINKS python" for bios_file in \ $source_path/pc-bios/*.bin \ $source_path/pc-bios/*.lid \ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000..befbcc6c3e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file, created by +# sphinx-quickstart on Thu Jan 31 16:40:14 2019. +# +# This config file can be used in one of two ways: +# (1) as a common config file which is included by the conf.py +# for each of QEMU's manuals: in this case sphinx-build is run multiple +# times, once per subdirectory. +# (2) as a top level conf file which will result in building all +# the manuals into a single document: in this case sphinx-build is +# run once, on the top-level docs directory. +# +# QEMU's makefiles take option (1), which allows us to install +# only the ones the user cares about (in particular we don't want +# to ship the 'devel' manual to end-users). +# Third-party sites such as readthedocs.org will take option (2). +# +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import os +import sys + +# The per-manual conf.py will set qemu_docdir for a single-manual build; +# otherwise set it here if this is an entire-manual-set build. +# This is always the absolute path of the docs/ directory in the source tree. +try: + qemu_docdir +except NameError: + qemu_docdir = os.path.abspath(".") + +# 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 an absolute path starting from qemu_docdir. +# +# sys.path.insert(0, os.path.join(qemu_docdir, "my_subdir")) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# 1.3 is where the 'alabaster' theme was shipped with Sphinx. +needs_sphinx = '1.3' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'QEMU' +copyright = u'2019, The QEMU Project Developers' +author = u'The QEMU Project Developers' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. + +# Extract this information from the VERSION file, for the benefit of +# standalone Sphinx runs as used by readthedocs.org. Builds run from +# the Makefile will pass version and release on the sphinx-build +# command line, which override this. +try: + extracted_version = None + with open(os.path.join(qemu_docdir, '../VERSION')) as f: + extracted_version = f.readline().strip() +except: + pass +finally: + if extracted_version: + version = release = extracted_version + else: + version = release = "unknown version" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +# Sphinx defaults to warning about use of :option: for options not defined +# with "option::" in the document being processed. Turn that off. +suppress_warnings = ["ref.option"] + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# We initialize this to empty here, so the per-manual conf.py can just +# add individual key/value entries. +html_theme_options = { +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# QEMU doesn't yet have any static files, so comment this out so we don't +# get a warning about a missing directory. +# If we do ever add this then it would probably be better to call the +# subdirectory sphinx_static, as the Linux kernel does. +# html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'searchbox.html', + ] +} + +# Don't copy the rST source files to the HTML output directory, +# and don't put links to the sources into the output HTML. +html_copy_source = False + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'QEMUdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'QEMU.tex', u'QEMU Documentation', + u'The QEMU Project Developers', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'qemu', u'QEMU Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'QEMU', u'QEMU Documentation', + author, 'QEMU', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/docs/cpu-hotplug.rst b/docs/cpu-hotplug.rst index cfeb79f571..d0b06403f1 100644 --- a/docs/cpu-hotplug.rst +++ b/docs/cpu-hotplug.rst @@ -60,7 +60,7 @@ vCPU hotplug hot-plugged (no "qom-path" member). From its output in step (3), we can see that ``IvyBridge-IBRS-x86_64-cpu`` is present in socket 0, while hot-plugging a CPU into socket 1 requires passing the listed - properties to QMP ``device_add``: + properties to QMP ``device_add``:: (QEMU) device_add id=cpu-2 driver=IvyBridge-IBRS-x86_64-cpu socket-id=1 core-id=0 thread-id=0 { diff --git a/docs/devel/conf.py b/docs/devel/conf.py new file mode 100644 index 0000000000..7441f87e7f --- /dev/null +++ b/docs/devel/conf.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'devel' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = u'Developer''s Guide' diff --git a/docs/devel/index.rst b/docs/devel/index.rst new file mode 100644 index 0000000000..cd0fa6c9ba --- /dev/null +++ b/docs/devel/index.rst @@ -0,0 +1,21 @@ +.. This is the top level page for the 'devel' manual. + + +QEMU Developer's Guide +====================== + +This manual documents various parts of the internals of QEMU. +You only need to read it if you are interested in reading or +modifying QEMU's source code. + +Contents: + +.. toctree:: + :maxdepth: 2 + + loads-stores + memory + migration + stable-process + testing + diff --git a/docs/devel/memory.txt b/docs/devel/memory.rst index 42577e1d86..b6a4c37ea5 100644 --- a/docs/devel/memory.txt +++ b/docs/devel/memory.rst @@ -1,19 +1,20 @@ +============== The memory API ============== The memory API models the memory and I/O buses and controllers of a QEMU machine. It attempts to allow modelling of: - - ordinary RAM - - memory-mapped I/O (MMIO) - - memory controllers that can dynamically reroute physical memory regions - to different destinations +- ordinary RAM +- memory-mapped I/O (MMIO) +- memory controllers that can dynamically reroute physical memory regions + to different destinations The memory model provides support for - - tracking RAM changes by the guest - - setting up coalesced memory for kvm - - setting up ioeventfd regions for kvm +- tracking RAM changes by the guest +- setting up coalesced memory for kvm +- setting up ioeventfd regions for kvm Memory is modelled as an acyclic graph of MemoryRegion objects. Sinks (leaves) are RAM and MMIO regions, while other nodes represent @@ -98,25 +99,30 @@ ROM device memory region types), this host memory needs to be copied to the destination on migration. These APIs which allocate the host memory for you will also register the memory so it is migrated: - - memory_region_init_ram() - - memory_region_init_rom() - - memory_region_init_rom_device() + +- memory_region_init_ram() +- memory_region_init_rom() +- memory_region_init_rom_device() For most devices and boards this is the correct thing. If you have a special case where you need to manage the migration of the backing memory yourself, you can call the functions: - - memory_region_init_ram_nomigrate() - - memory_region_init_rom_nomigrate() - - memory_region_init_rom_device_nomigrate() + +- memory_region_init_ram_nomigrate() +- memory_region_init_rom_nomigrate() +- memory_region_init_rom_device_nomigrate() + which only initialize the MemoryRegion and leave handling migration to the caller. The functions: - - memory_region_init_resizeable_ram() - - memory_region_init_ram_from_file() - - memory_region_init_ram_from_fd() - - memory_region_init_ram_ptr() - - memory_region_init_ram_device_ptr() + +- memory_region_init_resizeable_ram() +- memory_region_init_ram_from_file() +- memory_region_init_ram_from_fd() +- memory_region_init_ram_ptr() +- memory_region_init_ram_device_ptr() + are for special cases only, and so they do not automatically register the backing memory for migration; the caller must manage migration if necessary. @@ -218,7 +224,7 @@ For example, suppose we have a container A of size 0x8000 with two subregions B and C. B is a container mapped at 0x2000, size 0x4000, priority 2; C is an MMIO region mapped at 0x0, size 0x6000, priority 1. B currently has two of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at -offset 0x2000. As a diagram: +offset 0x2000. As a diagram:: 0 1000 2000 3000 4000 5000 6000 7000 8000 |------|------|------|------|------|------|------|------| @@ -228,8 +234,9 @@ offset 0x2000. As a diagram: D: [DDDDD] E: [EEEEE] -The regions that will be seen within this address range then are: - [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] +The regions that will be seen within this address range then are:: + + [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] Since B has higher priority than C, its subregions appear in the flat map even where they overlap with C. In ranges where B has not mapped anything @@ -237,8 +244,9 @@ C's region appears. If B had provided its own MMIO operations (ie it was not a pure container) then these would be used for any addresses in its range not handled by -D or E, and the result would be: - [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB] +D or E, and the result would be:: + + [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB] Priority values are local to a container, because the priorities of two regions are only compared when they are both children of the same container. @@ -257,6 +265,7 @@ guest accesses an address: - all direct subregions of the root region are matched against the address, in descending priority order + - if the address lies outside the region offset/size, the subregion is discarded - if the subregion is a leaf (RAM or MMIO), the search terminates, returning @@ -270,36 +279,39 @@ guest accesses an address: address range), then if this is a container with its own MMIO or RAM backing the search terminates, returning the container itself. Otherwise we continue with the next subregion in priority order + - if none of the subregions match the address then the search terminates with no match found Example memory map ------------------ -system_memory: container@0-2^48-1 - | - +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff) - | - +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff) - | - +---- vga-window: alias@0xa0000-0xbffff ---> #pci (0xa0000-0xbffff) - | (prio 1) - | - +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) - -pci (0-2^32-1) - | - +--- vga-area: container@0xa0000-0xbffff - | | - | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff) - | | - | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff) - | - +---- vram: ram@0xe1000000-0xe1ffffff - | - +---- vga-mmio: mmio@0xe2000000-0xe200ffff - -ram: ram@0x00000000-0xffffffff +:: + + system_memory: container@0-2^48-1 + | + +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff) + | + +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff) + | + +---- vga-window: alias@0xa0000-0xbffff ---> #pci (0xa0000-0xbffff) + | (prio 1) + | + +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) + + pci (0-2^32-1) + | + +--- vga-area: container@0xa0000-0xbffff + | | + | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff) + | | + | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff) + | + +---- vram: ram@0xe1000000-0xe1ffffff + | + +---- vga-mmio: mmio@0xe2000000-0xe200ffff + + ram: ram@0x00000000-0xffffffff This is a (simplified) PC memory map. The 4GB RAM block is mapped into the system address space via two aliases: "lomem" is a 1:1 mapping of the first @@ -336,16 +348,16 @@ rather than completing successfully; those devices can use the In addition various constraints can be supplied to control how these callbacks are called: - - .valid.min_access_size, .valid.max_access_size define the access sizes - (in bytes) which the device accepts; accesses outside this range will - have device and bus specific behaviour (ignored, or machine check) - - .valid.unaligned specifies that the *device being modelled* supports - unaligned accesses; if false, unaligned accesses will invoke the - appropriate bus or CPU specific behaviour. - - .impl.min_access_size, .impl.max_access_size define the access sizes - (in bytes) supported by the *implementation*; other access sizes will be - emulated using the ones available. For example a 4-byte write will be - emulated using four 1-byte writes, if .impl.max_access_size = 1. - - .impl.unaligned specifies that the *implementation* supports unaligned - accesses; if false, unaligned accesses will be emulated by two aligned - accesses. +- .valid.min_access_size, .valid.max_access_size define the access sizes + (in bytes) which the device accepts; accesses outside this range will + have device and bus specific behaviour (ignored, or machine check) +- .valid.unaligned specifies that the *device being modelled* supports + unaligned accesses; if false, unaligned accesses will invoke the + appropriate bus or CPU specific behaviour. +- .impl.min_access_size, .impl.max_access_size define the access sizes + (in bytes) supported by the *implementation*; other access sizes will be + emulated using the ones available. For example a 4-byte write will be + emulated using four 1-byte writes, if .impl.max_access_size = 1. +- .impl.unaligned specifies that the *implementation* supports unaligned + accesses; if false, unaligned accesses will be emulated by two aligned + accesses. diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 135743a2bf..60f897d915 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -600,7 +600,6 @@ the ``avocado_qemu.Test`` class. Here's a simple usage example: class Version(Test): """ - :avocado: enable :avocado: tags=quick """ def test_qmp_human_info_version(self): @@ -634,7 +633,46 @@ instance, available at ``self.vm``. Because many tests will tweak the QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) is left to the test writer. -At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine +The base test class has also support for tests with more than one +QEMUMachine. The way to get machines is through the ``self.get_vm()`` +method which will return a QEMUMachine instance. The ``self.get_vm()`` +method accepts arguments that will be passed to the QEMUMachine creation +and also an optional `name` attribute so you can identify a specific +machine and get it more than once through the tests methods. A simple +and hypothetical example follows: + +.. code:: + + from avocado_qemu import Test + + + class MultipleMachines(Test): + """ + :avocado: enable + """ + def test_multiple_machines(self): + first_machine = self.get_vm() + second_machine = self.get_vm() + self.get_vm(name='third_machine').launch() + + first_machine.launch() + second_machine.launch() + + first_res = first_machine.command( + 'human-monitor-command', + command_line='info version') + + second_res = second_machine.command( + 'human-monitor-command', + command_line='info version') + + third_res = self.get_vm(name='third_machine').command( + 'human-monitor-command', + command_line='info version') + + self.assertEquals(first_res, second_res, third_res) + +At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines shutdown. QEMUMachine diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000..3690955dd1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,15 @@ +.. QEMU documentation master file, created by + sphinx-quickstart on Thu Jan 31 16:40:14 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to QEMU's documentation! +================================ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + interop/index + devel/index + diff --git a/docs/interop/conf.py b/docs/interop/conf.py new file mode 100644 index 0000000000..cf3c69d4a7 --- /dev/null +++ b/docs/interop/conf.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'interop' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = u'System Emulation Management and Interoperability Guide' diff --git a/docs/interop/index.rst b/docs/interop/index.rst new file mode 100644 index 0000000000..2df977dd52 --- /dev/null +++ b/docs/interop/index.rst @@ -0,0 +1,18 @@ +.. This is the top level page for the 'interop' manual. + + +QEMU System Emulation Management and Interoperability Guide +=========================================================== + +This manual contains documents and specifications that are useful +for making QEMU interoperate with other software. + +Contents: + +.. toctree:: + :maxdepth: 2 + + bitmaps + live-block-operations + pr-helper + diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 4ee4fc5a89..06e376bcd2 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1177,9 +1177,7 @@ static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) usb_mtp_object_free_one(s, o); success = true; } - } - - if (o->format == FMT_ASSOCIATION) { + } else if (o->format == FMT_ASSOCIATION) { if (rmdir(o->path)) { partial_delete = true; } else { @@ -1591,17 +1589,21 @@ done: return ret; } -static void usb_mtp_update_object(MTPObject *parent, char *name) +static int usb_mtp_update_object(MTPObject *parent, char *name) { + int ret = -1; + MTPObject *o = usb_mtp_object_lookup_name(parent, name, strlen(name)); if (o) { - lstat(o->path, &o->stat); + ret = lstat(o->path, &o->stat); } + + return ret; } -static void usb_mtp_write_data(MTPState *s) +static int usb_mtp_write_data(MTPState *s) { MTPData *d = s->data_out; MTPObject *parent = @@ -1609,6 +1611,7 @@ static void usb_mtp_write_data(MTPState *s) char *path = NULL; uint64_t rc; mode_t mask = 0644; + int ret = 0; assert(d != NULL); @@ -1617,13 +1620,13 @@ static void usb_mtp_write_data(MTPState *s) if (!parent || !s->write_pending) { usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, 0, 0, 0, 0); - return; + return 1; } if (s->dataset.filename) { path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); if (s->dataset.format == FMT_ASSOCIATION) { - d->fd = mkdir(path, mask); + ret = mkdir(path, mask); goto free; } d->fd = open(path, O_CREAT | O_WRONLY | @@ -1653,15 +1656,21 @@ static void usb_mtp_write_data(MTPState *s) goto done; } if (d->write_status != WRITE_END) { - return; + g_free(path); + return ret; } else { - /* Only for < 4G file sizes */ - if (s->dataset.size != 0xFFFFFFFF && d->offset != s->dataset.size) { + /* + * Return an incomplete transfer if file size doesn't match + * for < 4G file or if lstat fails which will result in an incorrect + * file size + */ + if ((s->dataset.size != 0xFFFFFFFF && + d->offset != s->dataset.size) || + usb_mtp_update_object(parent, s->dataset.filename)) { usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, 0, 0, 0, 0); goto done; } - usb_mtp_update_object(parent, s->dataset.filename); } } @@ -1676,12 +1685,14 @@ done: */ if (d->fd != -1) { close(d->fd); + d->fd = -1; } free: g_free(s->dataset.filename); s->dataset.size = 0; g_free(path); s->write_pending = false; + return ret; } static void usb_mtp_write_metadata(MTPState *s, uint64_t dlen) @@ -1718,14 +1729,12 @@ static void usb_mtp_write_metadata(MTPState *s, uint64_t dlen) s->write_pending = true; if (s->dataset.format == FMT_ASSOCIATION) { - usb_mtp_write_data(s); - /* next_handle will be allocated to the newly created dir */ - if (d->fd == -1) { + if (usb_mtp_write_data(s)) { + /* next_handle will be allocated to the newly created dir */ usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); return; } - d->fd = -1; } usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 833250a886..67b7465915 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -82,7 +82,7 @@ struct USBHostDevice { uint32_t options; uint32_t loglevel; bool needs_autoscan; - + bool allow_guest_reset; /* state */ QTAILQ_ENTRY(USBHostDevice) next; int seen, errcount; @@ -1456,6 +1456,10 @@ static void usb_host_handle_reset(USBDevice *udev) USBHostDevice *s = USB_HOST_DEVICE(udev); int rc; + if (!s->allow_guest_reset) { + return; + } + trace_usb_host_reset(s->bus_num, s->addr); rc = libusb_reset_device(s->dh); @@ -1573,6 +1577,7 @@ static Property usb_host_dev_properties[] = { DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0), DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32), + DEFINE_PROP_BOOL("guest-reset", USBHostDevice, allow_guest_reset, true), DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel, LIBUSB_LOG_LEVEL_WARNING), DEFINE_PROP_BIT("pipeline", USBHostDevice, options, diff --git a/scripts/qemu.py b/python/qemu/__init__.py index f7269eefbb..fd144c0006 100644 --- a/scripts/qemu.py +++ b/python/qemu/__init__.py @@ -16,12 +16,13 @@ import errno import logging import os import subprocess -import qmp.qmp import re import shutil import socket import tempfile +from . import qmp + LOG = logging.getLogger(__name__) @@ -66,7 +67,7 @@ class QEMUMachineAddDeviceError(QEMUMachineError): failures reported by the QEMU binary itself. """ -class MonitorResponseError(qmp.qmp.QMPError): +class MonitorResponseError(qmp.QMPError): """ Represents erroneous QMP monitor reply """ @@ -266,8 +267,8 @@ class QEMUMachine(object): self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") self._qemu_log_file = open(self._qemu_log_path, 'wb') - self._qmp = qmp.qmp.QEMUMonitorProtocol(self._vm_monitor, - server=True) + self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, + server=True) def _post_launch(self): self._qmp.accept() @@ -319,6 +320,7 @@ class QEMUMachine(object): self._pre_launch() self._qemu_full_args = (self._wrapper + [self._binary] + self._base_args() + self._args) + LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) self._popen = subprocess.Popen(self._qemu_full_args, stdin=devnull, stdout=self._qemu_log_file, @@ -383,7 +385,7 @@ class QEMUMachine(object): """ reply = self.qmp(cmd, conv_keys, **args) if reply is None: - raise qmp.qmp.QMPError("Monitor is closed") + raise qmp.QMPError("Monitor is closed") if "error" in reply: raise MonitorResponseError(reply) return reply["return"] diff --git a/scripts/qmp/qmp.py b/python/qemu/qmp.py index 5c8cf6a056..5c8cf6a056 100644 --- a/scripts/qmp/qmp.py +++ b/python/qemu/qmp.py diff --git a/scripts/qtest.py b/python/qemu/qtest.py index afac3fe900..eb45824dd0 100644 --- a/scripts/qtest.py +++ b/python/qemu/qtest.py @@ -13,7 +13,8 @@ import socket import os -import qemu + +from . import QEMUMachine class QEMUQtestProtocol(object): @@ -79,7 +80,7 @@ class QEMUQtestProtocol(object): self._sock.settimeout(timeout) -class QEMUQtestMachine(qemu.QEMUMachine): +class QEMUQtestMachine(QEMUMachine): '''A QEMU VM''' def __init__(self, binary, args=None, name=None, test_dir="/var/tmp", diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 2a13fa4f84..a6748910ad 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -25,6 +25,7 @@ check for crashes and unexpected errors. """ from __future__ import print_function +import os import sys import glob import logging @@ -34,6 +35,7 @@ import random import argparse from itertools import chain +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) from qemu import QEMUMachine logger = logging.getLogger('device-crash-test') diff --git a/scripts/qmp/__init__.py b/scripts/qmp/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/scripts/qmp/__init__.py +++ /dev/null diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index e8cb7646a0..30cf8a9a0d 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -37,10 +37,13 @@ # from __future__ import print_function +import os +import sys import base64 import random -import qmp +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import qmp class QemuGuestAgent(qmp.QEMUMonitorProtocol): diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 770140772d..9fec46e2ed 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -66,7 +66,6 @@ # sent to QEMU, which is useful for debugging and documentation generation. from __future__ import print_function -import qmp import json import ast import readline @@ -76,6 +75,9 @@ import errno import atexit import shlex +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import qmp + class QMPCompleter(list): def complete(self, text, state): for cmd in self: diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py index ed7e581b4f..3e9d282a49 100755 --- a/scripts/render_block_graph.py +++ b/scripts/render_block_graph.py @@ -23,6 +23,8 @@ import sys import subprocess import json from graphviz import Digraph + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) from qemu import MonitorResponseError diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index 1e54fd5932..a66ec72daa 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -10,12 +10,12 @@ import os import sys +import uuid import avocado -SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) -SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR)) -sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts')) +SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..') +sys.path.append(os.path.join(SRC_ROOT_DIR, 'python')) from qemu import QEMUMachine @@ -42,13 +42,29 @@ def pick_default_qemu_bin(): class Test(avocado.Test): def setUp(self): - self.vm = None + self._vms = {} self.qemu_bin = self.params.get('qemu_bin', default=pick_default_qemu_bin()) if self.qemu_bin is None: self.cancel("No QEMU binary defined or found in the source tree") - self.vm = QEMUMachine(self.qemu_bin) + + def _new_vm(self, *args): + vm = QEMUMachine(self.qemu_bin) + if args: + vm.add_args(*args) + return vm + + @property + def vm(self): + return self.get_vm(name='default') + + def get_vm(self, *args, name=None): + if not name: + name = str(uuid.uuid4()) + if self._vms.get(name) is None: + self._vms[name] = self._new_vm(*args) + return self._vms[name] def tearDown(self): - if self.vm is not None: - self.vm.shutdown() + for vm in self._vms.values(): + vm.shutdown() diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index 98324f7591..beeb1e59e8 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -18,7 +18,6 @@ class BootLinuxConsole(Test): Boots a x86_64 Linux kernel and checks that the console is operational and the kernel command line is properly passed from QEMU to the kernel - :avocado: enable :avocado: tags=x86_64 """ diff --git a/tests/acceptance/linux_initrd.py b/tests/acceptance/linux_initrd.py index 737355c2ef..fbdb48e43f 100644 --- a/tests/acceptance/linux_initrd.py +++ b/tests/acceptance/linux_initrd.py @@ -8,6 +8,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +import logging import tempfile from avocado.utils.process import run @@ -18,20 +19,21 @@ class LinuxInitrd(Test): """ Checks QEMU evaluates correctly the initrd file passed as -initrd option. - :avocado: enable :avocado: tags=x86_64 """ - timeout = 60 + timeout = 300 - def test_with_2gib_file_should_exit_error_msg(self): + def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self): """ Pretends to boot QEMU with an initrd file with size of 2GiB and expect it exits with error message. + Fedora-18 shipped with linux-3.6 which have not supported xloadflags + cannot support more than 2GiB initrd. """ - kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/' - 'Everything/x86_64/os/images/pxeboot/vmlinuz') - kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora/li' + 'nux/releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz') + kernel_hash = '41464f68efe42b9991250bed86c7081d2ccdbb21' kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) max_size = 2 * (1024 ** 3) - 1 @@ -39,10 +41,44 @@ class LinuxInitrd(Test): initrd.seek(max_size) initrd.write(b'\0') initrd.flush() - cmd = "%s -kernel %s -initrd %s" % (self.qemu_bin, kernel_path, - initrd.name) + cmd = "%s -kernel %s -initrd %s -m 4096" % ( + self.qemu_bin, kernel_path, initrd.name) res = run(cmd, ignore_status=True) self.assertEqual(res.exit_status, 1) expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % ( max_size + 1) self.assertRegex(res.stderr_text, expected_msg) + + def test_with_2gib_file_should_work_with_linux_v4_16(self): + """ + QEMU has supported up to 4 GiB initrd for recent kernel + Expect guest can reach 'Unpacking initramfs...' + """ + kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/' + 'Everything/x86_64/os/images/pxeboot/vmlinuz') + kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + max_size = 2 * (1024 ** 3) + 1 + + with tempfile.NamedTemporaryFile() as initrd: + initrd.seek(max_size) + initrd.write(b'\0') + initrd.flush() + + self.vm.set_machine('pc') + self.vm.set_console() + kernel_command_line = 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-initrd', initrd.name, + '-m', '5120') + self.vm.launch() + console = self.vm.console_socket.makefile() + console_logger = logging.getLogger('console') + while True: + msg = console.readline() + console_logger.debug(msg.strip()) + if 'Unpacking initramfs...' in msg: + break + if 'Kernel panic - not syncing' in msg: + self.fail("Kernel panic reached") diff --git a/tests/acceptance/migration.py b/tests/acceptance/migration.py new file mode 100644 index 0000000000..6115cf6c24 --- /dev/null +++ b/tests/acceptance/migration.py @@ -0,0 +1,53 @@ +# Migration test +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Authors: +# Cleber Rosa <crosa@redhat.com> +# Caio Carrara <ccarrara@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from avocado_qemu import Test + +from avocado.utils import network +from avocado.utils import wait + + +class Migration(Test): + """ + :avocado: enable + """ + + timeout = 10 + + @staticmethod + def migration_finished(vm): + return vm.command('query-migrate')['status'] in ('completed', 'failed') + + def _get_free_port(self): + port = network.find_free_port() + if port is None: + self.cancel('Failed to find a free port') + return port + + + def test_migration_with_tcp_localhost(self): + source_vm = self.get_vm() + dest_uri = 'tcp:localhost:%u' % self._get_free_port() + dest_vm = self.get_vm('-incoming', dest_uri) + dest_vm.launch() + source_vm.launch() + source_vm.qmp('migrate', uri=dest_uri) + wait.wait_for( + self.migration_finished, + timeout=self.timeout, + step=0.1, + args=(source_vm,) + ) + self.assertEqual(dest_vm.command('query-migrate')['status'], 'completed') + self.assertEqual(source_vm.command('query-migrate')['status'], 'completed') + self.assertEqual(dest_vm.command('query-status')['status'], 'running') + self.assertEqual(source_vm.command('query-status')['status'], 'postmigrate') diff --git a/tests/acceptance/version.py b/tests/acceptance/version.py index 13b0a7440d..67c2192c93 100644 --- a/tests/acceptance/version.py +++ b/tests/acceptance/version.py @@ -14,7 +14,6 @@ from avocado_qemu import Test class Version(Test): """ - :avocado: enable :avocado: tags=quick """ def test_qmp_human_info_version(self): diff --git a/tests/acceptance/virtio_version.py b/tests/acceptance/virtio_version.py index ce990250d8..37fc01ea18 100644 --- a/tests/acceptance/virtio_version.py +++ b/tests/acceptance/virtio_version.py @@ -11,7 +11,7 @@ Check compatibility of virtio device types import sys import os -sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts")) +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import QEMUMachine from avocado_qemu import Test @@ -61,7 +61,6 @@ class VirtioVersionCheck(Test): same device tree created by `disable-modern` and `disable-legacy`. - :avocado: enable :avocado: tags=x86_64 """ diff --git a/tests/acceptance/vnc.py b/tests/acceptance/vnc.py index b1ef9d71b1..064ceabcc1 100644 --- a/tests/acceptance/vnc.py +++ b/tests/acceptance/vnc.py @@ -13,7 +13,6 @@ from avocado_qemu import Test class Vnc(Test): """ - :avocado: enable :avocado: tags=vnc,quick """ def test_no_vnc(self): diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index 398e3f2706..0e304660b8 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -24,13 +24,14 @@ import re import sys import time -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'scripts')) -import qemu -import qmp.qmp from guestperf.progress import Progress, ProgressStats from guestperf.report import Report from guestperf.timings import TimingRecord, Timings +sys.path.append(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'python')) +import qemu + class Engine(object): diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235 index d6edd97ab4..75c203b30c 100755 --- a/tests/qemu-iotests/235 +++ b/tests/qemu-iotests/235 @@ -23,7 +23,7 @@ import os import iotests from iotests import qemu_img_create, qemu_io, file_path, log -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import QEMUMachine diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238 index f81ee1112f..688abc9acb 100755 --- a/tests/qemu-iotests/238 +++ b/tests/qemu-iotests/238 @@ -23,7 +23,7 @@ import os import iotests from iotests import log -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import QEMUMachine diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 4910fb2005..3d15571688 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -32,8 +32,8 @@ import atexit import io from collections import OrderedDict -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts')) -import qtest +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import qtest # This will not work if arguments contain spaces but is necessary if we diff --git a/tests/requirements.txt b/tests/requirements.txt index 64c6e27a94..002ded6a22 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ # Add Python module requirements, one per line, to be installed # in the tests/venv Python virtual environment. For more info, # refer to: https://pip.pypa.io/en/stable/user_guide/#id1 -avocado-framework==65.0 +avocado-framework==68.0 diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index bdca6cb2fc..0556bdcf9e 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -17,7 +17,7 @@ import sys import logging import time import datetime -sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts")) +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import QEMUMachine, kvm_available import subprocess import hashlib |