You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
5.4 KiB
119 lines
5.4 KiB
# 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 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 container
|
|
|
|
try:
|
|
from chromite.lib import metrics
|
|
except ImportError:
|
|
metrics = utils.metrics_mock
|
|
|
|
|
|
class ContainerFactory(object):
|
|
"""A factory class for creating LXC container objects."""
|
|
|
|
def __init__(self, base_container, container_class=container.Container,
|
|
snapshot=True, force_cleanup=False,
|
|
lxc_path=constants.DEFAULT_CONTAINER_PATH):
|
|
"""Initializes a ContainerFactory.
|
|
|
|
@param base_container: The base container from which other containers
|
|
are cloned.
|
|
@param container_class: (optional) The Container class to instantiate.
|
|
By default, lxc.Container is instantiated.
|
|
@param snapshot: (optional) If True, creates LXC snapshot clones instead
|
|
of full clones. By default, snapshot clones are used.
|
|
@param force_cleanup: (optional) If True, if a container is created with
|
|
a name and LXC directory matching an existing
|
|
container, the existing container is destroyed,
|
|
and the new container created in its place. By
|
|
default, existing containers are not destroyed and
|
|
a ContainerError is raised.
|
|
@param lxc_path: (optional) The default LXC path that will be used for
|
|
new containers. If one is not provided, the
|
|
DEFAULT_CONTAINER_PATH from lxc.constants will be used.
|
|
Note that even if a path is provided here, it can still
|
|
be overridden when create_container is called.
|
|
"""
|
|
self._container_class = container_class
|
|
self._base_container = base_container
|
|
self._snapshot = snapshot
|
|
self._force_cleanup = force_cleanup
|
|
self._lxc_path = lxc_path
|
|
|
|
|
|
def create_container(self, cid=None, lxc_path=None):
|
|
"""Creates a new container.
|
|
|
|
@param cid: (optional) A ContainerId for the new container. If an ID is
|
|
provided, it determines both the name and the ID of the
|
|
container. If no ID is provided, a random name is generated
|
|
for the container, and it is not assigned an ID.
|
|
@param lxc_path: (optional) The LXC path for the new container. If one
|
|
is not provided, the factory's default lxc_path
|
|
(specified when the factory was constructed) is used.
|
|
"""
|
|
name = str(cid) if cid else None
|
|
if lxc_path is None:
|
|
lxc_path = self._lxc_path
|
|
|
|
logging.debug('Creating new container (name: %s, lxc_path: %s)',
|
|
name, lxc_path)
|
|
|
|
# If an ID is provided, use it as the container name.
|
|
new_container = self._create_from_base(name, lxc_path)
|
|
# If an ID is provided, assign it to the container. When the container
|
|
# is created just-in-time by the container bucket, this ensures that the
|
|
# resulting container is correctly registered with the autoserv system.
|
|
# If the container is being created by a container pool, the ID will be
|
|
# assigned later, when the continer is bound to an actual test process.
|
|
if cid:
|
|
new_container.id = cid
|
|
return new_container
|
|
|
|
|
|
# create_from_base_duration is the original name of the metric. Keep this
|
|
# so we have history.
|
|
@metrics.SecondsTimerDecorator(
|
|
'%s/create_from_base_duration' % constants.STATS_KEY)
|
|
def _create_from_base(self, name, lxc_path):
|
|
"""Creates a container from the base container.
|
|
|
|
@param name: Name of the container.
|
|
@param lxc_path: The LXC path of the new container.
|
|
|
|
@return: A Container object for the created container.
|
|
|
|
@raise ContainerError: If the container already exist.
|
|
@raise error.CmdError: If lxc-clone call failed for any reason.
|
|
"""
|
|
use_snapshot = constants.SUPPORT_SNAPSHOT_CLONE and self._snapshot
|
|
|
|
try:
|
|
return self._container_class.clone(src=self._base_container,
|
|
new_name=name,
|
|
new_path=lxc_path,
|
|
snapshot=use_snapshot,
|
|
cleanup=self._force_cleanup)
|
|
except error.CmdError:
|
|
if not use_snapshot:
|
|
raise
|
|
else:
|
|
logging.debug(
|
|
'Creating snapshot clone failed.'
|
|
' Attempting without snapshot...'
|
|
' This forces cleanup of old cloned container.'
|
|
)
|
|
return self._container_class.clone(src=self._base_container,
|
|
new_name=name,
|
|
new_path=lxc_path,
|
|
snapshot=False,
|
|
cleanup=True)
|