src/modules/smf.py
author thejaswini.k@oracle.com
Fri, 18 Apr 2014 10:14:06 +0530
branchs11-update
changeset 3063 bbaaf1579de3
parent 3048 2aecfba95fe8
permissions -rw-r--r--
15744850 no actuators are run when pkg manipulates a booted NGZ image from the GZ 17457046 pkg should display list of new / modified editable files for operation 18399970 file action should ignore preserve attribute for elf files 18414661 plan summary output should have more whitespace 18477216 ImagePlan.plan_desc doesn't include all package changes 17478601 provide a pkg(5) private module to compute SHA512/256 15615267 ability to require updates based on complete content 16273726 https support for svc:/application/pkg/depot

#!/usr/bin/python
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
#

# This module provides a basic interface to smf.

import os

import pkg.pkgsubprocess as subprocess

from pkg.client import global_settings
from pkg.client.debugvalues import DebugValues

logger = global_settings.logger

# 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

EXIT_OK              = 0
EXIT_FATAL           = 1
EXIT_INVALID_OPTION  = 2
EXIT_INSTANCE        = 3
EXIT_DEPENDENCY      = 4
EXIT_TIMEOUT         = 5

svcprop_path = "/usr/bin/svcprop"
svcadm_path  = "/usr/sbin/svcadm"
svccfg_path = "/usr/sbin/svccfg"
svcs_path = "/usr/bin/svcs"
zlogin_path = "/usr/sbin/zlogin"

class NonzeroExitException(Exception):
        def __init__(self, cmd, return_code, output):
                self.cmd = cmd
                self.return_code = return_code
                self.output = output

        def __unicode__(self):
                # To workaround python issues 6108 and 2517, this provides a
                # a standard wrapper for this class' exceptions so that they
                # have a chance of being stringified correctly.
                return str(self)

        def __str__(self):
                return "Cmd %s exited with status %d, and output '%s'" %\
                    (self.cmd, self.return_code, self.output)


def __call(args, zone=None):
        # a way to invoke a separate executable for testing
        cmds_dir = DebugValues.get_value("smf_cmds_dir")
        if cmds_dir:
                args = (
                    os.path.join(cmds_dir,
                    args[0].lstrip("/")),) + args[1:]
        if zone:
                cmd = DebugValues.get_value("bin_zlogin")
                if cmd is None:
                        cmd = zlogin_path
                args = (cmd, zone) + args

        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: %s" % (args, e)

        if ret != 0:
                raise NonzeroExitException(args, ret, buf)
        return buf

def get_state(fmri, zone=None):
        """ return state of smf service """

        props = get_props(fmri, zone=zone)
        if not props:
                return SMF_SVC_UNKNOWN

        if "maintenance" in props.get("restarter/state", []):
                return SMF_SVC_MAINTENANCE

        status = props.get("general_ovr/enabled", None)
        if status is not None:
                if "true" in status:
                        return SMF_SVC_TMP_ENABLED
                return SMF_SVC_TMP_DISABLED
        status = props.get("general/enabled", None)
        if status is not None and "true" in status:
                return SMF_SVC_ENABLED
        return SMF_SVC_DISABLED

def is_disabled(fmri, zone=None):
        return get_state(fmri, zone=zone) < SMF_SVC_TMP_ENABLED

def check_fmris(attr, fmris, zone=None):
        """ Walk a set of fmris checking that each is fully specifed with
        an instance.
        If an FMRI is not fully specified and does not contain at least
        one special match character from fnmatch(5) the fmri is dropped
        from the set that is returned and an error message is logged.
        """

        if isinstance(fmris, basestring):
                fmris = set([fmris])
        chars = "*?[!^"
        for fmri in fmris.copy():
                is_glob = False
                for c in chars:
                        if c in fmri:
                                is_glob = True

                tmp_fmri = fmri
                if fmri.startswith("svc:"):
                        tmp_fmri = fmri.replace("svc:", "", 1)

                # check to see if we've got an instance already
                if ":" in tmp_fmri and not is_glob:
                        continue

                fmris.remove(fmri)
                if is_glob:
                        cmd = (svcs_path, "-H", "-o", "fmri", "%s" % fmri)
                        try:
                                instances = __call(cmd, zone=zone)
                                for instance in instances:
                                        fmris.add(instance.rstrip())
                        except NonzeroExitException:
                                continue # non-zero exit == not installed

                else:
                        logger.error(_("FMRI pattern might implicitly match " \
                            "more than one service instance."))
                        logger.error(_("Actuators for %(attr)s will not be run " \
                            "for %(fmri)s.") % locals())
        return fmris

def get_props(svcfmri, zone=None):
        args = (svcprop_path, "-c", svcfmri)

        try:
                buf = __call(args, zone=zone)
        except NonzeroExitException:
                return {} # empty output == not installed

        return dict([
            l.strip().split(None, 1)
            for l in buf
        ])

def set_prop(fmri, prop, value, zone=None):
        args = (svccfg_path, "-s", fmri, "setprop", "%s=%s" % (prop, value))
        __call(args, zone=zone)

def get_prop(fmri, prop, zone=None):
        args = (svcprop_path, "-c", "-p", prop, fmri)
        buf = __call(args, zone=zone)
        assert len(buf) == 1, "Was expecting one entry, got:%s" % buf
        buf = buf[0].rstrip("\n")
        return buf

def enable(fmris, temporary=False, sync_timeout=0, zone=None):
        if not fmris:
                return
        if isinstance(fmris, basestring):
                fmris = (fmris,)

        args = [svcadm_path, "enable"]
        if sync_timeout:
                args.append("-s")
                if sync_timeout != -1:
                        args.append("-T %d" % sync_timeout)
        if temporary:
                args.append("-t")
        # fmris could be a list so explicit cast is necessary
        __call(tuple(args) + tuple(fmris), zone=zone)

def disable(fmris, temporary=False, sync_timeout=0, zone=None):
        if not fmris:
                return
        if isinstance(fmris, basestring):
                fmris = (fmris,)
        args = [svcadm_path, "disable", "-s"]
        if sync_timeout > 0:
                args.append("-T %d" % sync_timeout)
        if temporary:
                args.append("-t")
        # fmris could be a list so explicit cast is necessary
        __call(tuple(args) + tuple(fmris), zone=zone)

def mark(state, fmris, zone=None):
        if not fmris:
                return
        if isinstance(fmris, basestring):
                fmris = (fmris,)
        args = [svcadm_path, "mark", state]
        # fmris could be a list so explicit cast is necessary
        __call(tuple(args) + tuple(fmris), zone=zone)

def refresh(fmris, sync_timeout=0, zone=None):
        if not fmris:
                return
        if isinstance(fmris, basestring):
                fmris = (fmris,)
        args = [svcadm_path, "refresh"]
        if sync_timeout:
                args.append("-s")
                if sync_timeout != -1:
                        args.append("-T %d" % sync_timeout)
        # fmris could be a list so explicit cast is necessary
        __call(tuple(args) + tuple(fmris), zone=zone)

def restart(fmris, sync_timeout=0, zone=None):
        if not fmris:
                return
        if isinstance(fmris, basestring):
                fmris = (fmris,)
        args = [svcadm_path, "restart"]
        if sync_timeout:
                args.append("-s")
                if sync_timeout != -1:
                        args.append("-T %d" % sync_timeout)
        # fmris could be a list so explicit cast is necessary
        __call(tuple(args) + tuple(fmris), zone=zone)