Example #1
0
def resolve(args, img_dir):
    """Take a list of manifests and resolve any file dependencies, first
        against the other published manifests and then against what is installed
        on the machine."""
    out_dir = None
    echo_manifest = False
    output_to_screen = False
    suffix = None
    verbose = False
    use_system_to_resolve = True
    constraint_files = []
    extra_external_info = False
    try:
        opts, pargs = getopt.getopt(args, "d:e:Emos:Sv")
    except getopt.GetoptError as e:
        usage(_("illegal global option -- {0}").format(e.opt))
    for opt, arg in opts:
        if opt == "-d":
            out_dir = arg
        elif opt == "-e":
            constraint_files.append(arg)
        elif opt == "-E":
            extra_external_info = True
        elif opt == "-m":
            echo_manifest = True
        elif opt == "-o":
            output_to_screen = True
        elif opt == "-s":
            suffix = arg
        elif opt == "-S":
            use_system_to_resolve = False
        elif opt == "-v":
            verbose = True

    if (out_dir or suffix) and output_to_screen:
        usage(_("-o cannot be used with -d or -s"))

    manifest_paths = [os.path.abspath(fp) for fp in pargs]

    for manifest in manifest_paths:
        if not os.path.isfile(manifest):
            usage(_("The manifest file {0} could not be found.").format(
                manifest),
                  retcode=2)

    if out_dir:
        out_dir = os.path.abspath(out_dir)
        if not os.path.isdir(out_dir):
            usage(_("The output directory {0} is not a directory.").format(
                out_dir),
                  retcode=2)

    provided_image_dir = True
    pkg_image_used = False
    if img_dir == None:
        orig_cwd = None
        try:
            orig_cwd = os.getcwd()
        except OSError:
            # May be unreadable by user or have other problem.
            pass

        img_dir, provided_image_dir = api.get_default_image_root(
            orig_cwd=orig_cwd)
        if os.environ.get("PKG_IMAGE"):
            # It's assumed that this has been checked by the above
            # function call and hasn't been removed from the
            # environment.
            pkg_image_used = True

    if not img_dir:
        error(
            _("Could not find image.  Use the -R option or set "
              "$PKG_IMAGE to the\nlocation of an image."))
        return 1

    system_patterns = misc.EmptyI
    if constraint_files:
        system_patterns = []
        for f in constraint_files:
            try:
                with open(f, "rb") as fh:
                    for l in fh:
                        l = l.strip()
                        if l and not l.startswith("#"):
                            system_patterns.append(l)
            except EnvironmentError as e:
                if e.errno == errno.ENOENT:
                    error("{0}: '{1}'".format(e.args[1], e.filename),
                          cmd="resolve")
                    return 1
                raise api_errors._convert_error(e)
        if not system_patterns:
            error(
                _("External package list files were provided but "
                  "did not contain any fmri patterns."))
            return 1
    elif use_system_to_resolve:
        system_patterns = ["*"]

    # Becuase building an ImageInterface permanently changes the cwd for
    # python, it's necessary to do this step after resolving the paths to
    # the manifests.
    try:
        api_inst = api.ImageInterface(img_dir,
                                      CLIENT_API_VERSION,
                                      progress.QuietProgressTracker(),
                                      None,
                                      PKG_CLIENT_NAME,
                                      exact_match=provided_image_dir)
    except api_errors.ImageNotFoundException as e:
        if e.user_specified:
            if pkg_image_used:
                error(
                    _("No image rooted at '{0}' "
                      "(set by $PKG_IMAGE)").format(e.user_dir))
            else:
                error(_("No image rooted at '{0}'").format(e.user_dir))
        else:
            error(_("No image found."))
        return 1
    except api_errors.PermissionsException as e:
        error(e)
        return 1
    except api_errors.ImageFormatUpdateNeeded as e:
        # This should be a very rare error case.
        format_update_error(e)
        return 1

    try:
        pkg_deps, errs, unused_fmris, external_deps = \
            dependencies.resolve_deps(manifest_paths, api_inst,
                system_patterns, prune_attrs=not verbose)
    except (actions.MalformedActionError, actions.UnknownActionError) as e:
        error(
            _("Could not parse one or more manifests because of "
              "the following line:\n{0}").format(e.actionstr))
        return 1
    except dependencies.DependencyError as e:
        error(e)
        return 1
    except api_errors.ApiException as e:
        error(e)
        return 1
    ret_code = 0

    if output_to_screen:
        ret_code = pkgdeps_to_screen(pkg_deps, manifest_paths, echo_manifest)
    elif out_dir:
        ret_code = pkgdeps_to_dir(pkg_deps, manifest_paths, out_dir, suffix,
                                  echo_manifest)
    else:
        ret_code = pkgdeps_in_place(pkg_deps, manifest_paths, suffix,
                                    echo_manifest)

    if extra_external_info:
        if constraint_files and unused_fmris:
            msg(
                _("\nThe following fmris matched a pattern in a "
                  "constraint file but were not used in\ndependency "
                  "resolution:"))
            for pfmri in sorted(unused_fmris):
                msg("\t{0}".format(pfmri))
        if not constraint_files and external_deps:
            msg(_("\nThe following fmris had dependencies resolve "
                  "to them:"))
            for pfmri in sorted(external_deps):
                msg("\t{0}".format(pfmri))

    for e in errs:
        if ret_code == 0:
            ret_code = 1
        emsg(e)
    return ret_code
Example #2
0
    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()

        # For GRUB2 based media there is a .volsetid derived ident file that
        # identifies the root of the image during GRUB2 configuration. It
        # needs to be removed from the target if present.
        # eg. if the contents of .volsetid are "ABC-123" then the ident file
        # name will be (dot)volsetid: ".ABC-123"
        volsetid = None
        if VOLSETID in self.cleanup_list:
            vpath = os.path.join(self.target_dir, VOLSETID)
            with open(vpath, 'r') as vpath_fh:
                volsetid = vpath_fh.read().strip()
        if volsetid and \
            os.path.exists(os.path.join(self.target_dir, '.' + volsetid)):
            self.cleanup_list.append('.' + volsetid)

        # 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:
                # 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:
                # 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))

        if not dry_run:
            try:
                api_inst = api.ImageInterface(
                    self.target_dir, PKG5_API_VERSION,
                    InstallCLIProgressTracker(self.logger), 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))