pkgplan.py revision 2627
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#!/usr/bin/python
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# CDDL HEADER START
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# The contents of this file are subject to the terms of the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# Common Development and Distribution License (the "License").
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# You may not use this file except in compliance with the License.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# or http://www.opensolaris.org/os/licensing.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# See the License for the specific language governing permissions
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# and limitations under the License.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# When distributing Covered Code, include this CDDL HEADER in each
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# If applicable, add the following below this CDDL HEADER, with the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# fields enclosed by brackets "[]" replaced with your own identifying
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# information: Portions Copyright [yyyy] [name of copyright owner]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# CDDL HEADER END
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync# Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync#
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport copy
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport itertools
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncfrom pkg.client import global_settings
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsynclogger = global_settings.logger
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport pkg.actions
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport pkg.actions.directory as directory
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport pkg.client.api_errors as apx
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport pkg.manifest as manifest
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncfrom pkg.misc import expanddirs, get_pkg_otw_size, EmptyI
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncimport os.path
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsyncclass PkgPlan(object):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """A package plan takes two package FMRIs and an Image, and produces the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync set of actions required to take the Image from the origin FMRI to the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync destination FMRI.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync If the destination FMRI is None, the package is removed.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync __slots__ = [
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__destination_mfst",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__executed",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__license_status",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__origin_mfst",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__repair_actions",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__xferfiles",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "__xfersize",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "_autofix_pkgs",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "_hash",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "actions",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "destination_fmri",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "image",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "origin_fmri",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "pkg_summary",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync ]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def __init__(self, image=None):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.destination_fmri = None
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst = manifest.NullFactoredManifest
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.origin_fmri = None
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__origin_mfst = manifest.NullFactoredManifest
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.actions = manifest.ManifestDifference([], [], [])
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.image = image
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.pkg_summary = None
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__executed = False
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__license_status = {}
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__repair_actions = {}
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__xferfiles = -1
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__xfersize = -1
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self._autofix_pkgs = []
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def __str__(self):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync s = "%s -> %s\n" % (self.origin_fmri, self.destination_fmri)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for src, dest in itertools.chain(*self.actions):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync s += " %s -> %s\n" % (src, dest)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync return s
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def __add_license(self, src, dest):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """Adds a license status entry for the given src and dest
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync license actions.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync 'src' should be None or the source action for a license.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync 'dest' must be the destination action for a license."""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__license_status[dest.attrs["license"]] = {
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "src": src,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "dest": dest,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "accepted": False,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "displayed": False,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync }
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def setstate(self, state):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """Update the state of this object using the contents of
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync the supplied dictionary."""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync import pkg.fmri
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # convert fmri strings into objects
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for i in ["src", "dst"]:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if state[i] is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state[i] = pkg.fmri.PkgFmri(state[i])
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # convert lists into tuples/sets
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # convert action object list into string list
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for i in ["added", "changed", "removed"]:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for j in range(len(state[i])):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync src, dst = state[i][j]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if src is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync src = pkg.actions.fromstr(src)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if dst is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dst = pkg.actions.fromstr(dst)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state[i][j] = (src, dst)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.origin_fmri = state["src"]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.destination_fmri = state["dst"]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.pkg_summary = state["summary"]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.actions = manifest.ManifestDifference(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["added"], state["changed"], state["removed"])
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # update the license actions associated with this package
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for src, dest in itertools.chain(self.gen_update_actions(),
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.gen_install_actions()):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if dest.name == "license":
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__add_license(src, dest)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def getstate(self):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """Returns a dictionary containing the state of this object
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync so that it can be easily stored using JSON."""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state = {}
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["src"] = self.origin_fmri
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["dst"] = self.destination_fmri
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["summary"] = self.pkg_summary
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["added"] = copy.copy(self.actions.added)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["changed"] = copy.copy(self.actions.changed)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state["removed"] = copy.copy(self.actions.removed)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # convert fmri objects into strings
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for i in ["src", "dst"]:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if isinstance(state[i], pkg.fmri.PkgFmri):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state[i] = str(state[i])
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # convert tuples/sets into lists
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # convert actions objects into strings
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for i in ["added", "changed", "removed"]:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for j in range(len(state[i])):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync src, dst = state[i][j]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if src is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync src = str(src)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if dst is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dst = str(dst)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync state[i][j] = [src, dst]
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync return state
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def propose(self, of, om, df, dm):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """Propose origin and dest fmri, manifest"""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.origin_fmri = of
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__origin_mfst = om
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.destination_fmri = df
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst = dm
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def propose_repair(self, fmri, mfst, install, remove, autofix=False):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.propose(fmri, mfst, fmri, mfst)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # self.origin_fmri = None
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # I'd like a cleaner solution than this; we need to actually
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # construct a list of actions as things currently are rather
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # than just re-applying the current set of actions.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync #
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Create a list of (src, dst) pairs for the actions to send to
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # execute_repair.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__repair_actions = {
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # src is none for repairs.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "install": [(None, x) for x in install],
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # dest is none for removals.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "remove": [(x, None) for x in remove],
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync }
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if autofix:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self._autofix_pkgs.append(fmri)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def get_actions(self):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync raise NotImplementedError()
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def get_nactions(self):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync return len(self.actions.added) + len(self.actions.changed) + \
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync len(self.actions.removed)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def update_pkg_set(self, fmri_set):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """ updates a set of installed fmris to reflect
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync proposed new state"""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if self.origin_fmri:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync fmri_set.discard(self.origin_fmri)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if self.destination_fmri:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync fmri_set.add(self.destination_fmri)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def evaluate(self, old_excludes=EmptyI, new_excludes=EmptyI,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync can_exclude=False):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """Determine the actions required to transition the package."""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # If new actions are being installed, check the destination
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # manifest for signatures.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if self.destination_fmri is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync try:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dest_pub = self.image.get_publisher(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync prefix=self.destination_fmri.publisher)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync except apx.UnknownPublisher:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Since user removed publisher, assume this is
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # the same as if they had set signature-policy
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # ignore for the publisher.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync sig_pol = None
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync else:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync sig_pol = self.image.signature_policy.combine(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dest_pub.signature_policy)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync sigs = list(self.__destination_mfst.gen_actions_by_type(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "signature", new_excludes))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if sig_pol and (sigs or sig_pol.name != "ignore"):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Only perform signature verification logic if
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # there are signatures or if signature-policy
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # is not 'ignore'.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync try:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync sig_pol.process_signatures(sigs,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst.gen_actions(),
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dest_pub, self.image.trust_anchors,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.image.cfg.get_policy(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync "check-certificate-revocation"))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync except apx.SigningException, e:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync e.pfmri = self.destination_fmri
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if isinstance(e, apx.BrokenChain):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync e.ext_exs.extend(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.image.bad_trust_anchors
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync )
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync raise
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if can_exclude:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if self.__destination_mfst is not None:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst.exclude_content(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync new_excludes)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if self.__origin_mfst is not None and \
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst != self.__origin_mfst:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__origin_mfst.exclude_content(old_excludes)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync old_excludes = EmptyI
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync new_excludes = EmptyI
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.actions = self.__destination_mfst.difference(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__origin_mfst, old_excludes, new_excludes)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # figure out how many implicit directories disappear in this
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # transition and add directory remove actions. These won't
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # do anything unless no pkgs reference that directory in
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # new state....
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Retrieving origin_dirs first and then checking it for any
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # entries allows avoiding an unnecessary expanddirs for the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # destination manifest when it isn't needed.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync origin_dirs = expanddirs(self.__origin_mfst.get_directories(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync old_excludes))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Manifest.get_directories() returns implicit directories, which
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # means that this computation ends up re-adding all the explicit
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # directories getting removed to the removed list. This is
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # ugly, but safe.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if origin_dirs:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync absent_dirs = origin_dirs - \
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync expanddirs(self.__destination_mfst.get_directories(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync new_excludes))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for a in absent_dirs:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.actions.removed.append(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync [directory.DirectoryAction(path=a), None])
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Stash information needed by legacy actions.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.pkg_summary = \
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst.get("pkg.summary",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__destination_mfst.get("description", "none provided"))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Add any install repair actions to the update list
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.actions.changed.extend(self.__repair_actions.get("install",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync EmptyI))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.actions.removed.extend(self.__repair_actions.get("remove",
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync EmptyI))
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # No longer needed.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__repair_actions = None
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for src, dest in itertools.chain(self.gen_update_actions(),
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.gen_install_actions()):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if dest.name == "license":
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.__add_license(src, dest)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if not src:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # Initial installs require acceptance.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync continue
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync src_ma = src.attrs.get("must-accept", False)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dest_ma = dest.attrs.get("must-accept", False)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync if (dest_ma and src_ma) and \
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync src.hash == dest.hash:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # If src action required acceptance,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # then license was already accepted
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # before, and if the hashes are the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # same for the license payload, then
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync # it doesn't need to be accepted again.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync self.set_license_status(
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync dest.attrs["license"],
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync accepted=True)
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def get_licenses(self):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """A generator function that yields tuples of the form (license,
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync entry). Where 'entry' is a dict containing the license status
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync information."""
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync for lic, entry in self.__license_status.iteritems():
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync yield lic, entry
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync def set_license_status(self, plicense, accepted=None, displayed=None):
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync """Sets the license status for the given license entry.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync 'plicense' should be the value of the license attribute for the
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync destination license action.
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync 'accepted' is an optional parameter that can be one of three
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync values:
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync None leaves accepted status unchanged
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync False sets accepted status to False
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync True sets accepted status to True
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync
14ea49401f3c8c61422aefbda43809e275f60c6cvboxsync 'displayed' is an optional parameter that can be one of three
values:
None leaves displayed status unchanged
False sets displayed status to False
True sets displayed status to True"""
entry = self.__license_status[plicense]
if accepted is not None:
entry["accepted"] = accepted
if displayed is not None:
entry["displayed"] = displayed
def get_xferstats(self):
if self.__xfersize != -1:
return (self.__xferfiles, self.__xfersize)
self.__xfersize = 0
self.__xferfiles = 0
for src, dest in itertools.chain(*self.actions):
if dest and dest.needsdata(src, self):
self.__xfersize += get_pkg_otw_size(dest)
self.__xferfiles += 1
if dest.name == "signature":
self.__xfersize += \
dest.get_action_chain_csize()
self.__xferfiles += \
len(dest.attrs.get("chain",
"").split())
return (self.__xferfiles, self.__xfersize)
def get_bytes_added(self):
"""Return tuple of compressed bytes possibly downloaded
and number of bytes laid down; ignore removals
because they're usually pinned by snapshots"""
def sum_dest_size(a, b):
if b[1]:
return (a[0] + int(b[1].attrs.get("pkg.csize" ,0)),
a[1] + int(b[1].attrs.get("pkg.size", 0)))
return (a[0], a[1])
return reduce(sum_dest_size, itertools.chain(*self.actions),
(0, 0))
def get_xfername(self):
if self.destination_fmri:
return self.destination_fmri.get_name()
if self.origin_fmri:
return self.origin_fmri.get_name()
return None
def preexecute(self):
"""Perform actions required prior to installation or removal of
a package.
This method executes each action's preremove() or preinstall()
methods, as well as any package-wide steps that need to be taken
at such a time.
"""
# Determine if license acceptance requirements have been met as
# early as possible.
errors = []
for lic, entry in self.get_licenses():
dest = entry["dest"]
if (dest.must_accept and not entry["accepted"]) or \
(dest.must_display and not entry["displayed"]):
errors.append(apx.LicenseAcceptanceError(
self.destination_fmri, **entry))
if errors:
raise apx.PkgLicenseErrors(errors)
for src, dest in itertools.chain(*self.actions):
if dest:
dest.preinstall(self, src)
else:
src.preremove(self)
def download(self, progtrack, check_cancel):
"""Download data for any actions that need it."""
progtrack.download_start_pkg(self.get_xfername())
mfile = self.image.transport.multi_file(self.destination_fmri,
progtrack, check_cancel)
if mfile is None:
progtrack.download_end_pkg()
return
for src, dest in itertools.chain(*self.actions):
if dest and dest.needsdata(src, self):
mfile.add_action(dest)
mfile.wait_files()
progtrack.download_end_pkg()
def gen_install_actions(self):
for src, dest in self.actions.added:
yield src, dest
def gen_removal_actions(self):
for src, dest in self.actions.removed:
yield src, dest
def gen_update_actions(self):
for src, dest in self.actions.changed:
yield src, dest
def execute_install(self, src, dest):
""" perform action for installation of package"""
self.__executed = True
try:
dest.install(self, src)
except (pkg.actions.ActionError, EnvironmentError):
# Don't log these as they're expected, and should be
# handled by the caller.
raise
except Exception, e:
logger.error("Action install failed for '%s' (%s):\n "
"%s: %s" % (dest.attrs.get(dest.key_attr, id(dest)),
self.destination_fmri.get_pkg_stem(),
e.__class__.__name__, e))
raise
def execute_update(self, src, dest):
""" handle action updates"""
self.__executed = True
try:
dest.install(self, src)
except (pkg.actions.ActionError, EnvironmentError):
# Don't log these as they're expected, and should be
# handled by the caller.
raise
except Exception, e:
logger.error("Action upgrade failed for '%s' (%s):\n "
"%s: %s" % (dest.attrs.get(dest.key_attr, id(dest)),
self.destination_fmri.get_pkg_stem(),
e.__class__.__name__, e))
raise
def execute_removal(self, src, dest):
""" handle action removals"""
self.__executed = True
try:
src.remove(self)
except (pkg.actions.ActionError, EnvironmentError):
# Don't log these as they're expected, and should be
# handled by the caller.
raise
except Exception, e:
logger.error("Action removal failed for '%s' (%s):\n "
"%s: %s" % (src.attrs.get(src.key_attr, id(src)),
self.origin_fmri.get_pkg_stem(),
e.__class__.__name__, e))
raise
def postexecute(self):
"""Perform actions required after install or remove of a pkg.
This method executes each action's postremove() or postinstall()
methods, as well as any package-wide steps that need to be taken
at such a time.
"""
# record that package states are consistent
for src, dest in itertools.chain(*self.actions):
if dest:
dest.postinstall(self, src)
else:
src.postremove(self)
def salvage(self, path):
"""Used to save unexpected files or directories found during
plan execution. Salvaged items are tracked in the imageplan.
"""
assert self.__executed
spath = self.image.salvage(path)
# get just the file path that was salvaged
fpath = path[len(self.image.get_root()) + 1:]
self.image.imageplan.salvaged.append((fpath, spath))
def salvage_from(self, local_path, full_destination):
"""move unpackaged contents to specified destination"""
# remove leading / if present
if local_path.startswith(os.path.sep):
local_path = local_path[1:]
for fpath, spath in self.image.imageplan.salvaged[:]:
if fpath.startswith(local_path):
self.image.imageplan.salvaged.remove((fpath, spath))
break
else:
return
self.image.recover(spath, full_destination)
@property
def destination_manifest(self):
return self.__destination_mfst
def clear_dest_manifest(self):
self.__destination_mfst = None
@property
def origin_manifest(self):
return self.__origin_mfst
def clear_origin_manifest(self):
self.__origin_mfst = None