Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / redisbackend / cmredisdb.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
15 from __future__ import print_function
16 from urlparse import urlparse
17 import re
18 import time
19 import functools
20 import redis
21
22 from cmframework.apis import cmerror
23 from cmframework.apis import cmbackend
24
25
26 def retry(func):
27     @functools.wraps(func)
28     def wrapper(*args, **kwargs):
29         for _ in xrange(20):
30             try:
31                 return func(*args, **kwargs)
32             except Exception, e:  # pylint: disable=broad-except
33                 time.sleep(1)
34                 retExc = e
35         raise retExc
36
37     return wrapper
38
39
40 class CMRedisDB(cmbackend.CMBackend):
41
42     def __init__(self, **kw):
43         dburl = kw['uri']
44         urldata = urlparse(dburl)
45         self.vip = urldata.hostname
46         self.port = urldata.port
47         self.password = urldata.password
48         self.client = redis.StrictRedis(host=self.vip, port=self.port, password=self.password)
49
50     @retry
51     def set_property(self, prop_name, prop_value):
52         self.client.set(prop_name, prop_value)
53
54     def get_property(self, prop_name):
55         return self.client.get(prop_name)
56
57     @retry
58     def delete_property(self, prop_name):
59         self.client.delete(prop_name)
60
61     @retry
62     def set_properties(self, properties):
63         pipe = self.client.pipeline()
64         for name, value in properties.iteritems():
65             pipe.set(name, value)
66         pipe.execute()
67
68     def get_properties(self, prop_filter):
69         # seems redis does not understand regex, it understands only glob
70         # patterns, thus we need to handle the matching by ourselves :/
71         keys = self.client.keys()
72         pattern = re.compile(prop_filter)
73         props = {}
74         for key in keys:
75             if pattern.match(key):
76                 value = self.client.get(key)
77                 props[key] = value
78         return props
79
80     @retry
81     def delete_properties(self, arg):
82         if not isinstance(arg, list):
83             raise cmerror.CMError('Deleting with filter is not supported by the backend')
84         pipe = self.client.pipeline()
85         for prop in arg:
86             pipe.delete(prop)
87         pipe.execute()
88
89
90 def main():
91     import argparse
92     import sys
93     import traceback
94
95     parser = argparse.ArgumentParser(description='Test redis db plugin', prog=sys.argv[0])
96
97     parser.add_argument('--uri',
98                         required=True,
99                         dest='uri',
100                         metavar='URI',
101                         help='The redis db uri format redis://:password@<ip>:port',
102                         type=str,
103                         action='store')
104
105     parser.add_argument('--api',
106                         required=True,
107                         dest='api',
108                         metavar='API',
109                         help=('The api name, can be set_property, get_property, delete_property, '
110                               'set_properties, get_properties, delete_properties'),
111                         type=str,
112                         action='store')
113
114     parser.add_argument('--property',
115                         required=False,
116                         dest='properties',
117                         metavar='PROPERTY',
118                         help='The property in the format name[=value]',
119                         type=str,
120                         action='append')
121
122     parser.add_argument('--filter',
123                         required=False,
124                         dest='filter',
125                         metavar='FILTER',
126                         help='The regular expression matching the property names',
127                         type=str,
128                         action='store')
129
130     args = parser.parse_args(sys.argv[1:])
131
132     try:
133         uri = args.uri
134         api = args.api
135         p = {}
136         p['uri'] = uri
137         db = CMRedisDB(**p)
138         print('Involing %s' % api)
139         func = getattr(db, api)
140         if api == 'set_property':
141             if not args.properties:
142                 raise Exception('Missing --properties argument')
143             for prop in args.properties:
144                 i = prop.index('=')
145                 name = prop[:i]
146                 value = prop[(i + 1):]
147                 print("Setting %s to %s" % (name, value))
148                 func(name, value)
149         elif api == 'get_property':
150             if not args.properties:
151                 raise Exception('Missing --properties argument')
152             for prop in args.properties:
153                 value = func(prop)
154                 print('%s=%s' % (prop, value))
155         elif api == 'delete_property':
156             if not args.properties:
157                 raise Exception('Missing --properties argument')
158             for prop in args.properties:
159                 print('Deleting %s' % prop)
160                 func(prop)
161         elif api == 'set_properties':
162             if not args.properties:
163                 raise Exception('Missing --properties argument')
164             props = {}
165             for prop in args.properties:
166                 i = prop.index('=')
167                 name = prop[:i]
168                 value = prop[(i + 1):]
169                 props[name] = value
170             func(props)
171         elif api == 'get_properties':
172             if not args.filter:
173                 raise Exception('Missing --filter argument')
174             print('Getting properties matching %s' % args.filter)
175             props = func(args.filter)
176             for key, value in props.iteritems():
177                 print('%s=%s' % (key, value))
178         elif api == 'delete_properties':
179             if not args.properties:
180                 raise Exception('Missing --properties argument')
181             func(args.properties)
182     except Exception as exp:  # pylint: disable=broad-except
183         print('Failed with error %s', exp)
184         traceback.print_exc()
185         sys.exit(1)
186     sys.exit(0)
187
188
189 if __name__ == '__main__':
190     main()