[autotest] Refactor ContainerBucket.
Remove base container management code from ContainerBucket. This change:
- simplifies the ContainerBucket,
- makes it clearer that the base image setup is something that is
performed separately from normal ContainerBucket operation,
- starts the process of decoupling container creation from the
ContainerBucket, which will be necesary to move to a container pool.
BUG=chromium:720219
TEST=sudo python base_image_unittest.py
TEST=sudo python container_bucket_unittest.py
TEST=sudo python container_unittest.py
TEST=sudo python lxc_config_unittest.py
TEST=sudo python lxc_functional_test.py
TEST=sudo python zygote_unittest.py
Change-Id: I719f7b113729c7387ddacaec77c4c63154d52ea0
Reviewed-on: https://chromium-review.googlesource.com/636033
Commit-Ready: Ben Kwa <[email protected]>
Tested-by: Ben Kwa <[email protected]>
Reviewed-by: Ben Kwa <[email protected]>
diff --git a/site_utils/lxc.py b/site_utils/lxc.py
index ed1033c..8595163 100755
--- a/site_utils/lxc.py
+++ b/site_utils/lxc.py
@@ -57,11 +57,11 @@
utils.run('sudo true')
options = parse_options()
- bucket = lxc.ContainerBucket(container_path=options.path)
+ image = lxc.BaseImage(container_path=options.path)
if options.setup:
- bucket.setup_base(name=options.name, force_delete=options.force_delete)
+ image.setup(name=options.name, force_delete=options.force_delete)
elif options.force_delete:
- bucket.destroy_all()
+ image.cleanup()
if __name__ == '__main__':
diff --git a/site_utils/lxc/__init__.py b/site_utils/lxc/__init__.py
index d260302..1fcc7e7 100644
--- a/site_utils/lxc/__init__.py
+++ b/site_utils/lxc/__init__.py
@@ -10,6 +10,7 @@
5. Cleanup, e.g., destroy the container.
"""
+from base_image import BaseImage
from constants import *
from container import Container
from container_bucket import ContainerBucket
diff --git a/site_utils/lxc/base_image.py b/site_utils/lxc/base_image.py
new file mode 100644
index 0000000..e1ae695
--- /dev/null
+++ b/site_utils/lxc/base_image.py
@@ -0,0 +1,193 @@
+# 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 logging
+import os
+import sys
+
+import common
+from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
+from autotest_lib.site_utils.lxc import constants
+from autotest_lib.site_utils.lxc import lxc
+from autotest_lib.site_utils.lxc import utils as lxc_utils
+from autotest_lib.site_utils.lxc.container import Container
+
+
+class BaseImage(object):
+ """A class that manages a base container.
+
+ Instantiating this class will cause it to search for a base container under
+ the given path and name. If one is found, the class adopts it. If not, the
+ setup() method needs to be called, to download and install a new base
+ container.
+
+ The actual base container can be obtained by calling the get() method.
+
+ Calling cleanup() will delete the base container along with all of its
+ associated snapshot clones.
+ """
+
+ def __init__(self,
+ container_path=constants.DEFAULT_CONTAINER_PATH,
+ base_name=constants.BASE):
+ """Creates a new BaseImage.
+
+ If a valid base container already exists on this machine, the BaseImage
+ adopts it. Otherwise, setup needs to be called to download a base and
+ install a base container.
+
+ @param container_path: The LXC path for the base container.
+ @param base_name: The base container name.
+ """
+ self.container_path = container_path
+ self.base_name = base_name
+ try:
+ base_container = Container.create_from_existing_dir(
+ container_path, base_name);
+ base_container.refresh_status()
+ self.base_container = base_container
+ except error.ContainerError:
+ self.base_container = None
+
+
+ def setup(self, name=None, force_delete=False):
+ """Download and setup the base container.
+
+ @param name: Name of the base container, defaults to the name passed to
+ the constructor. If a different name is provided, that
+ name overrides the name originally passed to the
+ constructor.
+ @param force_delete: True to force to delete existing base container.
+ This action will destroy all running test
+ containers. Default is set to False.
+ """
+ if name is not None:
+ self.base_name = name
+
+ if not self.container_path:
+ raise error.ContainerError(
+ 'You must set a valid directory to store containers in '
+ 'global config "AUTOSERV/ container_path".')
+
+ if not os.path.exists(self.container_path):
+ os.makedirs(self.container_path)
+
+ if self.base_container and not force_delete:
+ logging.error(
+ 'Base container already exists. Set force_delete to True '
+ 'to force to re-stage base container. Note that this '
+ 'action will destroy all running test containers')
+ # Set proper file permission. base container in moblab may have
+ # owner of not being root. Force to update the folder's owner.
+ self._set_root_owner()
+ return
+
+ # Destroy existing base container if exists.
+ if self.base_container:
+ self.cleanup()
+
+ try:
+ self._download_and_install_base_container()
+ self._set_root_owner()
+ except:
+ # Clean up if something went wrong.
+ base_path = os.path.join(self.container_path, self.base_name)
+ if lxc_utils.path_exists(base_path):
+ exc_info = sys.exc_info()
+ container = Container.create_from_existing_dir(
+ self.container_path, self.base_name)
+ # Attempt destroy. Log but otherwise ignore errors.
+ try:
+ container.destroy()
+ except error.CmdError as e:
+ logging.error(e)
+ # Raise the cached exception with original backtrace.
+ raise exc_info[0], exc_info[1], exc_info[2]
+ else:
+ self.base_container = Container.create_from_existing_dir(
+ self.container_path, self.base_name)
+
+
+ def cleanup(self):
+ """Destroys the base container.
+
+ This operation will also destroy all snapshot clones of the base
+ container.
+ """
+ # Find and delete clones first.
+ for clone in self._find_clones():
+ clone.destroy()
+ base = Container.create_from_existing_dir(self.container_path,
+ self.base_name)
+ base.destroy()
+
+
+ def get(self):
+ """Returns the base container.
+
+ @raise ContainerError: If the base image is invalid or missing.
+ """
+ if self.base_container is None:
+ raise error.ContainerError('Invalid base container.')
+ else:
+ return self.base_container
+
+
+ def _download_and_install_base_container(self):
+ """Downloads the base image, untars and configures it."""
+ base_path = os.path.join(self.container_path, self.base_name)
+ tar_path = os.path.join(self.container_path,
+ '%s.tar.xz' % self.base_name)
+
+ # Force cleanup of any previously downloaded/installed base containers.
+ # This ensures a clean setup of the new base container.
+ #
+ # TODO(kenobi): Add a check to ensure that the base container doesn't
+ # get deleted while snapshot clones exist (otherwise running tests might
+ # get disrupted).
+ path_to_cleanup = [tar_path, base_path]
+ for path in path_to_cleanup:
+ if os.path.exists(path):
+ utils.run('sudo rm -rf "%s"' % path)
+ container_url = constants.CONTAINER_BASE_URL_FMT % self.base_name
+ lxc.download_extract(container_url, tar_path, self.container_path)
+ # Remove the downloaded container tar file.
+ utils.run('sudo rm "%s"' % tar_path)
+
+ # Update container config with container_path from global config.
+ config_path = os.path.join(base_path, 'config')
+ rootfs_path = os.path.join(base_path, 'rootfs')
+ utils.run(('sudo sed '
+ '-i "s|\(lxc\.rootfs[[:space:]]*=\).*$|\\1 {rootfs}|" '
+ '"{config}"').format(rootfs=rootfs_path,
+ config=config_path))
+
+ def _set_root_owner(self):
+ """Changes the container group and owner to root.
+
+ This is necessary because we currently run privileged containers.
+ """
+ # TODO(dshi): Change root to current user when test container can be
+ # unprivileged container.
+ base_path = os.path.join(self.container_path, self.base_name)
+ utils.run('sudo chown -R root "%s"' % base_path)
+ utils.run('sudo chgrp -R root "%s"' % base_path)
+
+
+ def _find_clones(self):
+ """Finds snapshot clones of the current base container."""
+ snapshot_file = os.path.join(self.container_path,
+ self.base_name,
+ 'lxc_snapshots')
+ if not lxc_utils.path_exists(snapshot_file):
+ return
+ cmd = 'sudo cat %s' % snapshot_file
+ clone_info = [line.strip()
+ for line in utils.run(cmd).stdout.splitlines()]
+ # lxc_snapshots contains pairs of lines (lxc_path, container_name).
+ for i in range(0, len(clone_info), 2):
+ lxc_path = clone_info[i]
+ name = clone_info[i+1]
+ yield Container.create_from_existing_dir(lxc_path, name)
diff --git a/site_utils/lxc/base_image_unittest.py b/site_utils/lxc/base_image_unittest.py
new file mode 100644
index 0000000..37c803f
--- /dev/null
+++ b/site_utils/lxc/base_image_unittest.py
@@ -0,0 +1,223 @@
+#!/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 argparse
+import logging
+import os
+import shutil
+import sys
+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_logging
+from autotest_lib.site_utils.lxc import utils as lxc_utils
+
+
+# Namespace object for parsing cmd line options.
+options = None
+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(unittest.TestCase):
+ """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:
+ self.assertFalse(lxc_utils.path_exists(
+ os.path.join(container.container_path, container.name)))
+
+
+class BaseImageSetupTests(unittest.TestCase):
+ """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())
+
+
+ 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 options.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 options.skip_cleanup:
+ if cleanup_ref_container:
+ reference_container.destroy()
+ shutil.rmtree(test_dir)
+
+
+def parse_options():
+ """Parse command line inputs."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Print out ALL entries.')
+ parser.add_argument('--skip_cleanup', action='store_true',
+ help='Skip deleting test containers.')
+ args, argv = parser.parse_known_args()
+
+ # Hack: python unittest also processes args. Construct an argv to pass to
+ # it, that filters out the options it won't recognize.
+ if args.verbose:
+ argv.insert(0, '-v')
+ argv.insert(0, sys.argv[0])
+
+ return args, argv
+
+
+if __name__ == '__main__':
+ options, unittest_argv = parse_options()
+
+ log_level=(logging.DEBUG if options.verbose else logging.INFO)
+ unittest_logging.setup(log_level)
+
+ unittest.main(argv=unittest_argv)
diff --git a/site_utils/lxc/container.py b/site_utils/lxc/container.py
index f6e8aef..4b514ea 100644
--- a/site_utils/lxc/container.py
+++ b/site_utils/lxc/container.py
@@ -77,7 +77,7 @@
@classmethod
- def createFromExistingDir(cls, lxc_path, name, **kwargs):
+ def create_from_existing_dir(cls, lxc_path, name, **kwargs):
"""Creates a new container instance for an lxc container that already
exists on disk.
@@ -113,7 +113,7 @@
if not cleanup:
raise error.ContainerError('Container %s already exists.' %
new_name)
- container = Container.createFromExistingDir(new_path, new_name)
+ container = Container.create_from_existing_dir(new_path, new_name)
try:
container.destroy()
except error.CmdError as e:
@@ -265,6 +265,9 @@
@raise ContainerError: If container does not exist or failed to destroy
the container.
"""
+ logging.debug('Destroying container %s/%s',
+ self.container_path,
+ self.name)
cmd = 'sudo lxc-destroy -P %s -n %s' % (self.container_path,
self.name)
if force:
diff --git a/site_utils/lxc/container_bucket.py b/site_utils/lxc/container_bucket.py
index ad062ab..f813672 100644
--- a/site_utils/lxc/container_bucket.py
+++ b/site_utils/lxc/container_bucket.py
@@ -9,12 +9,13 @@
import common
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
-from autotest_lib.site_utils.lxc import Container
from autotest_lib.site_utils.lxc import config as lxc_config
from autotest_lib.site_utils.lxc import constants
from autotest_lib.site_utils.lxc import lxc
from autotest_lib.site_utils.lxc import utils as lxc_utils
from autotest_lib.site_utils.lxc.cleanup_if_fail import cleanup_if_fail
+from autotest_lib.site_utils.lxc.base_image import BaseImage
+from autotest_lib.site_utils.lxc.container import Container
try:
from chromite.lib import metrics
@@ -37,14 +38,9 @@
"""
self.container_path = os.path.realpath(container_path)
self.shared_host_path = os.path.realpath(shared_host_path)
- # Try to create the base container.
- try:
- base_container = Container.createFromExistingDir(
- container_path, constants.BASE);
- base_container.refresh_status()
- self.base_container = base_container
- except error.ContainerError:
- self.base_container = None
+ # Try to create the base container. Use the default path and image
+ # name.
+ self.base_container = BaseImage().get()
def get_all(self):
@@ -56,8 +52,8 @@
info_collection = lxc.get_container_info(self.container_path)
containers = {}
for info in info_collection:
- container = Container.createFromExistingDir(self.container_path,
- **info)
+ container = Container.create_from_existing_dir(self.container_path,
+ **info)
containers[container.name] = container
return containers
@@ -138,74 +134,7 @@
return container
- @cleanup_if_fail()
- def setup_base(self, name=constants.BASE, force_delete=False):
- """Setup base container.
-
- @param name: Name of the base container, default to base.
- @param force_delete: True to force to delete existing base container.
- This action will destroy all running test
- containers. Default is set to False.
- """
- if not self.container_path:
- raise error.ContainerError(
- 'You must set a valid directory to store containers in '
- 'global config "AUTOSERV/ container_path".')
-
- if not os.path.exists(self.container_path):
- os.makedirs(self.container_path)
-
- base_path = os.path.join(self.container_path, name)
- if self.exist(name) and not force_delete:
- logging.error(
- 'Base container already exists. Set force_delete to True '
- 'to force to re-stage base container. Note that this '
- 'action will destroy all running test containers')
- # Set proper file permission. base container in moblab may have
- # owner of not being root. Force to update the folder's owner.
- # TODO(dshi): Change root to current user when test container can be
- # unprivileged container.
- utils.run('sudo chown -R root "%s"' % base_path)
- utils.run('sudo chgrp -R root "%s"' % base_path)
- return
-
- # Destroy existing base container if exists.
- if self.exist(name):
- # TODO: We may need to destroy all snapshots created from this base
- # container, not all container.
- self.destroy_all()
-
- # Download and untar the base container.
- tar_path = os.path.join(self.container_path, '%s.tar.xz' % name)
- path_to_cleanup = [tar_path, base_path]
- for path in path_to_cleanup:
- if os.path.exists(path):
- utils.run('sudo rm -rf "%s"' % path)
- container_url = constants.CONTAINER_BASE_URL_FMT % name
- lxc.download_extract(container_url, tar_path, self.container_path)
- # Remove the downloaded container tar file.
- utils.run('sudo rm "%s"' % tar_path)
- # Set proper file permission.
- # TODO(dshi): Change root to current user when test container can be
- # unprivileged container.
- utils.run('sudo chown -R root "%s"' % base_path)
- utils.run('sudo chgrp -R root "%s"' % base_path)
-
- # Update container config with container_path from global config.
- config_path = os.path.join(base_path, 'config')
- rootfs_path = os.path.join(base_path, 'rootfs')
- utils.run(('sudo sed '
- '-i "s|\(lxc\.rootfs[[:space:]]*=\).*$|\\1 {rootfs}|" '
- '"{config}"').format(rootfs=rootfs_path,
- config=config_path))
-
- self.base_container = Container.createFromExistingDir(
- self.container_path, name)
-
- self._setup_shared_host_path(force_delete)
-
-
- def _setup_shared_host_path(self, force_delete=False):
+ def setup_shared_host_path(self, force_delete=False):
"""Sets up the shared host directory.
@param force_delete: If True, the host dir will be cleared and
diff --git a/site_utils/lxc/container_bucket_unittest.py b/site_utils/lxc/container_bucket_unittest.py
index ec6dfa0..727e24d 100644
--- a/site_utils/lxc/container_bucket_unittest.py
+++ b/site_utils/lxc/container_bucket_unittest.py
@@ -55,7 +55,7 @@
self.shared_host_path)
# Set up, verify that the path is created.
- bucket.setup_base()
+ bucket.setup_shared_host_path()
self.assertTrue(os.path.isdir(self.shared_host_path))
# Clean up, verify that the path is removed.
@@ -84,56 +84,10 @@
bucket = lxc.ContainerBucket(container_path, self.shared_host_path)
# Setup then destroy the bucket. This should not emit any exceptions.
- bucket.setup_base()
+ bucket.setup_shared_host_path()
bucket.destroy_all()
-class ContainerBucketSetupBaseTests(unittest.TestCase):
- """Unit tests to verify the ContainerBucket setup_base method."""
-
- def setUp(self):
- self.tmpdir = tempfile.mkdtemp()
- self.shared_host_path = os.path.realpath(os.path.join(self.tmpdir,
- 'host'))
- self.bucket = lxc.ContainerBucket(container_path,
- self.shared_host_path)
-
-
- def tearDown(self):
- for container in self.bucket.get_all().values():
- container.stop()
- self.bucket.destroy_all()
- shutil.rmtree(self.tmpdir)
-
-
- # TODO(kenobi): Read moblab_config.ini to get the correct base version
- # instead of hard-coding it.
- def testSetupBase05(self):
- """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.downloadAndStart('base_05')
-
-
- # TODO(kenobi): Read shadow_config.ini to get the correct base version
- # instead of hard-coding it.
- def testSetupBase09(self):
- """Verifies that the setup_base code works with the base_09 image. """
- self.downloadAndStart('base_09')
-
-
- def downloadAndStart(self, name):
- """Calls setup_base with the given base image name, then starts the
- container and verifies that it is running.
-
- @param name: The name of the base image to download and test with.
- """
- self.bucket.setup_base(name=name)
- base_container = self.bucket.get(name)
- base_container.start()
- self.assertTrue(base_container.is_running())
-
def parse_options():
"""Parse command line inputs."""
parser = argparse.ArgumentParser()
diff --git a/site_utils/lxc/container_unittest.py b/site_utils/lxc/container_unittest.py
index 24451c8..7dcc87a 100644
--- a/site_utils/lxc/container_unittest.py
+++ b/site_utils/lxc/container_unittest.py
@@ -58,7 +58,7 @@
def testInit(self):
"""Verifies that containers initialize correctly."""
# Make a container that just points to the base container.
- container = lxc.Container.createFromExistingDir(
+ container = lxc.Container.create_from_existing_dir(
self.base_container.container_path,
self.base_container.name)
# Calling is_running triggers an lxc-ls call, which should verify that
@@ -72,7 +72,8 @@
"""
with tempfile.NamedTemporaryFile(dir=self.test_dir) as tmpfile:
name = os.path.basename(tmpfile.name)
- container = lxc.Container.createFromExistingDir(self.test_dir, name)
+ container = lxc.Container.create_from_existing_dir(self.test_dir,
+ name)
with self.assertRaises(error.ContainerError):
container.refresh_status()
@@ -122,6 +123,7 @@
"""Verifies that cloning a container works as expected."""
clone = lxc.Container.clone(src=self.base_container,
new_name="testClone",
+ new_path=self.test_dir,
snapshot=True)
try:
# Throws an exception if the container is not valid.
@@ -136,10 +138,12 @@
"""
lxc.Container.clone(src=self.base_container,
new_name="testCloneWithoutCleanup",
+ new_path=self.test_dir,
snapshot=True)
with self.assertRaises(error.ContainerError):
lxc.Container.clone(src=self.base_container,
new_name="testCloneWithoutCleanup",
+ new_path=self.test_dir,
snapshot=True)
@@ -147,6 +151,7 @@
"""Verifies that cloning a container with cleanup works properly."""
clone0 = lxc.Container.clone(src=self.base_container,
new_name="testClone",
+ new_path=self.test_dir,
snapshot=True)
clone0.start(wait_for_network=False)
tmpfile = clone0.attach_run('mktemp').stdout
@@ -156,6 +161,7 @@
# Clone another container in place of the existing container.
clone1 = lxc.Container.clone(src=self.base_container,
new_name="testClone",
+ new_path=self.test_dir,
snapshot=True,
cleanup=True)
with self.assertRaises(error.CmdError):
@@ -283,7 +289,10 @@
"""
if name is None:
name = self.id().split('.')[-1]
- container = self.bucket.create_from_base(name)
+ container = lxc.Container.clone(src=self.base_container,
+ new_name=name,
+ new_path=self.test_dir,
+ snapshot=True)
try:
yield container
finally:
diff --git a/site_utils/lxc/lxc_functional_test.py b/site_utils/lxc/lxc_functional_test.py
index efdc6fc..d353ebe 100644
--- a/site_utils/lxc/lxc_functional_test.py
+++ b/site_utils/lxc/lxc_functional_test.py
@@ -182,7 +182,8 @@
@param bucket: ContainerBucket to interact with containers.
"""
logging.info('Rebuild base container in folder %s.', bucket.container_path)
- bucket.setup_base()
+ lxc.BaseImage().setup()
+ bucket.setup_shared_host_path()
containers = bucket.get_all()
logging.info('Containers created: %s', containers.keys())
diff --git a/site_utils/lxc/unittest_container_bucket.py b/site_utils/lxc/unittest_container_bucket.py
index 1200876..976b233 100644
--- a/site_utils/lxc/unittest_container_bucket.py
+++ b/site_utils/lxc/unittest_container_bucket.py
@@ -29,7 +29,7 @@
# Clone the base container (snapshot for speed) to make a base
# container for the unit test.
- base = lxc.Container.createFromExistingDir(
+ base = lxc.Container.create_from_existing_dir(
constants.DEFAULT_CONTAINER_PATH, constants.BASE)
lxc.Container.clone(src=base,
new_name=constants.BASE,
@@ -40,7 +40,7 @@
finally:
super(FastContainerBucket, self).__init__(lxc_path, host_path)
if self.base_container is not None:
- self._setup_shared_host_path()
+ self.setup_shared_host_path()
def setup_base(self, *args, **kwargs):
diff --git a/utils/unittest_suite.py b/utils/unittest_suite.py
index f15e94c..67ca62f 100755
--- a/utils/unittest_suite.py
+++ b/utils/unittest_suite.py
@@ -108,6 +108,7 @@
'des_01_test.py',
'des_02_test.py',
# Require lxc to be installed
+ 'base_image_unittest.py',
'container_bucket_unittest.py',
'container_unittest.py',
'lxc_functional_test.py',