# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the FreeBSD Project.
#
# Authors: Michal Minar <miminar@redhat.com>
#
"""
LMI service provider client library.
"""
import pywbem
import re
from lmi.shell import LMIInstance
from lmi.shell import LMIInstanceName
from lmi.scripts.common.errors import LmiFailed
from lmi.scripts.common import get_computer_system
from lmi.scripts.common import get_logger
LOG = get_logger(__name__)
SERVICE_KINDS = {'all', 'enabled', 'disabled'}
REQUESTED_STATE_ENABLED = 2
REQUESTED_STATE_DISABLED = 3
RE_SUFFIX = re.compile(r'\.service$')
[docs]def invoke_on_service(ns, method, service, description):
"""
Invoke parameter-less method on given service.
:param string method: Name of method of ``LMI_Service`` to invoke.
:param service: Name of service or an instance to operate upon.
:type service: string or :py:class:`lmi.shell.LMIInstanceName`
:param string description: Description of what has been done with
service. This is used just for logging.
:returns: Success flag.
:rtype: boolean
"""
inst = get_service(ns, service)
service = inst.Name
(rval, _, errorstr) = getattr(inst, method)()
if rval == 0:
LOG().info('%s service "%s".', description, service)
elif errorstr:
LOG().error('Operation failed on service "%s": %s.',
service, errorstr)
return rval
[docs]def list_services(ns, kind='enabled'):
"""
List services. Yields service instances.
:param string kind: What kind of services to list. Possible options are:
* 'enabled' - list only enabled services
* 'disabled' - list only disabled services
* 'all' - list all services
:returns: Instances of ``LMI_Service``.
:rtype: generator over :py:class:`lmi.shell.LMIInstance`.
"""
if not isinstance(kind, basestring):
raise TypeError("kind must be a string")
if not kind in SERVICE_KINDS:
raise ValueError("kind must be one of %s" % SERVICE_KINDS)
try:
for service in sorted(ns.LMI_Service.instances(client_filtering=True),
key=lambda i: i.Name):
if kind == 'disabled' and service.EnabledDefault != \
ns.LMI_Service.EnabledDefaultValues.Disabled:
continue
if kind == 'enabled' and service.EnabledDefault != \
ns.LMI_Service.EnabledDefaultValues.Enabled:
# list only enabled
continue
yield service
except pywbem.CIMError as err:
if err.args[0] == pywbem.CIM_ERR_NOT_SUPPORTED:
raise LmiFailed('Service provider is not installed or registered.')
raise LmiFailed('Failed to list services: %s' % err.args[1])
[docs]def start_service(ns, service):
"""
Start service.
:param service: Service name.
:type service: string or :py:class:`lmi.shell.LMIInstanceName`
"""
return invoke_on_service(ns, 'StartService', service, 'Started')
[docs]def stop_service(ns, service):
"""
Stop service.
:param string service: Service name or instance.
:type service: string or :py:class:`lmi.shell.LMIInstanceName`
"""
return invoke_on_service(ns, 'StopService', service, 'Stopped')
[docs]def restart_service(ns, service, just_try=False):
"""
Restart service.
:param service: Service name or instance.
:type service: string or :py:class:`lmi.shell.LMIInstanceName`
:param boolean just_try: When ``False``, the service will be started even
if it is not running. Otherwise only running service will be
restarted.
"""
method_name = 'RestartService'
if just_try:
method_name = 'Try' + method_name
return invoke_on_service(ns, method_name, service, 'Restarted')
[docs]def reload_service(ns, service, force=False, just_try=False):
"""
Reload service.
:param service: Service name or instance.
:type service: string or :py:class:`lmi.shell.LMIInstanceName`
:param boolean force: Whether the service should be restarted if the
reload can no be done.
:param boolean just_try: This applies only when ``force is True``.
If ``True``, only the the running service will be restarted. Nothing
is done for stopped service.
"""
if force:
if just_try:
method_name = 'ReloadOrTryRestart'
else:
method_name = 'ReloadOrRestart'
else:
method_name = 'Reload'
return invoke_on_service(ns, method_name, service, 'Reloaded')
[docs]def enable_service(ns, service, enable=True):
"""
Enable or disable service.
:param service: Service name or instance.
:type service: string or :py:class:`lmi.shell.LMIInstanceName`
:param boolean enable: Whether the service should be enabled or
disabled. Enabled service is started on system boot.
"""
if enable:
method_name = 'TurnServiceOn'
description = 'Enabled'
else:
method_name = 'TurnServiceOff'
description = 'Disabled'
return invoke_on_service(ns, method_name, service, description)
[docs]def get_service(ns, service):
"""
Return :py:class:`lmi.shell.LMIInstance` object matching the given
service name.
:param string service: Service name.
"""
try:
if isinstance(service, basestring):
if not service.endswith('.service'):
service += '.service'
cs = get_computer_system(ns)
iname = ns.LMI_Service.new_instance_name({
"Name": service,
"CreationClassName" : "LMI_Service",
"SystemName" : cs.Name,
"SystemCreationClassName" : cs.CreationClassName
})
inst = iname.to_instance()
elif isinstance(service, (LMIInstance, LMIInstanceName)):
try:
inst = service
service = inst.Name
if isinstance(inst, LMIInstanceName):
inst = inst.to_instance()
except AttributeError:
raise ValueError('Invalid service instance name. It\'s missing'
' Name property.')
else:
raise TypeError("service must be either string or ``LMIInstanceName``")
except pywbem.CIMError as err:
if err.args[0] == pywbem.CIM_ERR_NOT_FOUND:
raise LmiFailed('No such service "%s".' % service)
else:
raise LmiFailed('Failed to get service "%s": %s'
% (service, err.args[1]))
if inst is None:
raise LmiFailed('No such service "%s".' % service)
return inst
[docs]def get_status_string(ns, service):
"""
Return human friendly status description.
:param service: Either a service instance or its name.
:returns: Status description. One of
``{ OK, Running, Stopped - OK, Stopped - Error }``.
:rtype: string
"""
service = get_service(ns, service)
status = []
# first check for common statuses
if ns.LMI_Service.OperationalStatusValues.Completed in \
service.OperationalStatus:
status.append('Stopped')
if ns.LMI_Service.OperationalStatusValues.OK in service.OperationalStatus:
if not status:
status.append('Running')
else:
status.append('OK')
elif ns.LMI_Service.OperationalStatusValues.Error in \
service.OperationalStatus:
status.append('Error')
if not status:
# build the status from formal names
status = [ ns.LMI_Service.OperationalStatusValues.value_name(val)
for val in service.OperationalStatus]
if not status:
status.append = 'Unknown'
return ' - '.join(status)
[docs]def get_enabled_string(ns, service):
"""
Return human friendly string for enabled state.
:param service: Either a service instance of its name.
:returns: Status description. One of:
``{ Yes, No, Static }``.
``Static`` represents a service that can not be enabled or disabled,
and are run only if something depends on them. It lacks ``[Install]``
section.
:rtype: string
"""
service = get_service(ns, service)
if service.EnabledDefault == ns.LMI_Service.EnabledDefaultValues.Enabled:
return "Yes"
if service.EnabledDefault == ns.LMI_Service.EnabledDefaultValues.Disabled:
return "No"
return "Static"