Source code for openlmi.storage.SettingProvider

# Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#
# Authors: Jan Safranek <jsafrane@redhat.com>
# -*- coding: utf-8 -*-
""""
Module for SettingProvider class.

SettingProvider
---------------

.. autoclass:: SettingProvider
    :members:

ElementSettingDataProvider
--------------------------

.. autoclass:: ElementSettingDataProvider
    :members:

SettingHelperProvider
---------------------

.. autoclass:: SettingHelperProvider
    :members:

"""

import pywbem
import ast
from openlmi.storage.BaseProvider import BaseProvider
from openlmi.storage.SettingManager import Setting
import openlmi.common.cmpi_logging as cmpi_logging

[docs]class SettingProvider(BaseProvider): """ Base of all LMI_*Setting providers. Every setting class can have: - number of fixed preconfigured instances - number of configurable persistent instances - number of configurable in-memory (transient) instances - instances associated to managed elements This class provides all four instance types. The setting itself is represented by dictionary of key -> value. Preconfigured instances are stored in /etc/openlmi/storage/settings/<setting_classname>.ini Persistent instances are stored in /var/lib/openlmi-storage/settings/<setting_classname>.ini """ @cmpi_logging.trace_method
[docs] def __init__(self, setting_classname, supported_properties, validate_properties=None, ignore_defaults=None, *args, **kwargs): """ setting_classname = name of CIM class, which we provide supported_properties = hash property_name -> constructor constructor is a function which takes string argument and returns CIM value. (i.e. pywbem.Uint16 or bool or string etc). validate_properties = hash property_name -> validator validator is a function which takes pywbem (Uint32, bool ...) value as parameter and returns True, if the value is correct for the property. Not all properties do need to have a validator. ignore_defaults = hash property_name -> default value. If this value of the property is set, the ModifyInstance won't complain, but it will silently ignore the value. This is useful when someone tries to set default value of a property and the provider does not implement it. """ self.setting_classname = setting_classname supported_properties['Caption'] = str supported_properties['ConfigurationName'] = str supported_properties['Description'] = str supported_properties['ChangeableType'] = pywbem.Uint16 supported_properties['ElementName'] = str self.supported_properties = supported_properties self.validate_properties = validate_properties self.ignore_defaults = ignore_defaults super(SettingProvider, self).__init__(*args, **kwargs)
@cmpi_logging.trace_method
[docs] def enumerate_configurations(self): """ Enumerate all instances of LMI_*Setting, which are attached to managed elements, i.e. are not transient, persistent nor preconfigured. This method returns iterabe with Setting instances. Subclasses should override this method. """ return []
@cmpi_logging.trace_method
[docs] def parse_setting_id(self, instance_id): """ InstanceID should have format LMI:<classname>:<myid>. This method checks, that the format is OK and returns the myid. It returns None if the format is not OK. This method can be used in get_configuration_for_id. """ # some devices (raid) have ':' in their /dev/disk/by-id, do not # include it in the split parts = instance_id.split(":", 2) if len(parts) != 3: return None if parts[0] != "LMI": return None if parts[1] != self.setting_classname: return None return parts[2]
@cmpi_logging.trace_method
[docs] def create_setting_id(self, myid): """ InstanceID should have format LMI:<classname>:<ID>. This method returns string LMI:<classname>:<myid> """ return "LMI:" + self.setting_classname + ":" + myid
@cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def get_configuration_for_id(self, instance_id): """ Return Setting instance for given instance_id. Return None if no such Setting is found. Subclasses should override this method. """ return None
@cmpi_logging.trace_method # pylint: disable-msg=W0613
[docs] def get_associated_element_name(self, instance_id): """ Return CIMInstanceName for ElementSettingData association. Return None if no such element exist. Subclasses should override this method. """ return None
@cmpi_logging.trace_method
[docs] def enum_instances(self, env, model, keys_only): """ Provider implementation of EnumerateInstances intrinsic method. Subclasses should not override this method, they should override enumerate_configurations only. """ model.path.update({'InstanceID': None}) # handle transient, persistent and preconfigured settings settings = self.setting_manager.get_settings(self.setting_classname) for setting in settings.values(): model['InstanceID'] = setting.the_id if keys_only: yield model else: yield self.get_instance(env, model, setting) # Make sure we will not return old values in next loop. for key in model.keys(): del model[key] # handle configurations for setting in self.enumerate_configurations(): model['InstanceID'] = setting.the_id if keys_only: yield model else: yield self.get_instance(env, model, setting)
@cmpi_logging.trace_method
[docs] def find_instance(self, instance_id): """ Find an Setting instance with given InstanceID and return it. Return None if there is no such instance. """ # find the setting in setting_manager settings = self.setting_manager.get_settings(self.setting_classname) if settings.has_key(instance_id): return settings[instance_id] # find the setting in configurations return self.get_configuration_for_id(instance_id) # pylint: disable-msg=W0221
@cmpi_logging.trace_method
[docs] def get_instance(self, env, model, setting=None): """ Provider implementation of GetInstance intrinsic method. """ if not setting: setting = self.find_instance(model['InstanceID']) if not setting: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Cannot find setting.") # convert setting to model using supported_properties for (name, value) in setting.items(): if value is not None: if self.supported_properties.has_key(name): model[name] = self.supported_properties[name](value) types = self.Values.ChangeableType if setting.type == Setting.TYPE_CONFIGURATION: model['ChangeableType'] = types.Not_Changeable_Transient elif setting.type == Setting.TYPE_PERSISTENT: model['ChangeableType'] = types.Changeable_Persistent elif setting.type == Setting.TYPE_PRECONFIGURED: model['ChangeableType'] = types.Not_Changeable_Persistent elif setting.type == Setting.TYPE_TRANSIENT: model['ChangeableType'] = types.Changeable_Transient return model
@staticmethod @cmpi_logging.trace_function
[docs] def string_to_bool(value): """ Convert a string to boolean value. '1', 'true' and 'True' are True, the rest is False. """ if value == 1 or value == "true" or value == "True": return True elif value == 0 or value == "false" or value == "False": return False return bool(value)
@staticmethod @cmpi_logging.trace_function
[docs] def string_to_uint16_array(value): """ Convert a string to array of integers. The string must be enclosed in []. """ if not (value[0] == "[" and value[-1] == "]"): return None value = value[1:-1] values = value.split(",") return [pywbem.Uint16(i) for i in values]
@staticmethod @cmpi_logging.trace_function
[docs] def string_to_uint64_array(value): """ Convert a string to array of integers. The string must be enclosed in []. """ if not (value[0] == "[" and value[-1] == "]"): return None value = value[1:-1] values = value.split(",") return [pywbem.Uint64(i) for i in values]
@staticmethod @cmpi_logging.trace_function
[docs] def string_to_string_array(value): """ Convert a string to array of strings. The string must be enclosed in []. """ if not (value[0] == "[" and value[-1] == "]"): return None return ast.literal_eval(value)
@cmpi_logging.trace_function def _check_changeable_type_modify(self, instance, setting): """ Return True, if ModifInstance(instance) can modify ChangeableType property Setting instance. Return False, if the property should be skipped. Raise exception on error. """ types = self.Values.ChangeableType if setting.type == Setting.TYPE_TRANSIENT: if instance['ChangeableType'] == types.Changeable_Persistent: # Can change only transient -> persistent # -> modify it now setting.type = Setting.TYPE_PERSISTENT return False if instance['ChangeableType'] == types.Changeable_Transient: # Ignore transient -> transient return False elif (setting.type == Setting.TYPE_PERSISTENT and instance['ChangeableType'] == types.Changeable_Persistent): # Ignore persistent -> persistent return False raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Cannot modify ChangeableType property to new value.") @cmpi_logging.trace_function def _check_property_modify(self, instance, setting, property_name): """ Return True, if ModifInstance(instance) can modify property of this name in given Setting instance. Return False, if the property should be skipped. Raise exception on error. """ if property_name == 'InstanceID': # Ignore InstanceID property return False if self.ignore_defaults and self.ignore_defaults.has_key(property_name): # Ignore properties in self. ignore_defaults if self.ignore_defaults[property_name] != instance[property_name]: # But only if they have the default value raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Property is not supported: " + property_name) return False if not self.supported_properties.has_key(property_name): # We do not support the property if instance[property_name]: raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Property is not supported: " + property_name) return False if property_name == 'ChangeableType': # ChangeableType has special treatment, only some changes are # allowed return self._check_changeable_type_modify(instance, setting) # Finally, allow the property to be set to new value. return True @cmpi_logging.trace_function def _do_modify_instance(self, instance, setting): """ Modify instance of Setting with given CIMInstance. """ for name in instance.iterkeys(): if not self._check_property_modify(instance, setting, name): continue if instance[name] is not None: if (self.validate_properties and self.validate_properties.has_key(name)): # check validity of the property validator = self.validate_properties[name] if not validator(instance[name]): raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "The value of property %s is invalid." % (name)) setting[name] = str(instance[name]) else: setting[name] = None return setting @cmpi_logging.trace_function
[docs] def set_instance(self, env, instance, modify_existing): """Return a newly created or modified instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) instance -- The new pywbem.CIMInstance. If modifying an existing instance, the properties on this instance have been filtered by the PropertyList from the request. modify_existing -- True if ModifyInstance, False if CreateInstance Return the new instance. The keys must be set on the new instance. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_ALREADY_EXISTS (the CIM Instance already exists -- only valid if modify_existing is False, indicating that the operation was CreateInstance) CIM_ERR_NOT_FOUND (the CIM Instance does not exist -- only valid if modify_existing is True, indicating that the operation was ModifyInstance) CIM_ERR_FAILED (some other unspecified error occurred) """ setting = self.find_instance(instance['InstanceID']) if not setting: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Cannot find setting.") if not modify_existing: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_SUPPORTED, "CreateInstance is not supported.") if (setting.type == Setting.TYPE_CONFIGURATION or setting.type == Setting.TYPE_PRECONFIGURED): raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Cannot modify not-changeable setting.") setting = self._do_modify_instance(instance, setting) self.setting_manager.set_setting(self.setting_classname, setting) return instance
@cmpi_logging.trace_method
[docs] def delete_instance(self, env, instance_name): """Delete an instance. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) instance_name -- A pywbem.CIMInstanceName specifying the instance to delete. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_NOT_SUPPORTED CIM_ERR_INVALID_NAMESPACE CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_INVALID_CLASS (the CIM Class does not exist in the specified namespace) CIM_ERR_NOT_FOUND (the CIM Class does exist, but the requested CIM Instance does not exist in the specified namespace) CIM_ERR_FAILED (some other unspecified error occurred) """ setting = self.find_instance(instance_name['InstanceID']) if not setting: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Cannot find setting.") if (setting.type == Setting.TYPE_CONFIGURATION or setting.type == Setting.TYPE_PRECONFIGURED): raise pywbem.CIMError(pywbem.CIM_ERR_FAILED, "Cannot delete not-changeable setting.") self.setting_manager.delete_setting(self.setting_classname, setting)
@cmpi_logging.trace_method
[docs] def cim_method_clonesetting(self, env, object_name): """Implements LMI_DiskPartitionConfigurationSetting.CloneSetting() Create a copy of this instance. The resulting instance will have the same class and the same properties as the original instance except ChangeableType, which will be set to "Changeable - Transient" in the clone, and InstanceID. Keyword arguments: env -- Provider Environment (pycimmb.ProviderEnvironment) object_name -- A pywbem.CIMInstanceName or pywbem.CIMCLassName specifying the object on which the method CloneSetting() should be invoked. Returns a two-tuple containing the return value (type pywbem.Uint32) and a list of CIMParameter objects representing the output parameters Output parameters: Clone -- (type REF (pywbem.CIMInstanceName(setting_classname='CIM_StorageSetting', ...)) Created copy. Possible Errors: CIM_ERR_ACCESS_DENIED CIM_ERR_INVALID_PARAMETER (including missing, duplicate, unrecognized or otherwise incorrect parameters) CIM_ERR_NOT_FOUND (the target CIM Class or instance does not exist in the specified namespace) CIM_ERR_METHOD_NOT_AVAILABLE (the CIM Server is unable to honor the invocation request) CIM_ERR_FAILED (some other unspecified error occurred) """ setting = self.find_instance(object_name['InstanceID']) if not setting: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Cannot find setting.") instance_id = self.setting_manager.allocate_id(self.setting_classname) new_setting = self.setting_manager.create_setting( self.setting_classname, Setting.TYPE_TRANSIENT, instance_id) for (key, value) in setting.items(): new_setting[key] = value self.setting_manager.set_setting(self.setting_classname, new_setting) out_params = [] out_params += [pywbem.CIMParameter('Clone', type='reference', value=pywbem.CIMInstanceName( classname=self.setting_classname, namespace=self.config.namespace, keybindings={'InstanceID' : instance_id}))] return (self.Values.CloneSetting.Success, out_params)
class Values(object): class ChangeableType(object): Not_Changeable_Persistent = pywbem.Uint16(0) Changeable_Transient = pywbem.Uint16(1) Changeable_Persistent = pywbem.Uint16(2) Not_Changeable_Transient = pywbem.Uint16(3) class CloneSetting(object): Success = pywbem.Uint32(0) Not_Supported = pywbem.Uint32(1) Failed = pywbem.Uint32(4)
[docs]class ElementSettingDataProvider(BaseProvider): """ Implementation of CIM_ElementSettingData. It uses functionality provided by SettingProvider. """ @cmpi_logging.trace_method
[docs] def __init__(self, setting_provider, managed_element_classname, setting_data_classname, *args, **kwargs): self.setting_provider = setting_provider self.managed_element_classname = managed_element_classname self.setting_data_classname = setting_data_classname super(ElementSettingDataProvider, self).__init__(*args, **kwargs)
@cmpi_logging.trace_method
[docs] def get_instance(self, env, model): """ Provider implementation of GetInstance intrinsic method. """ instance_id = model['SettingData']['InstanceID'] element_name = self.setting_provider.get_associated_element_name( instance_id) if not element_name: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "Cannot find the ManagedElement") if element_name != model['ManagedElement']: raise pywbem.CIMError(pywbem.CIM_ERR_NOT_FOUND, "The ManagedElement is not associated to given SettingData") model['IsCurrent'] = pywbem.Uint16(1) # current return model
@cmpi_logging.trace_method
[docs] def enum_instances(self, env, model, keys_only): """ Provider implementation of EnumerateInstances intrinsic method. """ model.path.update({'ManagedElement': None, 'SettingData': None}) for setting in self.setting_provider.enumerate_configurations(): instance_id = setting.the_id provider = self.setting_provider model['ManagedElement'] = provider.get_associated_element_name( instance_id) model['SettingData'] = pywbem.CIMInstanceName( classname=self.setting_data_classname, namespace=self.config.namespace, keybindings={'InstanceID' : instance_id}) if keys_only: yield model else: yield self.get_instance(env, model)
@cmpi_logging.trace_method def references(self, env, object_name, model, result_class_name, role, result_role, keys_only): # If you want to get references for free, implemented in terms # of enum_instances, just leave the code below unaltered. return self.simple_references(env, object_name, model, result_class_name, role, result_role, keys_only, self.managed_element_classname, self.setting_data_classname)
[docs]class SettingHelperProvider(SettingProvider): """ Provider of LMI_*Setting class for managed element classes which implement SettingHelper. """ @cmpi_logging.trace_method
[docs] def __init__(self, setting_helper, *args, **kwargs): self.setting_helper = setting_helper properties = setting_helper.get_supported_setting_properties(self) validators = setting_helper.get_setting_validators(self) ignore = setting_helper.get_setting_ignore(self) super(SettingHelperProvider, self).__init__( supported_properties=properties, validate_properties=validators, ignore_defaults=ignore, *args, **kwargs)
@cmpi_logging.trace_method
[docs] def enumerate_configurations(self): """ Enumerate all instances of LMI_*Setting, which are attached to managed elements, i.e. are not transient, persistent nor preconfigured. It returns setting_helper.enumerate_settings. """ return self.setting_helper.enumerate_settings(self)
@cmpi_logging.trace_method
[docs] def get_configuration_for_id(self, instance_id): """ Return Setting instance for given instance_id. Return None if no such Setting is found. """ return self.setting_helper.get_setting_for_id(self, instance_id)
@cmpi_logging.trace_method
[docs] def get_associated_element_name(self, instance_id): """ Return CIMInstanceName for ElementSettingData association. Return None if no such element exist. """ return self.setting_helper.get_associated_element_name( self, instance_id)