common.py revision 3020
2339N/A#!/usr/bin/python
2339N/A#
2339N/A# CDDL HEADER START
2339N/A#
2339N/A# The contents of this file are subject to the terms of the
2339N/A# Common Development and Distribution License (the "License").
2339N/A# You may not use this file except in compliance with the License.
2339N/A#
2339N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2339N/A# or http://www.opensolaris.org/os/licensing.
2339N/A# See the License for the specific language governing permissions
2339N/A# and limitations under the License.
2339N/A#
2339N/A# When distributing Covered Code, include this CDDL HEADER in each
2339N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2339N/A# If applicable, add the following below this CDDL HEADER, with the
2339N/A# fields enclosed by brackets "[]" replaced with your own identifying
2339N/A# information: Portions Copyright [yyyy] [name of copyright owner]
2339N/A#
2339N/A# CDDL HEADER END
2339N/A#
2339N/A
2339N/A#
3014N/A# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
2339N/A#
2339N/A
2339N/A"""
2339N/ALinked image module classes.
2339N/A
2339N/AThe following classes for manipulating linked images are defined here:
2339N/A
2339N/A LinkedImage
2444N/A LinkedImageChild
2339N/A
2339N/AThe following template classes which linked image plugins should inherit from
2339N/Aare also defined here:
2339N/A
2339N/A LinkedImagePlugin
2339N/A LinkedImageChildPlugin
2339N/A
2339N/A"""
2339N/A
2339N/A# standard python classes
2690N/Aimport collections
2690N/Aimport copy
3020N/Aimport itertools
2339N/Aimport operator
2339N/Aimport os
2690N/Aimport select
2339N/Aimport simplejson as json
2339N/A
2339N/A# pkg classes
2339N/Aimport pkg.actions
2339N/Aimport pkg.altroot as ar
2339N/Aimport pkg.catalog
2339N/Aimport pkg.client.api_errors as apx
2339N/Aimport pkg.client.bootenv as bootenv
2339N/Aimport pkg.client.linkedimage
2339N/Aimport pkg.client.pkgdefs as pkgdefs
2339N/Aimport pkg.client.pkgplan as pkgplan
2690N/Aimport pkg.client.pkgremote
2690N/Aimport pkg.client.progress as progress
2925N/Aimport pkg.facet
2339N/Aimport pkg.fmri
2339N/Aimport pkg.misc as misc
2339N/Aimport pkg.pkgsubprocess
2339N/Aimport pkg.version
2339N/A
2339N/Afrom pkg.client import global_settings
2339N/A
2339N/Alogger = global_settings.logger
2339N/A
2339N/A# linked image relationship types (returned by LinkedImage.list_related())
2339N/AREL_PARENT = "parent"
2339N/AREL_SELF = "self"
2339N/AREL_CHILD = "child"
2339N/A
2339N/A# linked image properties
3020N/APROP_CURRENT_PARENT_PATH = "li-current-parent"
3020N/APROP_CURRENT_PATH = "li-current-path"
3020N/APROP_MODEL = "li-model"
3020N/APROP_NAME = "li-name"
3020N/APROP_PARENT_PATH = "li-parent"
3020N/APROP_PATH = "li-path"
3020N/APROP_PATH_TRANSFORM = "li-path-transform"
3020N/APROP_RECURSE = "li-recurse"
2339N/Aprop_values = frozenset([
3020N/A PROP_CURRENT_PARENT_PATH,
3020N/A PROP_CURRENT_PATH,
3020N/A PROP_MODEL,
2339N/A PROP_NAME,
3020N/A PROP_PARENT_PATH,
2339N/A PROP_PATH,
3020N/A PROP_PATH_TRANSFORM,
2339N/A PROP_RECURSE,
2339N/A])
2339N/A
2339N/A# properties that never get saved
2339N/Atemporal_props = frozenset([
3020N/A PROP_CURRENT_PARENT_PATH,
3020N/A PROP_CURRENT_PATH,
3020N/A PROP_PATH_TRANSFORM,
2339N/A])
2339N/A
2339N/A# special linked image name values (PROP_NAME)
2339N/APV_NAME_NONE = "-"
2339N/A
2339N/A# linked image model values (PROP_MODEL)
2339N/APV_MODEL_PUSH = "push"
2339N/APV_MODEL_PULL = "pull"
2339N/Amodel_values = frozenset([
2339N/A PV_MODEL_PUSH,
2339N/A PV_MODEL_PULL,
2339N/A])
2339N/A
2339N/A# files which contain linked image data
2339N/A__DATA_DIR = "linked"
2925N/APATH_PFACETS = os.path.join(__DATA_DIR, "linked_pfacets")
2339N/APATH_PPKGS = os.path.join(__DATA_DIR, "linked_ppkgs")
2339N/APATH_PROP = os.path.join(__DATA_DIR, "linked_prop")
2339N/APATH_PUBS = os.path.join(__DATA_DIR, "linked_ppubs")
2339N/A
3020N/A#
3020N/A# we define PATH_TRANSFORM_NONE as a tuple instead of just None because this
3020N/A# will prevent it from being accidently serialized to json.
3020N/A#
3020N/APATH_TRANSFORM_NONE = ("/", "/")
3020N/A
2690N/ALI_RVTuple = collections.namedtuple("LI_RVTuple", "rvt_rv rvt_e rvt_p_dict")
2690N/A
2690N/Adef _li_rvtuple_check(rvtuple):
2690N/A """Sanity check a linked image operation return value tuple.
2690N/A The format of said tuple is:
2690N/A process return code
2690N/A LinkedImageException exception (optional)
2690N/A json dictionary containing planned image changes
2690N/A """
2690N/A
2690N/A # make sure we're using the LI_RVTuple class
2690N/A assert type(rvtuple) == LI_RVTuple
2690N/A
2690N/A # decode the tuple
2690N/A rv, e, p_dict = rvtuple
2690N/A
2690N/A # rv must be an integer
2690N/A assert type(rv) == int
2690N/A # any exception returned must be a LinkedImageException
2690N/A assert e is None or type(e) == apx.LinkedImageException
2690N/A # if specified, p_dict must be a dictionary
2690N/A assert p_dict is None or type(p_dict) is dict
2690N/A # some child return codes should never be associated with an exception
2690N/A assert rv not in [pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP] or e is None
2690N/A # a p_dict can only be returned if the child returned EXIT_OK
2690N/A assert rv == pkgdefs.EXIT_OK or p_dict is None
2690N/A
2690N/A # return the value that was passed in
2690N/A return rvtuple
2690N/A
2690N/Adef _li_rvdict_check(rvdict):
2690N/A """Given a linked image return value dictionary, sanity check all the
2690N/A entries."""
2690N/A
2690N/A assert(type(rvdict) == dict)
2690N/A for k, v in rvdict.iteritems():
2690N/A assert type(k) == LinkedImageName, \
2690N/A ("Unexpected rvdict key: ", k)
2690N/A _li_rvtuple_check(v)
2690N/A
2690N/A # return the value that was passed in
2690N/A return rvdict
2690N/A
2690N/Adef _li_rvdict_exceptions(rvdict):
2690N/A """Given a linked image return value dictionary, return a list of any
2690N/A exceptions that were encountered while processing children."""
2690N/A
2690N/A # sanity check rvdict
2690N/A _li_rvdict_check(rvdict)
2690N/A
2690N/A # get a list of exceptions
2690N/A return [
2690N/A rvtuple.rvt_e
2690N/A for rvtuple in rvdict.values()
2690N/A if rvtuple.rvt_e is not None
2690N/A ]
2690N/A
2690N/Adef _li_rvdict_raise_exceptions(rvdict):
2690N/A """If an exception was encountered while operating on a linked
2690N/A child then raise that exception. If multiple exceptions were
2690N/A encountered while operating on multiple children, then bundle
2690N/A those exceptions together and raise them."""
2690N/A
2690N/A # get a list of exceptions
2690N/A exceptions = _li_rvdict_exceptions(rvdict)
2690N/A
2690N/A if len(exceptions) == 1:
2690N/A # one exception encountered
2690N/A raise exceptions[0]
2690N/A
2690N/A if exceptions:
2690N/A # multiple exceptions encountered
2690N/A raise apx.LinkedImageException(bundle=exceptions)
2690N/A
2339N/Aclass LinkedImagePlugin(object):
2339N/A """This class is a template that all linked image plugins should
2339N/A inherit from. Linked image plugins derived from this class are
2339N/A designed to manage linked aspects of the current image (vs managing
2339N/A linked aspects of a specific child of the current image).
2339N/A
2339N/A All the interfaces exported by this class and its descendants are
2339N/A private to the linked image subsystem and should not be called
2339N/A directly by any other subsystem."""
2339N/A
2339N/A # functionality flags
2339N/A support_attach = False
2339N/A support_detach = False
2339N/A
2828N/A # Unused argument; pylint: disable=W0613
2339N/A def __init__(self, pname, linked):
2339N/A """Initialize a linked image plugin.
2339N/A
2339N/A 'pname' is the name of the plugin class derived from this
2339N/A base class.
2339N/A
2339N/A 'linked' is the LinkedImage object initializing this plugin.
2339N/A """
2339N/A
2339N/A return
2339N/A
3020N/A def init_root(self, root):
2339N/A """Called when the path to the image that we're operating on
2339N/A is changing. This normally occurs when we clone an image
2339N/A after we've planned and prepared to do an operation."""
2339N/A
2339N/A # return value: None
2339N/A raise NotImplementedError
2339N/A
3020N/A def guess_path_transform(self, ignore_errors=False):
2339N/A """If the linked image plugin is able to detect that we're
3020N/A operating on an image in an alternate root then return an
3020N/A transform that can be used to translate between the original
3020N/A image path and the current one."""
2339N/A
2339N/A # return value: string or None
2339N/A raise NotImplementedError
2339N/A
2444N/A def get_child_list(self, nocache=False, ignore_errors=False):
3020N/A """Return a list of the child images and paths associated with
3020N/A the current image. The paths that are returned should be
3020N/A absolute paths to the original child image locations."""
2339N/A
2339N/A # return value: list
2339N/A raise NotImplementedError
2339N/A
2339N/A def get_child_props(self, lin):
2339N/A """Get the linked image properties associated with the
2339N/A specified child image."""
2339N/A
2339N/A # return value: dict
2339N/A raise NotImplementedError
2339N/A
2339N/A def attach_child_inmemory(self, props, allow_relink):
2339N/A """Attach the specified child image. This operation should
2339N/A only affect in-memory state of the current image. It should
2339N/A not update any persistent on-disk linked image state or access
2339N/A the child image in any way. This routine should assume that
2339N/A the linked image properties have already been validated."""
2339N/A
2339N/A # return value: None
2339N/A raise NotImplementedError
2339N/A
2339N/A def detach_child_inmemory(self, lin):
2339N/A """Detach the specified child image. This operation should
2339N/A only affect in-memory state of the current image. It should
2339N/A not update any persistent on-disk linked image state or access
2339N/A the child image in any way."""
2339N/A
2339N/A # return value: None
2339N/A raise NotImplementedError
2339N/A
2339N/A def sync_children_todisk(self):
2339N/A """Sync out the in-memory linked image state of this image to
2339N/A disk."""
2339N/A
2690N/A # return value: LI_RVTuple()
2339N/A raise NotImplementedError
2339N/A
2339N/A
2339N/Aclass LinkedImageChildPlugin(object):
2339N/A """This class is a template that all linked image child plugins should
2339N/A inherit from. Linked image child plugins derived from this class are
2339N/A designed to manage linked aspects of children of the current image.
2339N/A (vs managing linked aspects of the current image itself).
2339N/A
2339N/A All the interfaces exported by this class and its descendants are
2339N/A private to the linked image subsystem and should not be called
2339N/A directly by any other subsystem."""
2339N/A
2828N/A def __init__(self, lic): # Unused argument; pylint: disable=W0613
2339N/A """Initialize a linked image child plugin.
2339N/A
2339N/A 'lic' is the LinkedImageChild object initializing this plugin.
2339N/A """
2339N/A
2339N/A return
2339N/A
2339N/A def munge_props(self, props):
2339N/A """Called before a parent image saves linked image properties
2339N/A into a child image. Gives the linked image child plugin a
2339N/A chance to update the properties that will be saved within the
2339N/A child image."""
2339N/A
2339N/A # return value: None
2339N/A raise NotImplementedError
2339N/A
2339N/A
2339N/Aclass LinkedImageName(object):
2339N/A """A class for naming child linked images. Linked image names are
2339N/A used for all child images (and only child images), and they encode two
2339N/A pieces of information. The name of the plugin used to manage the
2339N/A image and a linked image name. Linked image names have the following
2339N/A format "<linked_image_plugin>:<linked_image_name>"""
2339N/A
2339N/A def __init__(self, name):
2339N/A assert type(name) == str
2339N/A
2339N/A self.lin_type = self.lin_name = None
2339N/A
2339N/A try:
2339N/A self.lin_type, self.lin_name = name.split(":")
2339N/A except ValueError:
2339N/A raise apx.LinkedImageException(lin_malformed=name)
2339N/A
2339N/A if len(self.lin_type) == 0 or len(self.lin_name) == 0 :
2339N/A raise apx.LinkedImageException(lin_malformed=name)
2339N/A
2339N/A if self.lin_type not in pkg.client.linkedimage.p_types:
2339N/A raise apx.LinkedImageException(lin_malformed=name)
2339N/A
2690N/A @staticmethod
2690N/A def getstate(obj, je_state=None):
2690N/A """Returns the serialized state of this object in a format
2690N/A that that can be easily stored using JSON, pickle, etc."""
2828N/A # Unused argument; pylint: disable=W0613
2690N/A return str(obj)
2690N/A
2690N/A @staticmethod
2690N/A def fromstate(state, jd_state=None):
2690N/A """Allocate a new object using previously serialized state
2690N/A obtained via getstate()."""
2828N/A # Unused argument; pylint: disable=W0613
2690N/A return LinkedImageName(state)
2690N/A
2339N/A def __str__(self):
2339N/A return "%s:%s" % (self.lin_type, self.lin_name)
2339N/A
2339N/A def __len__(self):
2339N/A return len(self.__str__())
2339N/A
2339N/A def __cmp__(self, other):
2339N/A assert (type(self) == LinkedImageName)
2339N/A if not other:
2339N/A return 1
2339N/A if other == PV_NAME_NONE:
2339N/A return 1
2339N/A assert type(other) == LinkedImageName
2339N/A c = cmp(self.lin_type, other.lin_type)
2339N/A if c != 0:
2339N/A return c
2339N/A c = cmp(self.lin_name, other.lin_name)
2339N/A return c
2339N/A
2339N/A def __hash__(self):
2339N/A return hash(str(self))
2339N/A
2339N/A def __eq__(self, other):
2339N/A if not isinstance(other, LinkedImageName):
2339N/A return False
2339N/A
2339N/A return str(self) == str(other)
2339N/A
2339N/A def __ne__(self, other):
2690N/A return not self.__eq__(other)
2339N/A
2339N/Aclass LinkedImage(object):
2339N/A """A LinkedImage object is used to manage the linked image aspects of
2339N/A an image. This image could be a child image, a parent image, or both
2339N/A a parent and child. This object allows for access to linked image
2339N/A properties and also provides routines that allow operations to be
2339N/A performed on child images."""
2339N/A
2339N/A # Properties that a parent image with push children should save locally.
2339N/A __parent_props = frozenset([
2339N/A PROP_PATH
2339N/A ])
2339N/A
2339N/A # Properties that a pull child image should save locally.
2339N/A __pull_child_props = frozenset([
2339N/A PROP_NAME,
2339N/A PROP_PATH,
2339N/A PROP_MODEL,
2339N/A PROP_PARENT_PATH,
2339N/A ])
2339N/A
2339N/A # Properties that a parent image with push children should save in
2339N/A # those children.
2339N/A __push_child_props = frozenset([
2339N/A PROP_NAME,
2339N/A PROP_PATH,
2339N/A PROP_MODEL,
2339N/A PROP_RECURSE,
2339N/A ])
2339N/A
2339N/A # make sure there is no invalid overlap
2339N/A assert not (temporal_props & (
2339N/A __parent_props |
2339N/A __pull_child_props |
2339N/A __push_child_props))
2339N/A
2339N/A def __init__(self, img):
2339N/A """Initialize a new LinkedImage object."""
2339N/A
2339N/A # globals
2339N/A self.__img = img
2339N/A
2339N/A # variables reset by self.__update_props()
2339N/A self.__props = dict()
2339N/A self.__ppkgs = frozenset()
2339N/A self.__ppubs = None
2925N/A self.__pfacets = pkg.facet.Facets()
2339N/A self.__pimg = None
2339N/A
2690N/A # variables reset by self.__recursion_init()
2690N/A self.__lic_ignore = None
2690N/A self.__lic_dict = {}
2339N/A
2339N/A # variables reset by self._init_root()
2339N/A self.__root = None
2339N/A self.__path_ppkgs = None
2339N/A self.__path_prop = None
2339N/A self.__path_ppubs = None
2925N/A self.__path_pfacets = None
2925N/A self.__img_insync = True
2339N/A
2339N/A # initialize with no properties
2339N/A self.__update_props()
2339N/A
2339N/A # initialize linked image plugin objects
2339N/A self.__plugins = dict()
2339N/A for p in pkg.client.linkedimage.p_types:
2339N/A self.__plugins[p] = \
2339N/A pkg.client.linkedimage.p_classes[p](p, self)
2339N/A
2339N/A # if the image has a path setup, we can load data from it.
2339N/A if self.__img.imgdir:
2339N/A self._init_root()
2339N/A
2339N/A @property
2339N/A def image(self):
2339N/A """Get a pointer to the image object associated with this
2339N/A linked image object."""
2339N/A return self.__img
2339N/A
2339N/A def _init_root(self):
2339N/A """Called during object initialization and by
2339N/A image.py`__set_root() to let us know when we're changing the
2339N/A root location of the image. (The only time we change the root
2339N/A path is when changes BEs during operations which clone BEs.
2339N/A So when this happens most our metadata shouldn't actually
2339N/A change."""
2339N/A
2339N/A assert self.__img.root, \
2339N/A "root = %s" % str(self.__img.root)
2339N/A assert self.__img.imgdir, \
2339N/A "imgdir = %s" % str(self.__img.imgdir)
2339N/A
3020N/A # Check if this is our first time accessing the current image
3020N/A # or if we're just re-initializing ourselves.
3020N/A first_pass = self.__root is None
2339N/A
2339N/A # figure out the new root image path
3020N/A root = self.__img.root.rstrip(os.sep) + os.sep
2339N/A
2339N/A # initialize paths for linked image data files
3020N/A self.__root = root
3020N/A imgdir = self.__img.imgdir.rstrip(os.sep) + os.sep
2339N/A self.__path_ppkgs = os.path.join(imgdir, PATH_PPKGS)
2339N/A self.__path_prop = os.path.join(imgdir, PATH_PROP)
2339N/A self.__path_ppubs = os.path.join(imgdir, PATH_PUBS)
2925N/A self.__path_pfacets = os.path.join(imgdir, PATH_PFACETS)
2339N/A
2339N/A # if this isn't a reset, then load data from the image
3020N/A if first_pass:
2925N/A # the first time around we load non-temporary data (if
2925N/A # there is any) so that we can audit ourselves and see
2925N/A # if we're in currently in sync.
2925N/A self.__load(tmp=False)
2925N/A if self.ischild():
2925N/A self.__img_insync = self.__insync()
2925N/A
2925N/A # now re-load all the data taking into account any
2925N/A # temporary new data associated with an in-progress
2925N/A # operation.
2339N/A self.__load()
2339N/A
3020N/A # if we're not linked we're done
3020N/A if not self.__props:
2339N/A return
2339N/A
3020N/A # if this is a reset, update temporal properties
3020N/A if not first_pass:
3020N/A self.__set_current_path(self.__props, update=True)
2339N/A
2339N/A # Tell linked image plugins about the updated paths
2828N/A # Unused variable 'plugin'; pylint: disable=W0612
2339N/A for plugin, lip in self.__plugins.iteritems():
2828N/A # pylint: enable=W0612
3020N/A lip.init_root(root)
2339N/A
2339N/A # Tell linked image children about the updated paths
2690N/A for lic in self.__lic_dict.itervalues():
3020N/A lic.child_init_root()
2339N/A
2339N/A def __update_props(self, props=None):
2339N/A """Internal helper routine used when we want to update any
2339N/A linked image properties. This routine sanity check the
2339N/A new properties, updates them, and resets any cached state
2339N/A that is affected by property values."""
2339N/A
2339N/A if props == None:
2339N/A props = dict()
2339N/A elif props:
2339N/A self.__verify_props(props)
2339N/A
2339N/A # all temporal properties must exist
3020N/A for p in temporal_props:
3020N/A # PROP_CURRENT_PARENT_PATH can only be set if
3020N/A # we have PROP_PARENT_PATH.
3020N/A if p is PROP_CURRENT_PARENT_PATH and \
3020N/A PROP_PARENT_PATH not in props:
3020N/A continue
3020N/A assert p in props, \
3020N/A "'%s' not in %s" % (p, set(props))
2339N/A
2339N/A # update state
2339N/A self.__props = props
2339N/A self.__ppkgs = frozenset()
2339N/A self.__ppubs = None
2925N/A self.__pfacets = pkg.facet.Facets()
2339N/A self.__pimg = None
2339N/A
2339N/A def __verify_props(self, props):
2339N/A """Perform internal consistency checks for a set of linked
2339N/A image properties. Don't update any state."""
2339N/A
2339N/A props_set = set(props)
2339N/A
2339N/A # if we're not a child image ourselves, then we're done
2339N/A if (props_set - temporal_props) == self.__parent_props:
2339N/A return props
2339N/A
2339N/A # make sure PROP_MODEL was specified
2339N/A if PROP_NAME not in props:
2339N/A _rterr(path=self.__root,
2339N/A missing_props=[PROP_NAME])
2339N/A
2339N/A # validate the linked image name
2339N/A try:
2339N/A lin = LinkedImageName(str(props[PROP_NAME]))
2339N/A except apx.LinkedImageException:
2339N/A _rterr(path=self.__root,
2339N/A bad_prop=(PROP_NAME, props[PROP_NAME]))
2339N/A
2339N/A if lin.lin_type not in self.__plugins:
2339N/A _rterr(path=self.__root, lin=lin,
2339N/A bad_lin_type=lin.lin_type)
2339N/A
2339N/A # make sure PROP_MODEL was specified
2339N/A if PROP_MODEL not in props:
2339N/A _rterr(path=self.__root, lin=lin,
2339N/A missing_props=[PROP_MODEL])
2339N/A
2339N/A model = props[PROP_MODEL]
2339N/A if model not in model_values:
2339N/A _rterr(path=self.__root, lin=lin,
2339N/A bad_prop=(PROP_MODEL, model))
2339N/A
2339N/A if model == PV_MODEL_PUSH:
2339N/A missing = self.__push_child_props - props_set
2339N/A if missing:
2339N/A _rterr(path=self.__root, lin=lin,
2339N/A missing_props=missing)
2339N/A
2339N/A if model == PV_MODEL_PULL:
2339N/A missing = self.__pull_child_props - props_set
2339N/A if missing:
2339N/A _rterr(path=self.__root, lin=lin,
2339N/A missing_props=missing)
2339N/A
2339N/A @staticmethod
3020N/A def set_path_transform(props, path_transform,
3020N/A path=None, current_path=None, update=False):
3020N/A """Given a new path_transform, update path properties."""
3020N/A
3020N/A if update:
3020N/A assert (set(props) & temporal_props), \
3020N/A "no temporal properties are set: %s" % props
3020N/A else:
3020N/A assert not (set(props) & temporal_props), \
3020N/A "temporal properties already set: %s" % props
3020N/A
3020N/A # Either 'path' or 'current_path' must be specified.
3020N/A assert path is None or current_path is None
3020N/A assert path is not None or current_path is not None
3020N/A
3020N/A if path is not None:
3020N/A current_path = path_transform_apply(path,
3020N/A path_transform)
3020N/A
3020N/A elif current_path is not None:
3020N/A path = path_transform_revert(current_path,
3020N/A path_transform)
3020N/A
3020N/A props[PROP_PATH] = path
3020N/A props[PROP_CURRENT_PATH] = current_path
3020N/A props[PROP_PATH_TRANSFORM] = path_transform
2339N/A if PROP_PARENT_PATH in props:
3020N/A props[PROP_CURRENT_PARENT_PATH] = path_transform_apply(
3020N/A props[PROP_PARENT_PATH], path_transform)
3020N/A
3020N/A def __set_current_path(self, props, update=False):
2339N/A """Given a set of linked image properties, the image paths
2339N/A stored within those properties may not match the actual image
2339N/A paths if we're executing within an alternate root environment.
3020N/A To deal with this situation we create temporal in-memory
3020N/A properties that represent the current path to the image, and a
3020N/A transform that allows us to translate between the current path
3020N/A and the original path."""
3020N/A
3020N/A current_path = self.__root
3020N/A path_transform = compute_path_transform(props[PROP_PATH],
3020N/A current_path)
3020N/A
3020N/A self.set_path_transform(props, path_transform,
3020N/A current_path=current_path, update=update)
3020N/A
3020N/A def __guess_path_transform(self, ignore_errors=False):
2339N/A """If we're initializing parent linked image properties for
2339N/A the first time (or if those properties somehow got deleted)
2339N/A then we need to know if the parent image that we're currently
2339N/A operating on is located within an alternate root. One way to
2339N/A do this is to ask our linked image plugins if they can
2339N/A determine this (the zones linked image plugin usually can
2339N/A if the image is a global zone)."""
2339N/A
2339N/A # ask each plugin if we're operating in an alternate root
3020N/A p_transforms = []
2339N/A for plugin, lip in self.__plugins.iteritems():
3020N/A p_transform = lip.guess_path_transform(
2444N/A ignore_errors=ignore_errors)
3020N/A if p_transform is not PATH_TRANSFORM_NONE:
3020N/A p_transforms.append((plugin, p_transform))
3020N/A
3020N/A if not p_transforms:
3020N/A # no transform suggested by plugins
3020N/A return PATH_TRANSFORM_NONE
3020N/A
3020N/A # check for conflicting transforms
3020N/A transforms = list(set([
3020N/A p_transform
2828N/A # Unused variable; pylint: disable=W0612
3020N/A for pname, p_transform in p_transforms
2828N/A # pylint: enable=W0612
2339N/A ]))
2339N/A
3020N/A if len(transforms) == 1:
3020N/A # we have a transform from our plugins
3020N/A return transforms[0]
3020N/A
3020N/A # we have conflicting transforms, time to die
3020N/A _rterr(li=self, multiple_transforms=p_transforms)
2339N/A
2444N/A def __fabricate_parent_props(self, ignore_errors=False):
2339N/A """Fabricate the minimum set of properties required for a
2339N/A parent image."""
2339N/A
3020N/A # ask our plugins if we're operating with alternate image paths
3020N/A path_transform = self.__guess_path_transform(
3020N/A ignore_errors=ignore_errors)
3020N/A
2339N/A props = dict()
3020N/A self.set_path_transform(props, path_transform,
3020N/A current_path=self.__root)
2339N/A return props
2339N/A
2339N/A def __load_ondisk_props(self, tmp=True):
2339N/A """Load linked image properties from disk and return them to
2339N/A the caller. We sanity check the properties, but we don't
2339N/A update any internal linked image state.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2339N/A path = self.__path_prop
2690N/A path_tmp = "%s.%d" % (self.__path_prop,
2690N/A global_settings.client_runid)
2339N/A
2339N/A # read the linked image properties from disk
2339N/A if tmp and path_exists(path_tmp):
2339N/A path = path_tmp
2339N/A props = load_data(path)
2339N/A elif path_exists(path):
2339N/A props = load_data(path)
2339N/A else:
2339N/A return None
2339N/A
2339N/A # make sure there are no saved temporal properties
2339N/A assert not (set(props) & temporal_props)
2339N/A
2339N/A if PROP_NAME in props:
2339N/A # convert PROP_NAME into a linked image name obj
2339N/A name = props[PROP_NAME]
2339N/A try:
2339N/A lin = LinkedImageName(name)
2339N/A props[PROP_NAME] = lin
2339N/A except apx.LinkedImageException:
2339N/A _rterr(path=self.__root,
2339N/A bad_prop=(PROP_NAME, name))
2339N/A
2339N/A # sanity check our properties
2339N/A self.__verify_props(props)
2339N/A return props
2339N/A
2925N/A def __load_ondisk_pfacets(self, tmp=True):
2925N/A """Load linked image inherited facets from disk.
2339N/A Don't update any internal state.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2925N/A pfacets = misc.EmptyDict
2925N/A path = "%s.%d" % (self.__path_pfacets,
2925N/A global_settings.client_runid)
2925N/A if tmp and path_exists(path):
2925N/A pfacets = load_data(path)
2925N/A else:
2925N/A path = self.__path_pfacets
2925N/A pfacets = load_data(path, missing_ok=True)
2925N/A
2925N/A if pfacets is None:
2925N/A return None
2925N/A
2925N/A rv = pkg.facet.Facets()
2925N/A for k, v in pfacets.iteritems():
2925N/A # W0212 Access to a protected member
2925N/A # pylint: disable=W0212
2925N/A rv._set_inherited(k, v)
2925N/A return rv
2925N/A
2925N/A def __load_ondisk_ppkgs(self, tmp=True):
2925N/A """Load linked image parent packages from disk.
2925N/A Don't update any internal state.
2925N/A
2925N/A 'tmp' determines if we should read/write to the official
2925N/A linked image metadata files, or if we should access temporary
2925N/A versions (which have ".<runid>" appended to them."""
2925N/A
2925N/A fmri_strs = None
2690N/A path = "%s.%d" % (self.__path_ppkgs,
2690N/A global_settings.client_runid)
2339N/A if tmp and path_exists(path):
2925N/A fmri_strs = load_data(path)
2925N/A else:
2925N/A path = self.__path_ppkgs
2925N/A fmri_strs = load_data(path, missing_ok=True)
2925N/A
2925N/A if fmri_strs is None:
2925N/A return None
2925N/A
2925N/A return frozenset([
2925N/A pkg.fmri.PkgFmri(str(s))
2925N/A for s in fmri_strs
2925N/A ])
2339N/A
2339N/A def __load_ondisk_ppubs(self, tmp=True):
2339N/A """Load linked image parent publishers from disk.
2339N/A Don't update any internal state.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2925N/A ppubs = None
2690N/A path = "%s.%d" % (self.__path_ppubs,
2690N/A global_settings.client_runid)
2339N/A if tmp and path_exists(path):
2925N/A ppubs = load_data(path)
2925N/A else:
2925N/A path = self.__path_ppubs
2925N/A ppubs = load_data(path, missing_ok=True)
2925N/A
2925N/A return ppubs
2925N/A
2925N/A def __load(self, tmp=True):
2339N/A """Load linked image properties and constraints from disk.
2339N/A Update the linked image internal state with the loaded data."""
2339N/A
2444N/A #
2444N/A # Normally, if we're a parent image we'll have linked image
2444N/A # properties stored on disk. So load those now.
2444N/A #
2444N/A # If no properties are loaded, we may still be a parent image
2444N/A # that is just missing it's metadata. (oops.) We attempt to
2444N/A # detect this situation by invoking __isparent(), which will
2444N/A # ask each child if there are any children. This is a best
2444N/A # effort attempt, so when we do this we ignore any plugin
2444N/A # runtime errors since we really want Image object
2444N/A # initialization to succeed. If we don't have any linked
2444N/A # image metadata, and we're having runtime errors querying for
2444N/A # children, then we'll allow initialization here, but any
2444N/A # subsequent operation that tries to access children will fail
2444N/A # and the caller will have to specify that they want to ignore
2444N/A # all children to allow the operation to succeed.
2444N/A #
2925N/A props = self.__load_ondisk_props(tmp=tmp)
2444N/A if not props and not self.__isparent(ignore_errors=True):
2339N/A # we're not linked
2339N/A return
2339N/A
2339N/A if not props:
2444N/A #
2444N/A # Oops. We're a parent image with no properties
2444N/A # stored on disk. Rather than throwing an exception
2444N/A # try to fabricate up some props with reasonably
2444N/A # guessed values which the user can subsequently
2444N/A # change and/or fix.
2444N/A #
2444N/A props = self.__fabricate_parent_props(
2444N/A ignore_errors=True)
2339N/A else:
3020N/A self.__set_current_path(props)
2339N/A
2339N/A self.__update_props(props)
2339N/A
2925N/A if not self.ischild():
2925N/A return
2925N/A
2925N/A # load parent packages. if parent package data is missing just
2925N/A # continue along and hope for the best.
2925N/A ppkgs = self.__load_ondisk_ppkgs(tmp=tmp)
2925N/A if ppkgs is not None:
2339N/A self.__ppkgs = ppkgs
2339N/A
2925N/A # load inherited facets. if inherited facet data is missing
2925N/A # just continue along and hope for the best.
2925N/A pfacets = self.__load_ondisk_pfacets(tmp=tmp)
2925N/A if pfacets is not None:
2925N/A self.__pfacets = pfacets
2925N/A
2339N/A # load parent publisher data. if publisher data is missing
2339N/A # continue along and we'll just skip the publisher checks,
2339N/A # it's better than failing and preventing any image updates.
2925N/A self.__ppubs = self.__load_ondisk_ppubs(tmp=tmp)
2339N/A
2339N/A @staticmethod
2339N/A def __validate_prop_recurse(v):
2339N/A """Verify property value for PROP_RECURSE."""
2339N/A if v in [True, False]:
2339N/A return True
2339N/A if type(v) == str and v.lower() in ["true", "false"]:
2339N/A return True
2339N/A return False
2339N/A
2339N/A def __validate_attach_props(self, model, props):
2339N/A """Validate user supplied linked image attach properties.
2339N/A Don't update any internal state."""
2339N/A
2339N/A # make sure that only attach time options have been
2339N/A # specified, and that they have allowed values.
2339N/A validate_props = {
2339N/A PROP_RECURSE: self.__validate_prop_recurse
2339N/A }
2339N/A
2339N/A if model == PV_MODEL_PUSH:
2339N/A allowed_props = self.__push_child_props
2339N/A else:
2339N/A assert model == PV_MODEL_PULL
2339N/A allowed_props = self.__pull_child_props
2339N/A
2339N/A errs = []
2339N/A
2339N/A # check each property the user specified.
2339N/A for k, v in props.iteritems():
2339N/A
2339N/A # did the user specify an allowable property?
2339N/A if k not in validate_props:
2339N/A errs.append(apx.LinkedImageException(
2339N/A attach_bad_prop=k))
2339N/A continue
2339N/A
2339N/A # did the user specify a valid property value?
2339N/A if not validate_props[k](v):
2339N/A errs.append(apx.LinkedImageException(
2339N/A attach_bad_prop_value=(k, v)))
2339N/A continue
2339N/A
2339N/A # is this property valid for this type of image?
2339N/A if k not in allowed_props:
2339N/A errs.append(apx.LinkedImageException(
2339N/A attach_bad_prop=k))
2339N/A continue
2339N/A
2690N/A if len(errs) == 1:
2690N/A raise errs[0]
2339N/A if errs:
2339N/A raise apx.LinkedImageException(bundle=errs)
2339N/A
2339N/A def __init_pimg(self, path):
2339N/A """Initialize an Image object which can be used to access a
2339N/A parent image."""
2339N/A
2339N/A try:
2339N/A os.stat(path)
2339N/A except OSError:
2339N/A raise apx.LinkedImageException(parent_bad_path=path)
2339N/A
2339N/A try:
2339N/A pimg = self.__img.alloc(
2339N/A root=path,
2339N/A user_provided_dir=True,
2339N/A cmdpath=self.__img.cmdpath)
2339N/A except apx.ImageNotFoundException:
2339N/A raise apx.LinkedImageException(parent_bad_img=path)
2339N/A
2339N/A return pimg
2339N/A
2339N/A def nothingtodo(self):
2339N/A """If our in-memory linked image state matches the on-disk
2339N/A linked image state then there's nothing to do. If the state
2339N/A differs then there is stuff to do since the new state needs
2339N/A to be saved to disk."""
2339N/A
2925N/A # check if we're not a linked image.
2925N/A if not self.isparent() and not self.ischild():
2925N/A # if any linked image metadata files exist they need
2925N/A # to be deleted.
2925N/A paths = [
2925N/A self.__path_pfacets,
2925N/A self.__path_ppkgs,
2925N/A self.__path_ppubs,
2925N/A self.__path_prop,
2925N/A ]
2925N/A for path in paths:
2925N/A if path_exists(path):
2925N/A return False
2925N/A return True
2925N/A
2339N/A # compare in-memory and on-disk properties
2339N/A li_ondisk_props = self.__load_ondisk_props(tmp=False)
2339N/A if li_ondisk_props == None:
2339N/A li_ondisk_props = dict()
3020N/A li_inmemory_props = rm_dict_ent(self.__props,
2339N/A temporal_props)
2339N/A if li_ondisk_props != li_inmemory_props:
2339N/A return False
2339N/A
2925N/A # linked image metadata files with inherited data
2925N/A paths = [
2925N/A self.__path_pfacets,
2925N/A self.__path_ppkgs,
2925N/A self.__path_ppubs,
2925N/A ]
2925N/A
2925N/A # check if we're just a parent image.
2925N/A if not self.ischild():
2925N/A # parent images only have properties. if any linked
2925N/A # image metadata files that contain inherited
2925N/A # information exist they need to be deleted.
2925N/A for path in paths:
2925N/A if path_exists(path):
2925N/A return False
2925N/A return True
2925N/A
2925N/A # if we're missing any metadata files then there's work todo
2925N/A for path in paths:
2925N/A if not path_exists(path):
2925N/A return False
2925N/A
2925N/A # compare in-memory and on-disk inherited facets
2925N/A li_ondisk_pfacets = self.__load_ondisk_pfacets(tmp=False)
2925N/A if self.__pfacets != li_ondisk_pfacets:
2925N/A return False
2925N/A
2925N/A # compare in-memory and on-disk parent packages
2339N/A li_ondisk_ppkgs = self.__load_ondisk_ppkgs(tmp=False)
2339N/A if self.__ppkgs != li_ondisk_ppkgs:
2339N/A return False
2339N/A
2339N/A # compare in-memory and on-disk parent publishers
2339N/A li_ondisk_ppubs = self.__load_ondisk_ppubs(tmp=False)
2339N/A if self.__ppubs != li_ondisk_ppubs:
2339N/A return False
2339N/A
2339N/A return True
2339N/A
2690N/A def pubcheck(self):
2339N/A """If we're a child image's, verify that the parent image
2339N/A publisher configuration is a subset of the child images
2339N/A publisher configuration. This means that all publishers
2339N/A configured within the parent image must also be configured
2339N/A within the child image with the same:
2339N/A
2339N/A - publisher rank
2339N/A - sticky and disabled settings
2339N/A
2339N/A The child image may have additional publishers configured but
2339N/A they must all be lower ranked than the parent's publishers.
2339N/A """
2339N/A
2339N/A # if we're not a child image then bail
2339N/A if not self.ischild():
2339N/A return
2339N/A
2339N/A # if we're using the sysrepo then don't bother
2339N/A if self.__img.cfg.get_policy("use-system-repo"):
2339N/A return
2339N/A
2925N/A pubs = get_pubs(self.__img)
2339N/A ppubs = self.__ppubs
2339N/A
2339N/A if ppubs == None:
2339N/A # parent publisher data is missing, press on and hope
2339N/A # for the best.
2339N/A return
2339N/A
2339N/A # child image needs at least as many publishers as the parent
2339N/A if len(pubs) < len(ppubs):
2339N/A raise apx.PlanCreationException(
2339N/A linked_pub_error=(pubs, ppubs))
2339N/A
2339N/A # check rank, sticky, and disabled settings
2339N/A for (p, pp) in zip(pubs, ppubs):
2339N/A if p == pp:
2339N/A continue
2339N/A raise apx.PlanCreationException(
2339N/A linked_pub_error=(pubs, ppubs))
2339N/A
2925N/A def __syncmd_from_parent(self):
2339N/A """Update linked image constraint, publisher data, and
2339N/A state from our parent image."""
2339N/A
2339N/A if not self.ischild():
2339N/A # we're not a child image, nothing to do
2339N/A return
2339N/A
2339N/A if self.__props[PROP_MODEL] == PV_MODEL_PUSH:
2339N/A # parent pushes data to us, nothing to do
2339N/A return
2339N/A
2690N/A # initialize the parent image
2339N/A if not self.__pimg:
3020N/A path = self.parent_path()
2339N/A self.__pimg = self.__init_pimg(path)
2339N/A
2925N/A # get metadata from our parent image
2925N/A self.__ppubs = get_pubs(self.__pimg)
2925N/A self.__ppkgs = get_packages(self.__pimg)
2925N/A self.__pfacets = get_inheritable_facets(self.__pimg)
2925N/A
2925N/A def syncmd_from_parent(self, catch_exception=False):
2925N/A """Update linked image constraint, publisher data, and state
2925N/A from our parent image. If catch_exception is true catch any
2925N/A linked image exceptions and pack them up in a linked image
2925N/A return value tuple."""
2925N/A
2925N/A try:
2925N/A self.__syncmd_from_parent()
2925N/A except apx.LinkedImageException, e:
2925N/A if not catch_exception:
2925N/A raise e
2925N/A return LI_RVTuple(e.lix_exitrv, e, None)
2925N/A return
2339N/A
2339N/A def syncmd(self):
2339N/A """Write in-memory linked image state to disk."""
2339N/A
2339N/A # create a list of metadata file paths
2925N/A paths = [
2925N/A self.__path_pfacets,
2925N/A self.__path_ppkgs,
2925N/A self.__path_ppubs,
2925N/A self.__path_prop,
2925N/A ]
2339N/A
2339N/A # cleanup any temporary files
2339N/A for path in paths:
2690N/A path = "%s.%d" % (path,
2690N/A global_settings.client_runid)
2339N/A path_unlink(path, noent_ok=True)
2339N/A
2339N/A if not self.ischild() and not self.isparent():
2339N/A # we're no longer linked; delete metadata
2339N/A for path in paths:
2339N/A path_unlink(path, noent_ok=True)
2339N/A return
2339N/A
3020N/A # save our properties, but first remove any temporal properties
3020N/A props = rm_dict_ent(self.__props, temporal_props)
2339N/A save_data(self.__path_prop, props)
2339N/A
2339N/A if not self.ischild():
2925N/A # if we're not a child we don't have parent data
2925N/A path_unlink(self.__path_pfacets, noent_ok=True)
2339N/A path_unlink(self.__path_ppkgs, noent_ok=True)
2925N/A path_unlink(self.__path_ppubs, noent_ok=True)
2339N/A return
2339N/A
2339N/A # we're a child so save our latest constraints
2925N/A save_data(self.__path_pfacets, self.__pfacets)
2339N/A save_data(self.__path_ppkgs, self.__ppkgs)
2339N/A save_data(self.__path_ppubs, self.__ppubs)
2339N/A
2339N/A @property
2339N/A def child_name(self):
2339N/A """If the current image is a child image, this function
2339N/A returns a linked image name object which represents the name
2339N/A of the current image."""
2339N/A
2339N/A if not self.ischild():
2339N/A raise self.__apx_not_child()
2339N/A return self.__props[PROP_NAME]
2339N/A
2339N/A def ischild(self):
2339N/A """Indicates whether the current image is a child image."""
2339N/A
2339N/A return PROP_NAME in self.__props
2339N/A
2444N/A def __isparent(self, ignore_errors=False):
2444N/A """Indicates whether the current image is a parent image.
2444N/A
2444N/A 'ignore_plugin_errors' ignore plugin runtime errors when
2444N/A trying to determine if we're a parent image.
2444N/A """
2444N/A
2444N/A return len(self.__list_children(
2444N/A ignore_errors=ignore_errors)) > 0
2444N/A
2690N/A def isparent(self, li_ignore=None):
2339N/A """Indicates whether the current image is a parent image."""
2339N/A
2690N/A return len(self.__list_children(li_ignore=li_ignore)) > 0
2339N/A
3020N/A def islinked(self):
3020N/A """Indicates wether the current image is already linked."""
3020N/A return self.ischild() or self.isparent()
3020N/A
3020N/A def get_path_transform(self):
3020N/A """Return the current path transform property."""
3020N/A
3020N/A return self.__props.get(
3020N/A PROP_PATH_TRANSFORM, PATH_TRANSFORM_NONE)
3020N/A
3020N/A def inaltroot(self):
3020N/A """Check if we're accessing a linked image at an alternate
3020N/A location/path."""
3020N/A
3020N/A return self.get_path_transform() != PATH_TRANSFORM_NONE
3020N/A
3020N/A def path(self):
3020N/A """Report our current image path."""
3020N/A
3020N/A assert self.islinked()
3020N/A return self.__props[PROP_PATH]
3020N/A
3020N/A def current_path(self):
3020N/A """Report our current image path."""
3020N/A
3020N/A assert self.islinked()
3020N/A return self.__props[PROP_CURRENT_PATH]
3020N/A
3020N/A def parent_path(self):
3020N/A """If we know where our parent should be, report it's expected
3020N/A location."""
3020N/A
3020N/A if PROP_PARENT_PATH not in self.__props:
3020N/A return None
3020N/A
3020N/A path = self.__props[PROP_CURRENT_PARENT_PATH]
3020N/A assert path[-1] == "/"
3020N/A return path
3020N/A
2339N/A def child_props(self, lin=None):
2339N/A """Return a dictionary which represents the linked image
2339N/A properties associated with a linked image.
2339N/A
2339N/A 'lin' is the name of the child image. If lin is None then
2339N/A the current image is assumed to be a linked image and it's
2339N/A properties are returned.
2339N/A
2339N/A Always returns a copy of the properties in case the caller
2339N/A tries to update them."""
2339N/A
2339N/A if lin == None:
2339N/A # If we're not linked we'll return an empty
2339N/A # dictionary. That's ok.
2339N/A return self.__props.copy()
2339N/A
2339N/A # make sure the specified child exists
2339N/A self.__verify_child_name(lin, raise_except=True)
2339N/A
2339N/A # make a copy of the props in case they are updated
2339N/A lip = self.__plugins[lin.lin_type]
2339N/A props = lip.get_child_props(lin).copy()
2339N/A
2339N/A # add temporal properties
3020N/A self.set_path_transform(props, self.get_path_transform(),
3020N/A path=props[PROP_PATH])
2339N/A return props
2339N/A
2339N/A def __apx_not_child(self):
2339N/A """Raise an exception because the current image is not a child
2339N/A image."""
2339N/A
2339N/A return apx.LinkedImageException(self_not_child=self.__root)
2339N/A
2339N/A def __verify_child_name(self, lin, raise_except=False):
2339N/A """Check if a specific child image exists."""
2339N/A
2339N/A assert type(lin) == LinkedImageName, \
2339N/A "%s == LinkedImageName" % type(lin)
2339N/A
2339N/A for i in self.__list_children():
2339N/A if i[0] == lin:
2339N/A return True
2339N/A
2339N/A if raise_except:
2339N/A raise apx.LinkedImageException(child_unknown=lin)
2339N/A return False
2339N/A
2690N/A def verify_names(self, lin_list):
2690N/A """Given a list of linked image name objects, make sure all
2690N/A the children exist."""
2690N/A
2690N/A assert isinstance(lin_list, list), \
2690N/A "type(lin_list) == %s, str(lin_list) == %s" % \
2690N/A (type(lin_list), str(lin_list))
2690N/A
2690N/A for lin in lin_list:
2690N/A self.__verify_child_name(lin, raise_except=True)
2690N/A
2925N/A def inherited_facets(self):
2925N/A """Facets inherited from our parent image."""
2925N/A return self.__pfacets
2925N/A
2339N/A def parent_fmris(self):
2339N/A """A set of the fmris installed in our parent image."""
2339N/A
2339N/A if not self.ischild():
2339N/A # We return None since frozenset() would indicate
2339N/A # that there are no packages installed in the parent
2339N/A # image.
2339N/A return None
2339N/A
2339N/A return self.__ppkgs
2339N/A
2339N/A def parse_name(self, name, allow_unknown=False):
2339N/A """Given a string representing a linked image child name,
2339N/A returns linked image name object representing the same name.
2339N/A
2339N/A 'allow_unknown' indicates whether the name must represent
2339N/A actual children or simply be syntactically correct."""
2339N/A
2339N/A assert type(name) == str
2339N/A
2339N/A lin = LinkedImageName(name)
2339N/A if not allow_unknown:
2339N/A self.__verify_child_name(lin, raise_except=True)
2339N/A return lin
2339N/A
2444N/A def __list_children(self, li_ignore=None, ignore_errors=False):
2339N/A """Returns a list of linked child images associated with the
2339N/A current image.
2339N/A
2339N/A 'li_ignore' see list_related() for a description.
2339N/A
2339N/A The returned value is a list of tuples where each tuple
2339N/A contains (<li name>, <li path>)."""
2339N/A
2339N/A if li_ignore == []:
2339N/A # ignore all children
2339N/A return []
2339N/A
3020N/A li_children = []
3020N/A for p in pkg.client.linkedimage.p_types:
3020N/A for lin, path in self.__plugins[p].get_child_list(
3020N/A ignore_errors=ignore_errors):
3020N/A assert lin.lin_type == p
3020N/A path = path_transform_apply(path,
3020N/A self.get_path_transform())
3020N/A li_children.append([lin, path])
2339N/A
2339N/A # sort by linked image name
2339N/A li_children = sorted(li_children, key=operator.itemgetter(0))
2339N/A
2339N/A if li_ignore == None:
2339N/A # don't ignore any children
2339N/A return li_children
2339N/A
2339N/A li_all = set([lin for lin, path in li_children])
2339N/A errs = [
2339N/A apx.LinkedImageException(child_unknown=lin)
2339N/A for lin in (set(li_ignore) - li_all)
2339N/A ]
2690N/A if len(errs) == 1:
2690N/A raise errs[0]
2339N/A if errs:
2339N/A raise apx.LinkedImageException(bundle=errs)
2339N/A
2339N/A return [
2339N/A (lin, path)
2339N/A for lin, path in li_children
2339N/A if lin not in li_ignore
2339N/A ]
2339N/A
2339N/A def list_related(self, li_ignore=None):
2339N/A """Returns a list of linked images associated with the
2339N/A current image. This includes both child and parent images.
2339N/A
2339N/A 'li_ignore' is either None or a list. If it's None (the
2339N/A default), all children will be listed. If it's an empty list
2339N/A no children will be listed. Otherwise, any children listed
2339N/A in li_ignore will be ommited from the results.
2339N/A
2339N/A The returned value is a list of tuples where each tuple
2339N/A contains (<li name>, <relationship>, <li path>)."""
2339N/A
2339N/A li_children = self.__list_children(li_ignore=li_ignore)
2339N/A li_list = [
2339N/A (lin, REL_CHILD, path)
2339N/A for lin, path in li_children
2339N/A ]
2339N/A
2339N/A if not li_list and not self.ischild():
2339N/A # we're not linked
2339N/A return []
2339N/A
2339N/A # we're linked so append ourself to the list
2339N/A lin = PV_NAME_NONE
2339N/A if self.ischild():
2339N/A lin = self.child_name
3020N/A
3020N/A path = self.current_path()
3020N/A li_self = (lin, REL_SELF, path)
2339N/A li_list.append(li_self)
2339N/A
2339N/A # if we have a path to our parent then append that as well.
3020N/A path = self.parent_path()
3020N/A if path is not None:
3020N/A li_parent = (PV_NAME_NONE, REL_PARENT, path)
2339N/A li_list.append(li_parent)
2339N/A
2339N/A # sort by linked image name
2339N/A li_list = sorted(li_list, key=operator.itemgetter(0))
2339N/A
2339N/A return li_list
2339N/A
2339N/A def attach_parent(self, lin, path, props, allow_relink=False,
2339N/A force=False):
2339N/A """We only update in-memory state; nothing is written to
2339N/A disk, to sync linked image state to disk call syncmd."""
2339N/A
2339N/A assert type(lin) == LinkedImageName
2339N/A assert type(path) == str
2339N/A assert props == None or type(props) == dict, \
2339N/A "type(props) == %s" % type(props)
2339N/A if props == None:
2339N/A props = dict()
2339N/A
2339N/A lip = self.__plugins[lin.lin_type]
2339N/A
2339N/A if self.ischild() and not allow_relink:
2339N/A raise apx.LinkedImageException(self_linked=self.__root)
2339N/A
2339N/A if not lip.support_attach and not force:
2339N/A raise apx.LinkedImageException(
2339N/A attach_parent_notsup=lin.lin_type)
2339N/A
2339N/A # Path must be an absolute path.
2339N/A if not os.path.isabs(path):
2339N/A raise apx.LinkedImageException(parent_path_notabs=path)
2339N/A
2339N/A # we don't bother to cleanup the path to the parent image here
2339N/A # because when we allocate an Image object for the parent
2339N/A # image, it will do that work for us.
2339N/A pimg = self.__init_pimg(path)
2339N/A
2339N/A # get the cleaned up parent image path.
2339N/A path = pimg.root
2339N/A
3020N/A # Make sure our parent image is at it's default path. (We
3020N/A # don't allow attaching new images if an image is located at
3020N/A # an alternate path.)
3020N/A if pimg.linked.inaltroot():
3020N/A raise apx.LinkedImageException(attach_with_curpath=(
3020N/A pimg.linked.path(), pimg.current_path()))
3020N/A
3020N/A self.__validate_attach_props(PV_MODEL_PULL, props)
3020N/A self.__validate_attach_img_paths(path, self.__root)
3020N/A
3020N/A # make a copy of the properties and update them
3020N/A props = props.copy()
3020N/A props[PROP_NAME] = lin
3020N/A props[PROP_MODEL] = PV_MODEL_PULL
3020N/A
2339N/A # If we're in an alternate root, the parent must also be within
2339N/A # that alternate root.
3020N/A path_transform = self.get_path_transform()
3020N/A if not path_transform_applied(path, path_transform):
2339N/A raise apx.LinkedImageException(
3020N/A parent_not_in_altroot=(path, path_transform[1]))
3020N/A
3020N/A # Set path related properties. We use self.__root in place of
3020N/A # current_path() since we may not actually be linked yet.
3020N/A props[PROP_PARENT_PATH] = path.rstrip(os.sep) + os.sep
3020N/A self.set_path_transform(props, path_transform,
3020N/A current_path=self.__root)
2339N/A
2339N/A for k, v in lip.attach_props_def.iteritems():
2339N/A if k not in self.__pull_child_props:
2339N/A # this prop doesn't apply to pull images
2339N/A continue
2339N/A if k not in props:
2339N/A props[k] = v
2339N/A
2339N/A self.__update_props(props)
2339N/A self.__pimg = pimg
2339N/A
2339N/A def detach_parent(self, force=False):
2339N/A """We only update in memory state; nothing is written to
2339N/A disk, to sync linked image state to disk call syncmd."""
2339N/A
2339N/A lin = self.child_name
2339N/A lip = self.__plugins[lin.lin_type]
2339N/A if not force:
2339N/A if self.__props[PROP_MODEL] == PV_MODEL_PUSH:
2339N/A raise apx.LinkedImageException(
2339N/A detach_from_parent=self.__root)
2339N/A
2339N/A if not lip.support_detach:
2339N/A raise apx.LinkedImageException(
2339N/A detach_parent_notsup=lin.lin_type)
2339N/A
2339N/A # Generate a new set of linked image properties. If we have
2339N/A # no children then we don't need any more properties.
2339N/A props = None
2339N/A
2339N/A # If we have children we'll need to keep some properties.
2339N/A if self.isparent():
2339N/A strip = prop_values - \
2339N/A (self.__parent_props | temporal_props)
2339N/A props = rm_dict_ent(self.__props, strip)
2339N/A
2339N/A # Update our linked image properties.
2339N/A self.__update_props(props)
2339N/A
2339N/A def __insync(self):
2339N/A """Determine if an image is in sync with its constraints."""
2339N/A
2339N/A assert self.ischild()
2339N/A
2339N/A cat = self.__img.get_catalog(self.__img.IMG_CATALOG_INSTALLED)
2339N/A excludes = [ self.__img.cfg.variants.allow_action ]
2339N/A
2339N/A sync_fmris = []
2339N/A
2339N/A for fmri in cat.fmris():
2339N/A # get parent dependencies from the catalog
2339N/A parent_deps = [
2339N/A a
2339N/A for a in cat.get_entry_actions(fmri,
2339N/A [pkg.catalog.Catalog.DEPENDENCY],
2339N/A excludes=excludes)
2339N/A if a.name == "depend" and \
2339N/A a.attrs["type"] == "parent"
2339N/A ]
2339N/A
2339N/A if parent_deps:
2339N/A sync_fmris.append(fmri)
2339N/A
2339N/A if not sync_fmris:
2339N/A # No packages to sync
2339N/A return True
2339N/A
2339N/A # create a dictionary of packages installed in the parent
2339N/A ppkgs_dict = dict([
2339N/A (fmri.pkg_name, fmri)
2339N/A for fmri in self.parent_fmris()
2339N/A ])
2339N/A
2339N/A for fmri in sync_fmris:
2339N/A if fmri.pkg_name not in ppkgs_dict:
2339N/A return False
2339N/A pfmri = ppkgs_dict[fmri.pkg_name]
2339N/A if fmri.version != pfmri.version and \
2339N/A not pfmri.version.is_successor(fmri.version,
2339N/A pkg.version.CONSTRAINT_AUTO):
2339N/A return False
2339N/A return True
2339N/A
2925N/A def audit_self(self, latest_md=True):
2339N/A """If the current image is a child image, this function
2339N/A audits the current image to see if it's in sync with its
2339N/A parent."""
2339N/A
2339N/A if not self.ischild():
2690N/A e = self.__apx_not_child()
2690N/A return LI_RVTuple(pkgdefs.EXIT_OOPS, e, None)
2339N/A
2925N/A if not latest_md:
2925N/A # we don't use the latest linked image metadata.
2925N/A # instead return cached insync value which was
2925N/A # computed using the initial linked image metadata
2925N/A # that we loaded from disk.
2925N/A if not self.__img_insync:
2925N/A e = apx.LinkedImageException(
2925N/A child_diverged=self.child_name)
2925N/A return LI_RVTuple(pkgdefs.EXIT_DIVERGED, e,
2925N/A None)
2925N/A return LI_RVTuple(pkgdefs.EXIT_OK, None, None)
2339N/A
2339N/A if not self.__insync():
2339N/A e = apx.LinkedImageException(
2339N/A child_diverged=self.child_name)
2690N/A return LI_RVTuple(pkgdefs.EXIT_DIVERGED, e, None)
2690N/A
2690N/A return LI_RVTuple(pkgdefs.EXIT_OK, None, None)
2339N/A
2925N/A def insync(self, latest_md=True):
2925N/A """A convenience wrapper for audit_self(). Note that we
2925N/A consider non-child images as always in sync and ignore
2925N/A any runtime errors."""
2925N/A
2925N/A rv = self.image.linked.audit_self(latest_md=latest_md)[0]
2925N/A if rv == pkgdefs.EXIT_DIVERGED:
2925N/A return False
2925N/A return True
2925N/A
2339N/A @staticmethod
2339N/A def __rvdict2rv(rvdict, rv_map=None):
2339N/A """Internal helper function that takes a dictionary returned
2339N/A from an operations on multiple children and merges the results
2339N/A into a single return code."""
2339N/A
2690N/A _li_rvdict_check(rvdict)
2339N/A if type(rv_map) != type(None):
2339N/A assert type(rv_map) == list
2339N/A for (rv_set, rv) in rv_map:
2339N/A assert(type(rv_set) == set)
2339N/A assert(type(rv) == int)
2339N/A
2339N/A if not rvdict:
2690N/A return LI_RVTuple(pkgdefs.EXIT_OK, None, None)
2339N/A
2339N/A if not rv_map:
2339N/A rv_map = [(set([pkgdefs.EXIT_OK]), pkgdefs.EXIT_OK)]
2339N/A
2500N/A p_dicts = [
2690N/A rvtuple.rvt_p_dict
2690N/A for rvtuple in rvdict.itervalues()
2690N/A if rvtuple.rvt_p_dict is not None
2500N/A ]
2500N/A
2339N/A rv_mapped = set()
2690N/A rv_seen = set([
2690N/A rvtuple.rvt_rv
2690N/A for rvtuple in rvdict.itervalues()
2690N/A ])
2339N/A for (rv_map_set, rv_map_rv) in rv_map:
2339N/A if (rv_seen == rv_map_set):
2690N/A return LI_RVTuple(rv_map_rv, None, p_dicts)
2339N/A # keep track of all the return values that are mapped
2339N/A rv_mapped |= rv_map_set
2339N/A
2339N/A # the mappings better have included pkgdefs.EXIT_OK
2339N/A assert pkgdefs.EXIT_OK in rv_mapped
2339N/A
2339N/A # if we had errors for unmapped return values, bundle them up
2339N/A errs = [
2690N/A rvtuple.rvt_e
2690N/A for rvtuple in rvdict.itervalues()
2690N/A if rvtuple.rvt_e and rvtuple.rvt_rv not in rv_mapped
2339N/A ]
2690N/A if len(errs) == 1:
2690N/A err = errs[0]
2690N/A elif errs:
2339N/A err = apx.LinkedImageException(bundle=errs)
2339N/A else:
2339N/A err = None
2339N/A
2339N/A if len(rv_seen) == 1:
2339N/A # we have one consistent return value
2690N/A return LI_RVTuple(list(rv_seen)[0], err, p_dicts)
2690N/A
2690N/A return LI_RVTuple(pkgdefs.EXIT_PARTIAL, err, p_dicts)
2339N/A
2339N/A def audit_rvdict2rv(self, rvdict):
2339N/A """Convenience function that takes a dictionary returned from
2339N/A an operations on multiple children and merges the results into
2339N/A a single return code."""
2339N/A
2339N/A rv_map = [
2339N/A (set([pkgdefs.EXIT_OK]), pkgdefs.EXIT_OK),
2339N/A (set([pkgdefs.EXIT_DIVERGED]), pkgdefs.EXIT_DIVERGED),
2339N/A (set([pkgdefs.EXIT_OK, pkgdefs.EXIT_DIVERGED]),
2339N/A pkgdefs.EXIT_DIVERGED),
2339N/A ]
2339N/A return self.__rvdict2rv(rvdict, rv_map)
2339N/A
2339N/A def sync_rvdict2rv(self, rvdict):
2339N/A """Convenience function that takes a dictionary returned from
2339N/A an operations on multiple children and merges the results into
2339N/A a single return code."""
2339N/A
2339N/A rv_map = [
2339N/A (set([pkgdefs.EXIT_OK]), pkgdefs.EXIT_OK),
2339N/A (set([pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP]), pkgdefs.EXIT_OK),
2339N/A (set([pkgdefs.EXIT_NOP]), pkgdefs.EXIT_NOP),
2339N/A ]
2339N/A return self.__rvdict2rv(rvdict, rv_map)
2339N/A
2339N/A def detach_rvdict2rv(self, rvdict):
2339N/A """Convenience function that takes a dictionary returned from
2339N/A an operations on multiple children and merges the results into
2339N/A a single return code."""
2339N/A
2339N/A return self.__rvdict2rv(rvdict)
2339N/A
2339N/A def __validate_child_attach(self, lin, path, props,
2339N/A allow_relink=False):
2339N/A """Sanity check the parameters associated with a child image
2339N/A that we are trying to attach."""
2339N/A
2339N/A assert type(lin) == LinkedImageName
2339N/A assert type(props) == dict
2339N/A assert type(path) == str
2339N/A
2339N/A # check the name to make sure it doesn't already exist
2339N/A if self.__verify_child_name(lin) and not allow_relink:
2339N/A raise apx.LinkedImageException(child_dup=lin)
2339N/A
2339N/A self.__validate_attach_props(PV_MODEL_PUSH, props)
2339N/A
2339N/A # Path must be an absolute path.
2339N/A if not os.path.isabs(path):
2339N/A raise apx.LinkedImageException(child_path_notabs=path)
2339N/A
2339N/A # If we're in an alternate root, the child must also be within
2339N/A # that alternate root
3020N/A path_transform = self.__props[PROP_PATH_TRANSFORM]
3020N/A if not path_transform_applied(path, path_transform):
2339N/A raise apx.LinkedImageException(
3020N/A child_not_in_altroot=(path, path_transform[1]))
2339N/A
2339N/A # path must be an image
2339N/A try:
2339N/A img_prefix = ar.ar_img_prefix(path)
3020N/A except OSError, e:
3020N/A raise apx.LinkedImageException(lin=lin,
3020N/A child_op_failed=("find", path, e))
2339N/A if not img_prefix:
2339N/A raise apx.LinkedImageException(child_bad_img=path)
2339N/A
2339N/A # Does the parent image (ourselves) reside in clonable BE?
2828N/A # Unused variable 'be_uuid'; pylint: disable=W0612
2339N/A (be_name, be_uuid) = bootenv.BootEnv.get_be_name(self.__root)
2828N/A # pylint: enable=W0612
2339N/A if be_name:
2339N/A img_is_clonable = True
2339N/A else:
2339N/A img_is_clonable = False
2339N/A
2339N/A # If the parent image is clonable then the new child image
2339N/A # must be nested within the parents filesystem namespace.
2339N/A path = path.rstrip(os.sep) + os.sep
2339N/A p_root = self.__root.rstrip(os.sep) + os.sep
2339N/A if img_is_clonable and not path.startswith(p_root):
2339N/A raise apx.LinkedImageException(
2339N/A child_not_nested=(path, p_root))
2339N/A
2339N/A # Child image should not already be linked
2339N/A img_li_data_props = os.path.join(img_prefix, PATH_PROP)
2339N/A try:
2339N/A exists = ar.ar_exists(path, img_li_data_props)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
2339N/A raise apx._convert_error(e)
2339N/A if exists and not allow_relink:
2339N/A raise apx.LinkedImageException(img_linked=path)
2339N/A
3020N/A self.__validate_attach_img_paths(p_root, path)
3020N/A
3020N/A def __validate_attach_img_paths(self, ppath, cpath):
3020N/A """Make sure there are no additional images in between the
3020N/A parent and the child. For example, this prevents linking of
3020N/A images if one of the images is nested within another unrelated
3020N/A image. This is done by looking at all the parent directories
3020N/A for both the parent and the child image until we reach a
3020N/A common ancestor."""
3020N/A
3020N/A # Make sure each path has a trailing '/'.
3020N/A ppath = ppath.rstrip(os.sep) + os.sep
3020N/A cpath = cpath.rstrip(os.sep) + os.sep
3020N/A
3020N/A # Make sure we're not linking to ourselves.
3020N/A if ppath == cpath:
3020N/A raise apx.LinkedImageException(link_to_self=ppath)
3020N/A
3020N/A # The parent image can't be nested nested within child.
3020N/A if ppath.startswith(cpath):
3020N/A raise apx.LinkedImageException(
3020N/A parent_nested=(ppath, cpath))
3020N/A
3020N/A # Make sure we're not linking the root image as a child.
3020N/A if cpath == misc.liveroot():
3020N/A raise apx.LinkedImageException(
3020N/A attach_root_as_child=cpath)
3020N/A
3020N/A # Make sure our current image is at it's default path. (We
3020N/A # don't allow attaching new images if an image is located at
3020N/A # an alternate path.)
3020N/A if self.inaltroot():
3020N/A raise apx.LinkedImageException(attach_with_curpath=(
3020N/A self.path(), self.current_path()))
3020N/A
3020N/A def abort_if_imgdir(d):
3020N/A """Raise an exception if directory 'd' contains an
3020N/A image."""
3020N/A try:
3020N/A tmp = ar.ar_img_prefix(d)
3020N/A except OSError, e:
3020N/A # W0212 Access to a protected member
3020N/A # pylint: disable=W0212
3020N/A raise apx._convert_error(e)
3020N/A if tmp:
3020N/A raise apx.LinkedImageException(
3020N/A intermediate_image=(ppath, cpath, d))
3020N/A
3020N/A # Find the common parent directory of the both parent and the
3020N/A # child image.
3020N/A dir_common = os.sep
3020N/A pdirs = ppath.split(os.sep)[1:-1]
3020N/A cdirs = cpath.split(os.sep)[1:-1]
3020N/A for pdir, cdir in itertools.izip(pdirs, cdirs):
3020N/A if pdir != cdir:
3020N/A break
3020N/A dir_common = os.path.join(dir_common, pdir)
3020N/A dir_common = dir_common.rstrip(os.sep) + os.sep
3020N/A
3020N/A # Test the common parent.
3020N/A if ppath != dir_common and cpath != dir_common:
3020N/A abort_if_imgdir(dir_common)
3020N/A
3020N/A # First check the parent directories of the child.
3020N/A d = os.path.dirname(cpath.rstrip(os.sep)) + os.sep
3020N/A while len(d) > len(dir_common):
3020N/A abort_if_imgdir(d)
3020N/A d = os.path.dirname(d.rstrip(os.sep)) + os.sep
3020N/A
3020N/A # Then check the parent directories of the parent.
3020N/A d = os.path.dirname(ppath.rstrip(os.sep)) + os.sep
3020N/A while len(d) > len(dir_common):
3020N/A abort_if_imgdir(d)
3020N/A d = os.path.dirname(d.rstrip(os.sep)) + os.sep
3020N/A
2339N/A def attach_child(self, lin, path, props,
2339N/A accept=False, allow_relink=False, force=False, li_md_only=False,
2500N/A li_pkg_updates=True, noexecute=False,
2500N/A progtrack=None, refresh_catalogs=True, reject_list=misc.EmptyI,
2444N/A show_licenses=False, update_index=True):
2339N/A """Attach an image as a child to the current image (the
2339N/A current image will become a parent image. This operation
2339N/A results in attempting to sync the child image with the parent
2444N/A image.
2444N/A
2444N/A For descriptions of parameters please see the descriptions in
2444N/A api.py`gen_plan_*"""
2339N/A
2339N/A assert type(lin) == LinkedImageName
2339N/A assert type(path) == str
2339N/A assert props == None or type(props) == dict, \
2339N/A "type(props) == %s" % type(props)
2339N/A if props == None:
2339N/A props = dict()
2339N/A
2339N/A lip = self.__plugins[lin.lin_type]
2339N/A if not lip.support_attach and not force:
2339N/A e = apx.LinkedImageException(
2339N/A attach_child_notsup=lin.lin_type)
2690N/A return LI_RVTuple(e.lix_exitrv, e, None)
2339N/A
2339N/A # Path must be an absolute path.
2339N/A if not os.path.isabs(path):
2339N/A e = apx.LinkedImageException(child_path_notabs=path)
2690N/A return LI_RVTuple(e.lix_exitrv, e, None)
2339N/A
2339N/A # cleanup specified path
2339N/A cwd = os.getcwd()
2339N/A try:
2339N/A os.chdir(path)
2339N/A except OSError, e:
3020N/A e = apx.LinkedImageException(lin=lin,
3020N/A child_op_failed=("access", path, e))
2690N/A return LI_RVTuple(e.lix_exitrv, e, None)
2339N/A path = os.getcwd()
2339N/A os.chdir(cwd)
2339N/A
2339N/A # if the current image isn't linked yet then we need to
2339N/A # generate some linked image properties for ourselves
2339N/A if PROP_PATH not in self.__props:
2339N/A p_props = self.__fabricate_parent_props()
2339N/A self.__update_props(p_props)
2339N/A
2339N/A # sanity check the input
2339N/A try:
2339N/A self.__validate_child_attach(lin, path, props,
2339N/A allow_relink=allow_relink)
2339N/A except apx.LinkedImageException, e:
2690N/A return LI_RVTuple(e.lix_exitrv, e, None)
2339N/A
2339N/A # make a copy of the options and start updating them
2339N/A child_props = props.copy()
2339N/A child_props[PROP_NAME] = lin
2339N/A child_props[PROP_MODEL] = PV_MODEL_PUSH
3020N/A
3020N/A # set path related properties
3020N/A self.set_path_transform(child_props,
3020N/A self.get_path_transform(), current_path=path)
2339N/A
2339N/A # fill in any missing defaults options
2339N/A for k, v in lip.attach_props_def.iteritems():
2339N/A if k not in child_props:
2339N/A child_props[k] = v
2339N/A
2339N/A # attach the child in memory
2339N/A lip.attach_child_inmemory(child_props, allow_relink)
2339N/A
2339N/A if noexecute and li_md_only:
2339N/A # we've validated parameters, nothing else to do
2690N/A return LI_RVTuple(pkgdefs.EXIT_OK, None, None)
2339N/A
2339N/A # update the child
2339N/A try:
2339N/A lic = LinkedImageChild(self, lin)
2339N/A except apx.LinkedImageException, e:
2690N/A return LI_RVTuple(e.lix_exitrv, e, None)
2690N/A
2690N/A rvdict = {}
2690N/A list(self.__children_op(
2690N/A _pkg_op=pkgdefs.PKG_OP_SYNC,
2690N/A _lic_list=[lic],
2690N/A _rvdict=rvdict,
2690N/A _progtrack=progtrack,
2690N/A _failfast=False,
2690N/A _expect_plan=True,
2925N/A _syncmd_tmp=True,
2690N/A accept=accept,
2690N/A li_md_only=li_md_only,
2690N/A li_pkg_updates=li_pkg_updates,
2690N/A noexecute=noexecute,
2690N/A refresh_catalogs=refresh_catalogs,
2690N/A reject_list=reject_list,
2690N/A show_licenses=show_licenses,
2690N/A update_index=update_index))
2690N/A
2690N/A rvtuple = rvdict[lin]
2690N/A
2690N/A if noexecute or rvtuple.rvt_rv not in [
2690N/A pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP ]:
2690N/A return rvtuple
2690N/A
2690N/A # commit child image property updates
2690N/A rvtuple2 = lip.sync_children_todisk()
2690N/A _li_rvtuple_check(rvtuple2)
2690N/A if rvtuple2.rvt_e:
2690N/A return rvtuple2
2339N/A
2339N/A # save parent image properties
2339N/A self.syncmd()
2339N/A
2690N/A # The recursive child operation may have returned NOP, but
2690N/A # since we always update our own image metadata, we always
2690N/A # return OK.
2690N/A if rvtuple.rvt_rv == pkgdefs.EXIT_NOP:
2690N/A return LI_RVTuple(pkgdefs.EXIT_OK, None, None)
2690N/A return rvtuple
2690N/A
2690N/A def audit_children(self, lin_list):
2339N/A """Audit one or more children of the current image to see if
2339N/A they are in sync with this image."""
2339N/A
2690N/A if lin_list == []:
2690N/A lin_list = None
2690N/A
2690N/A lic_dict, rvdict = self.__children_init(lin_list=lin_list,
2690N/A failfast=False)
2690N/A
2690N/A list(self.__children_op(
2690N/A _pkg_op=pkgdefs.PKG_OP_AUDIT_LINKED,
2690N/A _lic_list=lic_dict.values(),
2690N/A _rvdict=rvdict,
2690N/A _progtrack=progress.QuietProgressTracker(),
2690N/A _failfast=False))
2690N/A return rvdict
2690N/A
2925N/A def sync_children(self, lin_list, accept=False,
2690N/A li_md_only=False, li_pkg_updates=True, progtrack=None,
2690N/A noexecute=False, refresh_catalogs=True, reject_list=misc.EmptyI,
2690N/A show_licenses=False, update_index=True):
2339N/A """Sync one or more children of the current image."""
2339N/A
2690N/A if progtrack is None:
2693N/A progtrack = progress.NullProgressTracker()
2690N/A
2690N/A if lin_list == []:
2690N/A lin_list = None
2690N/A
2690N/A lic_dict = self.__children_init(lin_list=lin_list)
2690N/A
2925N/A _syncmd_tmp = True
2925N/A if not noexecute and li_md_only:
2925N/A _syncmd_tmp = False
2925N/A
2690N/A rvdict = {}
2690N/A list(self.__children_op(
2690N/A _pkg_op=pkgdefs.PKG_OP_SYNC,
2690N/A _lic_list=lic_dict.values(),
2690N/A _rvdict=rvdict,
2690N/A _progtrack=progtrack,
2690N/A _failfast=False,
2690N/A _expect_plan=True,
2925N/A _syncmd_tmp=_syncmd_tmp,
2690N/A accept=accept,
2690N/A li_md_only=li_md_only,
2690N/A li_pkg_updates=li_pkg_updates,
2690N/A noexecute=noexecute,
2690N/A refresh_catalogs=refresh_catalogs,
2690N/A reject_list=reject_list,
2690N/A show_licenses=show_licenses,
2690N/A update_index=update_index))
2690N/A return rvdict
2690N/A
2925N/A def detach_children(self, lin_list, force=False, noexecute=False,
2925N/A li_md_only=False, li_pkg_updates=True):
2339N/A """Detach one or more children from the current image. This
2339N/A operation results in the removal of any constraint package
2339N/A from the child images."""
2339N/A
2690N/A if lin_list == []:
2690N/A lin_list = None
2690N/A
2690N/A lic_dict, rvdict = self.__children_init(lin_list=lin_list,
2690N/A failfast=False)
2690N/A
2690N/A # check if we support detach for these children. we don't use
2690N/A # iteritems() when walking lic_dict because we might modify
2690N/A # lic_dict.
2690N/A for lin in lic_dict:
2690N/A lip = self.__plugins[lin.lin_type]
2690N/A if lip.support_detach or force:
2690N/A continue
2690N/A
2690N/A # we can't detach this type of image.
2690N/A e = apx.LinkedImageException(
2690N/A detach_child_notsup=lin.lin_type)
2690N/A rvdict[lin] = LI_RVTuple(e.lix_exitrv, e, None)
2690N/A _li_rvtuple_check(rvdict[lin])
2690N/A del lic_dict[lin]
2690N/A
2690N/A # do the detach
2690N/A list(self.__children_op(
2690N/A _pkg_op=pkgdefs.PKG_OP_DETACH,
2690N/A _lic_list=lic_dict.values(),
2690N/A _rvdict=rvdict,
2693N/A _progtrack=progress.NullProgressTracker(),
2690N/A _failfast=False,
2925N/A li_md_only=li_md_only,
2925N/A li_pkg_updates=li_pkg_updates,
2690N/A noexecute=noexecute))
2690N/A
2690N/A # if any of the children successfully detached, then we want
2690N/A # to discard our metadata for that child.
2690N/A for lin, rvtuple in rvdict.iteritems():
2690N/A
2339N/A # if the detach failed leave metadata in parent
2690N/A if rvtuple.rvt_e and not force:
2339N/A continue
2339N/A
2339N/A # detach the child in memory
2339N/A lip = self.__plugins[lin.lin_type]
2339N/A lip.detach_child_inmemory(lin)
2339N/A
2690N/A if noexecute:
2690N/A continue
2690N/A
2690N/A # commit child image property updates
2690N/A rvtuple2 = lip.sync_children_todisk()
2690N/A _li_rvtuple_check(rvtuple2)
2690N/A
2690N/A # don't overwrite previous errors
2690N/A if rvtuple2.rvt_e and rvtuple.rvt_e is None:
2690N/A rvdict[lin] = rvtuple2
2339N/A
2339N/A if not (self.ischild() or self.isparent()):
2339N/A # we're not linked anymore, so delete all our linked
2339N/A # properties.
2339N/A self.__update_props()
2339N/A self.syncmd()
2339N/A
2339N/A return rvdict
2339N/A
2925N/A def __children_op(self, _pkg_op, _lic_list, _rvdict, _progtrack,
2925N/A _failfast, _expect_plan=False, _ignore_syncmd_nop=True,
2925N/A _syncmd_tmp=False, _pd=None, **kwargs):
2690N/A """An iterator function which performs a linked image
2690N/A operation on multiple children in parallel.
2690N/A
2690N/A '_pkg_op' is the pkg.1 operation that we're going to perform
2690N/A
2690N/A '_lic_list' is a list of linked image child objects to perform
2690N/A the operation on.
2690N/A
2690N/A '_rvdict' is a dictionary, indexed by linked image name, which
2690N/A contains rvtuples of the result of the operation for each
2690N/A child.
2690N/A
2690N/A '_prograck' is a ProgressTracker pointer.
2690N/A
2690N/A '_failfast' is a boolean. If True and we encounter a failure
2690N/A operating on a child then we raise an exception immediately.
2690N/A If False then we'll attempt to perform the operation on all
2690N/A children and rvdict will contain a LI_RVTuple result for all
2690N/A children.
2690N/A
2690N/A '_expect_plan' is a boolean that indicates if we expect this
2690N/A operation to generate an image plan.
2690N/A
2690N/A '_ignore_syncmd_nop' a boolean that indicates if we should
2690N/A always recurse into a child even if the linked image meta data
2690N/A isn't changing.
2690N/A
2925N/A '_syncmd_tmp' a boolean that indicates if we should write
2925N/A linked image metadata in a temporary location in child images,
2925N/A or just overwrite any existing data.
2925N/A
2690N/A '_pd' a PlanDescription pointer."""
2690N/A
2690N/A if _lic_list:
2748N/A _progtrack.li_recurse_start(_pkg_op, len(_lic_list))
2690N/A
2690N/A if _pkg_op in [ pkgdefs.PKG_OP_AUDIT_LINKED,
2690N/A pkgdefs.PKG_OP_PUBCHECK ]:
2896N/A # these operations are cheap so ideally we'd like to
2896N/A # use full parallelism. but if the user specified a
2896N/A # concurrency limit we should respect that.
2896N/A if global_settings.client_concurrency_set:
2896N/A concurrency = global_settings.client_concurrency
2896N/A else:
2896N/A # no limit was specified, use full concurrency
2896N/A concurrency = -1
2690N/A else:
2690N/A concurrency = global_settings.client_concurrency
2690N/A
2925N/A # If we have a plan for the current image that means linked
2925N/A # image metadata is probably changing so we always save it to
2925N/A # a temporary file (and we don't overwrite the existing
2925N/A # metadata until after we execute the plan).
2925N/A if _pd is not None:
2925N/A _syncmd_tmp = True
2925N/A
2925N/A # get parent metadata common to all child images
2925N/A _pmd = None
2925N/A if _pkg_op != pkgdefs.PKG_OP_DETACH:
2925N/A ppubs = get_pubs(self.__img)
2925N/A ppkgs = get_packages(self.__img, pd=_pd)
2925N/A pfacets = get_inheritable_facets(self.__img, pd=_pd)
2925N/A _pmd = (ppubs, ppkgs, pfacets)
2925N/A
2690N/A # setup operation for each child
2690N/A lic_setup = []
2690N/A for lic in _lic_list:
2690N/A try:
2925N/A lic.child_op_setup(_pkg_op, _pmd, _progtrack,
2925N/A _ignore_syncmd_nop, _syncmd_tmp, **kwargs)
2690N/A lic_setup.append(lic)
2690N/A except apx.LinkedImageException, e:
2690N/A _rvdict[lic.child_name] = \
2690N/A LI_RVTuple(e.lix_exitrv, e, None)
2690N/A
2690N/A # if _failfast is true, then throw an exception if we failed
2690N/A # to setup any of the children. if _failfast is false we'll
2690N/A # continue to perform the operation on any children that
2690N/A # successfully initialized and we'll report setup errors along
2690N/A # with the final results for all children.
2690N/A if _failfast and _li_rvdict_exceptions(_rvdict):
2690N/A # before we raise an exception we need to cleanup any
2690N/A # children that we setup.
2690N/A for lic in lic_setup:
2690N/A lic.child_op_abort()
2690N/A # raise an exception
2690N/A _li_rvdict_raise_exceptions(_rvdict)
2690N/A
2743N/A def __child_op_finish(lic, lic_list, _pkg_op, _rvdict,
2743N/A _progtrack, _failfast, _expect_plan):
2690N/A """An iterator function invoked when a child has
2690N/A finished an operation.
2690N/A
2690N/A 'lic' is the child that has finished execution.
2690N/A
2690N/A 'lic_list' a list of children to remove 'lic' from.
2690N/A
2690N/A See __children_op() for an explanation of the other
2690N/A parameters."""
2690N/A
2690N/A assert lic.child_op_is_done()
2690N/A
2690N/A lic_list.remove(lic)
2690N/A
2743N/A rvtuple, stdout, stderr = lic.child_op_rv(_pkg_op,
2743N/A _expect_plan)
2690N/A _li_rvtuple_check(rvtuple)
2690N/A _rvdict[lic.child_name] = rvtuple
2690N/A
2690N/A # check if we should raise an exception
2690N/A if _failfast and _li_rvdict_exceptions(_rvdict):
2690N/A
2690N/A # we're going to raise an exception. abort
2690N/A # the remaining children.
2690N/A for lic in lic_list:
2690N/A lic.child_op_abort()
2690N/A
2690N/A # raise an exception
2690N/A _li_rvdict_raise_exceptions(_rvdict)
2690N/A
2690N/A if rvtuple.rvt_rv in [ pkgdefs.EXIT_OK,
2690N/A pkgdefs.EXIT_NOP ]:
2690N/A
2690N/A # only display child output if there was no
2690N/A # error (otherwise the exception includes the
2690N/A # output so we'll display it twice.)
2690N/A _progtrack.li_recurse_output(lic.child_name,
2690N/A stdout, stderr)
2690N/A
2690N/A # check if we should yield a plan.
2690N/A if _expect_plan and rvtuple.rvt_rv == pkgdefs.EXIT_OK:
2690N/A yield rvtuple.rvt_p_dict
2690N/A
2690N/A # check if we did everything we needed to do during child
2690N/A # setup. (this can happen if we're just doing an implicit
2690N/A # syncmd during setup we discover the linked image metadata
2690N/A # isn't changing.) we iterate over a copy of lic_setup to
2690N/A # allow __child_op_finish() to remove elements from lic_setup
2690N/A # while we're walking through it.
2690N/A for lic in copy.copy(lic_setup):
2690N/A if not lic.child_op_is_done():
2690N/A continue
2690N/A for p_dict in __child_op_finish(lic, lic_setup,
2743N/A _pkg_op, _rvdict, _progtrack, _failfast,
2743N/A _expect_plan):
2690N/A yield p_dict
2690N/A
2690N/A # keep track of currently running children
2690N/A lic_running = []
2690N/A
2690N/A # keep going as long as there are children to process
2690N/A progtrack_update = False
2690N/A while len(lic_setup) or len(lic_running):
2690N/A
2690N/A while lic_setup and (
2690N/A concurrency > len(lic_running) or
2690N/A concurrency <= 0):
2690N/A # start processing on a child
2690N/A progtrack_update = True
2690N/A lic = lic_setup.pop()
2690N/A lic_running.append(lic)
2690N/A lic.child_op_start()
2690N/A
2690N/A if progtrack_update:
2690N/A # display progress on children
2690N/A progtrack_update = False
2690N/A done = len(_lic_list) - len(lic_setup) - \
2690N/A len(lic_running)
2693N/A lin_running = sorted([
2693N/A lic.child_name for lic in lic_running])
2693N/A _progtrack.li_recurse_status(lin_running,
2748N/A done)
2690N/A
2896N/A # poll on all the linked image children and see which
2896N/A # ones have pending output.
2896N/A fd_hash = dict([
2896N/A (lic.fileno(), lic)
2896N/A for lic in lic_running
2896N/A ])
2896N/A p = select.poll()
2896N/A for fd in fd_hash.keys():
2896N/A p.register(fd, select.POLLIN)
2896N/A events = p.poll()
2896N/A lic_list = [ fd_hash[event[0]] for event in events ]
2896N/A
2896N/A for lic in lic_list:
2690N/A _progtrack.li_recurse_progress(lic.child_name)
2690N/A if not lic.child_op_is_done():
2690N/A continue
2690N/A # a child finished processing
2690N/A progtrack_update = True
2690N/A for p_dict in __child_op_finish(lic,
2743N/A lic_running, _pkg_op, _rvdict, _progtrack,
2690N/A _failfast, _expect_plan):
2690N/A yield p_dict
2690N/A
2690N/A _li_rvdict_check(_rvdict)
2690N/A if _lic_list:
2690N/A _progtrack.li_recurse_end()
2690N/A
2690N/A def __children_init(self, lin_list=None, li_ignore=None, failfast=True):
2690N/A """Initialize LinkedImageChild objects for children specified
2690N/A in 'lin_list'. If 'lin_list' is not specified, then
2690N/A initialize objects for all children (excluding any being
2690N/A ignored via 'li_ignore')."""
2690N/A
2690N/A # you can't specify children to operate on and children to be
2690N/A # ignored at the same time
2690N/A assert lin_list is None or li_ignore is None
2690N/A
2690N/A # if no children we listed, build a list of children
2690N/A if lin_list is None:
2690N/A lin_list = [
2690N/A i[0]
2690N/A for i in self.__list_children(li_ignore)
2690N/A ]
2690N/A else:
2690N/A self.verify_names(lin_list)
2690N/A
2690N/A rvdict = {}
2690N/A lic_dict = {}
2339N/A for lin in lin_list:
2339N/A try:
2339N/A lic = LinkedImageChild(self, lin)
2690N/A lic_dict[lin] = lic
2339N/A except apx.LinkedImageException, e:
2690N/A rvdict[lin] = LI_RVTuple(e.lix_exitrv, e, None)
2690N/A
2690N/A if failfast:
2690N/A _li_rvdict_raise_exceptions(rvdict)
2690N/A return lic_dict
2690N/A
2690N/A return (lic_dict, rvdict)
2690N/A
2690N/A def __recursion_init(self, li_ignore):
2690N/A """Initialize child objects used during recursive packaging
2690N/A operations."""
2690N/A
2690N/A self.__lic_ignore = li_ignore
2690N/A self.__lic_dict = self.__children_init(li_ignore=li_ignore)
2690N/A
2690N/A def api_recurse_init(self, li_ignore=None, repos=None):
2690N/A """Initialize planning state. If we're a child image we save
2690N/A our current state (which may reflect a planned state that we
2690N/A have not committed to disk) into the plan. We also initialize
2690N/A all our children to prepare to recurse into them."""
2339N/A
2339N/A if PROP_RECURSE in self.__props and \
2339N/A not self.__props[PROP_RECURSE]:
2690N/A # we don't want to recurse
2690N/A self.__recursion_init(li_ignore=[])
2690N/A return
2690N/A
2690N/A # Initialize children
2690N/A self.__recursion_init(li_ignore)
2690N/A
2690N/A if not self.__lic_dict:
2690N/A # we don't need to recurse
2339N/A return
2339N/A
2690N/A # if we have any children we don't support operations using
2690N/A # temporary repositories.
2690N/A if repos:
2690N/A raise apx.PlanCreationException(no_tmp_origins=True)
2690N/A
2693N/A def api_recurse_pubcheck(self, progtrack):
2690N/A """Do a recursive publisher check"""
2690N/A
2690N/A # get a list of of children to recurse into.
2690N/A lic_list = self.__lic_dict.values()
2690N/A
2690N/A # do a publisher check on all of them
2690N/A rvdict = {}
2690N/A list(self.__children_op(
2690N/A _pkg_op=pkgdefs.PKG_OP_PUBCHECK,
2690N/A _lic_list=lic_list,
2690N/A _rvdict=rvdict,
2693N/A _progtrack=progtrack,
2690N/A _failfast=False))
2690N/A
2690N/A # raise an exception if one or more children failed the
2690N/A # publisher check.
2690N/A _li_rvdict_raise_exceptions(rvdict)
2690N/A
2690N/A def __api_recurse(self, stage, progtrack):
2690N/A """This is an iterator function. It recurses into linked
2690N/A image children to perform the specified operation.
2690N/A """
2690N/A
2690N/A # get a pointer to the current image plan
2690N/A pd = self.__img.imageplan.pd
2690N/A
2690N/A # get a list of of children to recurse into.
2690N/A lic_list = self.__lic_dict.values()
2690N/A
2690N/A # sanity check stage
2690N/A assert stage in [pkgdefs.API_STAGE_PLAN,
2690N/A pkgdefs.API_STAGE_PREPARE, pkgdefs.API_STAGE_EXECUTE]
2690N/A
2690N/A # if we're ignoring all children then we can't be recursing
2690N/A assert pd.children_ignored != [] or lic_list == []
2690N/A
2690N/A # sanity check the plan description state
2690N/A if stage == pkgdefs.API_STAGE_PLAN:
2690N/A # the state should be uninitialized
2690N/A assert pd.children_planned == []
2690N/A assert pd.children_nop == []
2690N/A else:
2690N/A # if we ignored all children, we better not have
2690N/A # recursed into any children.
2690N/A assert pd.children_ignored != [] or \
2690N/A pd.children_planned == pd.children_nop == []
2690N/A
2690N/A # there shouldn't be any overloap between sets of
2690N/A # children in the plan
2690N/A assert not (set(pd.children_planned) &
2690N/A set(pd.children_nop))
2690N/A if pd.children_ignored:
2690N/A assert not (set(pd.children_ignored) &
2690N/A set(pd.children_planned))
2690N/A assert not (set(pd.children_ignored) &
2690N/A set(pd.children_nop))
2690N/A
2690N/A # make sure set of child handles matches the set of
2690N/A # previously planned children.
2690N/A assert set(self.__lic_dict) == set(pd.children_planned)
2690N/A
2690N/A # if we're in the planning stage, we should pass the current
2690N/A # image plan onto the child and also expect an image plan from
2690N/A # the child.
2690N/A expect_plan = False
2690N/A if stage == pkgdefs.API_STAGE_PLAN:
2690N/A expect_plan = True
2690N/A
2690N/A # get target op and arguments
2690N/A pkg_op = pd.child_op
2690N/A
2690N/A # assume that for most operations we want to recurse into the
2690N/A # child image even if the linked image metadata isn't
2690N/A # changing. (this would be required for recursive operations,
2690N/A # update operations, etc.)
2690N/A _ignore_syncmd_nop = True
2925N/A if pd.child_op_implicit:
2690N/A # the exception is if we're doing an implicit sync.
2690N/A # to improve performance we assume the child is
2690N/A # already in sync, so if its linked image metadata
2690N/A # isn't changing then the child won't need any updates
2690N/A # so there will be no need to recurse into it.
2690N/A _ignore_syncmd_nop = False
2690N/A
2690N/A rvdict = {}
2690N/A for p_dict in self.__children_op(
2690N/A _pkg_op=pkg_op,
2690N/A _lic_list=lic_list,
2690N/A _rvdict=rvdict,
2690N/A _progtrack=progtrack,
2690N/A _failfast=True,
2690N/A _expect_plan=expect_plan,
2690N/A _ignore_syncmd_nop=_ignore_syncmd_nop,
2690N/A _pd=pd,
2690N/A stage=stage,
2690N/A **pd.child_kwargs):
2690N/A yield p_dict
2690N/A
2690N/A assert not _li_rvdict_exceptions(rvdict)
2690N/A
2690N/A for lin in rvdict:
2690N/A # check for children that don't need any updates
2690N/A if rvdict[lin].rvt_rv == pkgdefs.EXIT_NOP:
2690N/A assert lin not in pd.children_nop
2690N/A pd.children_nop.append(lin)
2690N/A del self.__lic_dict[lin]
2690N/A
2690N/A # record the children that are done planning
2690N/A if stage == pkgdefs.API_STAGE_PLAN and \
2690N/A rvdict[lin].rvt_rv == pkgdefs.EXIT_OK:
2690N/A assert lin not in pd.children_planned
2690N/A pd.children_planned.append(lin)
2690N/A
2690N/A @staticmethod
2690N/A def __recursion_op(api_op, api_kwargs):
2690N/A """Determine what pkg command to use when recursing into child
2690N/A images."""
2339N/A
2401N/A #
2401N/A # given the api operation being performed on the current
2401N/A # image, figure out what api operation should be performed on
2401N/A # child images.
2401N/A #
2401N/A # the recursion policy which hard coded here is that if we do
2401N/A # an pkg update in the parent image without any packages
2401N/A # specified (ie, we want to update everything) then when we
2401N/A # recurse we'll also do an update of everything. but if we're
2401N/A # doing any other operation like install, uninstall, an update
2401N/A # of specific packages, etc, then when we recurse we'll do a
2401N/A # sync in the child.
2401N/A #
2925N/A implicit = False
2690N/A if api_op == pkgdefs.API_OP_UPDATE and not \
2690N/A api_kwargs["pkgs_update"]:
2339N/A pkg_op = pkgdefs.PKG_OP_UPDATE
2925N/A elif api_op == pkgdefs.API_OP_SYNC:
2925N/A pkg_op = pkgdefs.PKG_OP_SYNC
2339N/A else:
2339N/A pkg_op = pkgdefs.PKG_OP_SYNC
2925N/A implicit = True
2925N/A return pkg_op, implicit
2690N/A
2690N/A @staticmethod
2690N/A def __recursion_args(pd, refresh_catalogs, update_index, api_kwargs):
2690N/A """Determine what pkg command arguments to use when recursing
2690N/A into child images."""
2690N/A
2690N/A kwargs = {}
2690N/A kwargs["noexecute"] = api_kwargs["noexecute"]
2690N/A kwargs["refresh_catalogs"] = refresh_catalogs
2690N/A kwargs["show_licenses"] = False
2690N/A kwargs["update_index"] = update_index
2690N/A
2690N/A #
2690N/A # when we recurse we always accept all new licenses (for now).
2690N/A #
2690N/A # ultimately (when start yielding back plan descriptions for
2690N/A # children) in addition to accepting licenses on the plan for
2690N/A # the current image the api client will also have to
2690N/A # explicitly accept licenses for all child images. but until
2690N/A # that happens we'll just assume that the parent image license
2690N/A # space is a superset of the child image license space (and
2690N/A # since the api consumer must accept licenses in the parent
2690N/A # before we'll do anything, we'll assume licenses in the child
2690N/A # are accepted as well).
2690N/A #
2690N/A kwargs["accept"] = True
2690N/A
2690N/A if "li_pkg_updates" in api_kwargs:
2690N/A # option specific to: attach, set-property-linked, sync
2690N/A kwargs["li_pkg_updates"] = api_kwargs["li_pkg_updates"]
2690N/A
2690N/A if pd.child_op == pkgdefs.PKG_OP_UPDATE:
2690N/A # skip ipkg up to date check for child images
2690N/A kwargs["force"] = True
2690N/A
2690N/A return kwargs
2690N/A
2690N/A def api_recurse_plan(self, api_kwargs, refresh_catalogs,
2690N/A update_index, progtrack):
2690N/A """Plan child image updates."""
2690N/A
2690N/A pd = self.__img.imageplan.pd
2690N/A api_op = pd.plan_type
2690N/A
2690N/A # update the plan arguments
2925N/A pd.child_op, pd.child_op_implicit = \
2925N/A self.__recursion_op(api_op, api_kwargs)
2690N/A pd.child_kwargs = self.__recursion_args(pd,
2690N/A refresh_catalogs, update_index, api_kwargs)
2690N/A pd.children_ignored = self.__lic_ignore
2690N/A
2690N/A # recurse into children
2690N/A for p_dict in self.__api_recurse(pkgdefs.API_STAGE_PLAN,
2690N/A progtrack):
2690N/A yield p_dict
2690N/A
2690N/A def api_recurse_prepare(self, progtrack):
2690N/A """Prepare child image updates."""
2690N/A list(self.__api_recurse(pkgdefs.API_STAGE_PREPARE, progtrack))
2690N/A
2690N/A def api_recurse_execute(self, progtrack):
2690N/A """Execute child image updates."""
2690N/A list(self.__api_recurse(pkgdefs.API_STAGE_EXECUTE, progtrack))
2690N/A
2690N/A def init_plan(self, pd):
2690N/A """Initialize our state in the PlanDescription."""
2690N/A
2690N/A # if we're a child, save our parent package state into the
2690N/A # plan description
3020N/A pd.li_props = rm_dict_ent(self.__props.copy(), temporal_props)
2690N/A pd.li_ppkgs = self.__ppkgs
2690N/A pd.li_ppubs = self.__ppubs
2925N/A pd.li_pfacets = self.__pfacets
2690N/A
2690N/A def setup_plan(self, pd):
2690N/A """Reload a previously created plan."""
2690N/A
3020N/A # make a copy of the linked image properties
3020N/A props = pd.li_props.copy()
3020N/A
3020N/A # generate temporal properties
3020N/A if props:
3020N/A self.__set_current_path(props)
3020N/A
2690N/A # load linked image state from the plan
3020N/A self.__update_props(props)
2690N/A self.__ppubs = pd.li_ppubs
2690N/A self.__ppkgs = pd.li_ppkgs
2925N/A self.__pfacets = pd.li_pfacets
2690N/A
2690N/A # now initialize our recursion state, this involves allocating
2690N/A # handles to operate on children. we don't need handles for
2690N/A # children that were either ignored during planning, or which
2690N/A # return EXIT_NOP after planning (since these children don't
2690N/A # need any updates).
2690N/A li_ignore = copy.copy(pd.children_ignored)
2690N/A
2690N/A # merge the children that returned nop into li_ignore (since
2690N/A # we don't need to recurse into them). if li_ignore is [],
2690N/A # then we ignored all children during planning
2690N/A if li_ignore != [] and pd.children_nop:
2690N/A if li_ignore is None:
2690N/A # no children were ignored during planning
2690N/A li_ignore = []
2690N/A li_ignore += pd.children_nop
2690N/A
2690N/A # Initialize children
2690N/A self.__recursion_init(li_ignore=li_ignore)
2339N/A
2339N/A def recurse_nothingtodo(self):
2339N/A """Return True if there is no planned work to do on child
2339N/A image."""
2339N/A
2690N/A for lic in self.__lic_dict.itervalues():
2690N/A if lic.child_name not in \
2690N/A self.__img.imageplan.pd.children_nop:
2339N/A return False
2339N/A return True
2339N/A
2339N/A
2339N/Aclass LinkedImageChild(object):
2339N/A """A LinkedImageChild object is used when a parent image wants to
2339N/A access a child image. These accesses may include things like:
2339N/A saving/pushing linked image metadata into a child image, syncing or
2339N/A auditing a child image, or recursing into a child image to keep it in
2339N/A sync with planned changes in the parent image."""
2339N/A
2339N/A def __init__(self, li, lin):
2339N/A assert isinstance(li, LinkedImage), \
2339N/A "isinstance(%s, LinkedImage)" % type(li)
2339N/A assert isinstance(lin, LinkedImageName), \
2339N/A "isinstance(%s, LinkedImageName)" % type(lin)
2339N/A
2339N/A # globals
2339N/A self.__linked = li
2339N/A self.__img = li.image
2339N/A
2339N/A # cache properties.
2339N/A self.__props = self.__linked.child_props(lin)
2339N/A assert self.__props[PROP_NAME] == lin
2339N/A
2339N/A try:
2339N/A imgdir = ar.ar_img_prefix(self.child_path)
3020N/A except OSError, e:
3020N/A raise apx.LinkedImageException(lin=lin,
3020N/A child_op_failed=("find", self.child_path, e))
2339N/A
2339N/A if not imgdir:
2339N/A raise apx.LinkedImageException(
2339N/A lin=lin, child_bad_img=self.child_path)
2339N/A
2339N/A # initialize paths for linked image data files
2339N/A self.__path_ppkgs = os.path.join(imgdir, PATH_PPKGS)
2339N/A self.__path_prop = os.path.join(imgdir, PATH_PROP)
2339N/A self.__path_ppubs = os.path.join(imgdir, PATH_PUBS)
2925N/A self.__path_pfacets = os.path.join(imgdir, PATH_PFACETS)
2339N/A
2339N/A # initialize a linked image child plugin
2339N/A self.__plugin = \
2339N/A pkg.client.linkedimage.p_classes_child[lin.lin_type](self)
2339N/A
2690N/A self.__pkg_remote = pkg.client.pkgremote.PkgRemote()
2690N/A self.__child_op_rvtuple = None
2339N/A
2339N/A @property
2339N/A def child_name(self):
2339N/A """Get the path associated with a child image."""
2339N/A return self.__props[PROP_NAME]
2339N/A
2339N/A @property
2339N/A def child_path(self):
2339N/A """Get the path associated with a child image."""
3020N/A
3020N/A if self.__linked.inaltroot():
3020N/A return self.__props[PROP_CURRENT_PATH]
2339N/A return self.__props[PROP_PATH]
2339N/A
2339N/A @property
2339N/A def child_pimage(self):
2339N/A """Get a pointer to the parent image object associated with
2339N/A this child."""
2339N/A return self.__img
2339N/A
3020N/A def __push_data(self, root, path, data, tmp, test):
2339N/A """Write data to a child image."""
2339N/A
2339N/A try:
3020N/A # first save our data to a temporary file
3020N/A path_tmp = "%s.%s" % (path,
3020N/A global_settings.client_runid)
3020N/A save_data(path_tmp, data, root=root,
3020N/A catch_exception=False)
3020N/A
3020N/A # Check if the data is changing. To do this
3020N/A # comparison we load the serialized on-disk json data
3020N/A # into memory because there are no guarantees about
3020N/A # data ordering during serialization. When loading
3020N/A # the data we don't bother decoding it into objects.
3020N/A updated = True
3020N/A old_data = load_data(path, missing_ok=True,
3020N/A root=root, decode=False,
3020N/A catch_exception=False)
3020N/A if old_data is not None:
3020N/A new_data = load_data(path_tmp,
3020N/A root=root, decode=False,
3020N/A catch_exception=False)
3020N/A if old_data == new_data:
3020N/A updated = False
3020N/A
3020N/A
3020N/A # If we're not actually updating any data, or if we
3020N/A # were just doing a test to see if the data has
3020N/A # changed, then delete the temporary data file.
3020N/A if not updated or test:
3020N/A ar.ar_unlink(root, path_tmp)
3020N/A return updated
3020N/A
3020N/A if not tmp:
3020N/A ar.ar_rename(root, path_tmp, path)
3020N/A
2339N/A except OSError, e:
3020N/A raise apx.LinkedImageException(lin=self.child_name,
3020N/A child_op_failed=("metadata update",
3020N/A self.child_path, e))
2339N/A
2339N/A return True
2339N/A
2925N/A def __push_ppkgs(self, ppkgs, tmp=False, test=False):
2339N/A """Sync linked image parent constraint data to a child image.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2925N/A # save the planned parent packages
2339N/A return self.__push_data(self.child_path, self.__path_ppkgs,
2339N/A ppkgs, tmp, test)
2339N/A
2925N/A def __push_pfacets(self, pfacets, tmp=False, test=False):
2925N/A """Sync linked image parent facet data to a child image.
2925N/A
2925N/A 'tmp' determines if we should read/write to the official
2925N/A linked image metadata files, or if we should access temporary
2925N/A versions (which have ".<runid>" appended to them."""
2925N/A
2925N/A # save the planned parent facets
2925N/A return self.__push_data(self.child_path, self.__path_pfacets,
2925N/A pfacets, tmp, test)
2925N/A
2925N/A
2339N/A def __push_props(self, tmp=False, test=False):
2339N/A """Sync linked image properties data to a child image.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2339N/A # make a copy of the props we want to push
2339N/A props = self.__props.copy()
2339N/A assert PROP_PARENT_PATH not in props
2339N/A
2339N/A self.__plugin.munge_props(props)
2339N/A
2339N/A # delete temporal properties
2339N/A props = rm_dict_ent(props, temporal_props)
2339N/A return self.__push_data(self.child_path, self.__path_prop,
2339N/A props, tmp, test)
2339N/A
2925N/A def __push_ppubs(self, ppubs, tmp=False, test=False):
2339N/A """Sync linked image parent publisher data to a child image.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2339N/A return self.__push_data(self.child_path, self.__path_ppubs,
2339N/A ppubs, tmp, test)
2339N/A
2925N/A def __syncmd(self, pmd, tmp=False, test=False):
2339N/A """Sync linked image data to a child image.
2339N/A
2339N/A 'tmp' determines if we should read/write to the official
2339N/A linked image metadata files, or if we should access temporary
2339N/A versions (which have ".<runid>" appended to them."""
2339N/A
2925N/A # unpack parent metadata tuple
2925N/A ppubs, ppkgs, pfacets = pmd
2925N/A
2925N/A ppkgs_updated = self.__push_ppkgs(ppkgs, tmp, test)
2339N/A props_updated = self.__push_props(tmp, test)
2925N/A pubs_updated = self.__push_ppubs(ppubs, tmp, test)
2925N/A pfacets_updated = self.__push_pfacets(pfacets, tmp, test)
2925N/A
2925N/A return (props_updated or ppkgs_updated or pubs_updated or
2925N/A pfacets_updated)
2925N/A
2925N/A def __child_op_setup_syncmd(self, pmd, ignore_syncmd_nop=True,
2925N/A tmp=False, test=False, stage=pkgdefs.API_STAGE_DEFAULT):
2690N/A """Prepare to perform an operation on a child image by syncing
2690N/A the latest linked image data to that image. As part of this
2690N/A operation, if we discover that the meta data hasn't changed we
2690N/A may report back that there is nothing to do (EXIT_NOP).
2690N/A
2925N/A 'pmd' is a tuple that contains parent metadata that we will
2925N/A sync to the child image. Note this is not all the metadata
2925N/A that we will sync, just the set which is common to all
2925N/A children.
2925N/A
2690N/A 'ignore_syncmd_nop' a boolean that indicates if we should
2690N/A always recurse into a child even if the linked image meta data
2690N/A isn't changing.
2690N/A
2690N/A 'tmp' a boolean that indicates if we should save the child
2690N/A image meta data into temporary files (instead of overwriting
2690N/A the persistent meta data files).
2690N/A
2690N/A 'test' a boolean that indicates we shouldn't save any child
2690N/A image meta data, instead we should just test to see if the
2690N/A meta data is changing.
2690N/A
2690N/A 'stage' indicates which stage of execution we should be
2690N/A performing on a child image."""
2690N/A
2925N/A # we don't update metadata during all stages of operation
2690N/A if stage not in [
2690N/A pkgdefs.API_STAGE_DEFAULT, pkgdefs.API_STAGE_PLAN]:
2690N/A return True
2339N/A
2339N/A try:
2925N/A updated = self.__syncmd(pmd, tmp=tmp, test=test)
2690N/A except apx.LinkedImageException, e:
2690N/A self.__child_op_rvtuple = \
2690N/A LI_RVTuple(e.lix_exitrv, e, None)
2690N/A return False
2690N/A
2690N/A if ignore_syncmd_nop:
2690N/A # we successfully updated the metadata
2690N/A return True
2690N/A
2690N/A # if the metadata changed then report success
2690N/A if updated:
2690N/A return True
2690N/A
2690N/A # the metadata didn't change, so this operation is a NOP
2690N/A self.__child_op_rvtuple = \
2690N/A LI_RVTuple(pkgdefs.EXIT_NOP, None, None)
2690N/A return False
2690N/A
2925N/A def __child_setup_sync(self, _pmd, _progtrack, _ignore_syncmd_nop,
2925N/A _syncmd_tmp,
2690N/A accept=False,
2690N/A li_md_only=False,
2690N/A li_pkg_updates=True,
2690N/A noexecute=False,
2690N/A refresh_catalogs=True,
2690N/A reject_list=misc.EmptyI,
2690N/A show_licenses=False,
2690N/A stage=pkgdefs.API_STAGE_DEFAULT,
2690N/A update_index=True):
2690N/A """Prepare to sync a child image. This involves updating the
2690N/A linked image metadata in the child and then possibly recursing
2690N/A into the child to actually update packages.
2444N/A
2444N/A For descriptions of parameters please see the descriptions in
2444N/A api.py`gen_plan_*"""
2339N/A
2339N/A if li_md_only:
2690N/A #
2339N/A # we're not going to recurse into the child image,
2339N/A # we're just going to update its metadata.
2690N/A #
2690N/A # we don't support updating packages in the parent
2690N/A # during attach metadata only sync.
2690N/A #
2925N/A if not self.__child_op_setup_syncmd(_pmd,
2690N/A ignore_syncmd_nop=False,
2690N/A test=noexecute, stage=stage):
2690N/A # the update failed
2690N/A return
2690N/A self.__child_op_rvtuple = \
2690N/A LI_RVTuple(pkgdefs.EXIT_OK, None, None)
2690N/A return
2690N/A
2690N/A #
2690N/A # first sync the metadata
2690N/A #
2690N/A # if we're doing this sync as part of an attach, then
2690N/A # temporarily sync the metadata since we don't know yet if the
2690N/A # attach will succeed. if the attach doesn't succeed this
2690N/A # means we don't have to delete any metadata. if the attach
2690N/A # succeeds the child will make the temporary metadata
2690N/A # permanent as part of the commit.
2690N/A #
2690N/A # we don't support updating packages in the parent
2690N/A # during attach.
2690N/A #
2925N/A if not self.__child_op_setup_syncmd(_pmd,
2690N/A ignore_syncmd_nop=_ignore_syncmd_nop,
2925N/A tmp=_syncmd_tmp, stage=stage):
2690N/A # the update failed or the metadata didn't change
2690N/A return
2690N/A
2690N/A self.__pkg_remote.setup(self.child_path,
2690N/A pkgdefs.PKG_OP_SYNC,
2690N/A accept=accept,
2690N/A backup_be=None,
2690N/A backup_be_name=None,
2690N/A be_activate=True,
2690N/A be_name=None,
2690N/A li_ignore=None,
2690N/A li_md_only=li_md_only,
2690N/A li_parent_sync=True,
2690N/A li_pkg_updates=li_pkg_updates,
2690N/A li_target_all=False,
2690N/A li_target_list=[],
2690N/A new_be=None,
2690N/A noexecute=noexecute,
2690N/A origins=[],
2693N/A parsable_version=\
2743N/A global_settings.client_output_parsable_version,
2693N/A quiet=global_settings.client_output_quiet,
2690N/A refresh_catalogs=refresh_catalogs,
2690N/A reject_pats=reject_list,
2690N/A show_licenses=show_licenses,
2690N/A stage=stage,
2690N/A update_index=update_index,
2693N/A verbose=global_settings.client_output_verbose)
2690N/A
2925N/A def __child_setup_update(self, _pmd, _progtrack, _ignore_syncmd_nop,
2925N/A _syncmd_tmp,
2690N/A accept=False,
2690N/A force=False,
2690N/A noexecute=False,
2690N/A refresh_catalogs=True,
2690N/A reject_list=misc.EmptyI,
2690N/A show_licenses=False,
2690N/A stage=pkgdefs.API_STAGE_DEFAULT,
2690N/A update_index=True):
2690N/A """Prepare to update a child image."""
2690N/A
2690N/A # first sync the metadata
2925N/A if not self.__child_op_setup_syncmd(_pmd,
2925N/A ignore_syncmd_nop=_ignore_syncmd_nop,
2925N/A tmp=_syncmd_tmp, stage=stage):
2690N/A # the update failed or the metadata didn't change
2690N/A return
2690N/A
2690N/A self.__pkg_remote.setup(self.child_path,
2690N/A pkgdefs.PKG_OP_UPDATE,
2690N/A accept=accept,
2690N/A backup_be=None,
2690N/A backup_be_name=None,
2690N/A be_activate=True,
2690N/A be_name=None,
2690N/A force=force,
2690N/A li_ignore=None,
2690N/A li_parent_sync=True,
2690N/A new_be=None,
2690N/A noexecute=noexecute,
2690N/A origins=[],
2693N/A parsable_version=\
2743N/A global_settings.client_output_parsable_version,
2693N/A quiet=global_settings.client_output_quiet,
2690N/A refresh_catalogs=refresh_catalogs,
2690N/A reject_pats=reject_list,
2690N/A show_licenses=show_licenses,
2690N/A stage=stage,
2690N/A update_index=update_index,
3014N/A verbose=global_settings.client_output_verbose,
3014N/A act_timeout=0)
2690N/A
2925N/A def __child_setup_detach(self, _progtrack, li_md_only=False,
2925N/A li_pkg_updates=True, noexecute=False):
2690N/A """Prepare to detach a child image."""
2690N/A
2690N/A self.__pkg_remote.setup(self.child_path,
2690N/A pkgdefs.PKG_OP_DETACH,
2690N/A force=True,
2925N/A li_md_only=li_md_only,
2925N/A li_pkg_updates=li_pkg_updates,
2690N/A li_target_all=False,
2690N/A li_target_list=[],
2690N/A noexecute=noexecute,
2693N/A quiet=global_settings.client_output_quiet,
2693N/A verbose=global_settings.client_output_verbose)
2690N/A
2925N/A def __child_setup_pubcheck(self, _pmd):
2690N/A """Prepare to a check if a child's publishers are in sync."""
2690N/A
2690N/A # first sync the metadata
2925N/A # a pubcheck should never update persistent meta data
2925N/A if not self.__child_op_setup_syncmd(_pmd, tmp=True):
2690N/A # the update failed
2690N/A return
2690N/A
2690N/A # setup recursion into the child image
2690N/A self.__pkg_remote.setup(self.child_path,
2690N/A pkgdefs.PKG_OP_PUBCHECK)
2690N/A
2925N/A def __child_setup_audit(self, _pmd):
2690N/A """Prepare to a child image to see if it's in sync with its
2690N/A constraints."""
2339N/A
2339N/A # first sync the metadata
2925N/A if not self.__child_op_setup_syncmd(_pmd, tmp=True):
2690N/A # the update failed
2690N/A return
2690N/A
2690N/A # setup recursion into the child image
2690N/A self.__pkg_remote.setup(self.child_path,
2690N/A pkgdefs.PKG_OP_AUDIT_LINKED,
2690N/A li_parent_sync=True,
2690N/A li_target_all=False,
2690N/A li_target_list=[],
2690N/A omit_headers=True,
2690N/A quiet=True)
2690N/A
2690N/A def child_op_abort(self):
2690N/A """Public interface to abort an operation on a child image."""
2690N/A
2690N/A self.__pkg_remote.abort()
2690N/A self.__child_op_rvtuple = None
2690N/A
2925N/A def child_op_setup(self, _pkg_op, _pmd, _progtrack, _ignore_syncmd_nop,
2925N/A _syncmd_tmp, **kwargs):
2690N/A """Public interface to setup an operation that we'd like to
2690N/A perform on a child image."""
2690N/A
2690N/A assert self.__child_op_rvtuple is None
2690N/A
2690N/A if _pkg_op == pkgdefs.PKG_OP_AUDIT_LINKED:
2925N/A self.__child_setup_audit(_pmd, **kwargs)
2690N/A elif _pkg_op == pkgdefs.PKG_OP_DETACH:
2690N/A self.__child_setup_detach(_progtrack, **kwargs)
2690N/A elif _pkg_op == pkgdefs.PKG_OP_PUBCHECK:
2925N/A self.__child_setup_pubcheck(_pmd, **kwargs)
2690N/A elif _pkg_op == pkgdefs.PKG_OP_SYNC:
2925N/A self.__child_setup_sync(_pmd, _progtrack,
2925N/A _ignore_syncmd_nop, _syncmd_tmp, **kwargs)
2690N/A elif _pkg_op == pkgdefs.PKG_OP_UPDATE:
2925N/A self.__child_setup_update(_pmd, _progtrack,
2925N/A _ignore_syncmd_nop, _syncmd_tmp, **kwargs)
2690N/A else:
2690N/A raise RuntimeError(
2690N/A "Unsupported package client op: %s" % _pkg_op)
2690N/A
2690N/A def child_op_start(self):
2690N/A """Public interface to start an operation on a child image."""
2690N/A
2690N/A # if we have a return value this operation is done
2690N/A if self.__child_op_rvtuple is not None:
2690N/A return True
2690N/A
2690N/A self.__pkg_remote.start()
2690N/A
2690N/A def child_op_is_done(self):
2690N/A """Public interface to query if an operation on a child image
2690N/A is done."""
2690N/A
2690N/A # if we have a return value this operation is done
2690N/A if self.__child_op_rvtuple is not None:
2690N/A return True
2690N/A
2690N/A # make sure there is some data from the child
2690N/A return self.__pkg_remote.is_done()
2690N/A
2743N/A def child_op_rv(self, pkg_op, expect_plan):
2690N/A """Public interface to get the result of an operation on a
2690N/A child image.
2690N/A
2690N/A 'expect_plan' boolean indicating if the child is performing a
2690N/A planning operation. this is needed because if we're running
2690N/A in parsable output mode then the child will emit a parsable
2690N/A json version of the plan on stdout, and we'll verify it by
2690N/A running it through the json parser.
2690N/A """
2690N/A
2690N/A # if we have a return value this operation is done
2690N/A if self.__child_op_rvtuple is not None:
2690N/A rvtuple = self.__child_op_rvtuple
2690N/A self.__child_op_rvtuple = None
2690N/A return (rvtuple, None, None)
2690N/A
2690N/A # make sure we're not going to block
2690N/A assert self.__pkg_remote.is_done()
2690N/A
2743N/A (rv, e, stdout, stderr) = self.__pkg_remote.result()
2690N/A if e is not None:
2690N/A rv = pkgdefs.EXIT_OOPS
2690N/A
2690N/A # if we got an exception, or a return value other than OK or
2690N/A # NOP, then return an exception.
2690N/A if e is not None or \
2690N/A rv not in [pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP]:
2690N/A e = apx.LinkedImageException(
2690N/A lin=self.child_name, exitrv=rv,
2690N/A pkg_op_failed=(pkg_op, rv, stdout + stderr, e))
2690N/A rvtuple = LI_RVTuple(rv, e, None)
2690N/A return (rvtuple, stdout, stderr)
2690N/A
2690N/A # check for NOP.
2690N/A if rv == pkgdefs.EXIT_NOP:
2690N/A assert e is None
2690N/A rvtuple = LI_RVTuple(rv, None, None)
2690N/A return (rvtuple, stdout, stderr)
2690N/A
2693N/A if global_settings.client_output_parsable_version is None or \
2690N/A not expect_plan:
2690N/A rvtuple = LI_RVTuple(rv, None, None)
2690N/A return (rvtuple, stdout, stderr)
2690N/A
2690N/A # If a plan was created and we're in parsable output mode then
2690N/A # parse the plan that should have been displayed to stdout.
2690N/A p_dict = None
2339N/A try:
2690N/A p_dict = json.loads(stdout)
2690N/A except ValueError, e:
2690N/A # JSON raises a subclass of ValueError when it
2690N/A # can't parse a string.
2690N/A
2690N/A e = apx.LinkedImageException(
2690N/A lin=self.child_name,
2690N/A unparsable_output=(pkg_op, stdout + stderr, e))
2690N/A rvtuple = LI_RVTuple(rv, e, None)
2690N/A return (rvtuple, stdout, stderr)
2690N/A
2690N/A p_dict["image-name"] = str(self.child_name)
2690N/A rvtuple = LI_RVTuple(rv, None, p_dict)
2690N/A return (rvtuple, stdout, stderr)
2690N/A
2690N/A def fileno(self):
2690N/A """Return the progress pipe associated with the PkgRemote
2690N/A instance that is operating on a child image."""
2690N/A return self.__pkg_remote.fileno()
2339N/A
3020N/A def child_init_root(self):
2339N/A """Our image path is being updated, so figure out our new
2339N/A child image paths. This interface only gets invoked when:
2339N/A
2339N/A - We're doing a packaging operation on a parent image and
2339N/A we've just cloned that parent to create a new BE that we're
2339N/A going to update. This clone also cloned all the children
2339N/A and so now we need to update our paths to point to the newly
2339N/A created children.
2339N/A
2339N/A - We tried to update a cloned image (as described above) and
2339N/A our update failed, hence we're changing paths back to the
2339N/A original images that were the source of the clone."""
2339N/A
3020N/A # PROP_PARENT_PATH better not be present because
3020N/A # LinkedImageChild objects are only used with push child
3020N/A # images.
3020N/A assert PROP_PARENT_PATH not in self.__props
3020N/A
3020N/A # Remove any path transform and reapply.
3020N/A self.__props = rm_dict_ent(self.__props, temporal_props)
3020N/A self.__linked.set_path_transform(self.__props,
3020N/A self.__linked.get_path_transform(),
3020N/A path=self.__props[PROP_PATH])
2339N/A
2339N/A
2339N/A# ---------------------------------------------------------------------------
2925N/A# Interfaces to obtain linked image metadata from an image
2925N/A#
2925N/Adef get_pubs(img):
2925N/A """Return publisher information for the specified image.
2925N/A
2925N/A Publisher information is returned in a sorted list of lists
2925N/A of the format:
2925N/A <publisher name>, <sticky>
2925N/A
2925N/A Where:
2925N/A <publisher name> is a string
2925N/A <sticky> is a boolean
2925N/A
2925N/A The tuples are sorted by publisher rank.
2925N/A """
2925N/A
2925N/A return [
2925N/A [str(p), p.sticky]
2925N/A for p in img.get_sorted_publishers(inc_disabled=False)
2925N/A ]
2925N/A
2925N/Adef get_packages(img, pd=None):
2925N/A """Figure out the current (or planned) list of packages in img."""
2925N/A
2925N/A ppkgs = set(img.get_catalog(img.IMG_CATALOG_INSTALLED).fmris())
2925N/A
2925N/A # if there's an image plan the we need to update the installed
2925N/A # packages based on that plan.
2925N/A if pd is not None:
2925N/A for src, dst in pd.plan_desc:
2925N/A if src == dst:
2925N/A continue
2925N/A if src:
2925N/A assert src in ppkgs
2925N/A ppkgs -= set([src])
2925N/A if dst:
2925N/A assert dst not in ppkgs
2925N/A ppkgs |= set([dst])
2925N/A
2925N/A # paranoia
2925N/A return frozenset(ppkgs)
2925N/A
2925N/Adef get_inheritable_facets(img, pd=None):
2925N/A """Get Facets from an image that a child should inherit.
2925N/A
2925N/A We only want to sync facets which affect packages that have parent
2925N/A dependencies on themselves. In practice this essentially limits us to
2925N/A "facet.version-lock.*" facets."""
2925N/A
2925N/A # get installed (or planned) parent packages and facets
2925N/A ppkgs = get_packages(img, pd=pd)
2925N/A facets = img.cfg.facets
2925N/A if pd is not None and pd.new_facets is not None:
2925N/A facets = pd.new_facets
2925N/A
2925N/A # create a packages dictionary indexed by package stem.
2925N/A ppkgs_dict = dict([
2925N/A (pfmri.pkg_name, pfmri)
2925N/A for pfmri in ppkgs
2925N/A ])
2925N/A
2925N/A #
2925N/A # iterate through all installed (or planned) package incorporation
2925N/A # dependency actions and find those that are affected by image facets.
2925N/A #
2925N/A # we don't check for package-wide facets here because they don't do
2925N/A # anything. (ie, facets defined via "set" actions in a package have
2925N/A # no effect on other actions within that package.)
2925N/A #
2925N/A faceted_deps = dict()
2925N/A cat = img.get_catalog(img.IMG_CATALOG_KNOWN)
2925N/A for pfmri in ppkgs:
2925N/A for act in cat.get_entry_actions(pfmri, [cat.DEPENDENCY]):
2925N/A # we're only interested in incorporate dependencies
2925N/A if act.name != "depend" or \
2925N/A act.attrs["type"] != "incorporate":
2925N/A continue
2925N/A
2925N/A # check if any image facets affect this dependency
2925N/A # W0212 Access to a protected member
2925N/A # pylint: disable=W0212
2925N/A matching_facets = facets._action_match(act)
2925N/A # pylint: enable=W0212
2925N/A if not matching_facets:
2925N/A continue
2925N/A
2925N/A # if all the matching facets are true we don't care
2925N/A # about the match.
2925N/A if set([i[1] for i in matching_facets]) == set([True]):
2925N/A continue
2925N/A
2925N/A # save this set of facets.
2925N/A faceted_deps[act] = matching_facets
2925N/A
2925N/A #
2925N/A # For each faceted incorporation dependency, check if it affects a
2925N/A # package that has parent dependencies on itself. This is really a
2925N/A # best effort in that we don't follow package renames or obsoletions,
2925N/A # etc.
2925N/A #
2925N/A # To limit the number of packages we inspect, we'll try to match the
2925N/A # incorporation dependency fmri targets packages by stem to packages
2925N/A # which are installed (or planned) within the parent image. This
2925N/A # allows us to quickly get a fully qualified fmri and check against a
2925N/A # package for which we have already downloaded a manifest.
2925N/A #
2925N/A # If we can't match the dependency fmri package stem against packages
2925N/A # installed (or planned) in the parent image, we don't bother
2925N/A # searching for allowable packages in the catalog, because even if we
2925N/A # found them in the catalog and they did have a parent dependency,
2925N/A # they'd all still be uninstallable in any children because there
2925N/A # would be no way to satisfy the parent dependency. (as we already
2925N/A # stated the package is not installed in the parent.)
2925N/A #
2925N/A faceted_linked_deps = dict()
2925N/A for act in faceted_deps:
2925N/A for fmri in act.attrlist("fmri"):
2958N/A pfmri = pkg.fmri.PkgFmri(fmri)
2925N/A pfmri = ppkgs_dict.get(pfmri.pkg_name, None)
2925N/A if pfmri is None:
2925N/A continue
2925N/A
2925N/A # check if this package has a dependency on itself in
2925N/A # its parent image.
2925N/A for act2 in cat.get_entry_actions(pfmri,
2925N/A [cat.DEPENDENCY]):
2925N/A if act2.name != "depend" or \
2925N/A act2.attrs["type"] != "parent":
2925N/A continue
2925N/A if pkg.actions.depend.DEPEND_SELF not in \
2925N/A act2.attrlist("fmri"):
2925N/A continue
2925N/A faceted_linked_deps[act] = faceted_deps[act]
2925N/A break
2925N/A del faceted_deps
2925N/A
2925N/A #
2925N/A # Create a set of all facets which affect incorporation dependencies
2925N/A # on synced packages.
2925N/A #
2925N/A # Note that we can't limit ourselves to only passing on facets that
2925N/A # affect dependencies which have been disabled. Doing this could lead
2925N/A # to incorrect results because facets allow for pattern matching. So
2925N/A # for example say we had the following dependencies on synced
2925N/A # packages:
2925N/A #
2925N/A # depend type=incorporation fmri=some_synced_pkg1 facet.123456=true
2925N/A # depend type=incorporation fmri=some_synced_pkg2 facet.456789=true
2925N/A #
2925N/A # and the following image facets:
2925N/A #
2925N/A # facet.123456 = True
2925N/A # facet.*456* = False
2925N/A #
2925N/A # if we only passed through facets which affected disabled packages
2925N/A # we'd just pass through "facet.*456*", but this would result in
2925N/A # disabling both dependencies above, not just the second dependency.
2925N/A #
2925N/A pfacets = pkg.facet.Facets()
2925N/A for facets in faceted_linked_deps.values():
2925N/A for k, v in facets:
2925N/A # W0212 Access to a protected member
2925N/A # pylint: disable=W0212
2925N/A pfacets._set_inherited(k, v)
2925N/A
2925N/A return pfacets
2925N/A
2925N/A# ---------------------------------------------------------------------------
2339N/A# Utility Functions
2339N/A#
3020N/Adef save_data(path, data, root="/", catch_exception=True):
2339N/A """Save JSON encoded linked image metadata to a file."""
2339N/A
2339N/A # make sure the directory we're about to save data into exists.
2339N/A path_dir = os.path.dirname(path)
2339N/A pathtmp = "%s.%d.tmp" % (path, os.getpid())
2339N/A
2339N/A try:
2339N/A if not ar.ar_exists(root, path_dir):
2339N/A ar.ar_mkdir(root, path_dir, misc.PKG_DIR_MODE)
2339N/A
2339N/A # write the output to a temporary file
2339N/A fd = ar.ar_open(root, pathtmp, os.O_WRONLY,
2339N/A mode=0644, create=True, truncate=True)
2339N/A fobj = os.fdopen(fd, "w")
2339N/A json.dump(data, fobj, encoding="utf-8",
2339N/A cls=pkg.client.linkedimage.PkgEncoder)
2339N/A fobj.close()
2339N/A
2339N/A # atomically create the desired file
2339N/A ar.ar_rename(root, pathtmp, path)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
3020N/A if catch_exception:
3020N/A raise apx._convert_error(e)
3020N/A raise e
3020N/A
3020N/Adef load_data(path, missing_ok=False, root="/", decode=True,
3020N/A catch_exception=False):
2339N/A """Load JSON encoded linked image metadata from a file."""
2339N/A
3020N/A object_hook = None
3020N/A if decode:
3020N/A object_hook = pkg.client.linkedimage.PkgDecoder
3020N/A
2339N/A try:
3020N/A if missing_ok and not path_exists(path, root=root):
2925N/A return None
3020N/A
3020N/A fd = ar.ar_open(root, path, os.O_RDONLY)
3020N/A fobj = os.fdopen(fd, "r")
2339N/A data = json.load(fobj, encoding="utf-8",
3020N/A object_hook=object_hook)
2339N/A fobj.close()
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
3020N/A if catch_exception:
3020N/A raise apx._convert_error(e)
2339N/A raise apx._convert_error(e)
2339N/A return data
2339N/A
2339N/A
2339N/Aclass PkgEncoder(json.JSONEncoder):
2339N/A """Utility class used when json encoding linked image metadata."""
2339N/A
2339N/A # E0202 An attribute inherited from JSONEncoder hide this method
2828N/A # pylint: disable=E0202
2339N/A def default(self, obj):
2339N/A """Required routine that overrides the default base
2339N/A class version. This routine must serialize 'obj' when
2339N/A attempting to save 'obj' json format."""
2339N/A
2339N/A if isinstance(obj, (pkg.fmri.PkgFmri,
2339N/A pkg.client.linkedimage.common.LinkedImageName)):
2339N/A return str(obj)
2339N/A
2339N/A if isinstance(obj, pkgplan.PkgPlan):
2339N/A return obj.getstate()
2339N/A
2339N/A if isinstance(obj, (set, frozenset)):
2339N/A return list(obj)
2339N/A
2339N/A return json.JSONEncoder.default(self, obj)
2339N/A
2339N/A
2339N/Adef PkgDecoder(dct):
2339N/A """Utility class used when json decoding linked image metadata."""
2339N/A # Replace unicode keys/values with strings
2339N/A rvdct = {}
2339N/A for k, v in dct.iteritems():
2339N/A
2339N/A # unicode must die
2339N/A if type(k) == unicode:
2339N/A k = k.encode("utf-8")
2339N/A if type(v) == unicode:
2339N/A v = v.encode("utf-8")
2339N/A
2339N/A # convert boolean strings values back into booleans
2339N/A if type(v) == str:
2339N/A if v.lower() == "true":
2339N/A v = True
2339N/A elif v.lower() == "false":
2339N/A v = False
2339N/A
2339N/A rvdct[k] = v
2339N/A return rvdct
2339N/A
2339N/Adef rm_dict_ent(d, keys):
2339N/A """Remove a set of keys from a dictionary."""
2339N/A return dict([
2339N/A (k, v)
2339N/A for k, v in d.iteritems()
2339N/A if k not in keys
2339N/A ])
2339N/A
2339N/Adef _rterr(li=None, lic=None, lin=None, path=None, err=None,
2339N/A bad_cp=None,
2339N/A bad_iup=None,
2339N/A bad_lin_type=None,
2339N/A bad_prop=None,
2339N/A missing_props=None,
3020N/A multiple_transforms=None,
2339N/A saved_temporal_props=None):
2339N/A """Oops. We hit a runtime error. Die with a nice informative
2339N/A message. Note that runtime errors should never happen and usually
2339N/A indicate bugs (or possibly corrupted linked image metadata), so they
2339N/A are not localized (just like asserts are not localized)."""
2339N/A
2339N/A assert not (li and lic)
2339N/A assert not ((lin or path) and li)
2339N/A assert not ((lin or path) and lic)
2339N/A assert path == None or type(path) == str
2339N/A
2339N/A if bad_cp:
2339N/A assert err == None
2339N/A err = "Invalid linked content policy: %s" % bad_cp
2339N/A elif bad_iup:
2339N/A assert err == None
2339N/A err = "Invalid linked image update policy: %s" % bad_iup
2339N/A elif bad_lin_type:
2339N/A assert err == None
2339N/A err = "Invalid linked image type: %s" % bad_lin_type
2339N/A elif bad_prop:
2339N/A assert err == None
2339N/A err = "Invalid linked property value: %s=%s" % bad_prop
2339N/A elif missing_props:
2339N/A assert err == None
2339N/A err = "Missing required linked properties: %s" % \
2339N/A ", ".join(missing_props)
3020N/A elif multiple_transforms:
2339N/A assert err == None
3020N/A err = "Multiple plugins reported different path transforms:"
3020N/A for plugin, transform in multiple_transforms:
3020N/A err += "\n\t%s = %s -> %s" % (plugin,
3020N/A transform[0], transform[1])
2339N/A elif saved_temporal_props:
2339N/A assert err == None
2339N/A err = "Found saved temporal linked properties: %s" % \
2339N/A ", ".join(saved_temporal_props)
2339N/A else:
2339N/A assert err != None
2339N/A
2339N/A if li:
2339N/A if li.ischild():
2339N/A lin = li.child_name
2339N/A path = li.image.root
2339N/A
2339N/A if lic:
2339N/A lin = lic.child_name
2339N/A path = lic.child_path
2339N/A
2339N/A err_prefix = "Linked image error: "
2339N/A if lin:
2339N/A err_prefix = "Linked image (%s) error: " % (str(lin))
2339N/A
2339N/A err_suffix = ""
2339N/A if path and lin:
2339N/A err_suffix = "\nLinked image (%s) path: %s" % (str(lin), path)
2339N/A elif path:
2339N/A err_suffix = "\nLinked image path: %s" % (path)
2339N/A
2339N/A raise RuntimeError(
2339N/A "%s: %s%s" % (err_prefix, err, err_suffix))
2339N/A
2339N/A# ---------------------------------------------------------------------------
2339N/A# Functions for accessing files in the current root
2339N/A#
3020N/Adef path_exists(path, root="/"):
2339N/A """Simple wrapper for accessing files in the current root."""
2339N/A
2339N/A try:
3020N/A return ar.ar_exists(root, path)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/Adef path_isdir(path):
2339N/A """Simple wrapper for accessing files in the current root."""
2339N/A
2339N/A try:
2339N/A return ar.ar_isdir("/", path)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/Adef path_mkdir(path, mode):
2339N/A """Simple wrapper for accessing files in the current root."""
2339N/A
2339N/A try:
2339N/A return ar.ar_mkdir("/", path, mode)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/Adef path_unlink(path, noent_ok=False):
2339N/A """Simple wrapper for accessing files in the current root."""
2339N/A
2339N/A try:
2339N/A return ar.ar_unlink("/", path, noent_ok=noent_ok)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2828N/A # pylint: disable=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/A# ---------------------------------------------------------------------------
2339N/A# Functions for managing images which may be in alternate roots
2339N/A#
3020N/A
3020N/Adef path_transform_applicable(path, path_transform):
3020N/A """Check if 'path_transform' can be applied to 'path'."""
3020N/A
3020N/A # Make sure path has a leading and trailing os.sep.
3020N/A assert os.path.isabs(path), "path is not absolute: %s" % path
2339N/A path = path.rstrip(os.sep) + os.sep
2339N/A
3020N/A # If there is no transform, then any any translation is valid.
3020N/A if path_transform == PATH_TRANSFORM_NONE:
3020N/A return True
3020N/A
2339N/A # check for nested or equal paths
3020N/A if path.startswith(path_transform[0]):
2339N/A return True
2339N/A return False
2339N/A
3020N/Adef path_transform_applied(path, path_transform):
3020N/A """Check if 'path_transform' has been applied to 'path'."""
3020N/A
3020N/A # Make sure path has a leading and trailing os.sep.
3020N/A assert os.path.isabs(path), "path is not absolute: %s" % path
3020N/A path = path.rstrip(os.sep) + os.sep
3020N/A
3020N/A # Reverse the transform.
3020N/A path_transform = (path_transform[1], path_transform[0])
3020N/A return path_transform_applicable(path, path_transform)
3020N/A
3020N/Adef path_transform_apply(path, path_transform):
3020N/A """Apply the 'path_transform' to 'path'."""
3020N/A
3020N/A # Make sure path has a leading and trailing os.sep.
3020N/A assert os.path.isabs(path), "path is not absolute: %s" % path
2339N/A path = path.rstrip(os.sep) + os.sep
3020N/A
3020N/A if path_transform == PATH_TRANSFORM_NONE:
3020N/A return path
3020N/A
3020N/A oroot, nroot = path_transform
3020N/A assert path_transform_applicable(path, path_transform)
3020N/A return os.path.join(nroot, path[len(oroot):])
3020N/A
3020N/Adef path_transform_revert(path, path_transform):
3020N/A """Unapply the 'path_transform' from 'path'."""
3020N/A
3020N/A # Reverse the transform.
3020N/A path_transform = (path_transform[1], path_transform[0])
3020N/A return path_transform_apply(path, path_transform)
3020N/A
3020N/Adef compute_path_transform(opath, npath):
3020N/A """Given an two paths create a transform that can be used to translate
3020N/A between them."""
3020N/A
3020N/A # Make sure all paths have a leading and trailing os.sep.
3020N/A assert os.path.isabs(opath), "opath is not absolute: %s" % opath
3020N/A assert os.path.isabs(npath), "npath is not absolute: %s" % npath
3020N/A opath = opath.rstrip(os.sep) + os.sep
3020N/A npath = npath.rstrip(os.sep) + os.sep
3020N/A
3020N/A # Remove the longest common path suffix. Do this by reversing the
3020N/A # path strings, finding the longest common prefix, removing the common
3020N/A # prefix, and reversing the paths strings again. Make sure there is a
3020N/A # trailing os.sep.
3020N/A i = 0
3020N/A opath_rev = opath[::-1]
3020N/A npath_rev = npath[::-1]
3020N/A for i in range(min(len(opath_rev), len(npath_rev))):
3020N/A if opath_rev[i] != npath_rev[i]:
3020N/A break
3020N/A oroot = opath_rev[i:][::-1].rstrip(os.sep) + os.sep
3020N/A nroot = npath_rev[i:][::-1].rstrip(os.sep) + os.sep
3020N/A
3020N/A # Old root and new root should start and end with a '/'.
3020N/A assert oroot[0] == nroot[0] == '/'
3020N/A assert oroot[-1] == nroot[-1] == '/'
3020N/A
3020N/A # Return the altroot transform tuple.
3020N/A if oroot == nroot:
3020N/A return PATH_TRANSFORM_NONE
3020N/A return (oroot, nroot)