18867978 pkg firmware revision dep mechanism should be expanded for platform-specifics s12b97
authorjmcp <James.McPherson@oracle.com>
Fri, 01 Apr 2016 17:41:13 -0700
changeset 3337 0f807ee4f0fd
parent 3336 5dddd93baf64
child 3338 3c357007fc55
18867978 pkg firmware revision dep mechanism should be expanded for platform-specifics
src/modules/actions/depend.py
src/modules/client/firmware.py
src/modules/client/pkg_solver.py
src/tests/cli/t_origin_cpu.py
src/tests/cli/t_origin_fw.py
--- a/src/modules/actions/depend.py	Mon Apr 04 16:35:45 2016 +1000
+++ b/src/modules/actions/depend.py	Fri Apr 01 17:41:13 2016 -0700
@@ -21,7 +21,7 @@
 #
 
 #
-# Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
 #
 
 
@@ -40,7 +40,7 @@
 import pkg.fmri
 import pkg.version
 
-from pkg.client.firmware import Firmware
+from pkg.client.firmware import Driver, Cpu
 
 known_types = (
     "conditional",
@@ -60,6 +60,7 @@
 #
 DEPEND_SELF = "feature/package/dependency/self"
 
+
 class DependencyAction(generic.Action):
         """Class representing a dependency packaging object.  The fmri attribute
         is expected to be the pkg FMRI that this package depends on.  The type
@@ -106,9 +107,10 @@
                 generic.Action.__init__(self, data, **attrs)
                 try:
                         if self.attrs["type"] not in known_types:
-                                raise pkg.actions.InvalidActionError(str(self),
-                                    _("Unknown type ({0}) in depend action").format(
-                                    self.attrs["type"]))
+                                raise pkg.actions.InvalidActionError(
+                                    str(self),
+                                    _("Unknown type ({0}) in depend action").
+                                    format(self.attrs["type"]))
                 except KeyError:
                         raise pkg.actions.InvalidActionError(
                             str(self), _("Missing type attribute"))
@@ -128,64 +130,67 @@
 
                 errors = []
                 if fmri.pkg_name not in ppkgs_dict:
-                        errors.append(_("Package is not installed in "
-                            "parent image {0}").format(fmri.pkg_name))
+                        errors.append(
+                                _("Package is not installed in "
+                                  "parent image {0}").format(fmri.pkg_name))
                         return errors
 
                 pf = ppkgs_dict[fmri.pkg_name]
                 if fmri.publisher and fmri.publisher != pf.publisher:
                         # package is from a different publisher
                         errors.append(_("Package in parent is from a "
-                            "different publisher: {0}").format(pf))
+                                        "different publisher: {0}").
+                                      format(pf))
                         return errors
 
                 if pf.version == fmri.version or pf.version.is_successor(
-                    fmri.version, pkg.version.CONSTRAINT_AUTO):
+                                fmri.version, pkg.version.CONSTRAINT_AUTO):
                         # parent dependency is satisfied
                         return []
 
                 if pf.version.is_successor(fmri.version,
-                    pkg.version.CONSTRAINT_NONE):
+                                           pkg.version.CONSTRAINT_NONE):
                         errors.append(_("Parent image has a newer "
-                            "version of package {0}").format(pf))
+                                        "version of package {0}").format(pf))
                 else:
                         errors.append(_("Parent image has an older "
-                            "version of package {0}").format(pf))
+                                        "version of package {0}").format(pf))
 
                 return errors
 
         def __check_installed(self, image, installed_version, min_fmri,
-            max_fmri, required, ctype):
+                              max_fmri, required, ctype):
                 errors = []
                 if not installed_version:
                         return errors
                 vi = installed_version.version
                 if min_fmri and min_fmri.version and \
-                    min_fmri.version.is_successor(vi,
-                    pkg.version.CONSTRAINT_NONE):
+                    min_fmri.version.is_successor(
+                            vi, pkg.version.CONSTRAINT_NONE):
                         errors.append(
                             _("{dep_type} dependency {dep_val} "
-                            "is downrev ({inst_ver})").format(
-                            dep_type=ctype, dep_val=min_fmri,
-                            inst_ver=installed_version))
+                              "is downrev ({inst_ver})").format(
+                                    dep_type=ctype, dep_val=min_fmri,
+                                    inst_ver=installed_version))
                         return errors
                 if max_fmri and max_fmri.version and  \
                     vi > max_fmri.version and \
                     not vi.is_successor(max_fmri.version,
-                    pkg.version.CONSTRAINT_AUTO):
+                                        pkg.version.CONSTRAINT_AUTO):
                         errors.append(
                             _("{dep_type} dependency {dep_val} "
-                            "is uprev ({inst_ver})").format(
-                            dep_type=ctype, dep_val=max_fmri,
-                            inst_ver=installed_version))
+                              "is uprev ({inst_ver})").format(
+                                    dep_type=ctype, dep_val=max_fmri,
+                                    inst_ver=installed_version))
                         return errors
                 if required and pkgdefs.PKG_STATE_OBSOLETE in \
-                    image.get_pkg_state(installed_version):
+                   image.get_pkg_state(installed_version):
                         errors.append(
-                            _("{dep_type} dependency on an obsolete package "
-                            "({obs_pkg}); this package must be uninstalled "
-                            "manually").format(
-                            dep_type=ctype, obs_pkg=installed_version))
+                            _("{dep_type} dependency on an obsolete "
+                              "package ({obs_pkg}); this package must "
+                              "be uninstalled manually").format(
+                                      dep_type=ctype,
+                                      obs_pkg=installed_version))
                         return errors
                 return errors
 
@@ -210,8 +215,8 @@
 
                 if ctype not in known_types:
                         errors.append(
-                            _("Unknown type ({0}) in depend action").format(
-                            ctype))
+                                _("Unknown type ({0}) in depend action").
+                                format(ctype))
                         return errors, warnings, info
 
                 # get a list of fmris and do fmri token substitution
@@ -225,8 +230,8 @@
                 if ctype == "parent":
                         # handle "parent" dependencies here
                         assert len(pfmris) == 1
-                        errors.extend(self.__check_parent_installed(image,
-                            pfmris[0]))
+                        errors.extend(self.__check_parent_installed(
+                                image, pfmris[0]))
                         return errors, warnings, info
 
                 installed_versions = [
@@ -257,45 +262,55 @@
                         cfmri = pkg.fmri.PkgFmri(self.attrs["predicate"])
                         installed_cversion = image.get_version_installed(cfmri)
                         if installed_cversion is not None and \
-                            installed_cversion.is_successor(cfmri):
+                           installed_cversion.is_successor(cfmri):
                                 min_fmri = pfmri
                                 required = True
                 elif ctype == "group":
                         if pfmri.pkg_name not in \
-                            (image.avoid_set_get() | image.obsolete_set_get()):
+                           (image.avoid_set_get() | image.obsolete_set_get()):
                                 required = True
                 elif ctype == "require-any":
                         for ifmri, rpfmri in zip(installed_versions, pfmris):
-                                e = self.__check_installed(image, ifmri, rpfmri,
-                                    None, True, ctype)
+                                e = self.__check_installed(image, ifmri,
+                                                           rpfmri, None, True,
+                                                           ctype)
                                 if ifmri and not e:
                                         # this one is present and happy
                                         return [], [], []
                                 else:
                                         errors.extend(e)
-                        if not errors: # none was installed
-                                errors.append(_("Required dependency on one of "
-                                    "{0} not met").format(
-                                    ", ".join((str(p) for p in pfmris))))
+                        if not errors:  # none was installed
+                                errors.append(
+                                        _("Required dependency on one of "
+                                          "{0} not met").
+                                        format(", ".join((str(p)
+                                                          for p in pfmris))))
                         return errors, warnings, info
                 elif ctype == "origin" and pfmri.pkg_name.startswith(
-                    "feature/firmware/"):
-                        ok, reason = Firmware().check_firmware(self, pfmri.pkg_name)
+                                "feature/firmware/"):
+                        ok, reason = Driver().check(self, pfmri.pkg_name)
+                        if ok:
+                                return [], [], []
+                        else:
+                                errors.append(reason)
+                elif ctype == "origin" and pfmri.pkg_name.startswith(
+                                "feature/cpu"):
+                        ok, reason = Cpu().check(self, pfmri.pkg_name)
                         if ok:
                                 return [], [], []
                         else:
                                 errors.append(reason)
 
-                        # can only check origin firmware dependencies
-
                 # do checking for other dependency types
 
-                errors.extend(self.__check_installed(image, installed_version,
-                    min_fmri, max_fmri, required, ctype))
+                errors.extend(self.__check_installed(image,
+                                                     installed_version,
+                                                     min_fmri, max_fmri,
+                                                     required, ctype))
 
                 if required and not installed_version:
                         errors.append(_("Required dependency {0} is not "
-                            "installed").format(pfmri))
+                                        "installed").format(pfmri))
 
                 # cannot verify origin since it applys to upgrade
                 # operation, not final state
@@ -313,11 +328,11 @@
                         return []
 
                 #
-                # XXX Ideally, we'd turn the string into a PkgFmri, and separate
-                # the stem from the version, or use get_dir_path, but we can't
-                # create a PkgFmri without supplying a build release and without
-                # it creating a dummy timestamp.  So we have to split it apart
-                # manually.
+                # XXX Ideally, we'd turn the string into a PkgFmri, and
+                # separate the stem from the version, or use get_dir_path,
+                # but we can't create a PkgFmri without supplying a build
+                # release and without it creating a dummy timestamp.
+                # Instead we have to split it apart manually.
                 #
                 if isinstance(pfmris, six.string_types):
                         pfmris = [pfmris]
@@ -382,6 +397,7 @@
                         return (kvord(a), a[0])
 
                 JOIN_TOK = " \\\n    " + base_indent
+
                 def grow(a, b, rem_values, force_nl=False):
                         if not force_nl:
                                 lastnl = a.rfind("\n")
@@ -394,8 +410,8 @@
                                         max_len = 80
                                 else:
                                         # If V1 format, or there are more
-                                        # attributes to output, then account for
-                                        # line-continuation marker.
+                                        # attributes to output, then account
+                                        # for the line-continuation marker.
                                         max_len = 78
 
                                 # Note this length comparison doesn't include
@@ -411,16 +427,17 @@
                         first_line = True
 
                         # Total number of remaining attribute values to output.
-                        rem_count = sum(len(act.attrlist(k)) for k in act.attrs)
+                        rem_count = sum(len(act.attrlist(k))
+                                        for k in act.attrs)
 
                         # Now build the action output string an attribute at a
                         # time.
                         for k, v in sorted(six.iteritems(act.attrs),
-                            key=key_func):
+                                           key=key_func):
                                 # Newline breaks are only forced when there is
                                 # more than one value for an attribute.
                                 if not (isinstance(v, list) or
-                                    isinstance(v, set)):
+                                        isinstance(v, set)):
                                         nv = [v]
                                         use_force_nl = False
                                 else:
@@ -430,11 +447,12 @@
                                 for lmt in sorted(nv):
                                         force_nl = use_force_nl and \
                                             k.startswith("pkg.debug")
-                                        aout = grow(aout,
-                                            "=".join((k,
-                                                generic.quote_attr_value(lmt))),
-                                            rem_count,
-                                            force_nl=force_nl)
+                                        aout = grow(aout, "=".join(
+                                                (k,
+                                                 generic.quote_attr_value(
+                                                         lmt))),
+                                                    rem_count,
+                                                    force_nl=force_nl)
                                         # Must be done for each value.
                                         if first_line and JOIN_TOK in aout:
                                                 first_line = False
@@ -450,24 +468,28 @@
                 This is primarily intended for use during publication or during
                 error handling to provide additional diagonostics.
 
-                'fmri' is an optional package FMRI (object or string) indicating
-                what package contained this action."""
+                'fmri' is an optional package FMRI (object or string)
+                indicating what package contained this action."""
 
                 required_attrs = ["type"]
                 dtype = self.attrs.get("type")
                 if dtype == "conditional":
                         required_attrs.append("predicate")
 
-                errors = generic.Action._validate(self, fmri=fmri,
-                    raise_errors=False, required_attrs=required_attrs,
-                    single_attrs=("predicate", "root-image", "ignore-check"))
+                errors = generic.Action._validate(
+                        self, fmri=fmri, raise_errors=False,
+                        required_attrs=required_attrs,
+                        single_attrs=("predicate", "root-image",
+                                      "ignore-check"))
 
                 if "predicate" in self.attrs and dtype != "conditional":
-                        errors.append(("predicate", _("a predicate may only be "
-                            "specified for conditional dependencies")))
+                        errors.append(("predicate",
+                                       _("a predicate may only be specified "
+                                         "for conditional dependencies")))
                 if "root-image" in self.attrs and dtype != "origin":
-                        errors.append(("root-image", _("the root-image "
-                            "attribute is only valid for origin dependencies")))
+                        errors.append(("root-image",
+                                       _("the root-image attribute is only "
+                                         "valid for origin dependencies")))
 
                 # Logic here intentionally treats 'predicate' and 'fmri' as
                 # having multiple values for simplicity.
@@ -476,16 +498,20 @@
                                 try:
                                         pkg.fmri.PkgFmri(f)
                                 except (pkg.version.VersionError,
-                                    pkg.fmri.FmriError) as e:
+                                        pkg.fmri.FmriError) as e:
                                         if attr == "fmri" and f == "__TBD":
                                                 # pkgdepend uses this special
                                                 # value.
                                                 continue
-                                        errors.append((attr, _("invalid "
-                                            "{attr} value '{value}': "
-                                            "{error}").format(attr=attr,
-                                            value=f, error=str(e))))
+                                        errors.append((
+                                                attr,
+                                                _("invalid {attr} value "
+                                                  "'{value}': {error}").
+                                                format(attr=attr,
+                                                       value=f,
+                                                       error=str(e))))
 
                 if errors:
-                        raise pkg.actions.InvalidActionAttributesError(self,
-                            errors, fmri=fmri)
+                        raise pkg.actions.InvalidActionAttributesError(
+                                self,
+                                errors, fmri=fmri)
--- a/src/modules/client/firmware.py	Mon Apr 04 16:35:45 2016 +1000
+++ b/src/modules/client/firmware.py	Fri Apr 01 17:41:13 2016 -0700
@@ -21,8 +21,9 @@
 #
 
 #
-# Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
 #
+
 import os.path
 import six
 import sys
@@ -37,92 +38,161 @@
 
 class Firmware(object):
         def __init__(self):
-            self.__firmware = {} # cache of things we've checked already
+            self._cache = {}  # cache of things we've checked already
 
-        def check_firmware(self, dep_action, firmware_name):
-                """Check firmware dependency.
-                returns ((true, false, none (internal error)),
-                error text)"""
+        def _check(self, dep_action, which):
+                """Performs the subprocess invocation and returns
+                (status, outputbuffer) to the caller"""
 
-                firmware_dir = "/usr/lib/fwenum"
                 # leverage smf test infrastructure
                 cmds_dir = DebugValues["smf_cmds_dir"]
                 if DebugValues["firmware-dependency-bypass"]:
                         return (True, None)
-                if cmds_dir: # we're testing;
+                if cmds_dir:  # we're testing;
                         firmware_dir = cmds_dir
+                else:
+                        firmware_dir = "/usr/lib/fwenum"
 
-                args = [os.path.join(firmware_dir, firmware_name[len("feature/firmware/"):])]
+                args = [os.path.join(firmware_dir, which)]
                 args.extend([
-                    "{0}={1}".format(k, quote_attr_value(v))
-                    for k,v in sorted(six.iteritems(dep_action.attrs))
-                    if k not in ["type", "root-image", "fmri"]
+                        "{0}={1}".format(k, quote_attr_value(v))
+                        for k, v in sorted(six.iteritems(dep_action.attrs))
                 ])
 
-                key = str(args)
+                # Set up the default return values
+                ret = 0
+                buf = ""
 
                 # use a cache since each check may be expensive and each
                 # pkg version may have the same dependency.
                 # ignore non-solaris systems here
 
-                if portable.osname != "sunos" and key not in self.firmware:
-                    self.__firmware[key] = (True, None)
+                if portable.osname != "sunos" and key not in self._cache:
+                        self._cache[key] = (True, None)
 
-                if key not in self.__firmware:
+                if str(args) not in self._cache:
                         try:
-                                proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+                                proc = subprocess.Popen(
+                                    args,
+                                    stdout=subprocess.PIPE,
                                     stderr=subprocess.STDOUT)
                                 buf = proc.stdout.readlines()
                                 ret = proc.wait()
-                                # if there was output, something went wrong.
-                                # Since generic errors are often exit(1),
-                                # map this to an internal error.
-                                if ret == 1 and len(buf) > 0:
-                                        ret = 255
-                                if ret == 0:
-                                        ans = (True, None)
-                                elif 0 < ret <= 239:
-                                        ans = (False, (_("There are {0} instances"
-                                            " of downrev firmware for the '{1}' "
-                                            " devices present on this system. "
-                                            "Update each to version {2} or better."
-                                            ).format(ret, args[1],
-                                            dep_action.attrs.get("minimum-version",
-                                            _("UNSPECIFIED")))))
-                                elif ret == 240:
-                                        ans = (False, (_("There are 240 or more "
-                                            "instances of downrev firmware for the"
-                                            "'{0}' devices present on this system. "
-                                            "Update each to version {1} or better."
-                                            ).format(args[1],
-                                            dep_action.attrs.get("minimum-version",
-                                            _("UNSPECIFIED")))))
-                                elif ret < 0:
-                                        ans = (None,
-                                            (_("Firmware dependency error: {0} "
-                                            " exited due to signal {1}").format(
-                                            " ".join(args), misc.signame(-ret))))
-                                else:
-                                        ans = (None,
-                                            (_("Firmware dependency error: General "
-                                            "internal error {0} running '{1}': '{2}'"
-                                            ).format(str(ret), " ".join(args),
-                                            "\n".join(buf))))
 
                         except OSError as e:
                                 # we have no enumerator installed.  This can
-                                # occur if this driver is being installed
-                                # for the first time or, more likely, we
-                                # just added enumerators & a firmware dependency
-                                # for the first time.  For now, drive on and
-                                # ignore this to permit the addition of such
-                                # dependencies concurrently with their
-                                # enumerarators.
-                                # ans = (None, (_("Firmware dependency error:"
-                                # " Cannot exec {0}: {1}").format(" ".join(args)
-                                # , str(e))))
-                                ans = (True, 0)
+                                # occur if this driver is being installed for
+                                # the first time or, more likely, we just added
+                                # enumerators and a firmware dependency for the
+                                # first time. For now, drive on and ignore this
+                                # to permit the addition of such dependencies
+                                # concurrently with their enumerarators.
+                                buf = (_("Firmware dependency error:"
+                                         " Cannot exec {0}: {1}").format(
+                                                 " ".join(args), str(e)))
+                                ret = -1
+                return (ret, buf, args)
+
+
+class Cpu(Firmware):
+        """Check cpu dependency.
+        returns ((true, false, none (internal error)), error text)"""
+
+        def check(self, dep_action, enumerator):
+                (ret, buf, args) = self._check(dep_action, which="cpu")
+                if ret == 0:
+                        ans = (True, None)
+                elif ret == -1:
+                        ans = (True, 0)
+                elif ret == 1:
+                        # the cpu version enumerator returns 1 and
+                        # prints the appropriate string if the system
+                        # we're running this on does not match the
+                        # required (include/exclude) condition.
+                        # We're not checking for valid args here since
+                        # since that's already been done by the enumerator
+                        checkargs = dict([j.split("=") for j in
+                                         args if j.startswith("check.")])
+                        pvtype = checkargs["check.version-type"]
+                        if pvtype == "iset":
+                            vtype = "Instruction Set Element(s)"
+                        elif pvtype == "plat":
+                            vtype = "Platform Name(s)"
+                        else:
+                            vtype = "CPU Name(s)"
+                        try:
+                            innit = checkargs["check.include"] or None
+                            mesg = "include: {0}".format(innit)
+                        except KeyError, ke:
+                            pass
+                        try:
+                            notit = checkargs["check.exclude"] or None
+                            mesg = "exclude: {0}".format(notit)
+                        except KeyError, ke:
+                            pass
 
-                        self.__firmware[key] = ans
+                        ans = (False,
+                               (_("cpu dependency error: '{0}' does "
+                                  "not meet the the minimum "
+                                  "requirement for {1} {2}\n").
+                                format("".join(buf).rstrip(),
+                                       vtype, mesg)))
+                else:
+                        # enumerator error
+                        ans = (False,
+                               (_("cpu dependency error: "
+                                  "Unable to verify cpu type\n")))
+                key = str(args)
+                self._cache[key] = ans
+                return self._cache[key]
+
+
+class Driver(Firmware):
+        """Check driver firmware dependency.
+        returns ((true, false, none (internal error)), error text)"""
+
+        def check(self, dep_action, enumerator):
+                which = enumerator[len("feature/firmware/"):]
+                (ret, buf, args) = self._check(dep_action, which=which)
+                # if there was output, something went wrong.
 
-                return self.__firmware[key]
+                min_ver = dep_action.attrs.get(
+                    "check.minimum-version",
+                    dep_action.attrs.get("minimum-version", _("UNSPECIFIED")))
+                # Since generic errors are often exit(1),
+                # map this to an internal error.
+                if ret == 1 and len(buf) > 0:
+                        ret = 255
+                if ret == 0:
+                        ans = (True, None)
+                elif ret == -1:
+                        ans = (True, 0)
+                elif 0 < ret <= 239:
+                        ans = (False,
+                               (_("There are {0} instances of downrev "
+                                  "firmware for the '{1}' devices present "
+                                  "on this system. Update each to version "
+                                  "{2} or newer.").
+                                format(ret, ret, min_ver)))
+                elif ret == 240:
+                        ans = (False,
+                               (_("There are 240 or more "
+                                  "instances of downrev firmware for the"
+                                  "'{0}' devices present on this system. "
+                                  "Update each to version {1} or better.").
+                                format(ret, min_ver)))
+                elif ret < 0:
+                        ans = (None,
+                               (_("Firmware dependency error: {0} "
+                                  "exited due to signal {1}").format(
+                                          " ".join(buf), misc.signame(-ret))))
+                else:
+                        ans = (None,
+                               (_("Firmware dependency error: General "
+                                  "internal error {0} running '{1}': '{2}'").
+                                format(str(ret),
+                                       " ".join(buf),
+                                       "\n".join(buf))))
+                key = str(args)
+                self._cache[key] = ans
+                return self._cache[key]
--- a/src/modules/client/pkg_solver.py	Mon Apr 04 16:35:45 2016 +1000
+++ b/src/modules/client/pkg_solver.py	Fri Apr 01 17:41:13 2016 -0700
@@ -46,7 +46,7 @@
 from functools import reduce
 from itertools import chain
 from pkg.client.debugvalues import DebugValues
-from pkg.client.firmware import Firmware
+from pkg.client.firmware import Driver, Cpu
 from pkg.client.pkgdefs import PKG_OP_UNINSTALL, PKG_OP_UPDATE
 from pkg.misc import EmptyI, EmptyDict, N_
 # Unable to import; pylint: disable=F0401
@@ -87,7 +87,8 @@
 _TRIM_VARIANT = 21                 # unsupported variant (e.g. i386 on sparc)
 _TRIM_EXPLICIT_INSTALL = 22        # pkg.depend.explicit-install is true.
 _TRIM_SYNCED_INC = 23              # incorporation must be in sync with parent
-_TRIM_MAX = 24                     # number of trim constants
+_TRIM_CPU = 24                     # cpu version requirement
+_TRIM_MAX = 25                     # number of trim constants
 
 
 class DependencyException(Exception):
@@ -219,8 +220,9 @@
                             for f in self.__parent_pkgs
                         ])
 
-                # cache of firmware dependencies
-                self.__firmware = Firmware()
+                # cache of firmware and cpu dependencies
+                self.__firmware = Driver()
+                self.__cpu = Cpu()
 
                 self.__triggered_ops = {
                     PKG_OP_UNINSTALL : {
@@ -287,6 +289,7 @@
                 self.__dependents = None
                 self.__fmridict = {}
                 self.__firmware = None
+                self.__cpu = None
                 self.__allowed_downgrades = None
                 self.__dg_incorp_cache = None
                 self.__linked_pkgs = set()
@@ -3093,11 +3096,11 @@
                         req_fmri = pkg.fmri.PkgFmri(da.attrs["fmri"])
 
                         if da.attrs.get("root-image", "").lower() == "true":
+                                # Are firmware (driver) updates needed?
                                 if req_fmri.pkg_name.startswith(
                                     "feature/firmware/"):
-                                        # this is a firmware dependency
                                         fw_ok, reason = \
-                                            self.__firmware.check_firmware(da,
+                                            self.__firmware.check(da,
                                             req_fmri.pkg_name)
                                         if not fw_ok:
                                                 self.__trim((fmri,),
@@ -3105,6 +3108,20 @@
                                                 return False
                                         continue
 
+                                # Check that the CPU is supported in the
+                                # new root-image
+                                if req_fmri.pkg_name.startswith(
+                                    "feature/cpu"):
+                                        cpu_ok, reason = \
+                                            self.__cpu.check(da,
+                                            req_fmri.pkg_name)
+                                        if not cpu_ok:
+                                                self.__trim((fmri,),
+                                                            _TRIM_CPU,
+                                                            reason)
+                                                return False
+                                        continue
+
                                 if self.__root_fmris is None:
                                         img = pkg.client.image.Image(
                                             misc.liveroot(),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tests/cli/t_origin_cpu.py	Fri Apr 01 17:41:13 2016 -0700
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+# -*- coding: utf-8
+# 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) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+
+import testutils
+if __name__ == "__main__":
+        testutils.setup_environment("../../../proto")
+
+import os
+import pkg5unittest
+import sys
+import unittest
+
+import pkg.portable as portable
+
+
+class TestPkgCpuDependencies(pkg5unittest.SingleDepotTestCase):
+        # Only start/stop the depot once (instead of for every test)
+        persistent_setup = True
+        # leverage smf test infrastructure here
+        smf_cmds = {
+            "cpu": """#!/usr/bin/python
+from __future__ import print_function
+import os
+import resource
+import sys
+
+
+def main():
+    installed_version = os.environ.get("PKG_INSTALLED_VERSION", None)
+    if not installed_version:
+        return 0
+    for n in range(1, len(sys.argv)):
+        key, value = sys.argv[n].split("=", 1)
+        if key == "check.include":
+            if installed_version > value is True:
+                 print("{0}".format(key), file=sys.stderr)
+                 return 1
+            return 0
+        if key == "check.exclude":
+            if value > installed_version is True:
+                 print("{0}".format(key), file=sys.stderr)
+                 return 1
+            return 0
+        if key == "dump_core":
+            # avoid creating a core file
+            resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
+            os.abort()
+    print("Neither check.include nor check.exclude specified in dependency")
+    return 242
+
+if __name__ == "__main__":
+        # Make all warnings be errors.
+        import warnings
+        warnings.simplefilter('error')
+
+        sys.exit(main())
+"""}
+
+        def setUp(self):
+
+                pkg5unittest.SingleDepotTestCase.setUp(self)
+
+                self.pkg_list = []
+
+                for t in (
+                                ("1.0", "cpu", "argo"),
+                                ("1.1", "cpu", "babinda"),
+                                ("1.2", "cpu", "coolabah"),
+                                ("1.3", "cpu", "drongo"),
+                                ("2.0", "platform", "dropbear"),
+                                ("2.1", "platform", "hoopsnake"),
+                                ("2.2", "platform", "oodnadatta"),
+                                ("3.0", "iset", "absinthe"),
+                                ("3.1", "iset", "bolli"),
+                                ("3.4", "iset", "limoncello")
+                ):
+                    self.pkg_list += ["""
+                    open A@{0},5.11-0
+                    add depend type=origin root-image=true fmri=pkg:/feature/cpu check.version-type={1} check.include={2}
+                    close
+                    open B@{3},5.12-0
+                    add depend type=origin root-image=true fmri=pkg:/feature/cpu check.version-type={4} check.exclude={5}
+                    close """.format(*(t + t))]
+
+                self.pkg_list += ["""
+                    open [email protected],5.11-0
+                    add depend type=origin root-image=true fmri=pkg:/feature/cpu
+                    close """]
+
+                self.pkg_list += ["""
+                    open [email protected],5.12-0
+                    add depend type=origin root-image=true fmri=pkg:/feature/cpu dump_core=1
+                    close"""]
+
+                self.pkg_list += ["""
+                    open [email protected],5.11-0
+                    add depend type=origin root-image=true fmri=pkg:/feature/cpu
+                    close"""]
+
+        def test_cpu_dependency(self):
+                """test origin cpu dependency
+                cpu test simulator uses alphabetic comparison"""
+
+                if portable.osname != "sunos":
+                        raise pkg5unittest.TestSkippedException(
+                            "cpu check unsupported on this platform.")
+
+                rurl = self.dc.get_repo_url()
+                plist = self.pkgsend_bulk(rurl, self.pkg_list)
+                self.image_create(rurl)
+                os.environ["PKG_INSTALLED_VERSION"] = "hoopsnake"
+                # trim some of the versions out; note that pkgs w/ cpu
+                # errors/problems are silently ignored.
+                self.pkg("install -v [email protected] [email protected]")
+                self.pkg("list -v")
+                os.environ["PKG_INSTALLED_VERSION"] = "dropbear"
+                self.pkg("install [email protected]", 1)
+                # exercise general error codes
+                self.pkg("install [email protected]", 1)
+                # verify that upreving the cpu lets us install more
+                self.pkg("list")
+                os.environ["PKG_INSTALLED_VERSION"] = "coolabah"
+                self.pkg("update *@latest")
+                self.pkg("list")
+                self.pkg("verify [email protected]", 1)
+                del os.environ["PKG_INSTALLED_VERSION"]
+                self.pkg("list")
+                # this is a trick, there is no v1.6
+                self.pkg("verify [email protected]", 1)
+                # check that we ignore dependencies w/ missing enumerators
+                self.pkg("install [email protected]")
+                self.pkg("list")
--- a/src/tests/cli/t_origin_fw.py	Mon Apr 04 16:35:45 2016 +1000
+++ b/src/tests/cli/t_origin_fw.py	Fri Apr 01 17:41:13 2016 -0700
@@ -20,8 +20,8 @@
 # CDDL HEADER END
 #
 # Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+from __future__ import print_function
 
-from __future__ import print_function
 import testutils
 if __name__ == "__main__":
         testutils.setup_environment("../../../proto")
@@ -29,16 +29,16 @@
 import os
 import pkg5unittest
 import unittest
+import sys
+import pkg.portable as portable
 
-import pkg.portable as portable
 
 class TestPkgFWDependencies(pkg5unittest.SingleDepotTestCase):
         # Only start/stop the depot once (instead of for every test)
         persistent_setup = True
         # leverage smf test infrastructure here
         smf_cmds = {
-            "testdriver" :
-"""#!/usr/bin/python
+            "fwenum": """#!/usr/bin/python
 import os
 import resource
 import sys
@@ -48,21 +48,20 @@
     devs_present = os.environ.get("PKG_NUM_FAKE_DEVICES", "1")
     if not installed_version:
         return 0
-    for arg in sys.argv[1:]:
-        if arg.startswith("minimum-version="):
-            a = installed_version
-            b = arg[len("minimum-version="):]
-            c = (a > b) - (a < b)
-            if c < 0:
+    for n in range(1, len(sys.argv)):
+        key, value = sys.argv[n].split("=", 1)
+        c = installed_version < value
+        if key == "check.minimum-version" or key == "minimum-version":
+            if c is False:
                if int(devs_present) > 240:
                    devs_present = "240"
                return int(devs_present)
             return 0
-        if arg.startswith("dump_core="):
+        if key == "dump_core":
             # avoid creating a core file
             resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
             os.abort()
-    print("attribute minimum-version not specified in dependency")
+    print("attribute check.minimum-version not specified in dependency")
     return 241
 
 if __name__ == "__main__":
@@ -84,71 +83,70 @@
                     ("1.1", "elf"),
                     ("1.2", "hobbit"),
                     ("1.3", "wizard")
-                    ):
-                    self.pkg_list+= ["""
+                ):
+                    self.pkg_list += ["""
                     open A@{0},5.11-0
-                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/testdriver minimum-version={1}
+                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/fwenum check.minimum-version={1}
                     close
                     open B@{2},5.11-0
-                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/testdriver minimum-version={3}
-                    close """.format(*(t + t)) ]
+                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/fwenum check.minimum-version={3}
+                    close """.format(*(t + t))]
 
-		self.pkg_list += ["""
+                self.pkg_list += ["""
                     open [email protected],5.11-0
-                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/testdriver
+                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/fwenum
                     close """]
 
-		self.pkg_list += ["""
+                self.pkg_list += ["""
                     open [email protected],5.11-0
-                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/testdriver dump_core=1
+                    add depend type=origin root-image=true fmri=pkg:/feature/firmware/fwenum dump_core=1
                     close"""]
 
-		self.pkg_list += ["""
+                self.pkg_list += ["""
                     open [email protected],5.11-0
                     add depend type=origin root-image=true fmri=pkg:/feature/firmware/no-such-enumerator
                     close"""]
 
         def test_fw_dependency(self):
                 """test origin firmware dependency"""
-		"""firmware test simulator uses alphabetic comparison"""
+                """firmware test simulator uses alphabetic comparison"""
 
-		if portable.osname != "sunos":
-			raise pkg5unittest.TestSkippedException(
-			    "Firmware check unsupported on this platform.")
+                if portable.osname != "sunos":
+                        raise pkg5unittest.TestSkippedException(
+                            "Firmware check unsupported on this platform.")
 
                 rurl = self.dc.get_repo_url()
                 plist = self.pkgsend_bulk(rurl, self.pkg_list)
                 self.image_create(rurl)
 
                 os.environ["PKG_INSTALLED_VERSION"] = "elf"
-		# trim some of the versions out; note that pkgs w/ firmware
-		# errors/problems are silently ignored.
+                # trim some of the versions out; note that pkgs w/ firmware
+                # errors/problems are silently ignored.
                 self.pkg("install A B")
-		self.pkg("list")
-                self.pkg("verify [email protected]")
-		# test verify by changing device version
-		os.environ["PKG_INSTALLED_VERSION"] = "dwarf"
-		self.pkg("verify [email protected]", 1)
+                self.pkg("list -v [email protected] [email protected]")
+                # test verify by changing device version
+                os.environ["PKG_INSTALLED_VERSION"] = "dwarf"
+                self.pkg("verify [email protected]", 1)
                 os.environ["PKG_INSTALLED_VERSION"] = "elf"
-		# exercise large number of devices code
+                # exercise large number of devices code
                 os.environ["PKG_NUM_FAKE_DEVICES"] = "500"
-		self.pkg("install [email protected]", 1)
-		# exercise general error codes
-		self.pkg("install [email protected]", 1)
-		self.pkg("install [email protected]", 1)
-		# verify that upreving the firmware lets us install more
+                self.pkg("install [email protected]", 4)
+                # exercise general error codes
+                self.pkg("install [email protected]", 1)
+                self.pkg("install [email protected]", 1)
+                # verify that upreving the firmware lets us install more
                 os.environ["PKG_INSTALLED_VERSION"] = "hobbit"
-		self.pkg("update")
-		self.pkg("verify [email protected]")
-		# simulate removing device
-		del os.environ["PKG_INSTALLED_VERSION"]
-		self.pkg("update")
-		self.pkg("list")
-		self.pkg("verify [email protected]")
-		# ok since we never drop core
-		# here as device
-		# doesn't exist.
+                del os.environ["PKG_NUM_FAKE_DEVICES"]
+                self.pkg("update -nv", 4)
+                self.pkg("verify [email protected]", 1)
+                # simulate removing device
+                del os.environ["PKG_INSTALLED_VERSION"]
+                self.pkg("list -v")
+                self.pkg("update")
+                self.pkg("list -v")
+                self.pkg("verify [email protected]")
+                # ok since we never drop core here since device
+                # doesn't exist.
 
-		# check that we ignore dependencies w/ missing enumerators for now
-		self.pkg("install [email protected]")
-
+                # check that we ignore dependencies w/ missing enumerators
+                self.pkg("install [email protected]")