Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / redisbackend / cmredisdb.py
diff --git a/cmframework/src/cmframework/redisbackend/cmredisdb.py b/cmframework/src/cmframework/redisbackend/cmredisdb.py
new file mode 100644 (file)
index 0000000..deb9b77
--- /dev/null
@@ -0,0 +1,190 @@
+# 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.
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
+from __future__ import print_function
+from urlparse import urlparse
+import re
+import time
+import functools
+import redis
+
+from cmframework.apis import cmerror
+from cmframework.apis import cmbackend
+
+
+def retry(func):
+    @functools.wraps(func)
+    def wrapper(*args, **kwargs):
+        for _ in xrange(20):
+            try:
+                return func(*args, **kwargs)
+            except Exception, e:  # pylint: disable=broad-except
+                time.sleep(1)
+                retExc = e
+        raise retExc
+
+    return wrapper
+
+
+class CMRedisDB(cmbackend.CMBackend):
+
+    def __init__(self, **kw):
+        dburl = kw['uri']
+        urldata = urlparse(dburl)
+        self.vip = urldata.hostname
+        self.port = urldata.port
+        self.password = urldata.password
+        self.client = redis.StrictRedis(host=self.vip, port=self.port, password=self.password)
+
+    @retry
+    def set_property(self, prop_name, prop_value):
+        self.client.set(prop_name, prop_value)
+
+    def get_property(self, prop_name):
+        return self.client.get(prop_name)
+
+    @retry
+    def delete_property(self, prop_name):
+        self.client.delete(prop_name)
+
+    @retry
+    def set_properties(self, properties):
+        pipe = self.client.pipeline()
+        for name, value in properties.iteritems():
+            pipe.set(name, value)
+        pipe.execute()
+
+    def get_properties(self, prop_filter):
+        # seems redis does not understand regex, it understands only glob
+        # patterns, thus we need to handle the matching by ourselves :/
+        keys = self.client.keys()
+        pattern = re.compile(prop_filter)
+        props = {}
+        for key in keys:
+            if pattern.match(key):
+                value = self.client.get(key)
+                props[key] = value
+        return props
+
+    @retry
+    def delete_properties(self, arg):
+        if not isinstance(arg, list):
+            raise cmerror.CMError('Deleting with filter is not supported by the backend')
+        pipe = self.client.pipeline()
+        for prop in arg:
+            pipe.delete(prop)
+        pipe.execute()
+
+
+def main():
+    import argparse
+    import sys
+    import traceback
+
+    parser = argparse.ArgumentParser(description='Test redis db plugin', prog=sys.argv[0])
+
+    parser.add_argument('--uri',
+                        required=True,
+                        dest='uri',
+                        metavar='URI',
+                        help='The redis db uri format redis://:password@<ip>:port',
+                        type=str,
+                        action='store')
+
+    parser.add_argument('--api',
+                        required=True,
+                        dest='api',
+                        metavar='API',
+                        help=('The api name, can be set_property, get_property, delete_property, '
+                              'set_properties, get_properties, delete_properties'),
+                        type=str,
+                        action='store')
+
+    parser.add_argument('--property',
+                        required=False,
+                        dest='properties',
+                        metavar='PROPERTY',
+                        help='The property in the format name[=value]',
+                        type=str,
+                        action='append')
+
+    parser.add_argument('--filter',
+                        required=False,
+                        dest='filter',
+                        metavar='FILTER',
+                        help='The regular expression matching the property names',
+                        type=str,
+                        action='store')
+
+    args = parser.parse_args(sys.argv[1:])
+
+    try:
+        uri = args.uri
+        api = args.api
+        p = {}
+        p['uri'] = uri
+        db = CMRedisDB(**p)
+        print('Involing %s' % api)
+        func = getattr(db, api)
+        if api == 'set_property':
+            if not args.properties:
+                raise Exception('Missing --properties argument')
+            for prop in args.properties:
+                i = prop.index('=')
+                name = prop[:i]
+                value = prop[(i + 1):]
+                print("Setting %s to %s" % (name, value))
+                func(name, value)
+        elif api == 'get_property':
+            if not args.properties:
+                raise Exception('Missing --properties argument')
+            for prop in args.properties:
+                value = func(prop)
+                print('%s=%s' % (prop, value))
+        elif api == 'delete_property':
+            if not args.properties:
+                raise Exception('Missing --properties argument')
+            for prop in args.properties:
+                print('Deleting %s' % prop)
+                func(prop)
+        elif api == 'set_properties':
+            if not args.properties:
+                raise Exception('Missing --properties argument')
+            props = {}
+            for prop in args.properties:
+                i = prop.index('=')
+                name = prop[:i]
+                value = prop[(i + 1):]
+                props[name] = value
+            func(props)
+        elif api == 'get_properties':
+            if not args.filter:
+                raise Exception('Missing --filter argument')
+            print('Getting properties matching %s' % args.filter)
+            props = func(args.filter)
+            for key, value in props.iteritems():
+                print('%s=%s' % (key, value))
+        elif api == 'delete_properties':
+            if not args.properties:
+                raise Exception('Missing --properties argument')
+            func(args.properties)
+    except Exception as exp:  # pylint: disable=broad-except
+        print('Failed with error %s', exp)
+        traceback.print_exc()
+        sys.exit(1)
+    sys.exit(0)
+
+
+if __name__ == '__main__':
+    main()