577 need service action for smf(5) manifests
authorBart Smaalders <Bart.Smaalders@Sun.COM>
Fri, 10 Oct 2008 22:15:14 -0700
changeset 584 22bc748edce6
parent 583 fc856572d86e
child 585 3ea97a12eea4
577 need service action for smf(5) manifests 578 need restart action or equivalent to poke smf(5) services
src/client.py
src/modules/client/actuator.py
src/modules/client/debugvalues.py
src/modules/client/imageplan.py
src/tests/baseline.txt
src/tests/cli/t_actuators.py
src/tests/run.py
src/util/distro-import/86/reboot
src/util/distro-import/86/smf_manifests
src/util/distro-import/87/common/SUNWcsl
src/util/distro-import/Makefile
src/util/distro-import/solaris.py
--- a/src/client.py	Fri Oct 10 16:32:27 2008 -0700
+++ b/src/client.py	Fri Oct 10 22:15:14 2008 -0700
@@ -72,6 +72,7 @@
 import pkg.version
 import pkg.Uuid25
 import pkg
+from pkg.client.debugvalues import DebugValues
 
 from pkg.client.history import RESULT_FAILED_UNKNOWN
 from pkg.client.history import RESULT_CANCELED
@@ -145,6 +146,7 @@
 
 Options:
         -R dir
+        -D/--debug name=value
 
 Environment:
         PKG_IMAGE"""))
@@ -1931,10 +1933,21 @@
         gettext.install("pkg", "/usr/lib/locale")
 
         try:
-                opts, pargs = getopt.getopt(sys.argv[1:], "R:")
+                opts, pargs = getopt.getopt(sys.argv[1:], "R:D:", ["debug="])
         except getopt.GetoptError, e:
                 usage(_("illegal global option -- %s") % e.opt)
 
+        for opt, arg in opts:
+                if opt == "-D" or opt == "--debug":
+                        try:
+                                key, value = arg.split('=', 1)
+                        except:
+                                usage(_("%s takes argument of form name=value, not %s")
+                                    % (opt, arg))
+                        DebugValues.set_value(key, value)
+                elif opt == "-R":
+                        mydir = arg
+
         if pargs == None or len(pargs) == 0:
                 usage()
 
@@ -1950,6 +1963,9 @@
             global_settings.PKG_TIMEOUT_MAX))
 
         if subcommand == "image-create":
+                if "mydir" in locals():
+                        usage(_("-R not allowed for %s subcommand") % 
+                              subcommand)
                 try:
                         ret = image_create(img, pargs)
                 except getopt.GetoptError, e:
@@ -1957,6 +1973,9 @@
                             (subcommand, e.opt))
                 return ret
         elif subcommand == "version":
+                if "mydir" in locals():
+                        usage(_("-R not allowed for %s subcommand") % 
+                              subcommand)
                 if pargs:
                         usage(_("version: command does not take operands " \
                             "('%s')") % " ".join(pargs))
@@ -1971,10 +1990,6 @@
         provided_image_dir = True
         pkg_image_used = False
                 
-        for opt, arg in opts:
-                if opt == "-R":
-                        mydir = arg
-
         if "mydir" not in locals():
                 try:
                         mydir = os.environ["PKG_IMAGE"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/client/actuator.py	Fri Oct 10 22:15:14 2008 -0700
@@ -0,0 +1,279 @@
+#!/usr/bin/python2.4
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+
+class GenericActuator(object):
+        """Actuators are action attributes that cause side effects
+        on live images when those actions are updated, installed
+        or removed.  Since no side effects are caused when the
+        affected image isn't the current root image, the OS may
+        need to cause the equivalent effect during boot.
+        """
+
+        actuator_attrs = set()
+
+        def __init__(self):
+                self.install = {}
+                self.removal = {}
+                self.update =  {}
+
+
+        def scan_install(self, attrs):
+                self.__scan(self.install, attrs)
+
+        def scan_removal(self, attrs):
+                self.__scan(self.removal, attrs)
+
+        def scan_update(self, attrs):
+                self.__scan(self.update, attrs)
+
+        def __scan(self, dictionary, attrs):
+                for a in set(attrs.keys()) & self.actuator_attrs:
+                        if a in dictionary:
+                                dictionary[a].add(attrs[a])
+                        else:
+                                dictionary[a] = set([attrs[a]])
+
+        def clone_required(self):
+                return False
+
+        def exec_prep(self, image):
+                pass
+
+        def exec_pre_actuators(self, image):
+                pass
+
+        def exec_post_actuators(self, image):
+                pass
+
+        def exec_fail_actuators(self, image):
+                pass
+
+        def __str__(self):
+                return "Removals: %s\nInstalls: %s\nUpdates: %s\n" % \
+                    (self.removal, self.install, self.update)
+
+# range of possible SMF service states
+SMF_SVC_UNKNOWN      = 0
+SMF_SVC_DISABLED     = 1
+SMF_SVC_MAINTENANCE  = 2
+SMF_SVC_TMP_DISABLED = 3
+SMF_SVC_TMP_ENABLED  = 4
+SMF_SVC_ENABLED      = 5
+
+import os
+import subprocess
+from pkg.client.debugvalues import DebugValues
+
+svcprop_path = "/usr/bin/svcprop"
+svcadm_path  = "/usr/sbin/svcadm"
+
+class Actuator(GenericActuator):
+        """Solaris specific Actuator implementation..."""
+
+        actuator_attrs = set([
+            "reboot_on_update", # have to reboot to update this file
+            "refresh_fmri",     # refresh this service on any change
+            "restart_fmri",     # restart this service on any change
+            "suspend_fmri",     # suspend this service during update
+            "disable_fmri"      # disable this service prior to removal
+            ])
+
+        def __init__(self):
+                GenericActuator.__init__(self)
+                self.suspend_fmris = None
+                self.tmp_suspend_fmris = None
+                self.do_nothing = True
+                self.cmd_path = ""
+
+        def clone_required(self):
+                return "reboot_on_update" in self.update
+
+        def exec_prep(self, image):
+                if not image.is_liveroot():
+                        dir = DebugValues.get_value("actuator_cmds_dir")
+                        if not dir:
+                                return
+                        self.cmd_path = dir
+                self.do_nothing = False
+
+        def exec_pre_actuators(self, image):
+                """do pre execution actuator processing..."""
+
+                if self.do_nothing:
+                        return
+
+                suspend_fmris = self.update.get("suspend_fmri", set())
+                tmp_suspend_fmris = set()
+
+                disable_fmris = self.removal.get("disable_fmri", set())
+
+                # eliminate services not loaded or not running
+                # remember those services enabled only temporarily
+
+                for fmri in suspend_fmris.copy():
+                        state = self.__smf_svc_get_state(fmri)
+                        if state <= SMF_SVC_TMP_ENABLED:
+                                suspend_fmris.remove(fmri)
+                        if state == SMF_SVC_TMP_ENABLED:
+                                tmp_suspend_fmris.add(fmri)
+
+                for fmri in disable_fmris.copy():
+                        if self.__smf_svc_is_disabled(fmri):
+                                disable_fmris.remove(fmri)
+
+                self.suspend_fmris = suspend_fmris
+                self.tmp_suspend_fmris = tmp_suspend_fmris
+
+                args = (svcadm_path, "disable", "-t")
+
+                params = tuple(suspend_fmris | tmp_suspend_fmris)
+
+                if params:
+                        self.__call(args + params)
+
+                args = (svcadm_path, "disable")
+                params = tuple(disable_fmris)
+
+                if params:
+                        self.__call(args + params)
+
+        def exec_fail_actuators(self, image):
+                """handle a failed install"""
+
+                if self.do_nothing:
+                        return
+
+                args = (svcadm_path, "mark", "maintenance")
+                params = tuple(self.suspend_fmris | 
+                    self.tmp_suspend_fmris)
+
+                if params:
+                        self.__call(args + params)
+
+        def exec_post_actuators(self, image):
+                """do post execution actuator processing"""
+
+                if self.do_nothing:
+                        return
+
+                refresh_fmris = self.removal.get("refresh_fmri", set()) | \
+                    self.update.get("refresh_fmri", set()) | \
+                    self.install.get("refresh_fmri", set())
+
+                restart_fmris = self.removal.get("restart_fmri", set()) | \
+                    self.update.get("restart_fmri", set()) | \
+                    self.install.get("restart_fmri", set())
+
+                # ignore services not present or not
+                # enabled
+
+                for fmri in refresh_fmris.copy():
+                        if self.__smf_svc_is_disabled(fmri):
+                                refresh_fmris.remove(fmri)
+
+                args = (svcadm_path, "refresh")
+                params = tuple(refresh_fmris)
+
+                if params:
+                    self.__call(args + params)
+
+                for fmri in restart_fmris.copy():
+                        if self.__smf_svc_is_disabled(fmri):
+                                restart_fmris.remove(fmri)
+
+                args = (svcadm_path, "restart")
+                params = tuple(restart_fmris)
+                if params:
+                        self.__call(args + params)
+
+                # reenable suspended services that were running
+                # be sure to not enable services that weren't running
+                # and temp. enable those services that were in that
+                # state.
+
+                args = (svcadm_path, "enable")
+                params = tuple(self.suspend_fmris)
+                if params:
+                        self.__call(args + params)
+
+                args = (svcadm_path, "enable", "-t")
+                params = tuple(self.tmp_suspend_fmris)
+                if params:
+                        self.__call(args + params)
+
+        def __smf_svc_get_state(self, fmri):
+                """ return state of smf service """
+
+                props = self.__get_smf_props(fmri)
+                if not props:
+                        return SMF_SVC_UNKNOWN
+
+                if "maintenance" in props["restarter/state"]:
+                        return SMF_SVC_MAINTENANCE
+
+                if "true" not in props["general/enabled"]:
+                        if "general_ovr/enabled" not in props:
+                                return SMF_SVC_DISABLED
+                        elif "true" in props["general_ovr/enabled"]:
+                                return SMF_SVC_TMP_ENABLED
+                else:
+                        if "general_ovr/enabled" not in props:
+                                return SMF_SVC_ENABLED
+                        elif "false" in props["general_ovr/enabled"]:
+                                return SMF_SVC_TMP_DISABLED
+
+        def __smf_svc_is_disabled(self, fmri):
+                return self.__smf_svc_get_state(fmri) < SMF_SVC_TMP_ENABLED
+
+        def __get_smf_props(self, svcfmri):
+                args = (svcprop_path, "-c", svcfmri)
+                buf = self.__call(args)
+                return dict([
+                    l.strip().split(None, 1)
+                    for l in buf
+                ])
+
+        def __call(self, args):
+                # a way to invoke a separate executable for testing
+                if self.cmd_path:
+                        args = (
+                            os.path.join(self.cmd_path,
+                            args[0].lstrip("/")),) + args[1:]
+                try:
+                        proc = subprocess.Popen(args, stdout = subprocess.PIPE,
+                            stderr = subprocess.STDOUT)
+                        buf = proc.stdout.readlines()
+                        ret = proc.wait()
+                except OSError, e:
+                        raise RuntimeError, "cannot execute %s" % args
+
+                if ret != 0:
+                        raise RuntimeError, "Subprocess (%s) non-zero exit code: %s" \
+                            % (args, buf)
+
+                return buf
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/client/debugvalues.py	Fri Oct 10 22:15:14 2008 -0700
@@ -0,0 +1,41 @@
+#!/usr/bin/python2.4
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+
+class DebugValues(object):
+        """ simple singleton class to handle debug variables """
+        __debug_values = {}
+
+        def __call__(self):
+                return self
+
+        def get_value(self, key):
+                """ returns None if not set """
+                return self.__debug_values.get(key, None)
+
+        def set_value(self, key, value):
+                self.__debug_values[key] = value
+
+DebugValues=DebugValues()
--- a/src/modules/client/imageplan.py	Fri Oct 10 16:32:27 2008 -0700
+++ b/src/modules/client/imageplan.py	Fri Oct 10 22:15:14 2008 -0700
@@ -31,6 +31,8 @@
 import pkg.client.indexer as indexer
 import pkg.search_errors as se
 from pkg.client.imageconfig import REQUIRE_OPTIONAL
+import pkg.client.actuator as actuator
+
 from pkg.client.filter import compile_filter
 from pkg.misc import msg
 from pkg.misc import CLIENT_DEFAULT_MEM_USE_KB
@@ -99,6 +101,8 @@
 
                 self.check_cancelation = check_cancelation
 
+                self.actuators = None
+
         def __str__(self):
                 if self.state == UNEVALUATED:
                         s = "UNEVALUATED:\n"
@@ -111,6 +115,8 @@
                 s = ""
                 for pp in self.pkg_plans:
                         s = s + "%s\n" % pp
+                
+                s = s + "Actuators:\n%s" % self.actuators
                 return s
 
         def get_plan(self, full=True):
@@ -127,6 +133,7 @@
         def display(self):
                 for pp in self.pkg_plans:
                         msg("%s -> %s" % (pp.origin_fmri, pp.destination_fmri))
+                msg("Actuators:\n" % self.actuators)
 
         def is_proposed_fmri(self, fmri):
                 for pf in self.target_fmris:
@@ -463,6 +470,8 @@
 
                 self.progtrack.evaluate_progress()
 
+                self.actuators = actuator.Actuator()
+
                 # iterate over copy of removals since we're modding list
                 # keep track of deletion count so later use of index works
                 named_removals = {}
@@ -485,6 +494,8 @@
                                     (i - deletions,
                                     id(self.removal_actions[i-deletions][1]))
 
+                        self.actuators.scan_removal(a[1].attrs)
+
                 self.progtrack.evaluate_progress()
 
                 for a in self.install_actions:
@@ -504,6 +515,8 @@
                                     cache_name
                                 a[2].attrs["save_file"] = cache_name
 
+                        self.actuators.scan_install(a[2].attrs)
+
                 self.progtrack.evaluate_progress()
                 # Go over update actions
                 l_actions = self.get_link_actions()
@@ -516,6 +529,12 @@
                                 path = a[2].attrs["path"]
                                 if path in l_actions:
                                         l_refresh.extend([(a[0], l, l) for l in l_actions[path]])
+
+                        # scan both old and new actions
+                        # repairs may result in update action w/o orig action
+                        if a[1]:
+                                self.actuators.scan_update(a[1].attrs)
+                        self.actuators.scan_update(a[2].attrs)
                 self.update_actions.extend(l_refresh)
 
                 # sort actions to match needed processing order
@@ -653,42 +672,55 @@
                         self.state = EXECUTED_OK
                         return
 
-                # execute removals
+                self.actuators.exec_prep(self.image)
+
+                self.actuators.exec_pre_actuators(self.image)
 
-                self.progtrack.actions_set_goal("Removal Phase", len(self.removal_actions))
-                for p, src, dest in self.removal_actions:
-                        p.execute_removal(src, dest)
-                        self.progtrack.actions_add_progress()
-                self.progtrack.actions_done()
+                try:
+                
+                        # execute removals
 
-                # execute installs
-
-                self.progtrack.actions_set_goal("Install Phase", len(self.install_actions))
+                        self.progtrack.actions_set_goal("Removal Phase",
+                            len(self.removal_actions))
+                        for p, src, dest in self.removal_actions:
+                                p.execute_removal(src, dest)
+                                self.progtrack.actions_add_progress()
+                        self.progtrack.actions_done()
 
-                for p, src, dest in self.install_actions:
-                        p.execute_install(src, dest)
-                        self.progtrack.actions_add_progress()
-                self.progtrack.actions_done()
+                        # execute installs
+
+                        self.progtrack.actions_set_goal("Install Phase",
+                            len(self.install_actions))
+
+                        for p, src, dest in self.install_actions:
+                                p.execute_install(src, dest)
+                                self.progtrack.actions_add_progress()
+                        self.progtrack.actions_done()
 
-                # execute updates
+                        # execute updates
 
-                self.progtrack.actions_set_goal("Update Phase", len(self.update_actions))
+                        self.progtrack.actions_set_goal("Update Phase",
+                            len(self.update_actions))
 
-                for p, src, dest in self.update_actions:
-                        p.execute_update(src, dest)
-                        self.progtrack.actions_add_progress()
+                        for p, src, dest in self.update_actions:
+                                p.execute_update(src, dest)
+                                self.progtrack.actions_add_progress()
 
-                self.progtrack.actions_done()
+                        self.progtrack.actions_done()
 
-
+                        # handle any postexecute operations
 
-                # handle any postexecute operations
+                        for p in self.pkg_plans:
+                                p.postexecute()
 
-                for p in self.pkg_plans:
-                        p.postexecute()
+                	self.image.clear_pkg_state()
+                        
+                except:
+                        self.actuators.exec_fail_actuators(self.image)                        
+                        raise
+                else:
+                        self.actuators.exec_post_actuators(self.image)
 
-                self.image.clear_pkg_state()
-                        
                 self.state = EXECUTED_OK
                 
                 # reduce memory consumption
@@ -700,6 +732,8 @@
                 del self.target_rem_fmris
                 del self.target_fmris
                 del self.__directories
+
+                del self.actuators
                 
                 # Perform the incremental update to the search indexes
                 # for all changed packages
--- a/src/tests/baseline.txt	Fri Oct 10 16:32:27 2008 -0700
+++ b/src/tests/baseline.txt	Fri Oct 10 22:15:14 2008 -0700
@@ -194,6 +194,7 @@
 api.t_version.py TestVersion.testversionsuccessor7|pass
 api.t_version.py TestVersion.testversionsuccessor8|pass
 api.t_version.py TestVersion.testversionsuccessor9|pass
+cli.t_actuators.py TestPkgActuators.test_actuators|pass
 cli.t_api.py TestPkgApi.test_bad_orderings|pass
 cli.t_api.py TestPkgApi.test_reset|pass
 cli.t_api_info.py TestApiInfo.test_info_local_remote|pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tests/cli/t_actuators.py	Fri Oct 10 22:15:14 2008 -0700
@@ -0,0 +1,339 @@
+#!/usr/bin/python2.4
+#
+# 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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+import testutils
+if __name__ == "__main__":
+        testutils.setup_environment("../../../proto")
+
+import os
+import unittest
+import shutil
+
+class TestPkgActuators(testutils.SingleDepotTestCase):
+        # Only start/stop the depot once (instead of for every test)
+        persistent_depot = True
+
+        svcprop_enabled_data = \
+"""general/enabled boolean true
+general/entity_stability astring Unstable
+general/single_instance boolean true
+restarter/start_pid count 4172
+restarter/start_method_timestamp time 1222382991.639687000
+restarter/start_method_waitstatus integer 0
+restarter/transient_contract count 
+restarter/auxiliary_state astring none
+restarter/next_state astring none
+restarter/state astring online
+restarter/state_timestamp time 1222382991.644413000
+restarter_actions/refresh integer 
+restarter_actions/maint_on integer 
+restarter_actions/maint_off integer 
+restarter_actions/restart integer 
+local-filesystems/entities fmri svc:/system/filesystem/local
+local-filesystems/grouping astring require_all
+local-filesystems/restart_on astring none
+local-filesystems/type astring service
+remote-filesystems/entities fmri svc:/network/nfs/client svc:/system/filesystem/autofs
+remote-filesystems/grouping astring optional_all
+remote-filesystems/restart_on astring none
+remote-filesystems/type astring service
+startd/duration astring transient
+start/timeout_seconds count 0
+start/type astring method
+stop/exec astring :true
+stop/timeout_seconds count 0
+stop/type astring method
+"""
+        svcprop_disabled_data = \
+"""general/enabled boolean false
+general/entity_stability astring Unstable
+general/single_instance boolean true
+restarter/start_pid count 4172
+restarter/start_method_timestamp time 1222382991.639687000
+restarter/start_method_waitstatus integer 0
+restarter/transient_contract count 
+restarter/auxiliary_state astring none
+restarter/next_state astring none
+restarter/state astring disabled
+restarter/state_timestamp time 1222992132.445811000
+restarter_actions/refresh integer 
+restarter_actions/maint_on integer 
+restarter_actions/maint_off integer 
+restarter_actions/restart integer 
+local-filesystems/entities fmri svc:/system/filesystem/local
+local-filesystems/grouping astring require_all
+local-filesystems/restart_on astring none
+local-filesystems/type astring service
+remote-filesystems/entities fmri svc:/network/nfs/client svc:/system/filesystem/autofs
+remote-filesystems/grouping astring optional_all
+remote-filesystems/restart_on astring none
+remote-filesystems/type astring service
+startd/duration astring transient
+start/timeout_seconds count 0
+start/type astring method
+stop/exec astring :true
+stop/timeout_seconds count 0
+stop/type astring method
+"""
+        svcprop_temp_enabled_data = \
+"""general/enabled boolean false
+general/entity_stability astring Unstable
+general/single_instance boolean true
+restarter/start_pid count 7816
+restarter/start_method_timestamp time 1222992237.506096000
+restarter/start_method_waitstatus integer 0
+restarter/transient_contract count 
+restarter/auxiliary_state astring none
+restarter/next_state astring none
+restarter/state astring online
+restarter/state_timestamp time 1222992237.527408000
+restarter_actions/refresh integer 
+restarter_actions/maint_on integer 
+restarter_actions/maint_off integer 
+restarter_actions/restart integer 
+general_ovr/enabled boolean true
+local-filesystems/entities fmri svc:/system/filesystem/local
+local-filesystems/grouping astring require_all
+local-filesystems/restart_on astring none
+local-filesystems/type astring service
+remote-filesystems/entities fmri svc:/network/nfs/client svc:/system/filesystem/autofs
+remote-filesystems/grouping astring optional_all
+remote-filesystems/restart_on astring none
+remote-filesystems/type astring service
+startd/duration astring transient
+start/timeout_seconds count 0
+start/type astring method
+stop/exec astring :true
+stop/timeout_seconds count 0
+stop/type astring method
+"""
+        svcprop_temp_disabled_data = \
+"""general/enabled boolean true
+general/entity_stability astring Unstable
+general/single_instance boolean true
+restarter/start_pid count 7816
+restarter/start_method_timestamp time 1222992237.506096000
+restarter/start_method_waitstatus integer 0
+restarter/transient_contract count 
+restarter/auxiliary_state astring none
+restarter/next_state astring none
+restarter/state astring disabled
+restarter/state_timestamp time 1222992278.822335000
+restarter_actions/refresh integer 
+restarter_actions/maint_on integer 
+restarter_actions/maint_off integer 
+restarter_actions/restart integer 
+general_ovr/enabled boolean false
+local-filesystems/entities fmri svc:/system/filesystem/local
+local-filesystems/grouping astring require_all
+local-filesystems/restart_on astring none
+local-filesystems/type astring service
+remote-filesystems/entities fmri svc:/network/nfs/client svc:/system/filesystem/autofs
+remote-filesystems/grouping astring optional_all
+remote-filesystems/restart_on astring none
+remote-filesystems/type astring service
+startd/duration astring transient
+start/timeout_seconds count 0
+start/type astring method
+stop/exec astring :true
+stop/timeout_seconds count 0
+stop/type astring method
+"""
+        empty_data = ""
+
+        svcprop_data = \
+"""#!/usr/bin/sh
+cat $PKG_TEST_DIR/$PKG_SVCPROP_OUTPUT
+exit $PKG_SVCPROP_EXIT_CODE
+"""
+        svcadm_data = \
+"""#!/usr/bin/sh
+echo $0 "$@" >> $PKG_TEST_DIR/svcadm_arguments
+exit $(PKG_SVCADM_EXIT_CODE)
+"""
+
+
+        misc_files = [ "svcprop_enabled", "svcprop_disabled", "svcprop_temp_enabled", "empty",
+                       "svcprop_temp_enabled", "usr/bin/svcprop", "usr/sbin/svcadm"]
+ 
+        testdata_dir = None
+
+        def setUp(self):
+
+                testutils.SingleDepotTestCase.setUp(self)
+                tp = self.get_test_prefix()
+                self.testdata_dir = os.path.join(tp, "testdata")
+                os.mkdir(self.testdata_dir)
+                self.pkg_list = []
+
+                self.pkg_list+= ["""
+                    open [email protected],5.11-0
+                    add file """ + self.testdata_dir + """/empty mode=0644 owner=root group=sys path=/test_restart restart_fmri=svc:system/test_restart_svc 
+                    close """]
+
+                self.pkg_list+= ["""
+                    open [email protected],5.11-0
+                    add file """ + self.testdata_dir + """/empty mode=0655 owner=root group=sys path=/test_restart restart_fmri=svc:system/test_restart_svc 
+                    close """]
+ 
+                self.pkg_list+= ["""
+                    open [email protected],5.11-0
+                    add file """ + self.testdata_dir + """/empty mode=0646 owner=root group=sys path=/test_restart restart_fmri=svc:system/test_restart_svc 
+                    close """]
+
+                self.pkg_list+= ["""
+                    open [email protected],5.11-0
+                    add file """ + self.testdata_dir + """/empty mode=0657 owner=root group=sys path=/test_restart refresh_fmri=svc:system/test_refresh_svc 
+                    close """]
+
+                self.pkg_list+= ["""
+                    open [email protected],5.11-0
+                    add file """ + self.testdata_dir + """/empty mode=0667 owner=root group=sys path=/test_restart suspend_fmri=svc:system/test_suspend_svc
+                    close """]
+
+                self.pkg_list+= ["""
+                    open [email protected],5.11-0
+                    add file """ + self.testdata_dir + """/empty mode=0677 owner=root group=sys path=/test_restart suspend_fmri=svc:system/test_suspend_svc disable_fmri=svc:system/test_disable_svc
+                    close """]
+
+                for f in self.misc_files:
+                        filename = os.path.join(self.testdata_dir, f)
+                        if not os.path.exists(os.path.dirname(filename)):
+                                os.makedirs(os.path.dirname(filename))
+                        file_handle = open(filename, 'wb')
+                        data = "self.%s_data" % os.path.basename(f)
+                        file_handle.write(eval(data))
+                        file_handle.close()
+                        os.chmod(filename, 0755)
+
+        def tearDown(self):
+                testutils.SingleDepotTestCase.tearDown(self)
+                if self.testdata_dir:
+                        shutil.rmtree(self.testdata_dir)
+        
+        def test_actuators(self):
+                """test actuators"""
+
+                durl = self.dc.get_depot_url()
+                for pkg in self.pkg_list:
+                        self.pkgsend_bulk(durl, pkg)
+                self.image_create(durl)
+                os.environ["PKG_TEST_DIR"] = self.testdata_dir
+                os.environ["PKG_SVCADM_EXIT_CODE"] = "0"
+                os.environ["PKG_SVCPROP_EXIT_CODE"] = "0"
+
+                svcadm_output = os.path.join(self.testdata_dir,
+                    "svcadm_arguments")
+
+                # make it look like our test service is enabled
+                os.environ["PKG_SVCPROP_OUTPUT"] = "svcprop_enabled"
+
+                # test to see if our test service is restarted on install
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " install [email protected]")
+                self.pkg("verify")
+                
+                self.file_contains(svcadm_output, "svcadm restart svc:system/test_restart_svc")
+                os.unlink(svcadm_output)
+
+                # test to see if our test service is restarted on upgrade
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " install [email protected]")
+                self.pkg("verify")
+                self.file_contains(svcadm_output, "svcadm restart svc:system/test_restart_svc")
+                os.unlink(svcadm_output)
+                
+                # test to see if our test service is restarted on uninstall
+                self.pkg(cmdstr + " uninstall basics")
+                self.pkg("verify")
+                self.file_contains(svcadm_output, "svcadm restart svc:system/test_restart_svc")
+                os.unlink(svcadm_output)
+
+                # make it look like our test service is not enabled
+                os.environ["PKG_SVCPROP_OUTPUT"] = "svcprop_disabled"
+
+                # test to see to make sure we don't restart disabled service
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " install [email protected]")
+                self.pkg("verify")
+                self.file_does_not_exist(svcadm_output)
+ 
+                # make it look like our test service(s) is/are enabled
+                os.environ["PKG_SVCPROP_OUTPUT"] = "svcprop_enabled"
+
+                # test to see if refresh works as designed, along w/ restart
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " install [email protected]")
+                self.pkg("verify")
+                self.file_contains(svcadm_output, "svcadm restart svc:system/test_restart_svc")
+                self.file_contains(svcadm_output, "svcadm refresh svc:system/test_refresh_svc")
+                os.unlink(svcadm_output)
+
+                # test if suspend works
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " install [email protected]")
+                self.pkg("verify")
+                self.file_contains(svcadm_output, "svcadm disable -t svc:system/test_suspend_svc")
+                self.file_contains(svcadm_output, "svcadm enable svc:system/test_suspend_svc")
+                os.unlink(svcadm_output)
+
+                # test if suspend works properly w/ temp. enabled service
+                # make it look like our test service(s) is/are temp enabled
+                os.environ["PKG_SVCPROP_OUTPUT"] = "svcprop_temp_enabled"
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " install [email protected]")
+                self.pkg("verify")
+                self.file_contains(svcadm_output, "svcadm disable -t svc:system/test_suspend_svc")
+                self.file_contains(svcadm_output, "svcadm enable -t svc:system/test_suspend_svc")
+                os.unlink(svcadm_output)
+
+                # test if service is disabled on uninstall
+                cmdstr = "--debug actuator_cmds_dir=%s" % self.testdata_dir
+                self.pkg(cmdstr + " uninstall basics")
+                self.pkg("verify")
+                self.file_contains(svcadm_output, "svcadm disable svc:system/test_disable_svc")
+ 
+        def file_does_not_exist(self, path):
+                file_path = os.path.join(self.get_img_path(), path)
+                if os.path.exists(file_path):
+                        self.assert_(False, "File %s exists" % path)
+                
+        def file_contains(self, path, string):
+                file_path = os.path.join(self.get_img_path(), path)
+                try:
+                        f = file(file_path)
+                except:
+                        self.assert_(False, "File %s does not exist or contain %s" % (path, string))
+
+                for line in f:
+                        if string in line:
+                                f.close()
+                                break
+                else:
+                        f.close()
+                        self.assert_(False, "File %s does not contain %s" % (path, string))
+
+if __name__ == "__main__":
+        unittest.main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/util/distro-import/86/reboot	Fri Oct 10 22:15:14 2008 -0700
@@ -0,0 +1,6 @@
+# tag kernel binaries as needing reboot
+chattr_glob kernel/* type file reboot_needed=true
+chattr_glob kernel/*.conf type file reboot_needed=false
+chattr_glob platform/* type file reboot_needed=true
+chattr_glob platform/*.conf type file reboot_needed=false
+# See SUNWcsl for tags for other libraries
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/util/distro-import/86/smf_manifests	Fri Oct 10 22:15:14 2008 -0700
@@ -0,0 +1,2 @@
+# make sure manifest changes are picked up
+chattr_glob var/svc/manifest/*.xml type file restart_fmri=svc:/system/manifest-import:default
--- a/src/util/distro-import/87/common/SUNWcsl	Fri Oct 10 16:32:27 2008 -0700
+++ b/src/util/distro-import/87/common/SUNWcsl	Fri Oct 10 22:15:14 2008 -0700
@@ -1,4 +1,12 @@
 package SUNWcsl
 import SUNWcsl
 import SUNWcslr
-end package
+# tag libc as needing reboot for now; this will change
+# when we handle overlay mounts correctly on x86
+# XXX work needed for SPARC
+chattr lib/libc.so.1 reboot_needed=true
+chattr lib/amd64/libc.so reboot_needed=true
+chattr usr/lib/libc/libc_hwcap1.so.1 reboot_needed=true
+chattr usr/lib/libc/libc_hwcap2.so.1 reboot_needed=true
+chattr usr/lib/libc/libc_hwcap3.so.1 reboot_needed=true
+end package 
--- a/src/util/distro-import/Makefile	Fri Oct 10 16:32:27 2008 -0700
+++ b/src/util/distro-import/Makefile	Fri Oct 10 22:15:14 2008 -0700
@@ -43,7 +43,7 @@
 
 TMPPKGS=SUNWfixes
 EXTRA_OPTIONS=
-SOLARIS.PY=./solaris.py -b 0.$(BUILDID) $(EXTRA_OPTIONS) -T \*.py
+SOLARIS.PY=./solaris.py -b 0.$(BUILDID) $(EXTRA_OPTIONS) -T \*.py -G smf_manifests -G reboot
 
 #
 # always remove the following (editable) files from packages we bulk import;
--- a/src/util/distro-import/solaris.py	Fri Oct 10 16:32:27 2008 -0700
+++ b/src/util/distro-import/solaris.py	Fri Oct 10 22:15:14 2008 -0700
@@ -966,8 +966,14 @@
                 elif token == "end":
                         endarg = lexer.get_token()
                         if endarg == "package":
-                                for f in global_includes:
-                                        SolarisParse(f)
+                                for filename in global_includes:
+                                        for i in include_path:
+                                                f = os.path.join(i, filename)
+                                                if os.path.exists(f):
+                                                        SolarisParse(f)
+                                                        break
+                                        else:
+                                                raise RuntimeError, "File not found: %s" % filename
                                 try:
                                         end_package(curpkg)
                                 except Exception, e: