#!/usr/bin/env python3
#
# Test for when a backing file is considered overridden (thus, a
# json:{} filename is generated for the overlay) and when it is not
#
# Copyright (C) 2018 Red Hat, Inc.
#
# 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, see <http://www.gnu.org/licenses/>.
#
# Creator/Owner: Max Reitz <mreitz@redhat.com>

import iotests
from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \
        filter_qmp_testfiles, filter_qmp_imgfmt

# Need backing file and change-backing-file support
iotests.script_initialize(
    supported_fmts=['qcow2', 'qed'],
    supported_platforms=['linux'],
)


def log_node_info(node):
    log('')

    log('bs->filename: ' + node['image']['filename'],
        filters=[filter_testfiles, filter_imgfmt])
    log('bs->backing_file: ' + node['image']['full-backing-filename'],
        filters=[filter_testfiles, filter_imgfmt])

    if 'backing-image' in node['image']:
        log('bs->backing->bs->filename: ' +
            node['image']['backing-image']['filename'],
            filters=[filter_testfiles, filter_imgfmt])
    else:
        log('bs->backing: (none)')

    log('')


with iotests.FilePath('base.img') as base_img_path, \
     iotests.FilePath('top.img') as top_img_path, \
     iotests.VM() as vm:

    assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
    # Choose a funny way to describe the backing filename
    assert qemu_img('create', '-f', iotests.imgfmt, '-b',
                    'file:' + base_img_path, '-F', iotests.imgfmt,
                    top_img_path) == 0

    vm.launch()

    log('--- Implicit backing file ---')
    log('')

    vm.qmp_log('blockdev-add',
                node_name='node0',
                driver=iotests.imgfmt,
                file={
                    'driver': 'file',
                    'filename': top_img_path
                },
                filters=[filter_qmp_testfiles, filter_qmp_imgfmt])

    # Filename should be plain, and the backing node filename should
    # not contain the "file:" prefix
    log_node_info(vm.node_info('node0'))

    vm.qmp_log('blockdev-del', node_name='node0')

    log('')
    log('--- change-backing-file ---')
    log('')

    vm.qmp_log('blockdev-add',
               node_name='node0',
               driver=iotests.imgfmt,
               file={
                   'driver': 'file',
                   'filename': top_img_path
               },
               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])

    # Changing the backing file to a qemu-reported filename should
    # result in qemu accepting the corresponding BDS as the implicit
    # backing BDS (and thus not generate a json:{} filename).
    # So, first, query the backing filename.

    backing_filename = \
        vm.node_info('node0')['image']['backing-image']['filename']

    # Next, change the backing file to something different

    vm.qmp_log('change-backing-file',
               image_node_name='node0',
               device='node0',
               backing_file='null-co://',
               filters=[filter_qmp_testfiles])

    # Now, verify that we get a json:{} filename
    # (Image header says "null-co://", actual backing file still is
    # base_img_path)

    log_node_info(vm.node_info('node0'))

    # Change it back
    # (To get header and backing file in sync)

    vm.qmp_log('change-backing-file',
               image_node_name='node0',
               device='node0',
               backing_file=backing_filename,
               filters=[filter_qmp_testfiles])

    # And verify that we get our original results

    log_node_info(vm.node_info('node0'))

    # Finally, try a "file:" prefix.  While this is actually what we
    # originally had in the image header, qemu will not reopen the
    # backing file here, so it cannot verify that this filename
    # "resolves" to the actual backing BDS's filename and will thus
    # consider both to be different.
    # (This may be fixed in the future.)

    vm.qmp_log('change-backing-file',
               image_node_name='node0',
               device='node0',
               backing_file=('file:' + backing_filename),
               filters=[filter_qmp_testfiles])

    # So now we should get a json:{} filename

    log_node_info(vm.node_info('node0'))

    # Remove and re-attach so we can see that (as in our first try),
    # opening the image anew helps qemu resolve the header backing
    # filename.

    vm.qmp_log('blockdev-del', node_name='node0')

    vm.qmp_log('blockdev-add',
               node_name='node0',
               driver=iotests.imgfmt,
               file={
                   'driver': 'file',
                   'filename': top_img_path
               },
               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])

    log_node_info(vm.node_info('node0'))

    vm.qmp_log('blockdev-del', node_name='node0')

    log('')
    log('--- Override backing file ---')
    log('')

    # For this test, we need the plain filename in the image header
    # (because qemu cannot "canonicalize"/"resolve" the backing
    # filename unless the backing file is opened implicitly with the
    # overlay)
    assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
                    '-F', iotests.imgfmt, top_img_path) == 0

    # You can only reliably override backing options by using a node
    # reference (or by specifying file.filename, but, well...)
    vm.qmp_log('blockdev-add', node_name='null', driver='null-co')

    vm.qmp_log('blockdev-add',
               node_name='node0',
               driver=iotests.imgfmt,
               file={
                   'driver': 'file',
                   'filename': top_img_path
               },
               backing='null',
               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])

    # Should get a json:{} filename (and bs->backing_file is
    # null-co://, because that field actually has not much to do
    # with the header backing filename (except that it is changed by
    # change-backing-file))

    log_node_info(vm.node_info('node0'))

    # Detach the backing file by reopening the whole thing

    vm.qmp_log('blockdev-del', node_name='node0')
    vm.qmp_log('blockdev-del', node_name='null')

    vm.qmp_log('blockdev-add',
               node_name='node0',
               driver=iotests.imgfmt,
               file={
                   'driver': 'file',
                   'filename': top_img_path
               },
               backing=None,
               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])

    # Should get a json:{} filename (because we overrode the backing
    # file to not be there)

    log_node_info(vm.node_info('node0'))

    # Open the original backing file

    vm.qmp_log('blockdev-add',
               node_name='original-backing',
               driver=iotests.imgfmt,
               file={
                   'driver': 'file',
                   'filename': base_img_path
               },
               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])

    # Attach the original backing file to its overlay

    vm.qmp_log('blockdev-snapshot',
               node='original-backing',
               overlay='node0')

    # This should give us the original plain result

    log_node_info(vm.node_info('node0'))

    vm.qmp_log('blockdev-del', node_name='node0')
    vm.qmp_log('blockdev-del', node_name='original-backing')

    vm.shutdown()