# 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@: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()