Home | History | Annotate | Download | only in lxc
      1 # Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import logging
      6 
      7 import common
      8 from autotest_lib.client.bin import utils
      9 from autotest_lib.client.common_lib import error
     10 from autotest_lib.site_utils.lxc import constants
     11 from autotest_lib.site_utils.lxc import container
     12 
     13 try:
     14     from chromite.lib import metrics
     15 except ImportError:
     16     metrics = utils.metrics_mock
     17 
     18 
     19 class ContainerFactory(object):
     20     """A factory class for creating LXC container objects."""
     21 
     22     def __init__(self, base_container, container_class=container.Container,
     23                  snapshot=True, force_cleanup=False,
     24                  lxc_path=constants.DEFAULT_CONTAINER_PATH):
     25         """Initializes a ContainerFactory.
     26 
     27         @param base_container: The base container from which other containers
     28                                are cloned.
     29         @param container_class: (optional) The Container class to instantiate.
     30                                 By default, lxc.Container is instantiated.
     31         @param snapshot: (optional) If True, creates LXC snapshot clones instead
     32                          of full clones.  By default, snapshot clones are used.
     33         @param force_cleanup: (optional) If True, if a container is created with
     34                               a name and LXC directory matching an existing
     35                               container, the existing container is destroyed,
     36                               and the new container created in its place. By
     37                               default, existing containers are not destroyed and
     38                               a ContainerError is raised.
     39         @param lxc_path: (optional) The default LXC path that will be used for
     40                          new containers.  If one is not provided, the
     41                          DEFAULT_CONTAINER_PATH from lxc.constants will be used.
     42                          Note that even if a path is provided here, it can still
     43                          be overridden when create_container is called.
     44         """
     45         self._container_class = container_class
     46         self._base_container = base_container
     47         self._snapshot = snapshot
     48         self._force_cleanup = force_cleanup
     49         self._lxc_path = lxc_path
     50 
     51 
     52     def create_container(self, cid=None, lxc_path=None):
     53         """Creates a new container.
     54 
     55         @param cid: (optional) A ContainerId for the new container.  If an ID is
     56                     provided, it determines both the name and the ID of the
     57                     container.  If no ID is provided, a random name is generated
     58                     for the container, and it is not assigned an ID.
     59         @param lxc_path: (optional) The LXC path for the new container.  If one
     60                          is not provided, the factory's default lxc_path
     61                          (specified when the factory was constructed) is used.
     62         """
     63         name = str(cid) if cid else None
     64         if lxc_path is None:
     65             lxc_path = self._lxc_path
     66 
     67         logging.debug('Creating new container (name: %s, lxc_path: %s)',
     68                       name, lxc_path)
     69 
     70         # If an ID is provided, use it as the container name.
     71         new_container = self._create_from_base(name, lxc_path)
     72         # If an ID is provided, assign it to the container.  When the container
     73         # is created just-in-time by the container bucket, this ensures that the
     74         # resulting container is correctly registered with the autoserv system.
     75         # If the container is being created by a container pool, the ID will be
     76         # assigned later, when the continer is bound to an actual test process.
     77         if cid:
     78             new_container.id = cid
     79         return new_container
     80 
     81 
     82     # create_from_base_duration is the original name of the metric.  Keep this
     83     # so we have history.
     84     @metrics.SecondsTimerDecorator(
     85             '%s/create_from_base_duration' % constants.STATS_KEY)
     86     def _create_from_base(self, name, lxc_path):
     87         """Creates a container from the base container.
     88 
     89         @param name: Name of the container.
     90         @param lxc_path: The LXC path of the new container.
     91 
     92         @return: A Container object for the created container.
     93 
     94         @raise ContainerError: If the container already exist.
     95         @raise error.CmdError: If lxc-clone call failed for any reason.
     96         """
     97         use_snapshot = constants.SUPPORT_SNAPSHOT_CLONE and self._snapshot
     98 
     99         try:
    100             return self._container_class.clone(src=self._base_container,
    101                                                new_name=name,
    102                                                new_path=lxc_path,
    103                                                snapshot=use_snapshot,
    104                                                cleanup=self._force_cleanup)
    105         except error.CmdError:
    106             logging.debug('Creating snapshot clone failed. Attempting without '
    107                            'snapshot...')
    108             if not use_snapshot:
    109                 raise
    110             else:
    111                 # Snapshot clone failed, retry clone without snapshot.
    112                 return self._container_class.clone(src=self._base_container,
    113                                                    new_name=name,
    114                                                    new_path=lxc_path,
    115                                                    snapshot=False,
    116                                                    cleanup=self._force_cleanup)
    117