common.py revision 2616
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#
2339N/A# Too many lines in module; pylint: disable-msg=C0302
2339N/A#
2339N/A
2339N/A# standard python classes
2339N/Aimport operator
2339N/Aimport os
2339N/Aimport simplejson as json
2339N/Aimport sys
2410N/Aimport tempfile
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
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/Afrom pkg.client.debugvalues import DebugValues
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
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
2339N/A # return value: tuple:
2339N/A # (pkgdefs EXIT_* return value, exception object or None)
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
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):
2339N/A return not self.__eq__(self, 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 # Too many instance attributes; pylint: disable-msg=R0902
2339N/A # Too many public methods; pylint: disable-msg=R0904
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
2339N/A # variables reset by self.reset_recurse()
2339N/A self.__lic_list = []
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 self.reset_recurse()
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
2339N/A for lic in self.__lic_list:
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
2339N/A path_tmp = "%s.%d" % (self.__path_prop, self.__img.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
2339N/A path = "%s.%d" % (self.__path_ppkgs, self.__img.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
2339N/A path = "%s.%d" % (self.__path_ppubs, self.__img.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
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 runid=self.__img.runid,
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
2339N/A def check_pubs(self, op):
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 if op in [pkgdefs.API_OP_DETACH]:
2339N/A # we don't need to do a pubcheck for detach
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
2339N/A def syncmd_from_parent(self, 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
2339N/A # initalize 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.
2339N/A if 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:
2339N/A path = "%s.%d" % (path, self.__img.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
2339N/A def isparent(self):
2339N/A """Indicates whether the current image is a parent image."""
2339N/A
2444N/A return self.__isparent()
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
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 ]
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():
2500N/A return (pkgdefs.EXIT_OOPS, self.__apx_not_child(), 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:
2500N/A return (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)
2500N/A return (pkgdefs.EXIT_DIVERGED, e, None)
2500N/A
2500N/A return (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
2339N/A assert not rvdict or type(rvdict) == dict
2500N/A for k, (rv, err, p_dict) in rvdict.iteritems():
2339N/A assert type(k) == LinkedImageName
2339N/A assert type(rv) == int
2339N/A assert err is None or \
2339N/A isinstance(err, apx.LinkedImageException)
2500N/A assert p_dict is None or isinstance(p_dict, dict)
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:
2500N/A return (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 = [
2500N/A p_dict for (rv, e, p_dict) in rvdict.itervalues()
2500N/A if p_dict is not None
2500N/A ]
2500N/A
2339N/A rv_mapped = set()
2500N/A rv_seen = set([rv for (rv, e, p_dict) in rvdict.itervalues()])
2339N/A for (rv_map_set, rv_map_rv) in rv_map:
2339N/A if (rv_seen == rv_map_set):
2500N/A return (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 = [
2339N/A e
2500N/A for (rv, e, p_dict) in rvdict.itervalues()
2339N/A if e and rv not in rv_mapped
2339N/A ]
2339N/A if 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
2500N/A return (list(rv_seen)[0], err, p_dicts)
2500N/A
2500N/A return (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 # Too many arguments; pylint: disable-msg=R0913
2339N/A # Too many return statements; pylint: disable-msg=R0911
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)
2500N/A return (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)
2500N/A return (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)
2500N/A return (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:
2500N/A return (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
2500N/A return (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:
2500N/A return (e.lix_exitrv, e, None)
2500N/A
2500N/A rv, e, p_dict = self.__sync_child(lic,
2339N/A accept=accept, li_attach_sync=True, li_md_only=li_md_only,
2339N/A li_pkg_updates=li_pkg_updates, noexecute=noexecute,
2500N/A progtrack=progtrack,
2500N/A refresh_catalogs=refresh_catalogs, reject_list=reject_list,
2500N/A show_licenses=show_licenses, update_index=update_index)
2339N/A
2339N/A assert isinstance(e, (type(None), apx.LinkedImageException))
2339N/A
2339N/A if rv not in [pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP]:
2500N/A return (rv, e, p_dict)
2339N/A
2339N/A if noexecute:
2339N/A # if noexecute then we're done
2500N/A return (pkgdefs.EXIT_OK, None, p_dict)
2339N/A
2339N/A # save child image properties
2339N/A rv, e = lip.sync_children_todisk()
2339N/A assert isinstance(e, (type(None), apx.LinkedImageException))
2339N/A if e:
2500N/A return (pkgdefs.EXIT_OOPS, e, p_dict)
2339N/A
2339N/A # save parent image properties
2339N/A self.syncmd()
2339N/A
2500N/A return (pkgdefs.EXIT_OK, None, p_dict)
2339N/A
2339N/A def audit_children(self, lin_list, **kwargs):
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
2339N/A return self.__children_op(lin_list,
2339N/A self.__audit_child, **kwargs)
2339N/A
2339N/A def sync_children(self, lin_list, **kwargs):
2339N/A """Sync one or more children of the current image."""
2339N/A
2339N/A return self.__children_op(lin_list,
2339N/A self.__sync_child, **kwargs)
2339N/A
2339N/A def detach_children(self, lin_list, **kwargs):
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
2339N/A # get parameter meant for __detach_child()
2339N/A force = noexecute = False
2339N/A if "force" in kwargs:
2339N/A force = kwargs["force"]
2339N/A if "noexecute" in kwargs:
2339N/A noexecute = kwargs["noexecute"]
2339N/A
2339N/A # expand lin_list before calling __detach_child()
2339N/A if not lin_list:
2339N/A lin_list = [i[0] for i in self.__list_children()]
2339N/A
2339N/A rvdict = self.__children_op(lin_list,
2339N/A self.__detach_child, **kwargs)
2339N/A
2339N/A for lin in lin_list:
2339N/A # if the detach failed leave metadata in parent
2339N/A # Unused variable 'rv'; pylint: disable-msg=W0612
2500N/A rv, e, p_dict = rvdict[lin]
2339N/A # pylint: enable-msg=W0612
2339N/A assert e == None or \
2339N/A (isinstance(e, apx.LinkedImageException))
2339N/A if 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
2339N/A if not noexecute:
2339N/A # sync out the fact that we detached the child
2339N/A rv2, e2 = lip.sync_children_todisk()
2339N/A assert e2 == None or \
2339N/A (isinstance(e2, apx.LinkedImageException))
2339N/A if not e:
2339N/A # don't overwrite previous errors
2500N/A rvdict[lin] = (rv2, e2, p_dict)
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
2339N/A def __children_op(self, lin_list, op, **kwargs):
2339N/A """Perform a linked image operation on multiple children."""
2339N/A
2339N/A assert type(lin_list) == list
2339N/A assert type(kwargs) == dict
2339N/A assert "lin" not in kwargs
2339N/A assert "lic" not in kwargs
2339N/A
2339N/A if not lin_list:
2339N/A lin_list = [i[0] for i in self.__list_children()]
2339N/A
2339N/A rvdict = dict()
2339N/A for lin in lin_list:
2339N/A try:
2339N/A lic = LinkedImageChild(self, lin)
2339N/A
2339N/A # perform the requested operation
2444N/A rvdict[lin] = op(lic, **kwargs)
2339N/A
2339N/A # Unused variable; pylint: disable-msg=W0612
2500N/A rv, e, p_dict = rvdict[lin]
2339N/A # pylint: enable-msg=W0612
2339N/A assert e == None or \
2339N/A (isinstance(e, apx.LinkedImageException))
2339N/A
2339N/A except apx.LinkedImageException, e:
2500N/A rvdict[lin] = (e.lix_exitrv, e, None)
2339N/A
2339N/A return rvdict
2339N/A
2339N/A @staticmethod
2444N/A def __audit_child(lic):
2339N/A """Recurse into a child image and audit it."""
2339N/A return lic.child_audit()
2339N/A
2339N/A @staticmethod
2444N/A def __sync_child(lic, **kwargs):
2339N/A """Recurse into a child image and sync it."""
2444N/A return lic.child_sync(**kwargs)
2444N/A
2444N/A def __detach_child(self, lic, force=False, noexecute=False,
2339N/A progtrack=None):
2339N/A """Recurse into a child image and detach it."""
2339N/A
2444N/A lin = lic.child_name
2339N/A lip = self.__plugins[lin.lin_type]
2339N/A if not force and not lip.support_detach:
2339N/A # we can't detach this type of image.
2339N/A e = apx.LinkedImageException(
2339N/A detach_child_notsup=lin.lin_type)
2500N/A return (pkgdefs.EXIT_OOPS, e, None)
2339N/A
2339N/A # remove linked data from the child
2339N/A return lic.child_detach(noexecute=noexecute,
2339N/A progtrack=progtrack)
2339N/A
2339N/A def reset_recurse(self):
2339N/A """Reset all child recursion state."""
2339N/A
2339N/A self.__lic_list = []
2339N/A
2500N/A def init_recurse(self, op, li_ignore, accept,
2500N/A refresh_catalogs, update_index, args):
2339N/A """When planning changes on a parent image, prepare to
2339N/A recurse into all child images and operate on them as well."""
2339N/A
2500N/A # Too many arguments; pylint: disable-msg=R0913
2500N/A
2339N/A if op == pkgdefs.API_OP_DETACH:
2339N/A # we don't need to recurse for these operations
2339N/A self.__lic_list = []
2339N/A return
2339N/A
2339N/A if PROP_RECURSE in self.__props and \
2339N/A not self.__props[PROP_RECURSE]:
2339N/A # don't bother to recurse into children
2339N/A self.__lic_list = []
2339N/A return
2339N/A
2339N/A self.__lic_list = []
2339N/A # Unused variable 'path'; pylint: disable-msg=W0612
2339N/A for (lin, path) in self.__list_children(li_ignore):
2339N/A # pylint: enable-msg=W0612
2339N/A self.__lic_list.append(LinkedImageChild(self, lin))
2339N/A
2339N/A if not self.__lic_list:
2339N/A # no child images to recurse into
2339N/A return
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 #
2401N/A if op == pkgdefs.API_OP_UPDATE and not args["pkgs_update"]:
2339N/A pkg_op = pkgdefs.PKG_OP_UPDATE
2339N/A else:
2339N/A pkg_op = pkgdefs.PKG_OP_SYNC
2339N/A
2339N/A for lic in self.__lic_list:
2339N/A lic.child_init_recurse(pkg_op, accept,
2500N/A refresh_catalogs, update_index,
2500N/A args)
2339N/A
2339N/A def do_recurse(self, stage, ip=None):
2339N/A """When planning changes within a parent image, recurse into
2339N/A all child images and operate on them as well."""
2339N/A
2339N/A assert stage in pkgdefs.api_stage_values
2339N/A assert stage != pkgdefs.API_STAGE_DEFAULT
2339N/A
2500N/A res = []
2339N/A for lic in self.__lic_list:
2500N/A res.append(lic.child_do_recurse(stage=stage, ip=ip))
2500N/A return res
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
2339N/A for lic in self.__lic_list:
2339N/A if not lic.child_nothingtodo():
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 # Used * or ** magic; pylint: disable-msg=W0142
2339N/A pda = pkg.actions.depend.DependencyAction(**attrs)
2339N/A # pylint: enable-msg=W0142
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 # Too many instance attributes; pylint: disable-msg=R0902
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
2339N/A # variables reset by self.child_reset_recurse()
2339N/A self.__r_op = None
2339N/A self.__r_args = None
2339N/A self.__r_progtrack = None
2339N/A self.__r_rv_nop = False
2339N/A self.child_reset_recurse()
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
2339N/A def __push_data(self, 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
2339N/A path_tmp = "%s.%s" % (path, self.__img.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
2339N/A def __push_ppkgs(self, tmp=False, test=False, ip=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
2339N/A if ip != None and ip.plan_desc:
2339N/A # if there's an image plan the we need to update the
2339N/A # installed packages based on that plan.
2339N/A for src, dst in ip.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
2339N/A def __syncmd(self, tmp=False, test=False, ip=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
2339N/A if ip:
2339N/A tmp = True
2339N/A
2339N/A ppkgs_updated = self.__push_ppkgs(tmp, test, ip=ip)
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
2339N/A @staticmethod
2339N/A def __flush_output():
2339N/A """We flush stdout and stderr before and after operating on
2339N/A child images to avoid any out-of-order output problems that
2339N/A could be caused by caching of output."""
2339N/A
2339N/A try:
2339N/A sys.stdout.flush()
2339N/A except IOError:
2339N/A pass
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 try:
2339N/A sys.stderr.flush()
2339N/A except IOError:
2339N/A pass
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 def __pkg_cmd(self, pkg_op, pkg_args, stage=None, progtrack=None):
2339N/A """Perform a pkg(1) operation on a child image."""
2339N/A
2339N/A if stage == None:
2339N/A stage = pkgdefs.API_STAGE_DEFAULT
2339N/A assert stage in pkgdefs.api_stage_values
2339N/A
2339N/A #
2339N/A # Build up a command line to execute. Note that we take care
2339N/A # to try to run the exact same pkg command that we were
2339N/A # executed with. We do this because pkg commonly tries to
2339N/A # access the image that the command is being run from.
2339N/A #
2339N/A pkg_bin = "pkg"
2339N/A cmdpath = self.__img.cmdpath
2339N/A if cmdpath and os.path.basename(cmdpath) == "pkg":
2339N/A try:
2339N/A # check if the currently running pkg command
2339N/A # exists and is accessible.
2339N/A os.stat(cmdpath)
2339N/A pkg_bin = cmdpath
2339N/A except OSError:
2339N/A pass
2339N/A
2339N/A pkg_cmd = [
2339N/A pkg_bin,
2339N/A "-R", str(self.child_path),
2339N/A "--runid=%s" % self.__img.runid,
2339N/A ]
2339N/A
2339N/A # propagate certain debug options
2339N/A for k in [
2339N/A "broken-conflicting-action-handling",
2339N/A "disp_linked_cmds",
2339N/A "plan"]:
2339N/A if DebugValues[k]:
2339N/A pkg_cmd.append("-D")
2339N/A pkg_cmd.append("%s=1" % k)
2339N/A
2339N/A # add the subcommand argument
2339N/A pkg_cmd.append(pkg_op)
2339N/A
2339N/A # propagate stage option
2339N/A if stage != pkgdefs.API_STAGE_DEFAULT:
2339N/A pkg_cmd.append("--stage=%s" % stage)
2339N/A
2339N/A # add the subcommand argument options
2339N/A pkg_cmd.extend(pkg_args)
2339N/A
2339N/A if progtrack:
2339N/A progtrack.li_recurse_start(self.child_name)
2339N/A
2339N/A # flush all output before recursing into child
2339N/A self.__flush_output()
2339N/A
2339N/A disp_linked_cmds = DebugValues.get_value("disp_linked_cmds")
2339N/A if not disp_linked_cmds and \
2339N/A "PKG_DISP_LINKED_CMDS" in os.environ:
2339N/A disp_linked_cmds = True
2500N/A pv_in_args = False
2500N/A for a in pkg_args:
2500N/A if a.startswith("--parsable="):
2500N/A pv_in_args = True
2500N/A # If we're using --parsable, don't emit the child cmd
2500N/A # information as info because it will confuse the JSON parser.
2500N/A if disp_linked_cmds and not pv_in_args:
2339N/A logger.info("child cmd: %s" % " ".join(pkg_cmd))
2339N/A else:
2339N/A logger.debug("child cmd: %s" % " ".join(pkg_cmd))
2339N/A
2410N/A #
2410N/A # Start the operation on the child. let the child have direct
2410N/A # access to stdout but capture stderr.
2410N/A #
2444N/A ferrout = tempfile.TemporaryFile()
2500N/A # If we're using --parsable, then we need to capture stdout so
2500N/A # that we can parse the plan of the child image and include it
2500N/A # in our plan.
2500N/A outloc = None
2500N/A if pv_in_args:
2500N/A outloc = tempfile.TemporaryFile()
2339N/A try:
2500N/A p = pkg.pkgsubprocess.Popen(pkg_cmd, stderr=ferrout,
2500N/A stdout=outloc)
2339N/A p.wait()
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
2410N/A # flush output generated by the child
2339N/A self.__flush_output()
2339N/A
2410N/A # get error output generated by the child
2444N/A ferrout.seek(0)
2444N/A errout = "".join(ferrout.readlines())
2410N/A
2339N/A if progtrack:
2339N/A progtrack.li_recurse_end(self.child_name)
2339N/A
2500N/A p_dict = None
2500N/A # A parsable plan is only displayed if the operation was
2500N/A # successful and the stage was default or plan.
2500N/A if pv_in_args and stage in (pkgdefs.API_STAGE_PLAN,
2500N/A pkgdefs.API_STAGE_DEFAULT) and p.returncode == 0:
2500N/A outloc.seek(0)
2500N/A output = outloc.read()
2500N/A try:
2500N/A p_dict = json.loads(output)
2500N/A except ValueError, e:
2500N/A # JSON raises a subclass of ValueError when it
2500N/A # can't parse a string.
2500N/A raise apx.UnparsableJSON(output, e)
2500N/A p_dict["image-name"] = str(self.child_name)
2500N/A
2500N/A return (p.returncode, errout, p_dict)
2339N/A
2339N/A def child_detach(self, noexecute=False, progtrack=None):
2339N/A """Detach a child image."""
2339N/A
2339N/A # When issuing a detach from a prent we must always use the
2339N/A # force flag. (Normally a child will refuse to detach from a
2339N/A # parent unless it attached to the parent, which is never the
2339N/A # case here.)
2339N/A pkg_args = ["-f"]
2339N/A pkg_args.extend(["-v"] * progtrack.verbose)
2339N/A if progtrack.quiet:
2339N/A pkg_args.append("-q")
2339N/A if noexecute:
2339N/A pkg_args.append("-n")
2339N/A
2500N/A rv, errout, p_dict = self.__pkg_cmd(pkgdefs.PKG_OP_DETACH,
2339N/A pkg_args)
2339N/A
2339N/A # if the detach command ran, return its status.
2339N/A if rv in [pkgdefs.EXIT_OK, pkgdefs.EXIT_NOPARENT]:
2500N/A return (pkgdefs.EXIT_OK, None, p_dict)
2339N/A
2410N/A e = apx.LinkedImageException(lin=self.child_name, exitrv=rv,
2444N/A pkg_op_failed=(pkgdefs.PKG_OP_DETACH, rv, errout))
2500N/A return (rv, e, p_dict)
2339N/A
2339N/A def child_audit(self):
2339N/A """Audit a child image to see if it's in sync with its
2339N/A constraints."""
2339N/A
2339N/A # first sync our metadata
2339N/A self.__syncmd()
2339N/A
2339N/A # recurse into the child image
2339N/A pkg_args = ["-q"]
2339N/A
2500N/A rv, errout, p_dict = self.__pkg_cmd(pkgdefs.PKG_OP_AUDIT_LINKED,
2339N/A pkg_args)
2339N/A
2339N/A # if the audit command ran, return its status.
2339N/A if rv in [pkgdefs.EXIT_OK, pkgdefs.EXIT_DIVERGED]:
2500N/A return (rv, None, p_dict)
2339N/A
2339N/A # something went unexpectedly wrong.
2410N/A e = apx.LinkedImageException(lin=self.child_name, exitrv=rv,
2444N/A pkg_op_failed=(pkgdefs.PKG_OP_AUDIT_LINKED, rv, errout))
2500N/A return (rv, e, p_dict)
2339N/A
2339N/A def child_sync(self, accept=False, li_attach_sync=False,
2339N/A li_md_only=False, li_pkg_updates=True, progtrack=None,
2444N/A noexecute=False, refresh_catalogs=True, reject_list=misc.EmptyI,
2444N/A show_licenses=False, update_index=True):
2339N/A """Try to bring a child image into sync with its
2444N/A constraints.
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 # Too many arguments; pylint: disable-msg=R0913
2339N/A
2339N/A if li_md_only:
2339N/A # we're not going to recurse into the child image,
2339N/A # we're just going to update its metadata.
2339N/A try:
2339N/A updated = self.__syncmd(test=noexecute)
2339N/A except apx.LinkedImageException, e:
2500N/A return (e.lix_exitrv, e, None)
2339N/A
2339N/A if updated:
2500N/A return (pkgdefs.EXIT_OK, None, None)
2339N/A else:
2500N/A return (pkgdefs.EXIT_NOP, None, None)
2339N/A
2339N/A # first sync the metadata
2339N/A try:
2339N/A # if we're doing this sync as part of an attach, then
2339N/A # temporarily sync the metadata since we don't know
2339N/A # yet if the attach will succeed. if the attach
2339N/A # doesn't succeed this means we don't have to delete
2339N/A # any metadata. if the attach succeeds the child will
2339N/A # make the temporary metadata permanent as part of the
2339N/A # commit.
2339N/A self.__syncmd(tmp=li_attach_sync)
2339N/A except apx.LinkedImageException, e:
2500N/A return (e.lix_exitrv, e, None)
2339N/A
2339N/A pkg_args = []
2339N/A pkg_args.extend(["-v"] * progtrack.verbose)
2339N/A if progtrack.quiet:
2339N/A pkg_args.append("-q")
2339N/A if noexecute:
2339N/A pkg_args.append("-n")
2339N/A if accept:
2339N/A pkg_args.append("--accept")
2339N/A if show_licenses:
2339N/A pkg_args.append("--licenses")
2339N/A if not refresh_catalogs:
2339N/A pkg_args.append("--no-refresh")
2339N/A if not update_index:
2339N/A pkg_args.append("--no-index")
2339N/A if not li_pkg_updates:
2339N/A pkg_args.append("--no-pkg-updates")
2500N/A if progtrack.parsable_version is not None:
2500N/A assert progtrack.quiet
2500N/A pkg_args.append("--parsable=%s" %
2500N/A progtrack.parsable_version)
2444N/A for pat in reject_list:
2444N/A pkg_args.extend(["--reject", str(pat)])
2444N/A
2500N/A rv, errout, p_dict = self.__pkg_cmd(pkgdefs.PKG_OP_SYNC,
2410N/A pkg_args, progtrack=progtrack)
2339N/A
2339N/A # if the audit command ran, return its status.
2339N/A if rv in [pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP]:
2500N/A return (rv, None, p_dict)
2339N/A
2339N/A # something went unexpectedly wrong.
2410N/A e = apx.LinkedImageException(lin=self.child_name, exitrv=rv,
2444N/A pkg_op_failed=(pkgdefs.PKG_OP_SYNC, rv, errout))
2500N/A return (rv, e, p_dict)
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 def child_nothingtodo(self):
2339N/A """Check if there are any changes planned for a child
2339N/A image."""
2339N/A return self.__r_rv_nop
2339N/A
2339N/A def child_reset_recurse(self):
2339N/A """Reset child recursion state for child."""
2339N/A
2339N/A self.__r_op = None
2339N/A self.__r_args = None
2339N/A self.__r_progtrack = None
2339N/A self.__r_rv_nop = False
2339N/A
2339N/A def child_init_recurse(self, pkg_op, accept, refresh_catalogs,
2339N/A update_index, args):
2339N/A """When planning changes on a parent image, prepare to
2339N/A recurse into a child image."""
2339N/A
2339N/A assert pkg_op in [pkgdefs.PKG_OP_SYNC, pkgdefs.PKG_OP_UPDATE]
2339N/A
2339N/A progtrack = args["progtrack"]
2339N/A noexecute = args["noexecute"]
2339N/A
2339N/A pkg_args = []
2339N/A
2339N/A pkg_args.extend(["-v"] * progtrack.verbose)
2339N/A if progtrack.quiet:
2339N/A pkg_args.append("-q")
2339N/A if noexecute:
2339N/A pkg_args.append("-n")
2500N/A if progtrack.parsable_version is not None:
2500N/A pkg_args.append("--parsable=%s" %
2500N/A progtrack.parsable_version)
2339N/A
2339N/A # W0511 XXX / FIXME Comments; pylint: disable-msg=W0511
2339N/A # XXX: also need to support --licenses.
2339N/A # pylint: enable-msg=W0511
2339N/A if accept:
2339N/A pkg_args.append("--accept")
2339N/A if not refresh_catalogs:
2339N/A pkg_args.append("--no-refresh")
2339N/A if not update_index:
2339N/A pkg_args.append("--no-index")
2339N/A
2339N/A # options specific to: attach, set-property-linked, sync
2339N/A if "li_pkg_updates" in args and not args["li_pkg_updates"]:
2339N/A pkg_args.append("--no-pkg-updates")
2339N/A
2339N/A if pkg_op == pkgdefs.PKG_OP_UPDATE:
2339N/A # skip ipkg up to date check for child images
2339N/A pkg_args.append("-f")
2339N/A
2339N/A self.__r_op = pkg_op
2339N/A self.__r_args = pkg_args
2339N/A self.__r_progtrack = progtrack
2339N/A
2339N/A def child_do_recurse(self, stage, ip=None):
2339N/A """When planning changes within a parent image, recurse into
2339N/A a child image."""
2339N/A
2339N/A assert stage in pkgdefs.api_stage_values
2339N/A assert stage != pkgdefs.API_STAGE_DEFAULT
2339N/A assert stage != pkgdefs.API_STAGE_PLAN or ip != None
2339N/A
2339N/A assert self.__r_op != None
2339N/A assert self.__r_args != None
2339N/A
2339N/A if stage == pkgdefs.API_STAGE_PUBCHECK:
2339N/A self.__syncmd()
2339N/A
2339N/A if stage == pkgdefs.API_STAGE_PLAN:
2339N/A # sync our metadata
2410N/A if not self.__syncmd(ip=ip):
2410N/A # no metadata changes in the child image.
2410N/A self.__r_rv_nop = True
2339N/A
2339N/A if self.__r_rv_nop:
2339N/A if stage == pkgdefs.API_STAGE_EXECUTE:
2339N/A self.child_reset_recurse()
2339N/A # the child image told us it has no changes planned.
2500N/A return pkgdefs.EXIT_NOP, None
2500N/A
2500N/A rv, errout, p_dict = self.__pkg_cmd(self.__r_op, self.__r_args,
2410N/A stage=stage, progtrack=self.__r_progtrack)
2339N/A
2339N/A if rv in [pkgdefs.EXIT_OK, pkgdefs.EXIT_NOP]:
2339N/A # common case (we hope)
2339N/A pass
2339N/A else:
2410N/A e = apx.LinkedImageException(
2410N/A lin=self.child_name, exitrv=rv,
2444N/A pkg_op_failed=(self.__r_op, rv, errout))
2339N/A self.child_reset_recurse()
2410N/A raise e
2339N/A
2339N/A if stage == pkgdefs.API_STAGE_PLAN and rv == pkgdefs.EXIT_NOP:
2339N/A self.__r_rv_nop = True
2339N/A
2339N/A if stage == pkgdefs.API_STAGE_EXECUTE:
2339N/A # we're done with this operation
2339N/A self.child_reset_recurse()
2339N/A
2500N/A return rv, p_dict
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 # Too many arguments; pylint: disable-msg=R0913
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