summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rwxr-xr-xutils/generate-command-code.py126
-rwxr-xr-xutils/generate-command-help.rb151
-rwxr-xr-xutils/req-res-log-validator.py47
3 files changed, 112 insertions, 212 deletions
diff --git a/utils/generate-command-code.py b/utils/generate-command-code.py
index 81d8c19f1..dc66ce81f 100755
--- a/utils/generate-command-code.py
+++ b/utils/generate-command-code.py
@@ -196,7 +196,7 @@ class Argument(object):
def struct_code(self):
"""
Output example:
- "expiration",ARG_TYPE_ONEOF,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.value.subargs=SET_expiration_Subargs
+ MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=GETEX_expiration_Subargs
"""
def _flags_code():
@@ -210,7 +210,7 @@ class Argument(object):
s += "CMD_ARG_MULTIPLE_TOKEN|"
return s[:-1] if s else "CMD_ARG_NONE"
- s = "\"%s\",%s,%d,%s,%s,%s,%s" % (
+ s = "MAKE_ARG(\"%s\",%s,%d,%s,%s,%s,%s,%d,%s)" % (
self.name,
ARG_TYPES[self.type],
self.desc.get("key_spec_index", -1),
@@ -218,9 +218,9 @@ class Argument(object):
get_optional_desc_string(self.desc, "summary"),
get_optional_desc_string(self.desc, "since"),
_flags_code(),
+ len(self.subargs),
+ get_optional_desc_string(self.desc, "deprecated_since"),
)
- if "deprecated_since" in self.desc:
- s += ",.deprecated_since=\"%s\"" % self.desc["deprecated_since"]
if "display" in self.desc:
s += ",.display_text=\"%s\"" % self.desc["display"].lower()
if self.subargs:
@@ -234,10 +234,9 @@ class Argument(object):
subarg.write_internal_structs(f)
f.write("/* %s argument table */\n" % self.fullname())
- f.write("struct redisCommandArg %s[] = {\n" % self.subarg_table_name())
+ f.write("struct COMMAND_ARG %s[] = {\n" % self.subarg_table_name())
for subarg in self.subargs:
f.write("{%s},\n" % subarg.struct_code())
- f.write("{0}\n")
f.write("};\n\n")
@@ -339,11 +338,14 @@ class Command(object):
return "%s_History" % (self.fullname().replace(" ", "_"))
def tips_table_name(self):
- return "%s_tips" % (self.fullname().replace(" ", "_"))
+ return "%s_Tips" % (self.fullname().replace(" ", "_"))
def arg_table_name(self):
return "%s_Args" % (self.fullname().replace(" ", "_"))
+ def key_specs_table_name(self):
+ return "%s_Keyspecs" % (self.fullname().replace(" ", "_"))
+
def reply_schema_name(self):
return "%s_ReplySchema" % (self.fullname().replace(" ", "_"))
@@ -356,22 +358,37 @@ class Command(object):
s = ""
for tupl in self.desc["history"]:
s += "{\"%s\",\"%s\"},\n" % (tupl[0], tupl[1])
- s += "{0}"
return s
+ def num_history(self):
+ if not self.desc.get("history"):
+ return 0
+ return len(self.desc["history"])
+
def tips_code(self):
if not self.desc.get("command_tips"):
return ""
s = ""
for hint in self.desc["command_tips"]:
s += "\"%s\",\n" % hint.lower()
- s += "NULL"
return s
+ def num_tips(self):
+ if not self.desc.get("command_tips"):
+ return 0
+ return len(self.desc["command_tips"])
+
+ def key_specs_code(self):
+ s = ""
+ for spec in self.key_specs:
+ s += "{%s}," % KeySpec(spec).struct_code()
+ return s[:-1]
+
+
def struct_code(self):
"""
Output example:
- "set","Set the string value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,SET_History,SET_tips,setCommand,-3,"write denyoom @string",{{"write read",KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SET_Args
+ MAKE_CMD("set","Set the string value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,4,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args
"""
def _flags_code():
@@ -392,13 +409,7 @@ class Command(object):
s += "CMD_DOC_%s|" % flag
return s[:-1] if s else "CMD_DOC_NONE"
- def _key_specs_code():
- s = ""
- for spec in self.key_specs:
- s += "{%s}," % KeySpec(spec).struct_code()
- return s[:-1]
-
- s = "\"%s\",%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s," % (
+ s = "MAKE_CMD(\"%s\",%s,%s,%s,%s,%s,%s,%s,%s,%s,%d,%s,%d,%s,%d,%s,%s,%s,%d,%s,%d)," % (
self.name.lower(),
get_optional_desc_string(self.desc, "summary"),
get_optional_desc_string(self.desc, "complexity"),
@@ -406,22 +417,22 @@ class Command(object):
_doc_flags_code(),
get_optional_desc_string(self.desc, "replaced_by"),
get_optional_desc_string(self.desc, "deprecated_since"),
+ "\"%s\"" % self.group,
GROUPS[self.group],
self.history_table_name(),
+ self.num_history(),
self.tips_table_name(),
+ self.num_tips(),
self.desc.get("function", "NULL"),
self.desc["arity"],
_flags_code(),
- _acl_categories_code()
+ _acl_categories_code(),
+ self.key_specs_table_name(),
+ len(self.key_specs),
+ self.desc.get("get_keys_function", "NULL"),
+ len(self.args),
)
- specs = _key_specs_code()
- if specs:
- s += "{%s}," % specs
-
- if self.desc.get("get_keys_function"):
- s += "%s," % self.desc["get_keys_function"]
-
if self.subcommands:
s += ".subcommands=%s," % self.subcommand_table_name()
@@ -440,7 +451,7 @@ class Command(object):
subcommand.write_internal_structs(f)
f.write("/* %s command table */\n" % self.fullname())
- f.write("struct redisCommand %s[] = {\n" % self.subcommand_table_name())
+ f.write("struct COMMAND_STRUCT %s[] = {\n" % self.subcommand_table_name())
for subcommand in subcommand_list:
f.write("{%s},\n" % subcommand.struct_code())
f.write("{0}\n")
@@ -448,33 +459,47 @@ class Command(object):
f.write("/********** %s ********************/\n\n" % self.fullname())
+ f.write("#ifndef SKIP_CMD_HISTORY_TABLE\n")
f.write("/* %s history */\n" % self.fullname())
code = self.history_code()
if code:
f.write("commandHistory %s[] = {\n" % self.history_table_name())
- f.write("%s\n" % code)
- f.write("};\n\n")
+ f.write("%s" % code)
+ f.write("};\n")
else:
- f.write("#define %s NULL\n\n" % self.history_table_name())
+ f.write("#define %s NULL\n" % self.history_table_name())
+ f.write("#endif\n\n")
+ f.write("#ifndef SKIP_CMD_TIPS_TABLE\n")
f.write("/* %s tips */\n" % self.fullname())
code = self.tips_code()
if code:
f.write("const char *%s[] = {\n" % self.tips_table_name())
+ f.write("%s" % code)
+ f.write("};\n")
+ else:
+ f.write("#define %s NULL\n" % self.tips_table_name())
+ f.write("#endif\n\n")
+
+ f.write("#ifndef SKIP_CMD_KEY_SPECS_TABLE\n")
+ f.write("/* %s key specs */\n" % self.fullname())
+ code = self.key_specs_code()
+ if code:
+ f.write("keySpec %s[%d] = {\n" % (self.key_specs_table_name(), len(self.key_specs)))
f.write("%s\n" % code)
- f.write("};\n\n")
+ f.write("};\n")
else:
- f.write("#define %s NULL\n\n" % self.tips_table_name())
+ f.write("#define %s NULL\n" % self.key_specs_table_name())
+ f.write("#endif\n\n")
if self.args:
for arg in self.args:
arg.write_internal_structs(f)
f.write("/* %s argument table */\n" % self.fullname())
- f.write("struct redisCommandArg %s[] = {\n" % self.arg_table_name())
+ f.write("struct COMMAND_ARG %s[] = {\n" % self.arg_table_name())
for arg in self.args:
f.write("{%s},\n" % arg.struct_code())
- f.write("{0}\n")
f.write("};\n\n")
if self.reply_schema and args.with_reply_schema:
@@ -543,15 +568,40 @@ if check_command_error_counter != 0:
exit(1)
commands_filename = "commands_with_reply_schema" if args.with_reply_schema else "commands"
-print("Generating %s.c..." % commands_filename)
-with open("%s/%s.c" % (srcdir, commands_filename), "w") as f:
+print("Generating %s.def..." % commands_filename)
+with open("%s/%s.def" % (srcdir, commands_filename), "w") as f:
f.write("/* Automatically generated by %s, do not edit. */\n\n" % os.path.basename(__file__))
- f.write("#include \"server.h\"\n")
f.write(
"""
/* We have fabulous commands from
* the fantastic
- * Redis Command Table! */\n
+ * Redis Command Table! */
+
+/* Must match redisCommandGroup */
+const char *COMMAND_GROUP_STR[] = {
+ "generic",
+ "string",
+ "list",
+ "set",
+ "sorted-set",
+ "hash",
+ "pubsub",
+ "transactions",
+ "connection",
+ "server",
+ "scripting",
+ "hyperloglog",
+ "cluster",
+ "sentinel",
+ "geo",
+ "stream",
+ "bitmap",
+ "module"
+};
+
+const char *commandGroupStr(int index) {
+ return COMMAND_GROUP_STR[index];
+}
"""
)
@@ -560,7 +610,7 @@ with open("%s/%s.c" % (srcdir, commands_filename), "w") as f:
command.write_internal_structs(f)
f.write("/* Main command table */\n")
- f.write("struct redisCommand redisCommandTable[] = {\n")
+ f.write("struct COMMAND_STRUCT redisCommandTable[] = {\n")
curr_group = None
for command in command_list:
if curr_group != command.group:
diff --git a/utils/generate-command-help.rb b/utils/generate-command-help.rb
deleted file mode 100755
index 1042ce6d2..000000000
--- a/utils/generate-command-help.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env ruby -w
-# Usage: generate-command-help.r [path/to/commands.json]
-# or: generate-commands-json.py | generate-command-help.rb -
-#
-# Defaults to downloading commands.json from the redis-doc repo if not provided
-# or STDINed.
-
-GROUPS = [
- "generic",
- "string",
- "list",
- "set",
- "sorted-set",
- "hash",
- "pubsub",
- "transactions",
- "connection",
- "server",
- "scripting",
- "hyperloglog",
- "cluster",
- "geo",
- "stream",
- "bitmap"
-].freeze
-
-GROUPS_BY_NAME = Hash[*
- GROUPS.each_with_index.map do |n,i|
- [n,i]
- end.flatten
-].freeze
-
-def argument arg
- if "block" == arg["type"]
- name = arg["arguments"].map do |entry|
- argument entry
- end.join " "
- elsif "oneof" == arg["type"]
- name = arg["arguments"].map do |entry|
- argument entry
- end.join "|"
- elsif "pure-token" == arg["type"]
- name = nil # prepended later
- else
- name = arg["name"].is_a?(Array) ? arg["name"].join(" ") : arg["name"]
- end
- if arg["multiple"]
- if arg["multiple_token"]
- name = "#{name} [#{arg["token"]} #{name} ...]"
- else
- name = "#{name} [#{name} ...]"
- end
- end
- if arg["token"]
- name = [arg["token"], name].compact.join " "
- end
- if arg["optional"]
- name = "[#{name}]"
- end
- name
-end
-
-def arguments command
- return "" unless command["arguments"]
- command["arguments"].map do |arg|
- argument arg
- end.join " "
-end
-
-def commands
- return @commands if @commands
-
- require "rubygems"
- require "net/http"
- require "net/https"
- require "json"
- require "uri"
- if ARGV.length > 0
- if ARGV[0] == '-'
- data = STDIN.read
- elsif FileTest.exist? ARGV[0]
- data = File.read(ARGV[0])
- else
- raise Exception.new "File not found: #{ARGV[0]}"
- end
- else
- url = URI.parse "https://raw.githubusercontent.com/redis/redis-doc/master/commands.json"
- client = Net::HTTP.new url.host, url.port
- client.use_ssl = true
- response = client.get url.path
- if !response.is_a?(Net::HTTPSuccess)
- response.error!
- return
- else
- data = response.body
- end
- end
- @commands = JSON.parse(data)
-end
-
-def generate_groups
- GROUPS.map do |n|
- "\"#{n}\""
- end.join(",\n ");
-end
-
-def generate_commands
- commands.to_a.sort do |x,y|
- x[0] <=> y[0]
- end.map do |key, command|
- group = GROUPS_BY_NAME[command["group"]]
- if group.nil?
- STDERR.puts "Please update groups array in #{__FILE__}"
- raise "Unknown group #{command["group"]}"
- end
-
- ret = <<-SPEC
-{ "#{key}",
- "#{arguments(command)}",
- "#{command["summary"]}",
- #{group},
- "#{command["since"]}" }
- SPEC
- ret.strip
- end.join(",\n ")
-end
-
-# Write to stdout
-puts <<-HELP_H
-/* Automatically generated by #{__FILE__}, do not edit. */
-
-#ifndef __REDIS_HELP_H
-#define __REDIS_HELP_H
-
-static char *commandGroups[] = {
- #{generate_groups}
-};
-
-struct commandHelp {
- char *name;
- char *params;
- char *summary;
- int group;
- char *since;
-} commandHelp[] = {
- #{generate_commands}
-};
-
-#endif
-HELP_H
-
diff --git a/utils/req-res-log-validator.py b/utils/req-res-log-validator.py
index e2b9d4f8d..46c110019 100755
--- a/utils/req-res-log-validator.py
+++ b/utils/req-res-log-validator.py
@@ -12,7 +12,6 @@ import argparse
import multiprocessing
import collections
import io
-import signal
import traceback
from datetime import timedelta
from functools import partial
@@ -44,7 +43,9 @@ Future validations:
1. Fail the script if one or more of the branches of the reply schema (e.g. oneOf, anyOf) was not hit.
"""
-IGNORED_COMMANDS = [
+IGNORED_COMMANDS = {
+ # Commands that don't work in a req-res manner (see logreqres.c)
+ "debug", # because of DEBUG SEGFAULT
"sync",
"psync",
"monitor",
@@ -54,11 +55,10 @@ IGNORED_COMMANDS = [
"sunsubscribe",
"psubscribe",
"punsubscribe",
- "debug",
+ # Commands to which we decided not write a reply schema
"pfdebug",
"lolwut",
-]
-
+}
class Request(object):
"""
@@ -169,7 +169,7 @@ class Response(object):
value = Response(f, line_counter)
self.json[field] = value.json
if line[0] == '|':
- # We don't care abou the attributes, read the real response
+ # We don't care about the attributes, read the real response
real_res = Response(f, line_counter)
self.__dict__.update(real_res.__dict__)
@@ -180,7 +180,7 @@ class Response(object):
def process_file(docs, path):
"""
- This function processes a single filegenerated by logreqres.c
+ This function processes a single file generated by logreqres.c
"""
line_counter = [0] # A list with one integer: to force python to pass it by reference
command_counter = dict()
@@ -190,7 +190,7 @@ def process_file(docs, path):
# Convert file to StringIO in order to minimize IO operations
with open(path, "r", newline="\r\n", encoding="latin-1") as f:
content = f.read()
-
+
with io.StringIO(content) as fakefile:
while True:
try:
@@ -217,6 +217,9 @@ def process_file(docs, path):
if res.error or res.queued:
continue
+ if req.command in IGNORED_COMMANDS:
+ continue
+
try:
jsonschema.validate(instance=res.json, schema=req.schema, cls=schema_validator)
except (jsonschema.ValidationError, jsonschema.exceptions.SchemaError) as err:
@@ -244,7 +247,7 @@ def fetch_schemas(cli, port, args, docs):
break
except Exception as e:
time.sleep(0.1)
- pass
+
print('Connected')
cli_proc = subprocess.Popen([cli, '-p', str(port), '--json', 'command', 'docs'], stdout=subprocess.PIPE)
@@ -287,16 +290,6 @@ if __name__ == '__main__':
fetch_schemas(args.cli, args.port, redis_args, docs)
- missing_schema = [k for k, v in docs.items()
- if "reply_schema" not in v and k not in IGNORED_COMMANDS]
- if missing_schema:
- print("WARNING! The following commands are missing a reply_schema:")
- for k in sorted(missing_schema):
- print(f" {k}")
- if args.fail_missing_reply_schemas:
- print("ERROR! at least one command does not have a reply_schema")
- sys.exit(1)
-
# Fetch schemas from a sentinel
print('Starting Redis sentinel')
@@ -308,9 +301,19 @@ if __name__ == '__main__':
fetch_schemas(args.cli, args.port, sentinel_args, docs)
os.unlink(config_file)
+ missing_schema = [k for k, v in docs.items()
+ if "reply_schema" not in v and k not in IGNORED_COMMANDS]
+ if missing_schema:
+ print("WARNING! The following commands are missing a reply_schema:")
+ for k in sorted(missing_schema):
+ print(f" {k}")
+ if args.fail_missing_reply_schemas:
+ print("ERROR! at least one command does not have a reply_schema")
+ sys.exit(1)
+
start = time.time()
- # Obtain all the files toprocesses
+ # Obtain all the files to processes
paths = []
for path in glob.glob('%s/tmp/*/*.reqres' % testdir):
paths.append(path)
@@ -335,9 +338,7 @@ if __name__ == '__main__':
print("Hits per command:")
for k, v in sorted(command_counter.items()):
print(f" {k}: {v}")
- # We don't care about SENTINEL commands
- not_hit = set(filter(lambda x: not x.startswith("sentinel"),
- set(docs.keys()) - set(command_counter.keys()) - set(IGNORED_COMMANDS)))
+ not_hit = set(set(docs.keys()) - set(command_counter.keys()) - set(IGNORED_COMMANDS))
if not_hit:
if args.verbose:
print("WARNING! The following commands were not hit at all:")