import os
import argparse
import datetime
from .smax_redis_client import SmaxRedisClient, join, normalize_pair, print_tree, print_smax, \
_TYPE_MAP, _REVERSE_TYPE_MAP, ConnectionError, TimeoutError
desc = """
A simple Python command line utility to pull or push SMA-X values.
"""
default_server = "localhost"
default_port = 6379
default_db = 0
[docs]
def main():
if "SMAX_SERVER" in os.environ:
smax_server = os.environ["SMAX_SERVER"]
else:
smax_server = default_server
if "SMAX_PORT" in os.environ:
smax_port = os.environ["SMAX_PORT"]
else:
smax_port = default_port
if "SMAX_DB" in os.environ:
smax_db = os.environ["SMAX_DB"]
else:
smax_db = default_db
parser = argparse.ArgumentParser(description=desc)
parser.add_argument("--redis_ip", "-i", help="Host name or IP of SMA-X Redis host", default=smax_server)
parser.add_argument("--port", "-p", help="Port number of the SMA-X Redis server", default=smax_port)
parser.add_argument("--db", "-d", help="Database number of the SMA-X Redis database", default=smax_db)
parser.add_argument("--table", "-t", help="SMA-X table to pull or push from")
parser.add_argument("--key", "-k", help="SMA-X key to pull or push")
parser.add_argument("--type", "-T", help="Type for the value to be set.", choices=list(_TYPE_MAP.keys()), default=None)
parser.add_argument("--set", "-s", help="SMA-X value to set", default=None)
parser.add_argument("--search", "-S",
help="Search a string in SMA-X keys in Redis, and return matching keys. Wildcards can be included within the string, and are automatically prepended and appended", default=None)
parser.add_argument("--purge", action='store_true',
help='Purge the specified key from table, or an entire table if no key is given. Wildcards can be included. This is a dangerous operation.')
parser.add_argument("--purge-volatile", action='store_true', help='Purge everything except the "persistent" branch. An _EXTREMELY_ dangerous option')
parser.add_argument("--list", action='store_true', help="List all SMA-X keys in the Redis database. This is an expensive operation.")
parser.add_argument("--verbose", "-v", action='store_true', help="Show all SMA-X metadata associated with the key")
parser.add_argument("--debug", action='store_true', help="Show debug information")
args = parser.parse_args()
if args.debug:
print(args)
with SmaxRedisClient(redis_ip=args.redis_ip, redis_port=args.port, redis_db=args.db, program_name="smax-cli", debug=args.debug) as smax_client:
# If search is set, search for the string
if args.search is not None:
# Get all the keys matching the string
search_str = f"*{args.search}*"
keys = smax_client._client.execute_command(f"keys {search_str}")
for k in keys:
key = k.decode("UTF-8")
if key[0] == "<" or key == "scripts":
continue
else:
print(key)
elif args.list:
keys = smax_client._client.execute_command(f"keys *")
for k in keys:
key = k.decode("UTF-8")
if key[0] == "<" or key == "scripts":
continue
else:
if args.table is not None:
if not key.startswith(args.table):
continue
print(key)
elif args.purge:
if args.table is not None or args.key is not None:
table, key = normalize_pair(args.table, args.key)
pattern = join(table, key)
confirm = input(f"Really purge everything matching {pattern} [yes/NO] ")
if confirm.lower() == "yes":
fields_del = smax_client.smax_purge(args.table, args.key)
print(f'{fields_del} keys deleted.')
else:
print('Not confirmed - exiting.')
else:
print("Table or pattern to delete must be specified")
elif args.purge_volatile:
confirm = input("Really purge everything except the 'persistent' branch? [yes/NO] ")
if confirm.lower() == "yes":
smax_client.smax_purge_volatile()
else:
print('Not confirmed - exiting.')
elif args.table is not None or args.key is not None:
table, key = normalize_pair(args.table, args.key)
if args.debug:
print(f"Got {table}, key {key}")
# If set is set, set the value
if args.set is not None:
if args.type is None:
# Try to convert set value to the current type of the SMA-X variable
try:
smax_type = smax_client.smax_pull(table, key).type
except RuntimeError:
smax_type = None
except (TimeoutError, ConnectionError):
smax_type = None
print("Could not connect to Redis")
if smax_type is None:
# Value isn't in Redis yet
print(f"Type not given and {join(table, key)} not yet in Redis. Trying to determine best type from int, float, str")
try:
v = float(args.set)
smax_type = "float"
if v.is_integer():
smax_type = "integer"
except ValueError:
smax_type = "str"
print(f"Using type {smax_type}")
else:
smax_type = args.type
python_type = _TYPE_MAP[smax_type]
try:
val = python_type(args.set)
except ValueError:
try:
# Python can't convert a float type string representing an integer to an int.
val = python_type(float(args.set))
except ValueError:
val = args.set
print(f"Could not convert {args.set} to {smax_type}, falling back to str.")
smax_client.smax_share(table, key, val)
# Set not given, so return value
else:
try:
result = smax_client.smax_pull(table, key)
except RuntimeError:
result = None
except (TimeoutError, ConnectionError):
print("Could not connect to Redis")
if result is None:
print(f"Can't find {join(table, key)} in Redis")
else:
if type(result) is dict:
# We have a struct of SMA-X values and need to walk through the values
print_tree(result, args.verbose)
else:
print_smax(result, args.verbose)
else:
parser.print_help()
smax_client.smax_disconnect()
if __name__ == "__main__":
main()