RLTest 0.7.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,183 @@
1
+ from __future__ import print_function
2
+ import subprocess
3
+ import redis
4
+ import os
5
+ from RLTest.utils import wait_for_conn, Colors
6
+
7
+
8
+ class CcsMock():
9
+ CCS_UNIX_SOCKET_DEFAULT_PATH = '/tmp/ccs.sock'
10
+ CCS_DB_RDB_FILE_NAME = 'syncer-pixie-ccs.rdb'
11
+ CCS_LOG_FILE_NAME = 'ccs.log'
12
+
13
+ def __init__(self, redisBinaryPath, proxyPort, directory=None, useSlaves=False, password=None, libPath=None):
14
+ self.redisBinaryPath = os.path.expanduser(redisBinaryPath) if redisBinaryPath.startswith('~/') else redisBinaryPath
15
+ self.useSlaves = useSlaves
16
+ self.directory = directory
17
+ self.generateArgs()
18
+ self.libPath = os.path.expanduser(libPath) if libPath.startswith('~/') else libPath
19
+ self.env = {}
20
+ if self.libPath:
21
+ self.env['LD_LIBRARY_PATH'] = self.libPath
22
+ self.password = password
23
+ self.proxyPort = proxyPort
24
+
25
+ def generateArgs(self):
26
+ self.args = self.redisBinaryPath.split()
27
+ if self.directory:
28
+ self.args += ['--dir', self.directory]
29
+ self.args += ['--port', '0',
30
+ '--unixsocket', self.CCS_UNIX_SOCKET_DEFAULT_PATH,
31
+ '--dbfilename', self.CCS_DB_RDB_FILE_NAME,
32
+ '--logfile', self.CCS_LOG_FILE_NAME]
33
+
34
+ def Start(self, shards, bdb_fields=None, endpoint_ccs_params=None, legacy_hash_slots=True, extra_keys=None):
35
+ self.setup(shards, bdb_fields, endpoint_ccs_params, legacy_hash_slots, extra_keys)
36
+
37
+ def Stop(self):
38
+ if self.process:
39
+ try:
40
+ self.process.terminate()
41
+ self.process.wait()
42
+ except OSError:
43
+ pass
44
+ self.process = None
45
+
46
+ def PrintEnvData(self, prefix=''):
47
+ print(Colors.Yellow(prefix + 'pid: %d' % self.process.pid))
48
+ print(Colors.Yellow(prefix + 'unix socket: %s' % self.CCS_UNIX_SOCKET_DEFAULT_PATH))
49
+ print(Colors.Yellow(prefix + 'binary path: %s' % self.redisBinaryPath))
50
+ if self.directory:
51
+ print(Colors.Yellow(prefix + 'db dir path: %s' % (self.directory)))
52
+ print(Colors.Yellow(prefix + 'rdb file name: %s' % (self.CCS_DB_RDB_FILE_NAME)))
53
+ print(Colors.Yellow(prefix + 'log file name: %s' % (self.CCS_LOG_FILE_NAME)))
54
+ if self.libPath:
55
+ print(Colors.Yellow(prefix + 'lib path: %s' % (self.libPath)))
56
+
57
+ def setup(self, shards, bdb_fields=None, endpoint_ccs_params=None, legacy_hash_slots=True, extra_keys=None):
58
+
59
+ self.process = subprocess.Popen(stdout=subprocess.PIPE, stderr=subprocess.STDOUT, args=self.args, env=self.env)
60
+
61
+ # Create fake cluster, node and dmc objects
62
+ self.conn = redis.Redis(unix_socket_path=self.CCS_UNIX_SOCKET_DEFAULT_PATH)
63
+ wait_for_conn(self.conn)
64
+
65
+ self.conn.hmset('cluster', {'name': 'fakecluster'})
66
+ self.conn.hmset('node:1', {'addr': '127.0.0.1',
67
+ 'dmc_uid': '1'})
68
+ self.conn.sadd('node:all', '1')
69
+ self.conn.hmset('dmc:1', {'threads': '1',
70
+ 'max_threads': '1',
71
+ 'log_level': 'info'})
72
+ self.conn.sadd('dmc:all', '1')
73
+ # self.conn.hmset('cluster_cert', {"syncer_cert": "syncer_cert", "syncer_key": "syncer_key"}) todo: check if needed
74
+ self.disperse_shards = False
75
+ self.next_node_uid = 1
76
+
77
+ self.create_bdb(shards, bdb_fields, endpoint_ccs_params, legacy_hash_slots, extra_keys)
78
+
79
+ def set_server_ccs_values(self, serverId, port, assigned_slots, bdb_uid, role):
80
+ """
81
+ Set all the neccesary values of a server in the ccs
82
+ """
83
+ redis_uid = serverId
84
+ self.conn.hmset('redis:%s' % str(redis_uid),
85
+ {'port': port,
86
+ 'node_uid': str(self.next_node_uid),
87
+ 'bdb_uid': bdb_uid,
88
+ 'role': role,
89
+ 'assigned_slots': assigned_slots})
90
+ self.conn.sadd('redis:all', redis_uid)
91
+ # we promote the node uid just in case of master shard because we want disperion of master shards
92
+ if role == 'master' and self.disperse_shards:
93
+ n_nodes = len(self.conn.smembers('node:all'))
94
+ self.next_node_uid = self.next_node_uid % n_nodes + 1
95
+
96
+ def update_bdb_redis_list(self, bdb_uid, shards):
97
+ """
98
+ Update the redis list in the ccs of the given bdb
99
+ """
100
+ bdb_uid = str(bdb_uid)
101
+ redis_uids = []
102
+ for shard in shards:
103
+ redis_uids.append(str(shard.masterServerId))
104
+ if self.useSlaves:
105
+ redis_uids.append(str(shard.slaveServerId))
106
+ self.conn.hset('bdb:%s' % bdb_uid, 'redis_list', ','.join(redis_uids))
107
+
108
+ def create_bdb(self, shards, bdb_fields=None, endpoint_ccs_params=None, legacy_hash_slots=True, extra_keys=None):
109
+ bdb_uid = 1
110
+ port = self.proxyPort
111
+
112
+ if legacy_hash_slots:
113
+ number_of_slots_in_bdb = 4096
114
+ translation_offset = 1
115
+ else:
116
+ number_of_slots_in_bdb = 16384
117
+ translation_offset = 0
118
+ slots_per_shard = float(number_of_slots_in_bdb) / len(shards)
119
+ first_slot = translation_offset
120
+ last_slot = number_of_slots_in_bdb - 1 + translation_offset
121
+
122
+ start_slots = [int(round(first_slot + i * slots_per_shard)) for i in range(len(shards))]
123
+ end_slots = [next_start_slot - 1 for next_start_slot in start_slots[1:]] + [last_slot]
124
+ slots = ["%s-%s" % (pair[0], pair[1]) for pair in zip(start_slots, end_slots)]
125
+
126
+ for index, shard in enumerate(shards):
127
+ self.set_server_ccs_values(shard.masterServerId, shard.port, str(slots[index]), bdb_uid, 'master')
128
+
129
+ if self.useSlaves:
130
+ self.set_server_ccs_values(shard.slaveServerId, shard.GetSlavePort(), str(slots[index]), bdb_uid, 'slave')
131
+
132
+ self.conn.hmset('bdb:%s' % bdb_uid,
133
+ {'shards_count': len(shards),
134
+ 'type': 'redis',
135
+ 'endpoint_list': '%s:1' % bdb_uid,
136
+ 'replication': ('enabled' if self.useSlaves else 'disabled'),
137
+ 'authentication_admin_pass': 'secret',
138
+ 'internal_pass': self.password,
139
+ 'redis_version': '4.0',
140
+ 'implicit_shard_key': 'enabled'})
141
+
142
+ if legacy_hash_slots:
143
+ self.conn.hmset('bdb:%s' % bdb_uid,
144
+ {'hash_slots_policy': 'legacy',
145
+ 'shard_function': 'crc12'})
146
+ else:
147
+ self.conn.hmset('bdb:%s' % bdb_uid,
148
+ {'hash_slots_policy': '16k'})
149
+
150
+ if bdb_fields:
151
+ self.conn.hmset('bdb:%s' % bdb_uid, bdb_fields)
152
+ # if 'ssl' in bdb_fields and bdb_fields['ssl'] in ["enabled", "replica_ssl"]:
153
+ # with open(CLIENT_CERT_FILE, 'r') as cert_file:
154
+ # cert = cert_file.readlines()
155
+ # self.conn.hmset('bdb:%s' % bdb_uid,
156
+ # {'authentication_ssl_client_certs': "".join(cert)})
157
+
158
+ if extra_keys:
159
+ for key, val in extra_keys.items():
160
+ self.conn.hmset(key, val)
161
+
162
+ # Set up endpoint
163
+ proxy_policy = self.conn.hget('bdb:%s' % bdb_uid, 'proxy_policy')
164
+ if not proxy_policy:
165
+ proxy_policy = 'all-nodes'
166
+
167
+ self.conn.hmset('endpoint:%s:1' % bdb_uid,
168
+ {'bdb_uid': bdb_uid,
169
+ 'port': port,
170
+ 'proxy_policy': proxy_policy})
171
+ self.conn.hmset('endpoint:%s:1' % bdb_uid,
172
+ {'bdb_uid': bdb_uid,
173
+ 'port': port,
174
+ 'proxy_policy': proxy_policy,
175
+ 'proxy_uids': '1'})
176
+
177
+ if endpoint_ccs_params:
178
+ self.conn.hmset('endpoint:%s:1' % bdb_uid, endpoint_ccs_params)
179
+
180
+ self.conn.sadd('endpoint:all', '%s:1' % bdb_uid)
181
+
182
+ self.conn.sadd('bdb:all', bdb_uid)
183
+ self.update_bdb_redis_list(bdb_uid, shards)
@@ -0,0 +1,47 @@
1
+ from __future__ import print_function
2
+
3
+ import os
4
+ import psutil
5
+ import subprocess
6
+ from RLTest.utils import Colors
7
+
8
+
9
+ class Dmc():
10
+ DMC_LOG_FILE_NAME = 'dmc.log'
11
+
12
+ def __init__(self, dmcBinaryPath, libPath, directory=None):
13
+ self.dmcBinaryPath = os.path.expanduser(dmcBinaryPath) if dmcBinaryPath.startswith('~/') else dmcBinaryPath
14
+ self.env = {'MEMTIER_NODE_ID': '1'}
15
+ self.libPath = libPath
16
+ if 'LD_LIBRARY_PATH' in os.environ:
17
+ self.env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
18
+ else:
19
+ self.env['LD_LIBRARY_PATH'] = os.path.expanduser(self.libPath) if dmcBinaryPath.startswith('~/') else self.libPath
20
+ self.directory = directory
21
+
22
+ logFile = self.DMC_LOG_FILE_NAME if self.directory is None else os.path.join(self.directory, self.DMC_LOG_FILE_NAME)
23
+ self.proxyArgs = [
24
+ self.dmcBinaryPath,
25
+ '-c', '500',
26
+ '-O', logFile
27
+ ]
28
+
29
+ def Start(self):
30
+ self.process = psutil.Popen(executable=self.dmcBinaryPath,
31
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
32
+ env=self.env,
33
+ args=self.proxyArgs)
34
+
35
+ def Stop(self):
36
+ if self.process:
37
+ self.process.kill()
38
+ self.process.wait()
39
+ self.process = None
40
+
41
+ def PrintEnvData(self, prefix=''):
42
+ print(Colors.Yellow(prefix + 'dmc binary path: %s' % self.dmcBinaryPath))
43
+ print(Colors.Yellow(prefix + 'dmc env: %s' % str(self.env)))
44
+ print(Colors.Yellow(prefix + 'dir path: %s' % str(self.directory)))
45
+ print(Colors.Yellow(prefix + 'log file name: %s' % str(self.DMC_LOG_FILE_NAME)))
46
+ if self.libPath:
47
+ print(Colors.Yellow(prefix + 'lib path: %s' % str(self.libPath)))
@@ -0,0 +1,158 @@
1
+ from __future__ import print_function
2
+
3
+ from RLTest.redis_std import StandardEnv
4
+ from RLTest.utils import Colors, wait_for_conn
5
+ from .CcsMock import CcsMock
6
+ from .Dmc import Dmc
7
+ import redis
8
+ import os
9
+ import json
10
+ from zipfile import ZipFile
11
+
12
+
13
+ SHARD_PASSWORD = 'password'
14
+
15
+
16
+ class EnterpriseClusterEnv():
17
+ DMC_PORT = 10000
18
+ MODULE_WORKING_DIR = '/tmp/'
19
+
20
+ def __init__(self, **kwargs):
21
+
22
+ self.shards = []
23
+ self.envIsUp = False
24
+ self.envIsHealthy = False
25
+ self.modulePath = kwargs.pop('modulePath')
26
+ self.moduleArgs = kwargs['moduleArgs']
27
+ self.shardsCount = kwargs.pop('shardsCount')
28
+ self.dmcBinaryPath = kwargs.pop('dmcBinaryPath')
29
+ useSlaves = kwargs.get('useSlaves', False)
30
+
31
+ self.preperModule()
32
+ startPort = 20000
33
+ totalRedises = self.shardsCount * (2 if useSlaves else 1)
34
+ for i in range(0, totalRedises, (2 if useSlaves else 1)):
35
+ shard = StandardEnv(port=startPort, serverId=(i + 1), password=SHARD_PASSWORD, modulePath=self.moduleSoFilePath, **kwargs)
36
+ self.shards.append(shard)
37
+ startPort += 2
38
+
39
+ self.ccs = CcsMock(redisBinaryPath=kwargs['redisBinaryPath'], directory=kwargs['dbDirPath'], useSlaves=kwargs['useSlaves'],
40
+ password=SHARD_PASSWORD, proxyPort=self.DMC_PORT, libPath=kwargs['libPath'])
41
+ self.dmc = Dmc(directory=kwargs['dbDirPath'], dmcBinaryPath=self.dmcBinaryPath, libPath=kwargs['libPath'])
42
+ self.envIsUp = False
43
+
44
+ def preperModule(self):
45
+ if self.modulePath is None:
46
+ self.moduleSoFilePath = None
47
+ self.moduleConfig = None
48
+ return
49
+ if not self.modulePath.endswith('zip'):
50
+ raise Exception('module on enterprise cluster must be a zip file')
51
+ with ZipFile(self.modulePath, 'r') as myzip:
52
+ moduleJson = [a.filename for a in myzip.infolist() if a.filename.endswith('.json')]
53
+ if len(moduleJson) != 1:
54
+ self.moduleSoFilePath = None
55
+ self.moduleConfig = None
56
+ return
57
+ moduleJson = moduleJson[0]
58
+ myzip.extractall(self.MODULE_WORKING_DIR)
59
+ with open(os.path.join(self.MODULE_WORKING_DIR, moduleJson), 'rt') as f:
60
+ self.moduleConfig = json.load(f)
61
+ self.moduleSoFilePath = os.path.join(self.MODULE_WORKING_DIR, self.moduleConfig['module_file'])
62
+ if self.moduleArgs is None:
63
+ self.moduleArgs = self.moduleConfig['command_line_args']
64
+
65
+ def printEnvData(self, prefix=''):
66
+ print(Colors.Yellow(prefix + 'bdb info:'))
67
+ print(Colors.Yellow(prefix + '\tlistening port:%d' % self.DMC_PORT))
68
+ print(Colors.Yellow(prefix + '\tshards count:%d' % len(self.shards)))
69
+ if self.modulePath:
70
+ print(Colors.Yellow(prefix + '\tzip module path:%s' % self.modulePath))
71
+ if self.moduleSoFilePath:
72
+ print(Colors.Yellow(prefix + '\tso module path:%s' % self.moduleSoFilePath))
73
+ if self.moduleArgs:
74
+ print(Colors.Yellow(prefix + '\tmodule args:%s' % self.moduleArgs))
75
+ for i, shard in enumerate(self.shards):
76
+ print(Colors.Yellow(prefix + 'shard: %d' % (i + 1)))
77
+ shard.printEnvData(prefix + '\t')
78
+ print(Colors.Yellow(prefix + 'ccs:'))
79
+ self.ccs.PrintEnvData(prefix + '\t')
80
+ print(Colors.Yellow(prefix + 'dmc:'))
81
+ self.dmc.PrintEnvData(prefix + '\t')
82
+
83
+ def startEnv(self, masters = True, slaves = True):
84
+ if self.envIsUp:
85
+ return # env is already up
86
+ for shard in self.shards:
87
+ shard.startEnv(masters, slaves)
88
+
89
+ ccs_bdb_config = {'shard_key_regex': '012.*\{(?<tag>.*)\}.*00a(?<tag>.*)',
90
+ 'sharding': 'enabled' if self.shardsCount > 0 else 'disabled'}
91
+
92
+ extra_keys = {}
93
+ if self.moduleConfig:
94
+ ccs_bdb_config['module_list'] = self.moduleConfig['module_name']
95
+ extra_keys['module:%s' % self.moduleConfig['module_name']] = {'module_name': self.moduleConfig['module_name'],
96
+ 'commands': ','.join([c['command_name'] for c in self.moduleConfig['commands']])}
97
+ for c in self.moduleConfig['commands']:
98
+ key_name = 'module_command:%s' % c['command_name']
99
+ extra_keys[key_name] = {}
100
+ for key, val in c.items():
101
+ extra_keys[key_name][key] = val
102
+
103
+ self.ccs.Start(self.shards, bdb_fields=ccs_bdb_config, legacy_hash_slots=False, extra_keys=extra_keys)
104
+ self.dmc.Start()
105
+ con = self.getConnection()
106
+ wait_for_conn(con, command='sping', shouldBe=['SPONG 0' for i in self.shards])
107
+ self.envIsUp = True
108
+ self.envIsHealthy = True
109
+
110
+ def stopEnv(self, masters = True, slaves = True):
111
+ for shard in self.shards:
112
+ shard.stopEnv(masters, slaves)
113
+ self.envIsUp = self.envIsUp or shard.envIsUp
114
+ self.envIsHealthy = self.envIsHealthy and shard.envIsUp
115
+ self.ccs.Stop()
116
+ self.dmc.Stop()
117
+
118
+ def getConnection(self, shardId=1):
119
+ return redis.Redis('localhost', self.DMC_PORT)
120
+
121
+ def getSlaveConnection(self):
122
+ raise Exception('unsupported')
123
+
124
+ def flush(self):
125
+ self.getConnection().flushall()
126
+
127
+ def dumpAndReload(self, restart=False, shardId=None, timeout_sec=40):
128
+ if shardId is None:
129
+ for shard in self.shards:
130
+ shard.dumpAndReload(restart=restart)
131
+ self.dmc.Stop()
132
+ self.dmc.Start()
133
+ con = self.getConnection()
134
+ wait_for_conn(con, command='sping', shouldBe=['SPONG 0' for i in self.shards])
135
+ else:
136
+ self.shards[shardId - 1].dumpAndReload(restart=restart, shardId=None)
137
+
138
+ def broadcast(self, *cmd):
139
+ for shard in self.shards:
140
+ shard.broadcast(*cmd)
141
+
142
+ def checkExitCode(self):
143
+ for shard in self.shards:
144
+ if not shard.checkExitCode():
145
+ return False
146
+ return True
147
+
148
+ def exists(self, val):
149
+ return self.getConnection().exists(val)
150
+
151
+ def hmset(self, *args):
152
+ return self.getConnection().hmset(*args)
153
+
154
+ def keys(self, reg):
155
+ return self.getConnection().keys(reg)
156
+
157
+ def isUp(self):
158
+ raise Exception('unsupported operation')
@@ -0,0 +1,5 @@
1
+ from .EnterpriseClusterEnv import EnterpriseClusterEnv
2
+
3
+ __all__ = [
4
+ 'EnterpriseClusterEnv'
5
+ ]
@@ -0,0 +1,71 @@
1
+ from __future__ import print_function
2
+ import os.path
3
+ import shutil
4
+ import distro
5
+ import subprocess
6
+ import sys
7
+
8
+ from RLTest.utils import Colors
9
+
10
+
11
+ OS_NAME = distro.linux_distribution()[2]
12
+ REPO_ROOT = os.path.expanduser('~/.RLTest')
13
+ ENTERPRISE_VERSION = '5.2.0'
14
+ ENTERPRISE_SUB_VERSION = '14'
15
+ ENTERPRISE_TAR_FILE_NAME = 'redislabs-%s-%s-%s-amd64.tar' % (
16
+ ENTERPRISE_VERSION, ENTERPRISE_SUB_VERSION, OS_NAME)
17
+ ENTERPRISE_URL = 'https://s3.amazonaws.com/rlec-downloads/%s/%s' % (
18
+ ENTERPRISE_VERSION, ENTERPRISE_TAR_FILE_NAME)
19
+ DEBIAN_PKG_NAME = 'redislabs_%s-%s~%s_amd64.deb' % (
20
+ ENTERPRISE_VERSION, ENTERPRISE_SUB_VERSION, OS_NAME)
21
+
22
+
23
+ class BinaryRepository(object):
24
+ """
25
+ Installation manager for Redis Enterprise.
26
+ The long-term idea behind this is to allow for managing different versions, etc.
27
+ but for now it's just here to cut some code out of the main executable
28
+ """
29
+ def __init__(self, root=REPO_ROOT, url=ENTERPRISE_URL, debname=DEBIAN_PKG_NAME):
30
+ self.root = root
31
+ self.url = url
32
+ self.debname = debname
33
+
34
+ def download_binaries(self, binariesName='binaries.tar'):
35
+ print(Colors.Yellow('installing enterprise binaries'))
36
+ print(Colors.Yellow('creating RLTest working dir: %s' % self.root))
37
+ try:
38
+ shutil.rmtree(self.root)
39
+ os.makedirs(self.root)
40
+ except Exception:
41
+ pass
42
+
43
+ print(Colors.Yellow('download binaries'))
44
+ args = ['wget', self.url, '-O', os.path.join(self.root, binariesName)]
45
+ process = subprocess.Popen(args=args, stdout=sys.stdout,
46
+ stderr=sys.stdout)
47
+ process.wait()
48
+ if process.poll() != 0:
49
+ raise Exception('failed to download enterprise binaries from s3')
50
+
51
+ print(Colors.Yellow('extracting binaries'))
52
+
53
+ args = ['tar', '-xvf', os.path.join(self.root, binariesName),
54
+ '--directory', self.root, self.debname]
55
+ process = subprocess.Popen(args=args, stdout=sys.stdout, stderr=sys.stdout)
56
+ process.wait()
57
+ if process.poll() != 0:
58
+ raise Exception(
59
+ 'failed to extract binaries to %s' % self.self.root)
60
+
61
+ # TODO: Support centos that does not have dpkg command
62
+ args = ['dpkg', '-x', os.path.join(self.root, self.debname),
63
+ self.root]
64
+ process = subprocess.Popen(args=args, stdout=sys.stdout,
65
+ stderr=sys.stdout)
66
+ process.wait()
67
+ if process.poll() != 0:
68
+ raise Exception(
69
+ 'failed to extract binaries to %s' % self.self.root)
70
+
71
+ print(Colors.Yellow('finished installing enterprise binaries'))
RLTest/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from RLTest.env import Env, Defaults
2
+ from RLTest.redis_std import StandardEnv
3
+ from ._version import __version__
4
+
5
+ __all__ = [
6
+ 'Defaults',
7
+ 'Env',
8
+ 'StandardEnv'
9
+ ]
10
+