pkgplan.py revision 2470
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# CDDL HEADER START
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# The contents of this file are subject to the terms of the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# Common Development and Distribution License (the "License").
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# You may not use this file except in compliance with the License.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# See the License for the specific language governing permissions
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# and limitations under the License.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# When distributing Covered Code, include this CDDL HEADER in each
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# If applicable, add the following below this CDDL HEADER, with the
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# fields enclosed by brackets "[]" replaced with your own identifying
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# information: Portions Copyright [yyyy] [name of copyright owner]
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# CDDL HEADER END
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainenfrom pkg.misc import expanddirs, get_pkg_otw_size, EmptyI
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen """A package plan takes two package FMRIs and an Image, and produces the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen set of actions required to take the Image from the origin FMRI to the
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen destination FMRI.
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen If the destination FMRI is None, the package is removed.
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen "actions", "check_cancelation", "destination_fmri", "image",
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen "origin_fmri", "pkg_summary", "__destination_mfst",
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen "__license_status", "__origin_mfst", "__progtrack",
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen "__repair_actions", "__xferfiles", "__xfersize",
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen def __init__(self, image, progtrack, check_cancelation):
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen self.__destination_mfst = manifest.NullFactoredManifest
98c1cf256927e254f0c092acd2ddcd7ea50bd009Timo Sirainen self.__origin_mfst = manifest.NullFactoredManifest
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen self.actions = manifest.ManifestDifference([], [], [])
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen s = "%s -> %s\n" % (self.origin_fmri, self.destination_fmri)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen for src, dest in itertools.chain(*self.actions):
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen """Adds a license status entry for the given src and dest
445f9e31c6c3aa6c0a72be8565da8f6e594d24fbTimo Sirainen license actions.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen 'src' should be None or the source action for a license.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen 'dest' must be the destination action for a license."""
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen self.__license_status[dest.attrs["license"]] = {
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen """Return a list of pickled actions."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen newpair = [None, None]
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen """Return a list of unpickled actions."""
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen newpair = [None, None]
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen """Update the state of this object using the contents of
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen the supplied dictionary."""
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen # if there is no origin, don't allocate an fmri obj
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # if there is no destination, don't allocate an fmri obj
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen self.actions = manifest.ManifestDifference([], [], [])
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen for src, dest in itertools.chain(self.gen_update_actions(),
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen """Returns a dictionary containing the state of this object
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen so that it can be easily stored using JSON, pickle, etc."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen state["add"] = self.__pickle_actions(self.actions.added)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen state["change"] = self.__pickle_actions(self.actions.changed)
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen state["remove"] = self.__pickle_actions(self.actions.removed)
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen """Propose origin and dest fmri, manifest"""
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen def propose_repair(self, fmri, mfst, install, remove, autofix=False):
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # self.origin_fmri = None
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # I'd like a cleaner solution than this; we need to actually
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # construct a list of actions as things currently are rather
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # than just re-applying the current set of actions.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # Create a list of (src, dst) pairs for the actions to send to
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # execute_repair.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # src is none for repairs.
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen # dest is none for removals.
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen return len(self.actions.added) + len(self.actions.changed) + \
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen """ updates a set of installed fmris to reflect
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen proposed new state"""
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen def evaluate(self, old_excludes=EmptyI, new_excludes=EmptyI):
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen """Determine the actions required to transition the package."""
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen # If new actions are being installed, check the destination
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen # manifest for signatures.
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen # Since user removed publisher, assume this is
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen # the same as if they had set signature-policy
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen # ignore for the publisher.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sig_pol = self.image.signature_policy.combine(
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sigs = list(self.__destination_mfst.gen_actions_by_type(
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if sig_pol and (sigs or sig_pol.name != "ignore"):
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen # Only perform signature verification logic if
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # there are signatures or if signature-policy
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen # is not 'ignore'.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "check-certificate-revocation"))
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen self.actions = self.__destination_mfst.difference(
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen self.__origin_mfst, old_excludes, new_excludes)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # figure out how many implicit directories disappear in this
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # transition and add directory remove actions. These won't
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # do anything unless no pkgs reference that directory in
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # new state....
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # Retrieving origin_dirs first and then checking it for any
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # entries allows avoiding an unnecessary expanddirs for the
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # destination manifest when it isn't needed.
2ae575a66f2a302f047f6de062a70b75f8bebc7bTimo Sirainen origin_dirs = expanddirs(self.__origin_mfst.get_directories(
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # No longer needed.
484efa22e65c509f787dbbc892351146c726c257Timo Sirainen # Manifest.get_directories() returns implicit directories, which
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # means that this computation ends up re-adding all the explicit
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # directories getting removed to the removed list. This is
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # ugly, but safe.
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen expanddirs(self.__destination_mfst.get_directories(
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # Stash information needed by legacy actions.
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen self.__destination_mfst.get("description", "none provided"))
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen # No longer needed.
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # Add any install repair actions to the update list
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen self.actions.changed.extend(self.__repair_actions.get("install",
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen self.actions.removed.extend(self.__repair_actions.get("remove",
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # No longer needed.
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen for src, dest in itertools.chain(self.gen_update_actions(),
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # Initial installs require acceptance.
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen dest_ma = dest.attrs.get("must-accept", False)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # If src action required acceptance,
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # then license was already accepted
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # before, and if the hashes are the
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # same for the license payload, then
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # it doesn't need to be accepted again.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """A generator function that yields tuples of the form (license,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen entry). Where 'entry' is a dict containing the license status
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen information."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for lic, entry in self.__license_status.iteritems():
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen def set_license_status(self, plicense, accepted=None, displayed=None):
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen """Sets the license status for the given license entry.
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen 'plicense' should be the value of the license attribute for the
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen destination license action.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'accepted' is an optional parameter that can be one of three
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen None leaves accepted status unchanged
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen False sets accepted status to False
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen True sets accepted status to True
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'displayed' is an optional parameter that can be one of three
8af07808ba203f8709e2ff9eaf2291e1c4a4d53dTimo Sirainen None leaves displayed status unchanged
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen False sets displayed status to False
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen True sets displayed status to True"""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if accepted is not None:
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen if displayed is not None:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for src, dest in itertools.chain(*self.actions):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Return tuple of compressed bytes possibly downloaded
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen and number of bytes laid down; ignore removals
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen because they're usually pinned by snapshots"""
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen return (a[0] + int(b[1].attrs.get("pkg.csize" ,0)),
77af8c68c416179e717fc2d551f72ec50b499c13Timo Sirainen return reduce(sum_dest_size, itertools.chain(*self.actions),
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen """Perform actions required prior to installation or removal of
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen This method executes each action's preremove() or preinstall()
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen methods, as well as any package-wide steps that need to be taken
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen at such a time.
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # Determine if license acceptance requirements have been met as
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # early as possible.
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen if (dest.must_accept and not entry["accepted"]) or \
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen (dest.must_display and not entry["displayed"]):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for src, dest in itertools.chain(*self.actions):
3023fb352cbc2052b156f6d325c2629531a1b5b4Timo Sirainen """Download data for any actions that need it."""
e8a35266a5ceacdfafeeffd6bddae77931ff97ebTimo Sirainen self.__progtrack.download_start_pkg(self.get_xfername())
e8a35266a5ceacdfafeeffd6bddae77931ff97ebTimo Sirainen mfile = self.image.transport.multi_file(self.destination_fmri,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for src, dest in itertools.chain(*self.actions):
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen """ perform action for installation of package"""
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen except (pkg.actions.ActionError, EnvironmentError):
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # Don't log these as they're expected, and should be
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # handled by the caller.
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen logger.error("Action install failed for '%s' (%s):\n "
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "%s: %s" % (dest.attrs.get(dest.key_attr, id(dest)),
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen """ handle action updates"""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen except (pkg.actions.ActionError, EnvironmentError):
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # Don't log these as they're expected, and should be
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # handled by the caller.
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen logger.error("Action upgrade failed for '%s' (%s):\n "
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen "%s: %s" % (dest.attrs.get(dest.key_attr, id(dest)),
ab3c1eab9ca13916358a9e8b12df8212fefb7dbfTimo Sirainen """ handle action removals"""
3021a062b16ff0138408be6107d6bcd0ced280b9Timo Sirainen except (pkg.actions.ActionError, EnvironmentError):
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen # Don't log these as they're expected, and should be
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen # handled by the caller.
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen logger.error("Action removal failed for '%s' (%s):\n "
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen "%s: %s" % (src.attrs.get(src.key_attr, id(src)),
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen """Perform actions required after install or remove of a pkg.
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen This method executes each action's postremove() or postinstall()
8907d617ce7c4f390c0f42f6f694db2fecdd5775Timo Sirainen methods, as well as any package-wide steps that need to be taken
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen at such a time.
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen # record that package states are consistent
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for src, dest in itertools.chain(*self.actions):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Used to save unexpected files or directories found during
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen plan execution. Salvaged items are tracked in the imageplan.
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen # get just the file path that was salvaged
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen self.image.imageplan.salvaged.append((fpath, spath))
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def salvage_from(self, local_path, full_destination):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """move unpackaged contents to specified destination"""
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen # remove leading / if present
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen for fpath, spath in self.image.imageplan.salvaged[:]: