#
# (C) Copyright 2020 InovaDevelopment.com
#
# 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 program 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 program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Karl Schopmeyer <inovadevelopment.com>
#
"""
The :class:`~pywbem_mock.InstanceWriteProvider` class provides the default
implementations of the ``CreateInstance``, ``ModifyInstance`` and
``DeleteInstance`` provider methods by means of
:meth:`~pywbem_mock.InstanceWriteProvider.CreateInstance`,
:meth:`~pywbem_mock.InstanceWriteProvider.ModifyInstance`, and
:meth:`~pywbem_mock.InstanceWriteProvider.DeleteInstance`.
A user-defined instance write provider may implement some or all of these
provider methods. The method descriptions linked above provide a detailed
definition of the input parameters, return values, and required behavior.
The following is a simple example of a user-defined instance write provider
that serves the 'CIM_Foo' and 'CIM_FooFoo' classes and implements
``CreateInstance`` for the purpose of setting a value for the 'InstanceID' key
property:
.. code-block:: python
import uuid
from pywbem_mock import InstanceWriteProvider
class MyInstanceProvider(InstanceWriteProvider):
# CIM classes served by this provider
provider_classnames = ['CIM_Foo', 'CIM_FooFoo']
def CreateInstance(self, namespace, new_instance):
new_instance.properties["InstanceID"] = \\
"{}.{}".format(new_instance.classname, uuid.uuid4())
return super(MyInstanceProvider, self).CreateInstance(
namespace, new_instance)
Because the provider in this example does not implement the
``ModifyInstance`` and ``DeleteInstance`` methods, these operations will
use their default implementations from
:class:`~pywbem_mock.InstanceWriteProvider`.
The provider in this example does not need to implement an ``__init__()``
method, because no additional init parameters are needed.
"""
from __future__ import absolute_import, print_function
from pywbem import CIMInstanceName, CIMError, \
CIM_ERR_INVALID_PARAMETER, CIM_ERR_ALREADY_EXISTS
from pywbem._utils import _format
from ._baseprovider import BaseProvider
# None of the request method names conform since they are camel case
# pylint: disable=invalid-name
__all__ = ['InstanceWriteProvider']
[docs]class InstanceWriteProvider(BaseProvider):
"""
This class defines those instance provider methods that may have user-
defined providers that override the default provider implementation in this
class.
User providers are defined by creating a subclass of this class and
defining a new provider method for one of the methods in this class with
the same signature.
Note that user-defined providers may, in turn, call the default providers
in this class.
"""
#: :term:`string`: Keyword defining the type of request the provider will
#: service. The type for this provider class is predefined as 'instance'.
provider_type = 'instance-write'
def __init__(self, cimrepository=None):
"""
Parameters:
cimrepository (:class:`~pywbem_mock.BaseRepository` or subclass):
The CIM repository to be used by the provider.
"""
super(InstanceWriteProvider, self).__init__(cimrepository)
[docs] def CreateInstance(self, namespace, new_instance):
"""
Default provider method for
:meth:`pywbem.WBEMConnection.CreateInstance`.
Create a new CIM instance in the CIM repository of the mock WBEM server.
Validation already performed by the provider dispatcher that calls
this provider method:
- The provider method is called only for the registered class and
namespace (only applies to user-defined providers).
- The Python types of all input parameters to this provider method are
as specified below.
- The namespace exists in the CIM repository.
- The creation class of the new instance exists in the namespace
in the CIM repository.
- All properties specified in the new instance are exposed (i.e.
defined and inherited with any overrides resolved) by the creation
class in the CIM repository, and have the same type-related
attributes (i.e. type, is_array, embedded_object).
Validation that should be performed by this provider method:
- new_instance does not specify any properties that are not
allowed to be initialized by the client, depending on the model
implemented by the provider.
- new_instance specifies all key properties needed by the
provider, depending on the model implemented by the provider.
The CIM repository will reject any new instance that does not have
all key properties specified.
- The new instance (i.e. an instance with the new instance path) does
not exist in the CIM repository.
This validation needs to be done by the provider because the
determination of the key properties for the new instance path may
depend on the model implemented by the provider.
The CIM repository will reject the creation of instances that already
exist, so this check can be delegated to the repository once the
new instance path has been determined.
Parameters:
namespace (:term:`string`):
The name of the CIM namespace in which the CIM instance is to be
created, in any lexical case, and with leading and trailing slash
characters removed.
new_instance (:class:`~pywbem.CIMInstance`):
A representation of the CIM instance to be created.
This object is a deep copy of the original client parameter, and may
be modified by the provider as needed, before storing it in the
CIM repository.
The property names in this object have been adjusted to match the
lexical case of the property definitions in the creation class
of the instance in the CIM repository.
The `classname` attribute of this object will specify the
creation class for the new instance, in any lexical case.
The `properties` attribute of this object will specify the
initial property values for the new CIM instance, with property
names in any lexical case. Key properties may or may not be
included.
The `path` attribute of this object will be `None`.
The `qualifiers` attribute of this object, if non-empty, should
be ignored by the provider, because instance-level qualifiers have
been deprecated in CIM.
Returns:
:class:`~pywbem.CIMInstanceName`: Instance path of the new CIM
instance.
Raises:
:exc:`~pywbem.CIMError`: The provider may raise CIMError with any
status code, and typically raises:
- CIM_ERR_INVALID_PARAMETER
- CIM_ERR_ALREADY_EXISTS
"""
# Get the creation class with all exposed properties and qualifiers.
# Since the existence of the class has already been verified, this
# will always succeed.
class_store = self.cimrepository.get_class_store(namespace)
creation_class = class_store.get(new_instance.classname, copy=False)
# This default provider determines the instance path from the key
# properties in the new instance. A user-defined provider may do that
# as well, or invent key properties such as InstanceID.
# Specifying strict=True in from_instance() verifies that all key
# properties exposed by the class are specified in the new instance,
# and raises ValueError if key properties are missing.
try:
new_instance.path = CIMInstanceName.from_instance(
creation_class, new_instance, namespace=namespace, strict=True)
except ValueError as exc:
raise CIMError(CIM_ERR_INVALID_PARAMETER, str(exc))
# Get the instance store of the CIM repository. Since the existence of
# the namespace has already been verified, this will always succeed.
instance_store = self.cimrepository.get_instance_store(namespace)
# Store the new instance in the CIM repository, verifying that it does
# not exist yet.
try:
instance_store.create(new_instance.path, new_instance)
except ValueError:
raise CIMError(
CIM_ERR_ALREADY_EXISTS,
_format("New instance {0!A} already exists in namespace "
"{1!A}.", new_instance.path, namespace))
# CreateInstance returns the instance path of the new instance
return new_instance.path
[docs] def ModifyInstance(self, modified_instance, IncludeQualifiers=None):
# pylint: disable=invalid-name,line-too-long,unused-argument
"""
Default provider method for
:meth:`pywbem.WBEMConnection.CreateInstance`.
Modify an existing CIM instance in the CIM repository of the mock WBEM
server.
NOTE: This method specifies the namespace in modified_instance.path
rather than as a separate input parameter.
The modification of the instance in the CIM repository that is performed
by the provider should be limited to property value changes (including
addition of properties if not yet set on the instance), because
instance-level qualifiers have been deprecated in CIM.
The set of properties that are to be modified in the CIM instance has
already been determined by the caller so that the modified_instance
parameter specifies exactly the set of properties to be modified.
Therefore, this provider method does not have a property list
parameter.
Validation already performed by the provider dispatcher that calls
this provider method:
- The provider method is called only for the registered class and
namespace (only applies to user-defined providers).
- The Python types of all input parameters to this provider method are
as specified below.
- The classnames in modified_instance are consistent between
instance and instance path.
- The namespace exists in the CIM repository.
- The creation class of the instance to be modified exists in the
namespace of the CIM repository.
- The instance to be modified exists in the namespace of the CIM
repository.
- All properties in modified_instance that are to be modified are
exposed (i.e. defined and inherited with any overrides resolved) by
the creation class in the CIM repository, and have the same
type-related attributes (i.e. type, is_array, embedded_object).
- No key properties are requested to change their values.
Validation that should be performed by this provider method:
- modified_instance does not specify any changed values for
properties that are not allowed to be changed by the client,
depending on the model implemented by the provider.
Parameters:
modified_instance (:class:`~pywbem.CIMInstance`):
A representation of the modified CIM instance, also indicating its
instance path, with exactly the set of properties to be modified.
This object is a deep copy of the original client parameter, and may
be modified by the provider as needed, before storing it in the
CIM repository.
The `path` attribute of this object will be set and is the
instance path of the instance to be modified in the CIM repository.
Its `namespace`, `classname` and `keybindings` attributes
will be set. The names will be in any lexical case.
The `classname` attribute of this object will specify the creation
class of the instance to be modified, in any lexical case.
The `properties` attribute of this object will specify exactly the
set of properties that are to be updated, taking into account the
original ModifiedInstance and PropertyList input parameters of the
ModifyInstance() client call.
The lexical case of the property names has been adjusted to match
the lexical cae of the property definitions in the creation class
in the CIM repository.
The `qualifiers` attribute of this object, if non-empty, should
be ignored by the provider, because instance-level qualifiers have
been deprecated in CIM.
IncludeQualifiers (:class:`py:bool`):
This parameter should be ignored by the provider, because
instance-level qualifiers have been deprecated in CIM.
Raises:
:exc:`~pywbem.CIMError`: The provider may raise CIMError with any
status code, and typically raises:
- CIM_ERR_INVALID_PARAMETER
""" # noqa: E501
# pylint: disable=invalid-name,line-too-long
namespace = modified_instance.path.namespace
# Get a copy of the instance to be modified from the CIM repository.
instance_store = self.cimrepository.get_instance_store(namespace)
instance = instance_store.get(modified_instance.path)
# Modify the properties of the local instance copy. The implemented
# approach is intentionally careful, to ensure that only the property
# values get updated.
for pn in modified_instance.properties:
mod_prop = modified_instance.properties[pn]
inst_prop = instance.properties[pn]
inst_prop.value = mod_prop.value # Update the property value
# Note that IncludeQualifiers is completely ignored.
# Replace the instance in the CIM repository with the local copy.
instance_store.update(modified_instance.path, instance)
[docs] def DeleteInstance(self, InstanceName):
"""
Default provider method for
:meth:`pywbem.WBEMConnection.DeleteInstance`.
Delete an existing instance in the CIM repository of the mock WBEM
server.
NOTE: This method specifies the namespace in InstanceName
rather than as a separate input parameter.
The provider is not responsible for determining other instances that
depend on the instance to be deleted (e.g. association instances
referencing the instance to be deleted); such dependency detection and
handling is done elsewhere.
Validation already performed by the provider dispatcher that calls
this provider method:
- The provider method is called only for the registered class and
namespace (only applies to user-defined providers).
- The Python types of all input parameters to this provider method are
as specified below.
- The namespace exists in the CIM repository.
- The creation class of the instance to be deleted exists in the
namespace of the CIM repository.
- The instance to be deleted exists in the namespace of the CIM
repository.
Parameters:
InstanceName (:class:`~pywbem.CIMInstanceName`):
The instance path of the instance to be deleted with the
following attributes:
* `classname`: Name of the creation class of the instance.
* `keybindings`: Keybindings of the instance.
* `namespace`: Name of the CIM namespace containing the instance.
Will not be `None`.
* `host`: Will be `None`.
Raises:
None
"""
namespace = InstanceName.namespace
instance_store = self.cimrepository.get_instance_store(namespace)
# Delete the instance from the CIM repository
instance_store.delete(InstanceName)
[docs] def post_register_setup(self, conn):
"""
Method called by provider registration after registation of provider
is successful. Using this method is optional for registration cases
where the provider must execute some activity (ex. modify the
CIM repository after successful provider registration).
Override this method in the user-defined provider subclass to execute
this method.
Parameters:
conn (:class:`~pywbem.WBEMConnection`):
Current connection which allows client methods to be executed
from within this method.
"""