pkgplan.py revision 258
409N/A#!/usr/bin/python
39N/A#
39N/A# CDDL HEADER START
39N/A#
39N/A# The contents of this file are subject to the terms of the
39N/A# Common Development and Distribution License (the "License").
39N/A# You may not use this file except in compliance with the License.
39N/A#
39N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
39N/A# or http://www.opensolaris.org/os/licensing.
39N/A# See the License for the specific language governing permissions
39N/A# and limitations under the License.
39N/A#
39N/A# When distributing Covered Code, include this CDDL HEADER in each
39N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
39N/A# If applicable, add the following below this CDDL HEADER, with the
39N/A# fields enclosed by brackets "[]" replaced with your own identifying
39N/A# information: Portions Copyright [yyyy] [name of copyright owner]
39N/A#
39N/A# CDDL HEADER END
39N/A#
926N/A
926N/A# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
926N/A# Use is subject to license terms.
39N/A
926N/Aimport errno
39N/Aimport itertools
39N/Aimport os
342N/A
1386N/Aimport pkg.manifest as manifest
838N/Aimport pkg.client.filelist as filelist
956N/A
39N/Aclass PkgPlan(object):
51N/A """A package plan takes two package FMRIs and an Image, and produces the
1352N/A set of actions required to take the Image from the origin FMRI to the
1066N/A destination FMRI.
1231N/A
1352N/A If the destination FMRI is None, the package is removed.
1352N/A """
296N/A
39N/A def __init__(self, image, progtrack):
39N/A self.origin_fmri = None
39N/A self.destination_fmri = None
39N/A self.origin_mfst = manifest.null
39N/A self.destination_mfst = manifest.null
39N/A
205N/A self.image = image
39N/A self.progtrack = progtrack
205N/A
39N/A self.actions = []
39N/A
39N/A self.xfersize = -1
39N/A self.xferfiles = -1
39N/A
39N/A self.origin_filters = []
39N/A self.destination_filters = []
39N/A
39N/A def __str__(self):
39N/A s = "%s -> %s\n" % (self.origin_fmri, self.destination_fmri)
39N/A
39N/A for src, dest in itertools.chain(*self.actions):
39N/A s += " %s -> %s\n" % (src, dest)
39N/A
39N/A return s
39N/A
39N/A def set_origin(self, fmri):
39N/A self.origin_fmri = fmri
39N/A #XXX this is busted, can't work self.origin_mfst = manifest.retrieve(fmri)
39N/A
39N/A def propose_destination(self, fmri, mfst):
39N/A self.destination_fmri = fmri
48N/A self.destination_mfst = mfst
48N/A
48N/A if self.image.install_file_present(fmri):
48N/A raise RuntimeError, "already installed"
48N/A
59N/A def propose_removal(self, fmri, mfst):
48N/A self.origin_fmri = fmri
39N/A self.origin_mfst = mfst
104N/A
39N/A if not self.image.install_file_present(fmri):
39N/A raise RuntimeError, "not installed"
39N/A
567N/A def get_actions(self):
838N/A raise NotImplementedError()
838N/A
838N/A def get_nactions(self):
39N/A return len(self.actions[0]) + len(self.actions[1]) + \
39N/A len(self.actions[2])
39N/A
956N/A def evaluate(self, filters = []):
315N/A """Determine the actions required to transition the package."""
39N/A # if origin unset, determine if we're dealing with an previously
227N/A # installed version or if we're dealing with the null package
315N/A #
315N/A # XXX Perhaps make the pkgplan creator make this explicit, so we
39N/A # don't have to check?
1352N/A f = None
1352N/A if not self.origin_fmri:
1352N/A f = self.image.older_version_installed(
1352N/A self.destination_fmri)
956N/A if f:
1352N/A self.origin_fmri = f
429N/A self.origin_mfst = self.image.get_manifest(f)
315N/A
1352N/A self.destination_filters = filters
429N/A
1352N/A # Try to load the filter used for the last install of the
1352N/A # package.
39N/A self.origin_filters = []
926N/A if self.origin_fmri:
926N/A try:
203N/A f = file("%s/pkg/%s/filters" % \
203N/A (self.image.imgdir,
203N/A self.origin_fmri.get_dir_path()), "r")
203N/A except IOError, e:
1045N/A if e.errno != errno.ENOENT:
1045N/A raise
72N/A else:
72N/A self.origin_filters = [
59N/A (l.strip(), compile(
1045N/A l.strip(), "<filter string>", "eval"))
1045N/A for l in f.readlines()
1045N/A ]
1045N/A
1045N/A self.destination_mfst.filter(self.destination_filters)
1045N/A self.origin_mfst.filter(self.origin_filters)
1045N/A
1045N/A # Assume that origin actions are unique, but make sure that
1045N/A # destination ones are.
72N/A ddups = self.destination_mfst.duplicates()
72N/A if ddups:
838N/A raise RuntimeError, ["Duplicate actions", ddups]
72N/A
72N/A self.actions = self.destination_mfst.difference(
72N/A self.origin_mfst)
838N/A
72N/A # over the list of update actions, check for any that are the
59N/A # target of hardlink actions, and add the renewal of those hardlinks
72N/A # to the install set
72N/A link_actions = self.image.get_link_actions()
59N/A
72N/A # iterate over copy since we're appending to list
72N/A
307N/A for a in self.actions[1][:]:
307N/A if a[1].name == "file" and a[1].attrs["path"] in link_actions:
307N/A la = link_actions[a[1].attrs["path"]]
72N/A self.actions[1].extend([(a, a) for a in la])
72N/A
72N/A def get_xferstats(self):
237N/A if self.xfersize != -1:
72N/A return (self.xferfiles, self.xfersize)
59N/A
72N/A self.xfersize = 0
72N/A self.xferfiles = 0
72N/A for src, dest in itertools.chain(*self.actions):
72N/A if dest and dest.needsdata(src):
59N/A self.xfersize += \
72N/A int(dest.attrs.get("pkg.size", 0))
72N/A self.xferfiles += 1
72N/A
202N/A return (self.xferfiles, self.xfersize)
72N/A
72N/A def will_xfer(self):
59N/A nf, nb = self.get_xferstats()
202N/A if nf > 0:
59N/A return True
838N/A else:
838N/A return False
838N/A
838N/A def get_xfername(self):
838N/A if self.destination_fmri:
926N/A return self.destination_fmri.get_name()
838N/A if self.origin_fmri:
838N/A return self.origin_fmri.get_name()
838N/A return None
838N/A
926N/A def preexecute(self):
838N/A """Perform actions required prior to installation or removal of a package.
926N/A
926N/A This method executes each action's preremove() or preinstall()
926N/A methods, as well as any package-wide steps that need to be taken
838N/A at such a time.
838N/A """
838N/A flist = None
838N/A flist_supported = True
838N/A
838N/A if flist_supported:
838N/A self.progtrack.download_start_pkg(self.get_xfername())
838N/A
838N/A # retrieval step
926N/A if self.destination_fmri == None:
838N/A self.image.remove_install_file(self.origin_fmri)
838N/A
838N/A try:
838N/A os.unlink("%s/pkg/%s/filters" % (
838N/A self.image.imgdir,
838N/A self.origin_fmri.get_dir_path()))
838N/A except OSError, e:
845N/A if e.errno != errno.ENOENT:
845N/A raise
926N/A
838N/A for src, dest in itertools.chain(*self.actions):
845N/A if dest:
845N/A dest.preinstall(self, src)
845N/A else:
838N/A src.preremove(self)
845N/A
845N/A if dest and dest.needsdata(src) and flist_supported:
838N/A
838N/A if flist and flist.is_full():
926N/A try:
203N/A flist.get_files()
315N/A self.progtrack.download_add_progress(flist.get_nfiles(), flist.get_nbytes())
838N/A except filelist.FileListException:
203N/A flist_supported = False
838N/A flist = None
48N/A continue
48N/A
48N/A flist = None
48N/A
48N/A if flist is None:
838N/A flist = filelist.FileList(
203N/A self.image,
48N/A self.destination_fmri)
203N/A
72N/A flist.add_action(dest)
181N/A
72N/A
181N/A # Get any remaining files
46N/A if flist:
181N/A try:
237N/A flist.get_files()
46N/A self.progtrack.download_add_progress(flist.get_nfiles(), flist.get_nbytes())
838N/A except filelist.FileListException:
838N/A pass
838N/A flist = None
838N/A
838N/A if flist_supported:
838N/A self.progtrack.download_end_pkg()
838N/A
838N/A def gen_install_actions(self):
342N/A for src, dest in self.actions[0]:
926N/A yield src, dest
838N/A
838N/A def gen_removal_actions(self):
926N/A for src, dest in self.actions[2]:
838N/A yield src, dest
838N/A
838N/A def gen_update_actions(self):
838N/A for src, dest in self.actions[1]:
838N/A yield src, dest
926N/A
926N/A def execute_install(self, src, dest):
615N/A """ perform action for installation of package"""
615N/A try:
615N/A dest.install(self, src)
615N/A except Exception, e:
615N/A print "Action install failed for '%s' (%s):\n %s: %s" % \
926N/A (dest.attrs.get(dest.key_attr, id(dest)),
615N/A self.destination_fmri.get_pkg_stem(),
615N/A e.__class__.__name__, e)
838N/A raise
111N/A
111N/A def execute_update(self, src, dest):
111N/A """ handle action updates"""
111N/A try:
111N/A dest.install(self, src)
111N/A except Exception, e:
111N/A print "Action upgrade failed for '%s' (%s):\n %s: %s" % \
111N/A (dest.attrs.get(dest.key_attr, id(dest)),
113N/A self.destination_fmri.get_pkg_stem(),
926N/A e.__class__.__name__, e)
838N/A raise
926N/A
113N/A def execute_removal(self, src, dest):
113N/A """ handle action removals"""
113N/A try:
113N/A src.remove(self)
113N/A except Exception, e:
113N/A print "Action removal failed for '%s' (%s):\n %s: %s" % \
113N/A (src.attrs.get(src.key_attr, id(src)),
113N/A self.origin_fmri.get_pkg_stem(),
113N/A e.__class__.__name__, e)
111N/A raise
104N/A
104N/A def postexecute(self):
39N/A """Perform actions required after installation or removal of a package.
39N/A
926N/A This method executes each action's postremove() or postinstall()
926N/A methods, as well as any package-wide steps that need to be taken
877N/A at such a time.
877N/A """
926N/A # record that package states are consistent
926N/A for src, dest in itertools.chain(*self.actions):
926N/A if dest:
926N/A dest.postinstall(self, src)
72N/A else:
72N/A src.postremove(self)
72N/A
72N/A # In the case of an upgrade, remove the installation turds from
72N/A # the origin's directory.
72N/A # XXX should this just go in preexecute?
72N/A if self.origin_fmri != None and self.destination_fmri != None:
926N/A self.image.remove_install_file(self.origin_fmri)
146N/A
146N/A try:
50N/A os.unlink("%s/pkg/%s/filters" % (
51N/A self.image.imgdir,
51N/A self.origin_fmri.get_dir_path()))
51N/A except OSError, e:
591N/A if e.errno != errno.ENOENT:
591N/A raise
591N/A
591N/A if self.destination_fmri != None:
1085N/A self.image.add_install_file(self.destination_fmri)
1085N/A
1085N/A # Save the filters we used to install the package, so
1085N/A # they can be referenced later.
1085N/A if self.destination_filters:
1085N/A f = file("%s/pkg/%s/filters" % \
51N/A (self.image.imgdir,
926N/A self.destination_fmri.get_dir_path()), "w")
926N/A
926N/A f.writelines([
926N/A myfilter + "\n"
926N/A for myfilter, code in self.destination_filters
123N/A ])
123N/A f.close()
123N/A
123N/A