autotest: revert recent lxc changes
This CL reverts d8542a8d5620deb087af921d08a03e70baa446e7 and
966db080d632ab2b7c6534e717762173dad3938b.
which correspond to
https://chromium-review.googlesource.com/c/558015/ and
https://chromium-review.googlesource.com/c/538127/
BUG=chromium:741139, chromium:738598, chromium:720219
TEST=None
Change-Id: I478804455eca0775b04ede5152d8345408ee9468
Reviewed-on: https://chromium-review.googlesource.com/567057
Tested-by: Aviv Keshet <akeshet@chromium.org>
Reviewed-by: Xixuan Wu <xixuan@chromium.org>
Reviewed-by: Ilja H. Friedel <ihf@chromium.org>
Reviewed-by: Dan Shi <dshi@google.com>
diff --git a/global_config.ini b/global_config.ini
index 426e4dd..32bc64c 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -74,8 +74,6 @@
# Directory stores LXC containers
container_path: /usr/local/autotest/containers
-# Shared mount point for host mounts for LXC containers.
-container_shared_host_path: /usr/local/autotest/containers/host
# `container_base` is replaced by `container_base_folder_url` and `container_base_name`
# The setting is kept for backwards compatibility reason.
diff --git a/site_utils/lxc/__init__.py b/site_utils/lxc/__init__.py
index d260302..8ee0426 100644
--- a/site_utils/lxc/__init__.py
+++ b/site_utils/lxc/__init__.py
@@ -16,4 +16,3 @@
from lxc import install_package
from lxc import install_packages
from lxc import install_python_package
-from zygote import Zygote
diff --git a/site_utils/lxc/constants.py b/site_utils/lxc/constants.py
index 9fbe485..910bf8e 100644
--- a/site_utils/lxc/constants.py
+++ b/site_utils/lxc/constants.py
@@ -38,10 +38,6 @@
# Default directory used to store LXC containers.
DEFAULT_CONTAINER_PATH = global_config.get_config_value('AUTOSERV',
'container_path')
-# Default directory for host mounts
-DEFAULT_SHARED_HOST_PATH = global_config.get_config_value(
- 'AUTOSERV',
- 'container_shared_host_path')
# Path to drone_temp folder in the container, which stores the control file for
# test job to run.
diff --git a/site_utils/lxc/container.py b/site_utils/lxc/container.py
index d1044d6..a4003e2 100644
--- a/site_utils/lxc/container.py
+++ b/site_utils/lxc/container.py
@@ -13,7 +13,6 @@
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
try:
from chromite.lib import metrics
@@ -45,73 +44,20 @@
The attributes available are defined in ATTRIBUTES constant.
"""
- def __init__(self, container_path, name, attribute_values, src=None,
- snapshot=False):
+ def __init__(self, container_path, attribute_values):
"""Initialize an object of LXC container with given attribute values.
@param container_path: Directory that stores the container.
- @param name: Name of the container.
@param attribute_values: A dictionary of attribute values for the
container.
- @param src: An optional source container. If provided, the source
- continer is cloned, and the new container will point to the
- clone.
- @param snapshot: If a source container was specified, this argument
- specifies whether or not to create a snapshot clone.
- The default is to attempt to create a snapshot.
- If a snapshot is requested and creating the snapshot
- fails, a full clone will be attempted.
"""
self.container_path = os.path.realpath(container_path)
# Path to the rootfs of the container. This will be initialized when
# property rootfs is retrieved.
self._rootfs = None
- self.name = name
for attribute, value in attribute_values.iteritems():
setattr(self, attribute, value)
- # Clone the container
- if src is not None:
- # Clone the source container to initialize this one.
- lxc_utils.clone(src.container_path, src.name, self.container_path,
- self.name, snapshot)
-
-
- @classmethod
- def createFromExistingDir(cls, lxc_path, name, **kwargs):
- """Creates a new container instance for an lxc container that already
- exists on disk.
-
- @param lxc_path: The LXC path for the container.
- @param name: The container name.
-
- @raise error.ContainerError: If the container doesn't already exist.
-
- @return: The new container.
- """
- container = cls(lxc_path, name, kwargs)
- container.refresh_status()
- return container
-
-
- @classmethod
- def clone(cls, src, new_name, new_path=None, snapshot=False):
- """Creates a clone of this container.
-
- @param src: The original container.
- @param new_name: Name for the cloned container.
- @param new_path: LXC path for the cloned container (optional; if not
- specified, the new container is created in the same directory as
- this container).
- @param snapshot: Whether to snapshot, or create a full clone.
- @param cleanup: If a container with the given name and path already
- exist, clean it up first.
- """
- if new_path is None:
- new_path = src.container_path
-
- return cls(new_path, new_name, {}, src, snapshot)
-
def refresh_status(self):
"""Refresh the status information of the container.
@@ -120,7 +66,7 @@
if not containers:
raise error.ContainerError(
'No container found in directory %s with name of %s.' %
- (self.container_path, self.name))
+ self.container_path, self.name)
attribute_values = containers[0]
for attribute, value in attribute_values.iteritems():
setattr(self, attribute, value)
@@ -161,8 +107,8 @@
'in the container config file is %s' %
(self.name, lxc_rootfs_config))
lxc_rootfs = match.group(1)
- cloned_from_snapshot = ':' in lxc_rootfs
- if cloned_from_snapshot:
+ self.clone_from_snapshot = ':' in lxc_rootfs
+ if self.clone_from_snapshot:
self._rootfs = lxc_rootfs.split(':')[-1]
else:
self._rootfs = lxc_rootfs
@@ -214,7 +160,8 @@
"""
cmd = 'sudo lxc-start -P %s -n %s -d' % (self.container_path, self.name)
output = utils.run(cmd).stdout
- if not self.is_running():
+ self.refresh_status()
+ if self.state != 'RUNNING':
raise error.ContainerError(
'Container %s failed to start. lxc command output:\n%s' %
(os.path.join(self.container_path, self.name),
@@ -348,9 +295,3 @@
"\"local\/lib\",\\n/g' %s" % site_module)
self.attach_run('sed -i "s/lib_placeholder/lib/g" %s' %
site_module)
-
-
- def is_running(self):
- """Returns whether or not this container is currently running."""
- self.refresh_status()
- return self.state == 'RUNNING'
diff --git a/site_utils/lxc/container_bucket.py b/site_utils/lxc/container_bucket.py
index 1164d35..6f7d73f 100644
--- a/site_utils/lxc/container_bucket.py
+++ b/site_utils/lxc/container_bucket.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -28,9 +28,7 @@
"""A wrapper class to interact with containers in a specific container path.
"""
- def __init__(self,
- container_path=constants.DEFAULT_CONTAINER_PATH,
- shared_host_path = constants.DEFAULT_SHARED_HOST_PATH):
+ def __init__(self, container_path=constants.DEFAULT_CONTAINER_PATH):
"""Initialize a ContainerBucket.
@param container_path: Path to the directory used to store containers.
@@ -38,13 +36,6 @@
global config.
"""
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:
- self.base_container = Container.createFromExistingDir(
- container_path, constants.BASE);
- except error.ContainerError:
- self.base_container = None
def get_all(self):
@@ -56,8 +47,7 @@
info_collection = lxc.get_container_info(self.container_path)
containers = {}
for info in info_collection:
- container = Container.createFromExistingDir(self.container_path,
- **info)
+ container = Container(self.container_path, info)
containers[container.name] = container
return containers
@@ -92,7 +82,6 @@
containers, key=lambda n: 1 if n.name == constants.BASE else 0):
logging.info('Destroy container %s.', container.name)
container.destroy()
- self._cleanup_shared_host_path()
@metrics.SecondsTimerDecorator(
@@ -169,7 +158,7 @@
if not cleanup:
raise error.ContainerError('Container %s already exists.' %
new_name)
- container = Container.createFromExistingDir(new_path, new_name)
+ container = Container(new_path, {'name': name})
try:
container.destroy()
except error.CmdError as e:
@@ -179,7 +168,15 @@
name, e)
utils.run('sudo rm -rf "%s"' % container_folder)
- lxc_utils.clone(path, name, new_path, new_name, snapshot)
+ snapshot_arg = '-s' if snapshot else ''
+ # overlayfs is the default clone backend storage. However it is not
+ # supported in Ganeti yet. Use aufs as the alternative.
+ aufs_arg = '-B aufs' if utils.is_vm() and snapshot else ''
+ cmd = (('sudo lxc-clone --lxcpath %s --newpath %s '
+ '--orig %s --new %s %s %s') %
+ (path, new_path, name, new_name, snapshot_arg, aufs_arg))
+
+ utils.run(cmd)
return self.get(new_name)
@@ -238,46 +235,8 @@
# 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()
-
-
- def _setup_shared_host_path(self):
- """Sets up the shared host directory."""
- # First, clear out the old shared host dir if it exists.
- if lxc_utils.path_exists(self.shared_host_path):
- self._cleanup_shared_host_path()
- # Create the dir and set it up as a shared mount point.
- utils.run(('sudo mkdir "{path}" && '
- 'sudo mount --bind "{path}" "{path}" && '
- 'sudo mount --make-unbindable "{path}" && '
- 'sudo mount --make-shared "{path}"')
- .format(path=self.shared_host_path))
-
-
- def _cleanup_shared_host_path(self):
- """Removes the shared host directory.
-
- This should only be called after all containers have been destroyed
- (i.e. all host mounts have been disconnected and removed, so the shared
- host directory should be empty).
- """
- if not os.path.exists(self.shared_host_path):
- return
-
- if len(os.listdir(self.shared_host_path)) > 0:
- raise RuntimeError('Attempting to clean up host dir before all '
- 'hosts have been disconnected')
- utils.run('sudo umount "{path}" && sudo rmdir "{path}"'
- .format(path=self.shared_host_path))
+ utils.run('sudo sed -i "s|container_dir|%s|g" "%s"' %
+ (self.container_path, config_path))
@metrics.SecondsTimerDecorator(
diff --git a/site_utils/lxc/container_bucket_unittest.py b/site_utils/lxc/container_bucket_unittest.py
deleted file mode 100644
index 3e70ae0..0000000
--- a/site_utils/lxc/container_bucket_unittest.py
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/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 tempfile
-import unittest
-
-import common
-from autotest_lib.site_utils import lxc
-from autotest_lib.site_utils.lxc import unittest_logging
-
-
-options = None
-container_path = None
-
-def setUpModule():
- """Creates a directory for running the unit tests. """
- global container_path
- container_path = tempfile.mkdtemp(
- dir=lxc.DEFAULT_CONTAINER_PATH,
- prefix='container_bucket_unittest_')
-
-
-def tearDownModule():
- """Deletes the test directory. """
- shutil.rmtree(container_path)
-
-
-class ContainerBucketTests(unittest.TestCase):
- """Unit tests for the ContainerBucket class."""
-
- def setUp(self):
- self.tmpdir = tempfile.mkdtemp()
- self.shared_host_path = os.path.realpath(os.path.join(self.tmpdir,
- 'host'))
-
-
- def tearDown(self):
- shutil.rmtree(self.tmpdir)
-
-
- def testHostDirCreationAndCleanup(self):
- """Verifies that the host dir is properly created and cleaned up when
- the container bucket is set up and destroyed.
- """
- bucket = lxc.ContainerBucket(container_path, self.shared_host_path)
-
- # Verify the host path in the container bucket.
- self.assertEqual(os.path.realpath(bucket.shared_host_path),
- self.shared_host_path)
-
- # Set up, verify that the path is created.
- bucket.setup_base()
- self.assertTrue(os.path.isdir(self.shared_host_path))
-
- # Clean up, verify that the path is removed.
- bucket.destroy_all()
- self.assertFalse(os.path.isdir(self.shared_host_path))
-
-
- def testHostDirMissing(self):
- """Verifies that a missing host dir does not cause container bucket
- destruction to crash.
- """
- bucket = lxc.ContainerBucket(container_path, self.shared_host_path)
-
- # Verify that the host path does not exist.
- self.assertFalse(os.path.exists(self.shared_host_path))
- # Do not call startup, just call destroy. This should not throw.
- 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()
- parser.add_argument('-v', '--verbose', action='store_true',
- help='Print out ALL entries.')
- args, _unused = parser.parse_known_args()
- return args
-
-
-if __name__ == '__main__':
- options = parse_options()
-
- log_level=(logging.DEBUG if options.verbose else logging.INFO)
- unittest_logging.setup(log_level)
-
- unittest.main()
diff --git a/site_utils/lxc/lxc_functional_test.py b/site_utils/lxc/lxc_functional_test.py
index 080aebb..9f5a623 100644
--- a/site_utils/lxc/lxc_functional_test.py
+++ b/site_utils/lxc/lxc_functional_test.py
@@ -15,13 +15,13 @@
import argparse
import logging
import os
+import sys
import tempfile
import time
import common
from autotest_lib.client.bin import utils
from autotest_lib.site_utils import lxc
-from autotest_lib.site_utils.lxc import unittest_logging
TEST_JOB_ID = 123
@@ -175,6 +175,21 @@
"""
+def setup_logging(log_level=logging.INFO):
+ """Direct logging to stdout.
+
+ @param log_level: Level of logging to redirect to stdout, default to INFO.
+ """
+ logger = logging.getLogger()
+ logger.setLevel(log_level)
+ handler = logging.StreamHandler(sys.stdout)
+ handler.setLevel(log_level)
+ formatter = logging.Formatter('%(asctime)s %(message)s')
+ handler.setFormatter(formatter)
+ logger.handlers = []
+ logger.addHandler(handler)
+
+
def setup_base(bucket):
"""Test setup base container works.
@@ -337,8 +352,8 @@
'grant root access to this process.')
utils.run('sudo true')
- log_level=(logging.DEBUG if options.verbose else logging.INFO)
- unittest_logging.setup(log_level)
+ setup_logging(log_level=(logging.DEBUG if options.verbose
+ else logging.INFO))
bucket = lxc.ContainerBucket(TEMP_DIR)
diff --git a/site_utils/lxc/unittest_cleanup.py b/site_utils/lxc/unittest_cleanup.py
deleted file mode 100644
index fe5e5d4..0000000
--- a/site_utils/lxc/unittest_cleanup.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/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 common
-from autotest_lib.client.bin import utils
-from autotest_lib.client.common_lib import error
-from autotest_lib.site_utils import lxc
-from autotest_lib.site_utils.lxc import utils as lxc_utils
-
-
-TEST_CONTAINER_PATH = os.path.join(lxc.DEFAULT_CONTAINER_PATH, 'test')
-TEST_HOST_PATH = os.path.join(TEST_CONTAINER_PATH, 'host')
-
-def main():
- """Clean up the remnants from any old aborted unit tests."""
- # Manually clean out the host dir.
- if lxc_utils.path_exists(TEST_HOST_PATH):
- for host_dir in os.listdir(TEST_HOST_PATH):
- host_dir = os.path.realpath(os.path.join(TEST_HOST_PATH, host_dir))
- try:
- utils.run('sudo umount %s' % host_dir)
- except error.CmdError:
- pass
- utils.run('sudo rm -r %s' % host_dir)
-
- # Utilize the container_bucket to clear out old test containers.
- bucket = lxc.ContainerBucket(TEST_CONTAINER_PATH, TEST_HOST_PATH)
- bucket.destroy_all()
-
-
-if __name__ == '__main__':
- main()
diff --git a/site_utils/lxc/unittest_logging.py b/site_utils/lxc/unittest_logging.py
deleted file mode 100644
index b19750b..0000000
--- a/site_utils/lxc/unittest_logging.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# 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 sys
-
-
-def setup(log_level=logging.INFO):
- """Direct logging to stdout.
-
- @param log_level: Level of logging to redirect to stdout, default to INFO.
- """
- logger = logging.getLogger()
- logger.setLevel(log_level)
- handler = logging.StreamHandler(sys.stdout)
- handler.setLevel(log_level)
- formatter = logging.Formatter('%(asctime)s %(message)s')
- handler.setFormatter(formatter)
- logger.handlers = []
- logger.addHandler(handler)
diff --git a/site_utils/lxc/utils.py b/site_utils/lxc/utils.py
index 310b121..29ad241 100644
--- a/site_utils/lxc/utils.py
+++ b/site_utils/lxc/utils.py
@@ -53,42 +53,3 @@
interface_names)
netif = interface.Interface(lxc_network)
return netif.ipv4_address
-
-def clone(lxc_path, src_name, new_path, dst_name, snapshot):
- """Clones a container.
-
- @param lxc_path: The LXC path of the source container.
- @param src_name: The name of the source container.
- @param new_path: The LXC path of the destination container.
- @param dst_name: The name of the destination container.
- @param snapshot: Whether or not to create a snapshot clone.
- """
- snapshot_arg = '-s' if snapshot else ''
- # overlayfs is the default clone backend storage. However it is not
- # supported in Ganeti yet. Use aufs as the alternative.
- aufs_arg = '-B aufs' if utils.is_vm() and snapshot else ''
- cmd = (('sudo lxc-clone --lxcpath {lxcpath} --newpath {newpath} '
- '--orig {orig} --new {new} {snapshot} {backing}')
- .format(
- lxcpath = lxc_path,
- newpath = new_path,
- orig = src_name,
- new = dst_name,
- snapshot = snapshot_arg,
- backing = aufs_arg
- ))
- utils.run(cmd)
-
-
-def cleanup_host_mount(host_dir):
- """Unmounts and removes the given host dir.
-
- @param host_dir: The host dir to unmount and remove.
- """
- try:
- utils.run('sudo umount "%s"' % host_dir)
- except error.CmdError:
- # Ignore errors. Most likely this occurred because the host dir
- # was already unmounted.
- pass
- utils.run('sudo rm -r "%s"' % host_dir)
diff --git a/site_utils/lxc/zygote.py b/site_utils/lxc/zygote.py
deleted file mode 100644
index fbb9ee7..0000000
--- a/site_utils/lxc/zygote.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright 2017 The Chromium 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 common
-from autotest_lib.client.bin import utils
-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 utils as lxc_utils
-
-
-class Zygote(Container):
- """A Container that implements post-bringup configuration.
- """
-
- def __init__(self, container_path, name, attribute_values, src=None,
- snapshot=False, host_path=None):
- """Initialize an object of LXC container with given attribute values.
-
- @param container_path: Directory that stores the container.
- @param name: Name of the container.
- @param attribute_values: A dictionary of attribute values for the
- container.
- @param src: An optional source container. If provided, the source
- continer is cloned, and the new container will point to the
- clone.
- @param snapshot: Whether or not to create a snapshot clone. By default,
- this is false. If a snapshot is requested and creating
- a snapshot clone fails, a full clone will be attempted.
- @param host_path: If set to None (the default), a host path will be
- generated based on constants.DEFAULT_SHARED_HOST_PATH.
- Otherwise, this can be used to override the host path
- of the new container, for testing purposes.
- """
- super(Zygote, self).__init__(container_path, name, attribute_values,
- src, snapshot)
-
- # Initialize host dir and mount
- if host_path is None:
- self.host_path = os.path.join(
- os.path.realpath(constants.DEFAULT_SHARED_HOST_PATH),
- self.name)
- else:
- self.host_path = host_path
-
- if src is not None:
- # If creating a new zygote, initialize the host dir.
- if not lxc_utils.path_exists(self.host_path):
- utils.run('sudo mkdir %s' % self.host_path)
- self.mount_dir(self.host_path, lxc_config.CONTAINER_AUTOTEST_DIR)
-
-
- def destroy(self, force=True):
- super(Zygote, self).destroy(force)
- if lxc_utils.path_exists(self.host_path):
- self._cleanup_host_mount()
-
-
- def set_hostname(self, hostname):
- """Sets the hostname within the container.
-
- @param hostname The new container hostname.
- """
- if self.is_running():
- self.attach_run('hostname %s' % (hostname))
- self.attach_run(constants.APPEND_CMD_FMT % {
- 'content': '127.0.0.1 %s' % (hostname),
- 'file': '/etc/hosts'})
- else:
- config_file = os.path.join(self.container_path, self.name, 'config')
- lxc_utsname_setting = (
- 'lxc.utsname = ' +
- constants.CONTAINER_UTSNAME_FORMAT % hostname)
- utils.run(
- constants.APPEND_CMD_FMT % {'content': lxc_utsname_setting,
- 'file': config_file})
-
-
- def _cleanup_host_mount(self):
- """Unmount and remove the host dir for this container."""
- lxc_utils.cleanup_host_mount(self.host_path);
diff --git a/site_utils/lxc/zygote_unittest.py b/site_utils/lxc/zygote_unittest.py
deleted file mode 100644
index d4ffdc0..0000000
--- a/site_utils/lxc/zygote_unittest.py
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/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 tempfile
-import shutil
-import sys
-import unittest
-from contextlib import contextmanager
-
-import common
-from autotest_lib.client.bin import utils
-from autotest_lib.site_utils import lxc
-from autotest_lib.site_utils.lxc import config as lxc_config
-from autotest_lib.site_utils.lxc import unittest_logging
-from autotest_lib.site_utils.lxc import utils as lxc_utils
-
-
-options = None
-
-class ZygoteTests(unittest.TestCase):
- """Unit tests for the Zygote class."""
-
- @classmethod
- def setUpClass(cls):
- cls.test_dir = tempfile.mkdtemp(dir=lxc.DEFAULT_CONTAINER_PATH,
- prefix='zygote_unittest_')
- cls.shared_host_path = os.path.join(cls.test_dir, 'host')
-
- # Use a container bucket just to download and set up the base image.
- cls.bucket = lxc.ContainerBucket(cls.test_dir, cls.shared_host_path)
-
- if cls.bucket.base_container is None:
- logging.debug('Base container not found - reinitializing')
- cls.bucket.setup_base()
-
- cls.base_container = cls.bucket.base_container
- assert(cls.base_container is not None)
-
-
- @classmethod
- def tearDownClass(cls):
- cls.base_container = None
- if not options.skip_cleanup:
- cls.bucket.destroy_all()
- shutil.rmtree(cls.test_dir)
-
- def tearDown(self):
- # Ensure host dirs from each test are completely destroyed.
- for host_dir in os.listdir(self.shared_host_path):
- host_dir = os.path.realpath(os.path.join(self.shared_host_path,
- host_dir))
- lxc_utils.cleanup_host_mount(host_dir);
-
-
- def testCleanup(self):
- """Verifies that the zygote cleans up after itself."""
- with self.createZygote() as zygote:
- host_path = zygote.host_path
-
- self.assertTrue(os.path.isdir(host_path))
-
- # Start/stop the zygote to exercise the host mounts.
- zygote.start(wait_for_network=False)
- zygote.stop()
-
- # After the zygote is destroyed, verify that the host path is cleaned
- # up.
- self.assertFalse(os.path.isdir(host_path))
-
-
- def testCleanupWithUnboundHostDir(self):
- """Verifies that cleanup works when the host dir is unbound."""
- with self.createZygote() as zygote:
- host_path = zygote.host_path
-
- self.assertTrue(os.path.isdir(host_path))
- # Don't start the zygote, so the host mount is not bound.
-
- # After the zygote is destroyed, verify that the host path is cleaned
- # up.
- self.assertFalse(os.path.isdir(host_path))
-
-
- def testCleanupWithNoHostDir(self):
- """Verifies that cleanup works when the host dir is missing."""
- with self.createZygote() as zygote:
- host_path = zygote.host_path
-
- utils.run('sudo rmdir %s' % zygote.host_path)
- self.assertFalse(os.path.isdir(host_path))
- # Zygote destruction should yield no errors if the host path is
- # missing.
-
-
- def testHostname(self):
- """Verifies that the zygote starts up with a default hostname that is
- the lxc container name."""
- test_name = 'testHostname'
- with self.createZygote(name=test_name) as zygote:
- zygote.start(wait_for_network=True)
- hostname = zygote.attach_run('hostname -f').stdout.strip()
- self.assertEqual(test_name, hostname)
-
-
- @unittest.skip('Setting the container hostname using lxc.utsname does not'
- 'work on goobuntu.')
- def testSetHostnameNotRunning(self):
- """Verifies that the hostname can be set on a stopped container."""
- with self.createZygote() as zygote:
- expected_hostname = 'my-new-hostname'
- zygote.set_hostname(expected_hostname)
- zygote.start(wait_for_network=True)
- hostname = zygote.attach_run('hostname -f').stdout.strip()
- self.assertEqual(expected_hostname, hostname)
-
-
- def testSetHostnameRunning(self):
- """Verifies that the hostname can be set on a running container."""
- with self.createZygote() as zygote:
- expected_hostname = 'my-new-hostname'
- zygote.start(wait_for_network=True)
- zygote.set_hostname(expected_hostname)
- hostname = zygote.attach_run('hostname -f').stdout.strip()
- self.assertEqual(expected_hostname, hostname)
-
-
- def testHostDir(self):
- """Verifies that the host dir on the container is created, and correctly
- bind-mounted."""
- with self.createZygote() as zygote:
- self.assertIsNotNone(zygote.host_path)
- self.assertTrue(os.path.isdir(zygote.host_path))
-
- zygote.start(wait_for_network=False)
-
- self.verifyBindMount(
- zygote,
- container_path=lxc_config.CONTAINER_AUTOTEST_DIR,
- host_path=zygote.host_path)
-
-
- def testHostDirExists(self):
- """Verifies that the host dir is just mounted if it already exists."""
- # Pre-create the host dir and put a file in it.
- test_host_path = os.path.join(self.shared_host_path,
- 'testHostDirExists')
- test_filename = 'test_file'
- test_host_file = os.path.join(test_host_path, test_filename)
- test_string = 'jackdaws love my big sphinx of quartz.'
- os.mkdir(test_host_path)
- with open(test_host_file, 'w+') as f:
- f.write(test_string)
-
- # Sanity check
- self.assertTrue(lxc_utils.path_exists(test_host_file))
-
- with self.createZygote(host_path=test_host_path) as zygote:
- zygote.start(wait_for_network=False)
-
- self.verifyBindMount(
- zygote,
- container_path=lxc_config.CONTAINER_AUTOTEST_DIR,
- host_path=zygote.host_path)
-
- # Verify that the old directory contents was preserved.
- cmd = 'cat %s' % os.path.join(lxc_config.CONTAINER_AUTOTEST_DIR,
- test_filename)
- test_output = zygote.attach_run(cmd).stdout.strip()
- self.assertEqual(test_string, test_output)
-
-
- @contextmanager
- def createZygote(self,
- name = None,
- attribute_values = None,
- snapshot = True,
- host_path = None):
- """Clones a zygote from the test base container.
- Use this to ensure that zygotes got properly cleaned up after each test.
-
- @param container_path: The LXC path for the new container.
- @param host_path: The host path for the new container.
- @param name: The name of the new container.
- @param attribute_values: Any attribute values for the new container.
- @param snapshot: Whether to create a snapshot clone.
- """
- if name is None:
- name = self.id().split('.')[-1]
- if host_path is None:
- host_path = os.path.join(self.shared_host_path, name)
- if attribute_values is None:
- attribute_values = {}
- zygote = lxc.Zygote(self.test_dir,
- name,
- attribute_values,
- self.base_container,
- snapshot,
- host_path)
- yield zygote
- if not options.skip_cleanup:
- zygote.destroy()
-
-
- def verifyBindMount(self, container, container_path, host_path):
- """Verifies that a given path in a container is bind-mounted to a given
- path in the host system.
-
- @param container: The Container instance to be tested.
- @param container_path: The path in the container to compare.
- @param host_path: The path in the host system to compare.
- """
- container_inode = (container.attach_run('ls -id %s' % container_path)
- .stdout.split()[0])
- host_inode = utils.run('ls -id %s' % host_path).stdout.split()[0]
- # Compare the container and host inodes - they should match.
- self.assertEqual(container_inode, host_inode)
-
-
-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.append('-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/utils/unittest_suite.py b/utils/unittest_suite.py
index 76631b2..cdeea33 100755
--- a/utils/unittest_suite.py
+++ b/utils/unittest_suite.py
@@ -106,10 +106,8 @@
# crbug.com/432621 These files are not tests, and will disappear soon.
'des_01_test.py',
'des_02_test.py',
- # Require lxc to be installed
- 'container_bucket_unittest.py',
+ # Rquire lxc to be installed
'lxc_functional_test.py',
- 'zygote_unittest.py',
# Require sponge utils installed in site-packages
'sponge_utils_functional_test.py',
))