普通文本  |  210行  |  7.47 KB

#!/usr/bin/python
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
import shutil
import tempfile
import unittest
from contextlib import contextmanager

import common
from autotest_lib.client.common_lib import error
from autotest_lib.site_utils import lxc
from autotest_lib.site_utils.lxc import BaseImage
from autotest_lib.site_utils.lxc import constants
from autotest_lib.site_utils.lxc import unittest_setup
from autotest_lib.site_utils.lxc import utils as lxc_utils


test_dir = None
# A reference to an existing base container that can be copied for tests that
# need a base container.  This is an optimization.
reference_container = None
# The reference container can either be a reference to an existing container, or
# to a container that was downloaded by this test.  If the latter, then it needs
# to be cleaned up when the tests are complete.
cleanup_ref_container = False


class BaseImageTests(lxc_utils.LXCTests):
    """Unit tests to verify the BaseImage class."""

    def testCreate_existing(self):
        """Verifies that BaseImage works with existing base containers."""
        with TestBaseContainer() as control:
            manager = BaseImage(control.container_path,
                                control.name)
            self.assertIsNotNone(manager.base_container)
            self.assertEquals(control.container_path,
                              manager.base_container.container_path)
            self.assertEquals(control.name, manager.base_container.name)
            try:
                manager.base_container.refresh_status()
            except error.ContainerError:
                self.fail('Base container was not valid.\n%s' %
                          error.format_error())


    def testCleanup_noClones(self):
        """Verifies that cleanup cleans up the base image."""
        base = lxc.Container.clone(src=reference_container,
                                   new_name=constants.BASE,
                                   new_path=test_dir,
                                   snapshot=True)

        manager = BaseImage(base.container_path, base.name)
        # Precondition: ensure base exists and is a valid container.
        base.refresh_status()

        manager.cleanup()

        # Verify that the base container was cleaned up.
        self.assertFalse(lxc_utils.path_exists(
                os.path.join(base.container_path, base.name)))


    def testCleanup_withClones(self):
        """Verifies that cleanup cleans up the base image.

        Ensure that it works even when clones of the base image exist.
        """
        # Do not snapshot, as snapshots of snapshots behave differently than
        # snapshots of full container clones.  BaseImage cleanup code assumes
        # that the base container is not a snapshot.
        base = lxc.Container.clone(src=reference_container,
                                   new_name=constants.BASE,
                                   new_path=test_dir,
                                   snapshot=False)
        manager = BaseImage(base.container_path, base.name)
        clones = []
        for i in range(3):
            clones.append(lxc.Container.clone(src=base,
                                              new_name='clone_%d' % i,
                                              snapshot=True))


        # Precondition: all containers are valid.
        base.refresh_status()
        for container in clones:
            container.refresh_status()

        manager.cleanup()

        # Verify that all containers were cleaned up
        self.assertFalse(lxc_utils.path_exists(
                os.path.join(base.container_path, base.name)))
        for container in clones:
            if constants.SUPPORT_SNAPSHOT_CLONE:
                # Snapshot clones should get deleted along with the base
                # container.
                self.assertFalse(lxc_utils.path_exists(
                        os.path.join(container.container_path, container.name)))
            else:
                # If snapshot clones aren't supported (e.g. on moblab), the
                # clones should not be affected by the destruction of the base
                # container.
                try:
                    container.refresh_status()
                except error.ContainerError:
                    self.fail(error.format_error())



class BaseImageSetupTests(lxc_utils.LXCTests):
    """Unit tests to verify the setup of specific images.

    Some images differ in layout from others.  These tests test specific images
    to make sure the setup code works for all of them.
    """

    def setUp(self):
        self.manager = BaseImage(container_path=test_dir)


    def tearDown(self):
        self.manager.cleanup()


    def testSetupBase05(self):
        """Verifies that setup works for moblab base container.

        Verifies that the code for installing the rootfs location into the
        lxc config, is working correctly.
        """
        # Set up the bucket, then start the base container, and verify it works.
        self.manager.setup('base_05')
        container = self.manager.base_container

        container.start(wait_for_network=False)
        self.assertTrue(container.is_running())


    @unittest.skipIf(constants.IS_MOBLAB,
                     "Moblab does not support the regular base container.")
    def testSetupBase09(self):
        """Verifies that setup works for base container.

        Verifies that the code for installing the rootfs location into the
        lxc config, is working correctly.
        """
        self.manager.setup('base_09')
        container = self.manager.base_container

        container.start(wait_for_network=False)
        self.assertTrue(container.is_running())


@contextmanager
def TestBaseContainer(name=constants.BASE):
    """Context manager for creating a scoped base container for testing.

    @param name: (optional) Name of the base container.  If this is not
                 provided, the default base container name is used.
    """
    container = lxc.Container.clone(src=reference_container,
                                    new_name=name,
                                    new_path=test_dir,
                                    snapshot=True,
                                    cleanup=False)
    try:
        yield container
    finally:
        if not unittest_setup.config.skip_cleanup:
            container.destroy()


def setUpModule():
    """Module setup for base image unittests.

    Sets up a test directory along with a reference container that is used by
    tests that need an existing base container.
    """
    global test_dir
    global reference_container
    global cleanup_ref_container

    test_dir = tempfile.mkdtemp(dir=lxc.DEFAULT_CONTAINER_PATH,
                                prefix='base_container_manager_unittest_')
    # Unfortunately, aside from duping the BaseImage code completely, there
    # isn't an easy way to download and configure a base container.  So even
    # though this is the BaseImage unittest, we use a BaseImage to set it up.
    bcm = BaseImage()
    if bcm.base_container is None:
        bcm.setup()
        cleanup_ref_container = True
    reference_container = bcm.base_container


def tearDownModule():
    """Deletes the test dir and reference container."""
    if not unittest_setup.config.skip_cleanup:
        if cleanup_ref_container:
            reference_container.destroy()
        shutil.rmtree(test_dir)


if __name__ == '__main__':
    unittest.main()