Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / apis / cmclient.py
diff --git a/cmframework/src/cmframework/apis/cmclient.py b/cmframework/src/cmframework/apis/cmclient.py
new file mode 100644 (file)
index 0000000..d5ef98b
--- /dev/null
@@ -0,0 +1,247 @@
+# Copyright 2019 Nokia
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import re
+
+from functools import wraps
+from cmframework import apis as cmapis
+
+
+def handle_exceptions(func):
+    @wraps(func)
+    def wrapper(self, *arg, **kwargs):
+        try:
+            return func(self, *arg, **kwargs)
+        except cmapis.cmerror.CMError as exp:
+            raise
+        except Exception as exp:
+            raise cmapis.cmerror.CMError(str(exp))
+
+    return wrapper
+
+
+class CMClient(object):
+    """
+        Usage Example:
+            class VerboseLogger:
+                def __call__(self, msg):
+                    print(msg)
+
+            logger = VerboseLogger()
+
+            client = CMClient(192.128.254.10, 51110, cmclient.CMClientImpl, logger)
+            try:
+                value = client.get_property('controller-1.ntp.servers')
+            except cmapis.cmerror.CMError as error:
+                print('Got exception %s' % str(error))
+    """
+
+    @handle_exceptions
+    def __init__(self, server_ip='config-manager', server_port=61100,
+                 client_lib_impl_module='cmframework.lib.CMClientImpl', verbose_logger=None):
+        """ initialize the client interface
+
+            Arguments:
+
+            server_ip:  The configuration management server ip address.
+
+            server_port: The configuration management server port number.
+
+            client_lib_impl_module: The module implementing the client library.
+
+            verbose_logger: The verbose logging callable. any callable which
+                            takes a string as input argument can be used.
+
+            Raise:
+
+            CMError exception in-case of a failure.
+        """
+
+        import socket
+        try:
+            serverip = socket.gethostbyname(server_ip)
+        except Exception:  # pylint: disable=broad-except
+            # use localhost in-case we cannot resolve the provided hostname
+            serverip = '127.0.0.1'
+
+        self.server_ip = serverip
+        self.server_port = server_port
+        self.client_lib = None
+        self.verbose_logger = verbose_logger
+
+        # Separate class path and module name
+        parts = client_lib_impl_module.rsplit('.', 1)
+        module_path = parts[0]
+        class_name = parts[1]
+        self.verbose_log('module_path = %s' % module_path)
+        self.verbose_log('class_name = %s' % class_name)
+        module = __import__(module_path, fromlist=[module_path])
+        classobj = getattr(module, class_name)
+        self.client_lib = classobj(self.server_ip, self.server_port, verbose_logger)
+
+    def verbose_log(self, msg):
+        if self.verbose_logger:
+            self.verbose_logger(msg)
+
+    @handle_exceptions
+    def get_property(self, prop_name, snapshot_name=None):
+        """get the value assoicated with a property.
+
+           This is the API used to read the value associated with a
+           configuration property.
+
+           Arguments:
+
+           prop_name: The property name
+           (optional) snapshot_name: The snapshot name
+
+           Raise:
+
+           CMError in-case of a failure.
+        """
+        result = self.client_lib.get_property(prop_name, snapshot_name)
+        return result
+
+    @handle_exceptions
+    def get_properties(self, prop_filter, snapshot_name=None):
+        """get a set of properties matching a filter.
+
+           This is the API used to read a group of properties matching some
+           filter.
+
+           Arguments:
+
+           prop_filter: A valid python re describing the filter used when
+                        matching the returned properties.
+           (optional) snapshot_name: The snapshot name
+
+          Raise:
+
+          CMError is raised in-case of a failure.
+        """
+        self._check_filter(prop_filter)
+        result = self.client_lib.get_properties(prop_filter, snapshot_name)
+        return result
+
+    @handle_exceptions
+    def set_property(self, prop_name, prop_value):
+        """set/update the value of a property.
+
+            This is the API used to set/update the value associated with a
+            property.
+
+            Arguments:
+
+            prop_name: A string representing the property name.
+
+            prop_value: A string representing the property value.
+
+            Raise:
+
+            CMError is raised in-case of failure.
+        """
+        return self.client_lib.set_property(prop_name, prop_value)
+
+    @handle_exceptions
+    def set_properties(self, props, overwrite=False):
+        """set/update a group of properties as a whole
+
+           This API is used to set/update the values associated with a group of
+           properties as a whole, the change is either accepted as a whole or
+           rejected as a whole.
+
+           Arguments:
+
+           props: A dictionary containing the changed properties.
+
+           overwrite: Replace the existing configuration dictionary with the new one.
+
+           Raise:
+
+           CMError is raised in-case of a failure.
+        """
+        return self.client_lib.set_properties(props, overwrite)
+
+    @handle_exceptions
+    def delete_property(self, prop_name):
+        """delete a property
+
+           This is the API used to delete a configuration property.
+
+           Arguments:
+
+           prop_name: The name of the property to be deleted.
+
+           Raise:
+
+           CMError is raised in-case of a failure.
+        """
+        return self.client_lib.delete_property(prop_name)
+
+    @handle_exceptions
+    def delete_properties(self, arg):
+        """delete a group of properties as a whole
+
+           This is the API used to delete a group of properties as whole, if the
+           deletion of one of the properties is rejected then the whole delete
+           operation will fail.
+
+           Arguments:
+
+           arg: This can be either a string representing the re used when
+                matching the properties to be deleted, or it can be a list of
+                properties names to be deleted.
+
+            Raise:
+
+            CMError is raised in-case of a failure.
+        """
+        if isinstance(arg, str):
+            self._check_filter(arg)
+        return self.client_lib.delete_properties(arg)
+
+    @handle_exceptions
+    def get_changes_states(self, change_uuid):
+        """get the config changes states
+
+           This is the API used to get the changes states
+
+           Arguments:
+
+           arg: This can be either a valid change uuid or None.
+
+           Raise:
+
+            CMError is raised in-case of a failure.
+        """
+        return self.client_lib.get_changes_states(change_uuid)
+
+    @handle_exceptions
+    def wait_activation(self, change_uuid):
+        """wait for activation of config changes to finish
+
+           This is the API used to wait for config changes to finish
+
+           Arguments:
+
+           arg: A valid change uuid.
+
+           Raise:
+
+            CMError is raised in-case of a failure.
+        """
+        return self.client_lib.wait_activation(change_uuid)
+
+    # pylint: disable=no-self-use
+    def _check_filter(self, prop_filter):
+        re.compile(prop_filter)