common.py revision 2690
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#
2616N/A# Copyright (c) 2011, 2012, 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
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
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
2339N/APROP_NAME = "li-name"
2339N/APROP_ALTROOT = "li-altroot"
2339N/APROP_PARENT_PATH = "li-parent"
2339N/APROP_PATH = "li-path"
2339N/APROP_MODEL = "li-model"
2339N/APROP_RECURSE = "li-recurse"
2339N/Aprop_values = frozenset([
2339N/A PROP_ALTROOT,
2339N/A PROP_NAME,
2339N/A PROP_PATH,
2339N/A PROP_MODEL,
2339N/A PROP_PARENT_PATH,
2339N/A PROP_RECURSE,
2339N/A])
2339N/A
2339N/A# properties that never get saved
2339N/Atemporal_props = frozenset([
2339N/A PROP_ALTROOT,
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"
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
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
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
2339N/A def init_root(self, old_altroot):
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
2444N/A def get_altroot(self, ignore_errors=False):
2339N/A """If the linked image plugin is able to detect that we're
2339N/A operating on an image in an alternate root then return the
2339N/A path of the alternate root."""
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):
2339N/A """Return a list of the child images associated with the
2339N/A current image."""
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
2339N/A def __init__(self, lic):
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."""
2690N/A # Unused argument; pylint: disable-msg=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()."""
2690N/A # Unused argument; pylint: disable-msg=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
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
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
2339N/A # save the old root image path
2339N/A old_root = None
2339N/A if self.__root:
2339N/A old_root = self.__root
2339N/A
2339N/A # figure out the new root image path
2339N/A new_root = self.__img.root.rstrip(os.sep)
2339N/A if new_root == "":
2339N/A new_root = os.sep
2339N/A
2339N/A # initialize paths for linked image data files
2339N/A self.__root = new_root
2339N/A imgdir = self.__img.imgdir.rstrip(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)
2339N/A
2339N/A # if this isn't a reset, then load data from the image
2339N/A if not old_root:
2339N/A self.__load()
2339N/A
2339N/A # we're not linked or we're not changing root paths we're done
2339N/A if not old_root or not self.__props:
2339N/A return
2339N/A
2339N/A # get the old altroot directory
2339N/A old_altroot = self.altroot()
2339N/A
2339N/A # update the altroot property
2339N/A self.__set_altroot(self.__props, old_root=old_root)
2339N/A
2339N/A # Tell linked image plugins about the updated paths
2339N/A # Unused variable 'plugin'; pylint: disable-msg=W0612
2339N/A for plugin, lip in self.__plugins.iteritems():
2339N/A # pylint: enable-msg=W0612
2339N/A lip.init_root(old_altroot)
2339N/A
2339N/A # Tell linked image children about the updated paths
2690N/A for lic in self.__lic_dict.itervalues():
2339N/A lic.child_init_root(old_altroot)
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
2339N/A assert (temporal_props - set(props)) == set(), \
2339N/A "%s - %s == set()" % (temporal_props, set(props))
2339N/A
2339N/A # update state
2339N/A self.__props = props
2339N/A self.__ppkgs = frozenset()
2339N/A self.__ppubs = None
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
2339N/A def __unset_altroot(props):
2339N/A """Given a set of linked image properties, strip out any
2339N/A altroot properties. This involves removing the altroot
2339N/A component from the image path property. This is normally done
2339N/A before we write image properties to disk."""
2339N/A
2339N/A # get the current altroot
2339N/A altroot = props[PROP_ALTROOT]
2339N/A
2339N/A # remove it from the image path
2339N/A props[PROP_PATH] = rm_altroot_path(
2339N/A props[PROP_PATH], altroot)
2339N/A
2339N/A if PROP_PARENT_PATH in props:
2339N/A # remove it from the parent image path
2339N/A props[PROP_PARENT_PATH] = rm_altroot_path(
2339N/A props[PROP_PARENT_PATH], altroot)
2339N/A
2339N/A # delete the current altroot
2339N/A del props[PROP_ALTROOT]
2339N/A
2339N/A def __set_altroot(self, props, old_root=None):
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.
2339N/A We try to detect this condition here, and if this situation
2339N/A occurs we update the linked image paths to reflect the current
2339N/A image paths and we fabricate a new linked image altroot
2339N/A property that points to the new path prefix that was
2339N/A pre-pended to the image paths."""
2339N/A
2339N/A # we may have to update the parent image path as well
2339N/A p_path = None
2339N/A if PROP_PARENT_PATH in props:
2339N/A p_path = props[PROP_PARENT_PATH]
2339N/A
2339N/A if old_root:
2339N/A # get the old altroot
2339N/A altroot = props[PROP_ALTROOT]
2339N/A
2339N/A # remove the altroot from the image paths
2339N/A path = rm_altroot_path(old_root, altroot)
2339N/A if p_path:
2339N/A p_path = rm_altroot_path(p_path, altroot)
2339N/A
2339N/A # get the new altroot
2339N/A altroot = get_altroot_path(self.__root, path)
2339N/A else:
2339N/A path = props[PROP_PATH]
2339N/A altroot = get_altroot_path(self.__root, path)
2339N/A
2339N/A # update properties with altroot
2339N/A props[PROP_ALTROOT] = altroot
2339N/A props[PROP_PATH] = add_altroot_path(path, altroot)
2339N/A if p_path:
2339N/A props[PROP_PARENT_PATH] = \
2339N/A add_altroot_path(p_path, altroot)
2339N/A
2444N/A def __guess_altroot(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
2339N/A p_altroots = []
2339N/A for plugin, lip in self.__plugins.iteritems():
2444N/A p_altroot = lip.get_altroot(
2444N/A ignore_errors=ignore_errors)
2339N/A if p_altroot:
2339N/A p_altroots.append((plugin, p_altroot))
2339N/A
2339N/A if not p_altroots:
2339N/A # no altroot suggested by plugins
2339N/A return os.sep
2339N/A
2339N/A # check for conflicting altroots
2339N/A altroots = list(set([
2339N/A p_altroot
2339N/A # Unused variable; pylint: disable-msg=W0612
2339N/A for pname, p_altroot in p_altroots
2339N/A # pylint: enable-msg=W0612
2339N/A ]))
2339N/A
2339N/A if len(altroots) == 1:
2339N/A # we have an altroot from our plugins
2339N/A return altroots[0]
2339N/A
2339N/A # we have conflicting altroots, time to die
2339N/A _rterr(li=self, multiple_altroots=p_altroots)
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
2339N/A props = dict()
2339N/A props[PROP_PATH] = self.__img.root
2444N/A props[PROP_ALTROOT] = self.__guess_altroot(
2444N/A ignore_errors=ignore_errors)
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
2339N/A def __load_ondisk_ppkgs(self, tmp=True):
2339N/A """Load linked image parent constraints 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
2690N/A path = "%s.%d" % (self.__path_ppkgs,
2690N/A global_settings.client_runid)
2339N/A if tmp and path_exists(path):
2339N/A return frozenset([
2339N/A pkg.fmri.PkgFmri(str(s))
2444N/A for s in load_data(path, missing_val=misc.EmptyI)
2339N/A ])
2339N/A
2339N/A path = self.__path_ppkgs
2339N/A if path_exists(path):
2339N/A return frozenset([
2339N/A pkg.fmri.PkgFmri(str(s))
2444N/A for s in load_data(path, missing_val=misc.EmptyI)
2339N/A ])
2339N/A
2339N/A return None
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
2690N/A path = "%s.%d" % (self.__path_ppubs,
2690N/A global_settings.client_runid)
2339N/A if tmp and path_exists(path):
2339N/A return load_data(path)
2339N/A
2339N/A path = self.__path_ppubs
2339N/A if path_exists(path):
2339N/A return load_data(path)
2339N/A
2339N/A return None
2339N/A
2339N/A def __load(self):
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 #
2339N/A props = self.__load_ondisk_props()
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:
2339N/A self.__set_altroot(props)
2339N/A
2339N/A self.__update_props(props)
2339N/A
2339N/A ppkgs = self.__load_ondisk_ppkgs()
2339N/A if self.ischild() and ppkgs == None:
2339N/A _rterr(li=self, err="Constraints data missing.")
2339N/A if self.ischild():
2339N/A self.__ppkgs = ppkgs
2339N/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.
2339N/A self.__ppubs = self.__load_ondisk_ppubs()
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 altroot(self):
2339N/A """Return the altroot path prefix for the current image."""
2339N/A
2339N/A return self.__props.get(PROP_ALTROOT, os.sep)
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
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()
2339N/A li_inmemory_props = self.__props.copy()
2339N/A if li_inmemory_props:
2339N/A self.__unset_altroot(li_inmemory_props)
2339N/A li_inmemory_props = rm_dict_ent(li_inmemory_props,
2339N/A temporal_props)
2339N/A if li_ondisk_props != li_inmemory_props:
2339N/A return False
2339N/A
2339N/A # compare in-memory and on-disk constraints
2339N/A li_ondisk_ppkgs = self.__load_ondisk_ppkgs(tmp=False)
2339N/A if li_ondisk_ppkgs == None:
2339N/A li_ondisk_ppkgs = frozenset()
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
2339N/A def get_pubs(self, img=None):
2339N/A """Return publisher information for the specified image. If
2339N/A no image is specified we return publisher information for the
2339N/A current image.
2339N/A
2339N/A Publisher information is returned in a sorted list of lists
2339N/A of the format:
2339N/A <publisher name>, <sticky>
2339N/A
2339N/A Where:
2339N/A <publisher name> is a string
2339N/A <sticky> is a boolean
2339N/A
2339N/A The tuples are sorted by publisher rank.
2339N/A """
2339N/A
2339N/A # default to ourselves
2339N/A if img == None:
2339N/A img = self.__img
2339N/A
2339N/A # get a sorted list of the images publishers
2339N/A pubs = img.get_sorted_publishers(inc_disabled=False)
2339N/A
2339N/A rv = []
2339N/A for p in pubs:
2339N/A rv.append([str(p), p.sticky])
2339N/A return rv
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
2339N/A pubs = self.get_pubs()
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
2690N/A def syncmd_from_parent(self, api_op=None):
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:
2339N/A path = self.__props[PROP_PARENT_PATH]
2339N/A self.__pimg = self.__init_pimg(path)
2339N/A
2339N/A # generate new constraints
2339N/A cati = self.__pimg.get_catalog(self.__img.IMG_CATALOG_INSTALLED)
2339N/A ppkgs = frozenset(cati.fmris())
2339N/A
2339N/A # generate new publishers
2339N/A ppubs = self.get_pubs(img=self.__pimg)
2339N/A
2339N/A # check if anything has changed
2339N/A need_sync = False
2339N/A
2339N/A if self.__ppkgs != ppkgs:
2339N/A # we have new constraints
2339N/A self.__ppkgs = ppkgs
2339N/A need_sync = True
2339N/A
2339N/A if self.__ppubs != ppubs:
2339N/A # parent has new publishers
2339N/A self.__ppubs = ppubs
2339N/A need_sync = True
2339N/A
2339N/A if not need_sync:
2339N/A # nothing changed
2339N/A return
2339N/A
2339N/A # if we're not planning an image attach operation then write
2339N/A # the linked image metadata to disk.
2690N/A if api_op != pkgdefs.API_OP_ATTACH:
2339N/A self.syncmd()
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
2339N/A paths = [self.__path_ppkgs, self.__path_prop,
2339N/A self.__path_ppubs]
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
2339N/A # save our properties, but first remove altroot path prefixes
2339N/A # and any temporal properties
2339N/A props = self.__props.copy()
2339N/A self.__unset_altroot(props)
2339N/A props = rm_dict_ent(props, temporal_props)
2339N/A save_data(self.__path_prop, props)
2339N/A
2339N/A if not self.ischild():
2339N/A # if we're not a child we don't have constraints
2339N/A path_unlink(self.__path_ppkgs, noent_ok=True)
2339N/A return
2339N/A
2339N/A # we're a child so save our latest constraints
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
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
2339N/A props[PROP_ALTROOT] = self.altroot()
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
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
2339N/A li_children = [
2339N/A entry
2339N/A for p in pkg.client.linkedimage.p_types
2444N/A for entry in self.__plugins[p].get_child_list(
2444N/A ignore_errors=ignore_errors)
2339N/A ]
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
2339N/A li_self = (lin, REL_SELF, self.__props[PROP_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.
2339N/A if PROP_PARENT_PATH in self.__props:
2339N/A li_parent = (PV_NAME_NONE, REL_PARENT,
2339N/A self.__props[PROP_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 # make sure we're not linking to ourselves
2339N/A if self.__img.root == pimg.root:
2339N/A raise apx.LinkedImageException(link_to_self=True)
2339N/A
2339N/A # make sure we're not linking the root image as a child
2339N/A if self.__img.root == misc.liveroot():
2339N/A raise apx.LinkedImageException(
2339N/A attach_root_as_child=True)
2339N/A
2339N/A # get the cleaned up parent image path.
2339N/A path = pimg.root
2339N/A
2339N/A # If we're in an alternate root, the parent must also be within
2339N/A # that alternate root.
2339N/A if not check_altroot_path(path, self.altroot()):
2339N/A raise apx.LinkedImageException(
2339N/A parent_not_in_altroot=(path, self.altroot()))
2339N/A
2339N/A self.__validate_attach_props(PV_MODEL_PULL, props)
2339N/A
2339N/A # make a copy of the properties
2339N/A props = props.copy()
2339N/A props[PROP_NAME] = lin
2339N/A props[PROP_PARENT_PATH] = path
2339N/A props[PROP_PATH] = self.__img.root
2339N/A props[PROP_MODEL] = PV_MODEL_PULL
2339N/A props[PROP_ALTROOT] = self.altroot()
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
2339N/A def audit_self(self, li_parent_sync=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
2339N/A try:
2339N/A if li_parent_sync:
2339N/A # try to refresh linked image constraints from
2339N/A # the parent image.
2339N/A self.syncmd_from_parent()
2339N/A
2339N/A except apx.LinkedImageException, e:
2690N/A return LI_RVTuple(e.lix_exitrv, e, 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
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
2339N/A if not check_altroot_path(path, self.altroot()):
2339N/A raise apx.LinkedImageException(
2339N/A child_not_in_altroot=(path, self.altroot()))
2339N/A
2339N/A # path must be an image
2339N/A try:
2339N/A img_prefix = ar.ar_img_prefix(path)
2339N/A except OSError:
2339N/A raise apx.LinkedImageException(child_path_eaccess=path)
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?
2339N/A # Unused variable 'be_uuid'; pylint: disable-msg=W0612
2339N/A (be_name, be_uuid) = bootenv.BootEnv.get_be_name(self.__root)
2339N/A # pylint: enable-msg=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 # Find the common parent directory of the both parent and the
2339N/A # child image.
2339N/A dir_common = os.path.commonprefix([p_root, path])
2339N/A dir_common.rstrip(os.sep)
2339N/A
2339N/A # Make sure there are no additional images in between the
2339N/A # parent and the child. (Ie, prevent linking of images if one
2339N/A # of the images is nested within another unrelated image.)
2339N/A # This is done by looking at all the parent directories for
2339N/A # both the parent and the child image until we reach a common
2339N/A # ancestor.
2339N/A
2339N/A # First check the parent directories of the child.
2339N/A d = os.path.dirname(path.rstrip(os.sep))
2339N/A while d != dir_common and d.startswith(dir_common):
2339N/A try:
2339N/A tmp = ar.ar_img_prefix(d)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=W0212
2339N/A raise apx._convert_error(e)
2339N/A if not tmp:
2339N/A d = os.path.dirname(d)
2339N/A continue
2339N/A raise apx.LinkedImageException(child_nested=(path, d))
2339N/A
2339N/A # Then check the parent directories of the parent.
2339N/A d = os.path.dirname(p_root.rstrip(os.sep))
2339N/A while d != dir_common and d.startswith(dir_common):
2339N/A try:
2339N/A tmp = ar.ar_img_prefix(d)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=W0212
2339N/A raise apx._convert_error(e)
2339N/A if not tmp:
2339N/A d = os.path.dirname(d)
2339N/A continue
2339N/A raise apx.LinkedImageException(child_nested=(path, d))
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
2339N/A # pylint: disable-msg=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
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:
2339N/A e = apx.LinkedImageException(child_path_eaccess=path)
2690N/A return LI_RVTuple(e.lix_exitrv, e, None)
2339N/A path = os.getcwd()
2339N/A os.chdir(cwd)
2339N/A
2339N/A # make sure we're not linking to ourselves
2339N/A if self.__img.root == path:
2339N/A raise apx.LinkedImageException(link_to_self=True)
2339N/A
2339N/A # make sure we're not linking the root image as a child
2339N/A if path == misc.liveroot():
2339N/A raise apx.LinkedImageException(
2339N/A attach_root_as_child=True)
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_PATH] = path
2339N/A child_props[PROP_MODEL] = PV_MODEL_PUSH
2339N/A child_props[PROP_ALTROOT] = self.altroot()
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,
2690N/A accept=accept,
2690N/A li_attach_sync=True,
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
2690N/A def sync_children(self, lin_list, accept=False, li_attach_sync=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:
2690N/A progtrack = progress.QuietProgressTracker()
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
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,
2690N/A accept=accept,
2690N/A li_attach_sync=li_attach_sync,
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
2690N/A def detach_children(self, lin_list, force=False, noexecute=False):
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,
2690N/A _progtrack=progress.QuietProgressTracker(),
2690N/A _failfast=False,
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
2690N/A @staticmethod
2690N/A def __children_op(_pkg_op, _lic_list, _rvdict, _progtrack, _failfast,
2690N/A _expect_plan=False, _ignore_syncmd_nop=True, _pd=None,
2690N/A **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
2690N/A '_pd' a PlanDescription pointer."""
2690N/A
2690N/A if _lic_list:
2690N/A _progtrack.li_recurse_start()
2690N/A
2690N/A if _pkg_op in [ pkgdefs.PKG_OP_AUDIT_LINKED,
2690N/A pkgdefs.PKG_OP_PUBCHECK ]:
2690N/A # these operations are cheap, use full parallelism
2690N/A concurrency = -1
2690N/A else:
2690N/A concurrency = global_settings.client_concurrency
2690N/A
2690N/A # setup operation for each child
2690N/A lic_setup = []
2690N/A for lic in _lic_list:
2690N/A try:
2690N/A lic.child_op_setup(_pkg_op, _progtrack,
2690N/A _ignore_syncmd_nop, _pd, **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
2690N/A def __child_op_finish(lic, lic_list, _rvdict, _progtrack,
2690N/A _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
2690N/A (rvtuple, stdout, stderr) = lic.child_op_rv(
2690N/A _progtrack, _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,
2690N/A _rvdict, _progtrack, _failfast, _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)
2690N/A pending = len(_lic_list) - len(lic_running) - \
2690N/A done
2690N/A _progtrack.li_recurse(lic_running,
2690N/A done, pending)
2690N/A
2690N/A rlistrv = select.select(lic_running, [], [])[0]
2690N/A for lic in rlistrv:
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,
2690N/A lic_running, _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
2690N/A def api_recurse_pubcheck(self):
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,
2690N/A _progtrack=progress.QuietProgressTracker(),
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
2690N/A if pkg_op == pkgdefs.API_OP_SYNC:
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 #
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
2339N/A else:
2339N/A pkg_op = pkgdefs.PKG_OP_SYNC
2690N/A return pkg_op
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
2690N/A pd.child_op = 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
2690N/A pd.li_props = self.__props
2690N/A pd.li_ppkgs = self.__ppkgs
2690N/A pd.li_ppubs = self.__ppubs
2690N/A
2690N/A def setup_plan(self, pd):
2690N/A """Reload a previously created plan."""
2690N/A
2690N/A # load linked image state from the plan
2690N/A self.__update_props(pd.li_props)
2690N/A self.__ppubs = pd.li_ppubs
2690N/A self.__ppkgs = pd.li_ppkgs
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 @staticmethod
2339N/A def __has_parent_dep(fmri, cat, excludes):
2339N/A """Check if a package has a parent dependency."""
2339N/A
2339N/A for a in cat.get_entry_actions(fmri,
2339N/A [pkg.catalog.Catalog.DEPENDENCY], excludes=excludes):
2339N/A if a.name == "depend" and a.attrs["type"] == "parent":
2339N/A return True
2339N/A return False
2339N/A
2444N/A def extra_dep_actions(self, excludes=misc.EmptyI,
2339N/A installed_catalog=False):
2339N/A """Since we don't publish packages with parent dependencies
2339N/A yet, but we want to be able to sync packages between zones,
2339N/A we'll need to fake up some extra package parent dependencies.
2339N/A
2339N/A Here we'll inspect the catalog to find packages that we think
2339N/A should have parent dependencies and then we'll return a
2339N/A dictionary, indexed by fmri, which contains the extra
2339N/A dependency actions that should be added to each package."""
2339N/A
2339N/A # create a parent dependency action with a nonglobal zone
2339N/A # variant tag.
2339N/A attrs = dict()
2339N/A attrs["type"] = "parent"
2339N/A attrs["fmri"] = pkg.actions.depend.DEPEND_SELF
2339N/A attrs["variant.opensolaris.zone"] = "nonglobal"
2339N/A
2339N/A pda = pkg.actions.depend.DependencyAction(**attrs)
2339N/A
2339N/A if not pda.include_this(excludes):
2339N/A # we're not operating on a nonglobal zone image so we
2339N/A # don't need to fabricate parent zone dependencies
2339N/A return dict()
2339N/A
2339N/A if not self.ischild():
2339N/A # we're not a child image so parent dependencies are
2339N/A # irrelevant
2339N/A return dict()
2339N/A
2410N/A osnet_incorp = "consolidation/osnet/osnet-incorporation"
2410N/A ips_incorp = "consolidation/osnet/ips-incorporation"
2410N/A
2410N/A #
2410N/A # it's time consuming to walk the catalog looking for packages
2410N/A # to dynamically add parent dependencies too. so to speed
2410N/A # things up we'll check if the currently installed osnet and
2410N/A # ips incorporations already have parent dependencies. if
2410N/A # they do then this image has already been upgraded to a build
2410N/A # where these dependencies are being published so there's no
2410N/A # need for us to dynamically add them.
2410N/A #
2410N/A osnet_has_pdep = False
2410N/A ips_has_pdep = False
2410N/A cat = self.__img.get_catalog(self.__img.IMG_CATALOG_INSTALLED)
2410N/A for (ver, fmris) in cat.fmris_by_version(osnet_incorp):
2410N/A if self.__has_parent_dep(fmris[0], cat, excludes):
2410N/A # osnet incorporation has parent deps
2410N/A osnet_has_pdep = True
2410N/A for (ver, fmris) in cat.fmris_by_version(ips_incorp):
2410N/A if self.__has_parent_dep(fmris[0], cat, excludes):
2410N/A # ips incorporation has parent deps
2410N/A ips_has_pdep = True
2410N/A if osnet_has_pdep and ips_has_pdep:
2410N/A return dict()
2410N/A
2410N/A if not installed_catalog:
2410N/A # search the known catalog
2410N/A cat = self.__img.get_catalog(
2410N/A self.__img.IMG_CATALOG_KNOWN)
2339N/A
2339N/A # assume that the osnet and ips incorporations should always
2339N/A # have a parent dependencies.
2339N/A inc_fmris = set()
2410N/A for tgt in [osnet_incorp, ips_incorp]:
2339N/A for (ver, fmris) in cat.fmris_by_version(tgt):
2339N/A for fmri in fmris:
2339N/A if not self.__has_parent_dep(fmri, cat,
2339N/A excludes):
2339N/A inc_fmris |= set([fmri])
2339N/A
2339N/A # find the fmris that each osnet/ips incorporation incorporates
2339N/A inc_pkgs = set()
2339N/A for fmri in inc_fmris:
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") or \
2339N/A (a.attrs["type"] != "incorporate"):
2339N/A continue
2339N/A
2339N/A # create an fmri for the incorporated package
2339N/A build_release = str(fmri.version.build_release)
2339N/A inc_pkgs |= set([pkg.fmri.PkgFmri(
2339N/A a.attrs["fmri"],
2339N/A build_release=build_release)])
2339N/A
2339N/A # translate the incorporated package fmris into actual
2339N/A # packages in the known catalog
2339N/A dep_fmris = set()
2339N/A for fmri in inc_pkgs:
2339N/A for (ver, fmris) in cat.fmris_by_version(fmri.pkg_name):
2339N/A if ver == fmri.version or ver.is_successor(
2339N/A fmri.version, pkg.version.CONSTRAINT_AUTO):
2339N/A dep_fmris |= set(fmris)
2339N/A
2339N/A # all the fmris we want to add dependencies to.
2339N/A all_fmris = inc_fmris | dep_fmris
2339N/A
2369N/A # remove some unwanted fmris
2339N/A rm_fmris = set()
2339N/A for pfmri in all_fmris:
2369N/A # eliminate renamed or obsoleted fmris
2339N/A entry = cat.get_entry(pfmri)
2339N/A state = entry["metadata"]["states"]
2616N/A if pkgdefs.PKG_STATE_OBSOLETE in state or \
2616N/A pkgdefs.PKG_STATE_RENAMED in state:
2339N/A rm_fmris |= set([pfmri])
2369N/A continue
2369N/A
2369N/A # eliminate any group packages
2369N/A if pfmri.pkg_name.startswith("group/"):
2369N/A rm_fmris |= set([pfmri])
2369N/A continue
2369N/A
2339N/A all_fmris -= rm_fmris
2339N/A
2339N/A return dict([(fmri, [pda]) for fmri in all_fmris])
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)
2339N/A except OSError:
2339N/A raise apx.LinkedImageException(
2339N/A lin=lin, child_path_eaccess=self.child_path)
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)
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."""
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
2690N/A @staticmethod
2690N/A def __push_data(root, path, data, tmp, test):
2339N/A """Write data to a child image."""
2339N/A
2339N/A # first save our data to a temporary file
2690N/A path_tmp = "%s.%s" % (path, global_settings.client_runid)
2339N/A save_data(path_tmp, data, root=root)
2339N/A
2339N/A # check if we're updating the data
2339N/A updated = True
2339N/A
2339N/A try:
2339N/A exists = ar.ar_exists(root, path)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/A if exists:
2339N/A try:
2339N/A updated = ar.ar_diff(root, path, path_tmp)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/A # if we're not actually updating any data, or if we were just
2339N/A # doing a test to see if the data has changed, then delete the
2339N/A # temporary data file
2339N/A if not updated or test:
2339N/A ar.ar_unlink(root, path_tmp)
2339N/A return updated
2339N/A
2339N/A if not tmp:
2339N/A # we are updating the real data.
2339N/A try:
2339N/A ar.ar_rename(root, path_tmp, path)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/A return True
2339N/A
2690N/A def __push_ppkgs(self, tmp=False, test=False, pd=None):
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
2339N/A # there has to be an image plan to export
2339N/A cati = self.__img.get_catalog(self.__img.IMG_CATALOG_INSTALLED)
2339N/A ppkgs = set(cati.fmris())
2339N/A
2690N/A if pd is not None:
2339N/A # if there's an image plan the we need to update the
2339N/A # installed packages based on that plan.
2690N/A for src, dst in pd.plan_desc:
2339N/A if src == dst:
2339N/A continue
2339N/A if src:
2339N/A assert src in ppkgs
2339N/A ppkgs -= set([src])
2339N/A if dst:
2339N/A assert dst not in ppkgs
2339N/A ppkgs |= set([dst])
2339N/A
2339N/A # paranoia
2339N/A ppkgs = frozenset(ppkgs)
2339N/A
2339N/A # save the planned cips
2339N/A return self.__push_data(self.child_path, self.__path_ppkgs,
2339N/A ppkgs, tmp, test)
2339N/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
2339N/A return self.__push_data(self.child_path, self.__path_prop,
2339N/A props, tmp, test)
2339N/A
2339N/A def __push_ppubs(self, 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 ppubs = self.__linked.get_pubs()
2339N/A return self.__push_data(self.child_path, self.__path_ppubs,
2339N/A ppubs, tmp, test)
2339N/A
2690N/A def __syncmd(self, tmp=False, test=False, pd=None):
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
2690N/A if pd:
2339N/A tmp = True
2339N/A
2690N/A ppkgs_updated = self.__push_ppkgs(tmp, test, pd=pd)
2339N/A props_updated = self.__push_props(tmp, test)
2339N/A pubs_updated = self.__push_ppubs(tmp, test)
2339N/A
2339N/A return (props_updated or ppkgs_updated or pubs_updated)
2339N/A
2690N/A def __child_op_setup_syncmd(self, ignore_syncmd_nop=True,
2690N/A tmp=False, test=False, pd=None, 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
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 'pd' an optional plan description object. this plan
2690N/A description describes changes that will be made to the parent
2690N/A image. if this is supplied then we derive the meta data that
2690N/A we write into the child from the planned parent image state
2690N/A (instead of the current parent image state).
2690N/A
2690N/A 'stage' indicates which stage of execution we should be
2690N/A performing on a child image."""
2690N/A
2690N/A # we don't actually update metadata during other stages of
2690N/A # 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:
2690N/A updated = self.__syncmd(tmp=tmp, test=test, pd=pd)
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
2690N/A def __child_setup_sync(self, _progtrack, _ignore_syncmd_nop, _pd,
2690N/A accept=False,
2690N/A li_attach_sync=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 'li_attach_sync' indicates if this sync is part of an attach
2444N/A operation.
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 #
2690N/A assert not _pd
2690N/A if not self.__child_op_setup_syncmd(
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 #
2690N/A assert not li_attach_sync or _pd is None
2690N/A if not self.__child_op_setup_syncmd(
2690N/A ignore_syncmd_nop=_ignore_syncmd_nop,
2690N/A tmp=li_attach_sync, stage=stage, pd=_pd):
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=[],
2690N/A parsable_version=_progtrack.parsable_version,
2690N/A quiet=_progtrack.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,
2690N/A verbose=_progtrack.verbose)
2690N/A
2690N/A def __child_setup_update(self, _progtrack, _ignore_syncmd_nop, _pd,
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
2690N/A if not self.__child_op_setup_syncmd(
2690N/A ignore_syncmd_nop=_ignore_syncmd_nop, pd=_pd, 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=[],
2690N/A parsable_version=_progtrack.parsable_version,
2690N/A quiet=_progtrack.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,
2690N/A verbose=_progtrack.verbose)
2690N/A
2690N/A def __child_setup_detach(self, _progtrack, 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,
2690N/A li_target_all=False,
2690N/A li_target_list=[],
2690N/A noexecute=noexecute,
2690N/A quiet=_progtrack.quiet,
2690N/A verbose=_progtrack.verbose)
2690N/A
2690N/A def __child_setup_pubcheck(self):
2690N/A """Prepare to a check if a child's publishers are in sync."""
2690N/A
2690N/A # first sync the metadata
2690N/A if not self.__child_op_setup_syncmd():
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
2690N/A def __child_setup_audit(self):
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
2690N/A if not self.__child_op_setup_syncmd():
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
2690N/A def child_op_setup(self, _pkg_op, _progtrack, _ignore_syncmd_nop, _pd,
2690N/A **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:
2690N/A self.__child_setup_audit(**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:
2690N/A self.__child_setup_pubcheck(**kwargs)
2690N/A elif _pkg_op == pkgdefs.PKG_OP_SYNC:
2690N/A self.__child_setup_sync(_progtrack,
2690N/A _ignore_syncmd_nop, _pd, **kwargs)
2690N/A elif _pkg_op == pkgdefs.PKG_OP_UPDATE:
2690N/A self.__child_setup_update(_progtrack,
2690N/A _ignore_syncmd_nop, _pd, **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
2690N/A def child_op_rv(self, progtrack, expect_plan):
2690N/A """Public interface to get the result of an operation on a
2690N/A child image.
2690N/A
2690N/A 'progtrack' progress tracker associated with this operation
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
2690N/A (pkg_op, 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
2690N/A if progtrack.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
2339N/A def child_init_root(self, old_altroot):
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
2339N/A # get the image path without the altroot
2339N/A altroot_path = self.__props[PROP_PATH]
2339N/A path = rm_altroot_path(altroot_path, old_altroot)
2339N/A
2339N/A # update the path with the current altroot
2339N/A altroot = self.__linked.altroot()
2339N/A path = add_altroot_path(path, altroot)
2339N/A
2339N/A # update properties with altroot
2339N/A self.__props[PROP_PATH] = path
2339N/A self.__props[PROP_ALTROOT] = altroot
2339N/A
2339N/A # we don't bother to update update PROP_PARENT_PATH since
2339N/A # that is only used when reading constraint data from the
2339N/A # parent image, and this interface is only invoked when we're
2339N/A # starting or finishing execution of a plan on a cloned image
2339N/A # (at which point we have no need to access the parent
2339N/A # anymore).
2339N/A
2339N/A
2339N/A# ---------------------------------------------------------------------------
2339N/A# Utility Functions
2339N/A#
2339N/Adef save_data(path, data, root="/"):
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
2339N/A # pylint: disable-msg=W0212
2339N/A raise apx._convert_error(e)
2339N/A
2339N/Adef load_data(path, missing_val=None):
2339N/A """Load JSON encoded linked image metadata from a file."""
2339N/A
2339N/A try:
2339N/A if (missing_val != None) and not path_exists(path):
2339N/A return missing_val
2339N/A fobj = open(path)
2339N/A data = json.load(fobj, encoding="utf-8",
2339N/A object_hook=pkg.client.linkedimage.PkgDecoder)
2339N/A fobj.close()
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=W0212
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
2339N/A # pylint: disable-msg=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,
2339N/A multiple_altroots=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)
2339N/A elif multiple_altroots:
2339N/A assert err == None
2339N/A err = "Multiple plugins reported different altroots:"
2339N/A for plugin, altroot in multiple_altroots:
2339N/A err += "\n\t%s = %s" % (plugin, altroot)
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#
2339N/Adef path_exists(path):
2339N/A """Simple wrapper for accessing files in the current root."""
2339N/A
2339N/A try:
2339N/A return ar.ar_exists("/", path)
2339N/A except OSError, e:
2339N/A # W0212 Access to a protected member
2339N/A # pylint: disable-msg=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
2339N/A # pylint: disable-msg=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
2339N/A # pylint: disable-msg=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
2339N/A # pylint: disable-msg=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#
2339N/Adef check_altroot_path(path, altroot):
2339N/A """Check if 'path' is nested within 'altroot'"""
2339N/A
2339N/A assert os.path.isabs(path), "os.path.isabs(%s)" % path
2339N/A assert os.path.isabs(altroot), "os.path.isabs(%s)" % altroot
2339N/A
2339N/A # make sure both paths have one trailing os.sep.
2339N/A altroot = altroot.rstrip(os.sep) + os.sep
2339N/A path = path.rstrip(os.sep) + os.sep
2339N/A
2339N/A # check for nested or equal paths
2339N/A if path.startswith(altroot):
2339N/A return True
2339N/A return False
2339N/A
2339N/Adef add_altroot_path(path, altroot):
2339N/A """Return a path where 'path' is nested within 'altroot'"""
2339N/A
2339N/A assert os.path.isabs(path), "os.path.isabs(%s)" % path
2339N/A assert os.path.isabs(altroot), "os.path.isabs(%s)" % altroot
2339N/A
2339N/A altroot = altroot.rstrip(os.sep) + os.sep
2339N/A path = path.lstrip(os.sep)
2339N/A altroot_path = altroot + path
2339N/A
2339N/A # sanity check
2339N/A assert check_altroot_path(altroot_path, altroot), \
2339N/A "check_altroot_path(%s, %s)" % (altroot_path, altroot)
2339N/A
2339N/A return altroot_path
2339N/A
2339N/Adef rm_altroot_path(path, altroot):
2339N/A """Return the relative porting of 'path', which must be nested within
2339N/A 'altroot'"""
2339N/A
2339N/A assert os.path.isabs(path), "not os.path.isabs(%s)" % path
2339N/A assert os.path.isabs(altroot), "not os.path.isabs(%s)" % altroot
2339N/A
2339N/A assert check_altroot_path(path, altroot), \
2339N/A "not check_altroot_path(%s, %s)" % (path, altroot)
2339N/A
2339N/A rv = path[len(altroot.rstrip(os.sep)):]
2339N/A if rv == "":
2339N/A rv = "/"
2339N/A assert os.path.isabs(rv)
2339N/A return rv
2339N/A
2339N/Adef get_altroot_path(path, path_suffix):
2339N/A """Given 'path', and a relative path 'path_suffix' that must match
2339N/A the suffix of 'path', return the unmatched prefix of 'path'."""
2339N/A
2339N/A assert os.path.isabs(path), "os.path.isabs(%s)" % path
2339N/A assert os.path.isabs(path_suffix), "os.path.isabs(%s)" % path_suffix
2339N/A
2339N/A # make sure both paths have one trailing os.sep.
2339N/A path = path.rstrip(os.sep) + os.sep
2339N/A path_suffix = path_suffix.rstrip(os.sep) + os.sep
2339N/A
2339N/A i = path.rfind(path_suffix)
2339N/A if i <= 0:
2339N/A # path and path_suffix are either unrelated or equal
2339N/A altroot = os.sep
2339N/A else:
2339N/A altroot = path[:i]
2339N/A
2339N/A # sanity check
2339N/A assert check_altroot_path(path, altroot), \
2339N/A "check_altroot_path(%s, %s)" % (path, altroot)
2339N/A
2339N/A return altroot