--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/install_ict/cleanup_cpio_install.py Mon Mar 28 16:50:59 2011 -0600
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+import os
+import shutil
+import sys
+
+import pkg.client.api as api
+import pkg.client.api_errors as api_errors
+import pkg.client.progress as progress
+import solaris_install.ict as ICT
+
+from stat import S_IREAD, S_IRGRP, S_IROTH
+
+from solaris_install.data_object import ObjectNotFoundError
+from solaris_install.transfer.info import Args
+from solaris_install.transfer.info import CPIOSpec
+from solaris_install.transfer.info import IPSSpec
+from solaris_install.transfer.ips import RedirectIPSTrans
+from solaris_install.transfer.info import Software
+
+
+class CleanupCPIOInstall(ICT.ICTBaseClass):
+ '''
+ The CleanupCPIOInstall checkpoint performs clean up on the system after a
+ live CD install. This checkpoint performs the following when the
+ the execute method is called:
+ - Creates /etc/mnttab if it doesn't already exist and chmods it to the
+ correct values
+ - Removes files and directories that are not needed by the installed
+ system
+ - Remove miscellanous directory trees used as work areas during
+ installation
+ - Relocate configuration files from the save directory
+ - Remove install-specific packages that are not needed by the installed
+ system
+ - Reset pkg(1) image UUID for preferred publisher
+ '''
+ def __init__(self, name):
+ '''Initializes the class
+ Parameters:
+ -name - this arg is required by the AbstractCheckpoint
+ '''
+ super(CleanupCPIOInstall, self).__init__(name)
+
+ self.cleanup_list = ['.livecd', '.volsetid', '.textinstall',
+ 'etc/sysconfig/language', '.liveusb',
+ 'a', 'bootcd_microroot']
+
+ def execute(self, dry_run=False):
+ '''
+ The AbstractCheckpoint class requires this method
+ in sub-classes.
+
+ Parameters:
+ - the dry_run keyword paramater. The default value is False.
+ If set to True, the log message describes the checkpoint tasks.
+
+ Returns:
+ - Nothing
+ On failure, errors raised are managed by the engine.
+ '''
+
+ # Variable used to store the result of a pkg api plan_uninstall
+ pkg_rval = False
+
+ # List to hold requested packages to be removed
+ pkg_rm_node = []
+
+ # Dictionary and lists used to hold the results of package info check
+ # for packages in the list of packages requested for removal
+ pkg_ret_info = {}
+ pkgs_found = []
+ pkgs_notfound = []
+ pkgs_illegal = []
+
+ # Variables used to check for valid packages
+ info_local = True
+ info_needed = api.PackageInfo.ALL_OPTIONS
+
+ # The software node containing packages, directories and files that
+ # need removal
+ soft_list = None
+
+ self.logger.debug('ICT current task: cleanup install')
+
+ # parse_doc populates variables necessary to execute the checkpoint
+ self.parse_doc()
+
+ # Get the list of install specific items that need to be removed.
+ # Check for both IPS packages and files and directories
+ # If no items are designated, an empty list is returned.
+ soft_list = self.doc.get_descendants(name=self.name,
+ class_type=Software)
+
+ if soft_list:
+ soft_node = soft_list[0]
+
+ # Get the list of install specific packages that need to
+ # be removed.
+ try:
+ pkg_rm_node = soft_node.get_children(class_type=IPSSpec)[0]
+ except IndexError, err:
+ # No IPS packages have been specified
+ pass
+
+ # Get the list of install specific files that need to be removed.
+ try:
+ file_rm_node = soft_node.get_children(class_type=CPIOSpec)[0]
+ self.cleanup_list.extend(file_rm_node.contents)
+ except IndexError, err:
+ # No additional CPIO contents have been specified
+ pass
+
+ # Create the mnttab file
+ self.logger.debug('Executing: Create /etc/mnttab file')
+ if not dry_run:
+ mnttab = os.path.join(self.target_dir, ICT.MNTTAB)
+ mnttab_dir = os.path.dirname(mnttab)
+
+ # Create the directory that holds the mnttab file,
+ # if it does not exist.
+ if not os.access(mnttab_dir, os.F_OK):
+ os.makedirs(mnttab_dir)
+
+ # Create the mnttab file if it does not exist.
+ if not os.access(mnttab, os.F_OK):
+ open(mnttab, 'w').close()
+ os.chmod(mnttab, S_IREAD | S_IRGRP | S_IROTH)
+
+ # Remove and miscellaneous directories used as work areas
+ self.logger.debug('Executing: Remove miscellaneous work directories '
+ 'from /var/tmp')
+ for root, dirs, files in os.walk(os.path.join(self.target_dir,
+ 'var/tmp'), topdown=False):
+ for name in files:
+ self.logger.debug('Removing %s', name)
+ if not dry_run:
+ os.unlink(os.path.join(root, name))
+
+ for work_dir in dirs:
+ self.logger.debug('Removing %s', work_dir)
+ if not dry_run:
+ os.rmdir(os.path.join(root, work_dir))
+
+ self.logger.debug('Executing: Remove miscellaneous work directories '
+ 'from /mnt')
+ for root, dirs, files in os.walk(os.path.join(self.target_dir,
+ 'mnt'), topdown=False):
+ for name in files:
+ self.logger.debug('Removing %s', name)
+ if not dry_run:
+ os.unlink(os.path.join(root, name))
+ for work_dir in dirs:
+ self.logger.debug('Removing %s', work_dir)
+ if not dry_run:
+ os.rmdir(os.path.join(root, work_dir))
+
+ # Relocate configuration files from the save directory
+ savedir = os.path.join(self.target_dir, 'save')
+ if os.path.exists(savedir):
+ self.logger.debug('Executing: Relocate configuration files')
+ for root, dirs, files in os.walk(savedir, topdown=False):
+ if not files:
+ continue
+
+ target = root.replace('/save', '')
+ if not dry_run:
+ if not os.access(target, os.F_OK):
+ os.makedirs(target, 0755)
+
+ for name in files:
+ move_file = os.path.join(root, name)
+ self.logger.debug('Moving %s to %s', move_file, target)
+ if not dry_run:
+ if os.access(os.path.join(target, name), os.F_OK):
+ self.logger.debug('%s exists at %s --skipping',
+ name, target)
+ else:
+ shutil.copy2(move_file, target)
+
+ if not dry_run:
+ try:
+ api_inst = api.ImageInterface(self.target_dir,
+ ICT.CLIENT_API_VERSION,
+ progress.CommandLineProgressTracker(),
+ None,
+ ICT.PKG_CLIENT_NAME)
+
+ except api_errors.VersionException, ips_err:
+ raise ValueError("The IPS API version specified, "
+ + str(ips_err.received_version) +
+ " does not agree with "
+ "the expected version, "
+ + str(ips_err.expected_version))
+
+ # Remove install-specific packages that are not needed
+ if pkg_rm_node and len(pkg_rm_node.contents) > 0:
+ self.logger.debug("Executing: Remove unneeded install-specific "
+ "packages")
+
+ # Check that all of the packages are valid packages
+ # before trying to uninstall them
+ self.logger.debug('Validating the packages to be uninstalled '
+ '%s', pkg_rm_node.contents)
+
+ if not dry_run:
+ try:
+ pkg_ret_info = api_inst.info(pkg_rm_node.contents,
+ info_local,
+ info_needed)
+ except api_errors.NoPackagesInstalledException:
+ self.logger.debug("no packages from the uninstall list "
+ "are installed")
+
+ if pkg_ret_info:
+ pkgs_found = pkg_ret_info[api.ImageInterface.INFO_FOUND]
+ pkgs_notfound = pkg_ret_info[
+ api.ImageInterface.INFO_MISSING]
+ pkgs_illegal = pkg_ret_info[
+ api.ImageInterface.INFO_ILLEGALS]
+
+ for notfound in pkgs_notfound:
+ self.logger.debug("'%s' is not installed - skipping"
+ % notfound)
+ pkg_rm_node.contents.remove(notfound)
+
+ for illegal in pkgs_illegal:
+ self.logger.debug("'%s' is not a legal package for "
+ "this install image - skipping"
+ % illegal)
+ pkg_rm_node.contents.remove(illegal)
+
+ # Uninstall the packages
+ if not pkgs_found:
+ self.logger.debug('No packages to uninstall')
+ else:
+ self.logger.debug('Uninstalling the packages...')
+ self.logger.debug('%s', pkg_rm_node.contents)
+ if not dry_run:
+ # Reset the value to false
+ pkg_rval = False
+ try:
+ pkg_args = pkg_rm_node.get_first_child(
+ Args.ARGS_LABEL, Args)
+ except ObjectNotFoundError, err:
+ # No package arguments have been defined
+ pass
+
+ if pkg_args:
+ if "recursive_removal" in pkg_args.arg_dict:
+ pkg_rval = api_inst.plan_uninstall(
+ pkg_list=pkg_rm_node.contents,
+ recursive_removal=pkg_args.arg_dict.pop(
+ "recursive_removal", **pkg_args.arg_dict))
+ else:
+ pkg_rval = api_inst.plan_uninstall(
+ pkg_list=pkg_rm_node.contents,
+ recursive_removal=False, **pkg_args.arg_dict)
+ else:
+ pkg_rval = api_inst.plan_uninstall(
+ pkg_list=pkg_rm_node.contents,
+ recursive_removal=False)
+
+ # Redirect stdout and stderr from the pkg image in order
+ # to capture the command line output from the pkg
+ # progress tracker into the transfer logs.
+ if pkg_rval:
+ tmp_stdout = sys.stdout
+ tmp_stderr = sys.stderr
+ sys.stdout = sys.stderr = RedirectIPSTrans(self.logger)
+
+ api_inst.prepare()
+ api_inst.execute_plan()
+ api_inst.reset()
+
+ # Release stdout and stderr
+ sys.stdout = tmp_stdout
+ sys.stderr = tmp_stderr
+ else:
+ self.logger.debug('Unable to uninstall install specific '
+ 'packages')
+
+ # Reset the pkg(1) image UUID to the preferred publisher
+ self.logger.debug('Executing: Setting the UUID to the preferred '
+ 'publisher')
+ if not dry_run:
+ publisher = api_inst.get_preferred_publisher()
+ publisher.reset_client_uuid()
+
+ # Remove the files and directories in the cleanup_list
+ self.logger.debug('Executing: Cleanup of %s', self.cleanup_list)
+ for cleanup_name in self.cleanup_list:
+ cleanup = os.path.join(self.target_dir, cleanup_name)
+ self.logger.debug("Removing %s", cleanup)
+ if not dry_run:
+ if os.access(cleanup, os.F_OK):
+ if os.path.isfile(cleanup):
+ os.unlink(cleanup)
+ else:
+ os.rmdir(cleanup)
+
+ def get_progress_estimate(self):
+ '''
+ The AbstractCheckpoint class requires this method
+ in sub-classes.
+
+ This returns an estimate of how long the execute() method
+ will take to run.
+ '''
+ #XXXThis needs to be determined more accurately
+ return 60