src/modules/client/api.py
changeset 2910 2bb9f6ffdff9
parent 2862 1ec958dbd40a
child 2925 3e1fbeb31067
--- a/src/modules/client/api.py	Wed Jun 12 15:44:50 2013 -0700
+++ b/src/modules/client/api.py	Wed Jun 19 15:54:08 2013 -0700
@@ -103,8 +103,8 @@
 # things like help(pkg.client.api.PlanDescription)
 from pkg.client.plandesc import PlanDescription # pylint: disable=W0611
 
-CURRENT_API_VERSION = 74
-COMPATIBLE_API_VERSIONS = frozenset([72, 73, CURRENT_API_VERSION])
+CURRENT_API_VERSION = 75
+COMPATIBLE_API_VERSIONS = frozenset([72, 73, 74, CURRENT_API_VERSION])
 CURRENT_P5I_VERSION = 1
 
 # Image type constants.
@@ -253,6 +253,10 @@
         needed.  Cancel may only be invoked while a cancelable method is
         running."""
 
+        FACET_ALL = 0
+        FACET_IMAGE = 1
+        FACET_INSTALLED = 2
+
         # Constants used to reference specific values that info can return.
         INFO_FOUND = 0
         INFO_MISSING = 1
@@ -268,6 +272,13 @@
         MATCH_FMRI = 1
         MATCH_GLOB = 2
 
+        VARIANT_ALL = 0
+        VARIANT_ALL_POSSIBLE = 1
+        VARIANT_IMAGE = 2
+        VARIANT_IMAGE_POSSIBLE = 3
+        VARIANT_INSTALLED = 4
+        VARIANT_INSTALLED_POSSIBLE = 5
+
         def __init__(self, img_path, version_id, progresstracker,
             cancel_state_callable, pkg_client_name, exact_match=True,
             cmdpath=None):
@@ -817,6 +828,173 @@
                 dependencies on this) """
                 return [a for a in self._img.get_avoid_dict().iteritems()]
 
+        def gen_facets(self, facet_list, patterns=misc.EmptyI):
+                """A generator function that produces tuples of the form:
+
+                    (
+                        name,    - (string) facet name (e.g. facet.doc)
+                        value    - (boolean) current facet value
+                    )
+
+                Results are always sorted by facet name.
+
+                'facet_list' is one of the following constant values indicating
+                which facets should be returned based on how they were set:
+
+                        FACET_ALL
+                                Return all facets set in the image and all
+                                facets listed in installed packages.
+
+                        FACET_IMAGE
+                                Return only the facets set in the image.
+
+                        FACET_INSTALLED
+                                Return only the facets listed in installed
+                                packages.
+
+                'patterns' is an optional list of facet wildcard strings to
+                filter results by."""
+
+                facets = self._img.cfg.facets
+                if facet_list != self.FACET_INSTALLED:
+                        # Include all facets set in image.
+                        fimg = set(facets.keys())
+                else:
+                        # Don't include any set only in image.
+                        fimg = set()
+
+                # Get all facets found in packages and determine state.
+                fpkg = set()
+                excludes = self._img.list_excludes()
+                if facet_list != self.FACET_IMAGE:
+                        for f in self._img.gen_installed_pkgs():
+                                # The manifest must be loaded without
+                                # pre-applying excludes so that gen_facets() can
+                                # choose how to filter the actions.
+                                mfst = self._img.get_manifest(f,
+                                    ignore_excludes=True)
+                                for facet in mfst.gen_facets(excludes=excludes):
+                                        # Use Facets object to determine
+                                        # effective facet state.
+                                        fpkg.add(facet)
+
+                # Generate the results.
+                for name in misc.yield_matching("facet.", sorted(fimg | fpkg),
+                    patterns):
+                        # The image's Facets dictionary will return the
+                        # effective value for any facets not explicitly set in
+                        # the image (wildcards or implicit).
+                        yield (name, facets[name])
+
+        def gen_variants(self, variant_list, patterns=misc.EmptyI):
+                """A generator function that produces tuples of the form:
+
+                    (
+                        name,    - (string) variant name (e.g. variant.arch)
+                        value    - (string) current variant value,
+                        possible - (list) list of possible variant values based
+                                   on installed packages; empty unless using
+                                   *_POSSIBLE variant_list.
+                    )
+
+                Results are always sorted by variant name.
+
+                'variant_list' is one of the following constant values indicating
+                which variants should be returned based on how they were set:
+
+                        VARIANT_ALL
+                                Return all variants set in the image and all
+                                variants listed in installed packages.
+
+                        VARIANT_ALL_POSSIBLE
+                                Return possible variant values (those found in
+                                any installed package) for all variants set in
+                                the image and all variants listed in installed
+                                packages.
+
+                        VARIANT_IMAGE
+                                Return only the variants set in the image.
+
+                        VARIANT_IMAGE_POSSIBLE
+                                Return possible variant values (those found in
+                                any installed package) for only the variants set
+                                in the image.
+
+                        VARIANT_INSTALLED
+                                Return only the variants listed in installed
+                                packages.
+
+                        VARIANT_INSTALLED_POSSIBLE
+                                Return possible variant values (those found in
+                                any installed package) for only the variants
+                                listed in installed packages.
+
+                'patterns' is an optional list of variant wildcard strings to
+                filter results by."""
+
+                variants = self._img.cfg.variants
+                if variant_list != self.VARIANT_INSTALLED and \
+                    variant_list != self.VARIANT_INSTALLED_POSSIBLE:
+                        # Include all variants set in image.
+                        vimg = set(variants.keys())
+                else:
+                        # Don't include any set only in image.
+                        vimg = set()
+
+                # Get all variants found in packages and determine state.
+                vpkg = {}
+                excludes = self._img.list_excludes()
+                vposs = collections.defaultdict(set)
+                if variant_list != self.VARIANT_IMAGE:
+                        # Only incur the overhead of reading through all
+                        # installed packages if not just listing variants set in
+                        # image or listing possible values for them.
+                        for f in self._img.gen_installed_pkgs():
+                                # The manifest must be loaded without
+                                # pre-applying excludes so that gen_variants()
+                                # can choose how to filter the actions.
+                                mfst = self._img.get_manifest(f,
+                                    ignore_excludes=True)
+                                for variant, vals in mfst.gen_variants(
+                                    excludes=excludes):
+                                        # Unlike facets, Variants class doesn't
+                                        # handle implicitly set values.
+                                        if variant[:14] == "variant.debug.":
+                                                # Debug variants are implicitly
+                                                # false and are not required
+                                                # to be set explicitly in the
+                                                # image.
+                                                vpkg[variant] = variants.get(
+                                                    variant, "false")
+                                        elif variant not in vimg:
+                                                # Although rare, packages with
+                                                # unknown variants (those not
+                                                # set in the image) can be
+                                                # installed as long as content
+                                                # does not conflict.  For those
+                                                # variants, return None.
+                                                vpkg[variant] = \
+                                                    variants.get(variant)
+
+                                        if (variant_list == \
+                                            self.VARIANT_ALL_POSSIBLE or
+                                            variant_list == \
+                                                self.VARIANT_IMAGE_POSSIBLE or
+                                            variant_list == \
+                                                self.VARIANT_INSTALLED_POSSIBLE):
+                                                # Build possible list of variant
+                                                # values.
+                                                vposs[variant].update(set(vals))
+
+                # Generate the results.
+                for name in misc.yield_matching("variant.",
+                    sorted(vimg | set(vpkg.keys())), patterns):
+                        try:
+                                yield (name, vpkg[name], sorted(vposs[name]))
+                        except KeyError:
+                                yield (name, variants[name],
+                                    sorted(vposs[name]))
+
         def freeze_pkgs(self, fmri_strings, dry_run=False, comment=None,
             unfreeze=False):
                 """Freeze/Unfreeze one or more packages."""
@@ -3915,7 +4093,7 @@
                                         pub = name = version = None
 
                                 links = hardlinks = files = dirs = \
-                                    size = licenses = cat_info = \
+                                    csize = size = licenses = cat_info = \
                                     description = None
 
                                 if PackageInfo.CATEGORIES in info_needed:
@@ -3966,7 +4144,7 @@
                                                     mfst, alt_pub=alt_pub)
 
                                         if PackageInfo.SIZE in info_needed:
-                                                size = mfst.get_size(
+                                                size, csize = mfst.get_size(
                                                     excludes=excludes)
 
                                         if act_opts & info_needed:
@@ -3987,7 +4165,7 @@
                                                             mfst.gen_key_attribute_value_by_type(
                                                             "dir", excludes))
                                 elif PackageInfo.SIZE in info_needed:
-                                        size = 0
+                                        size = csize = 0
 
                                 # Trim response set.
                                 if PackageInfo.STATE in info_needed:
@@ -4016,7 +4194,7 @@
                                     states=states, publisher=pub, version=release,
                                     build_release=build_release, branch=branch,
                                     packaging_date=packaging_date, size=size,
-                                    pfmri=pfmri, licenses=licenses,
+                                    csize=csize, pfmri=pfmri, licenses=licenses,
                                     links=links, hardlinks=hardlinks, files=files,
                                     dirs=dirs, dependencies=dependencies,
                                     description=description, attrs=attrs))