A lot more command line testing.
There'es some issue with tons of requests getting slowly blocked, though... git-svn-id: https://bucket.mit.edu/svn/nilm/nilmdb@10678 ddd99763-3ecb-0310-9145-efcb8ce7c51f
This commit is contained in:
parent
277b0c1d00
commit
a3f444eb25
|
@ -11,6 +11,7 @@ import re
|
|||
import os
|
||||
import urlparse
|
||||
import argparse
|
||||
import fnmatch
|
||||
|
||||
version = "0.1"
|
||||
|
||||
|
@ -32,8 +33,6 @@ class Cmdline(object):
|
|||
help='show this help message and exit')
|
||||
group.add_argument("-V", "--version", action="version",
|
||||
version=version_string)
|
||||
group.add_argument("-q", "--quiet", action="store_true",
|
||||
help="suppress unnecessary console output")
|
||||
|
||||
group = parser.add_argument_group("Server")
|
||||
group.add_argument("-u", "--url", action="store",
|
||||
|
@ -52,31 +51,64 @@ class Cmdline(object):
|
|||
List information about the server, like
|
||||
version.
|
||||
""")
|
||||
cmd.set_defaults(handler = self.cmd_info)
|
||||
|
||||
# list
|
||||
cmd = sub.add_parser("list", help="List streams",
|
||||
formatter_class = formatter,
|
||||
description="""
|
||||
List streams available in the database,
|
||||
optionally filtering by type or partial path.
|
||||
optionally filtering by type or path. Wildcards
|
||||
are accepted.
|
||||
""")
|
||||
cmd.set_defaults(handler = self.cmd_list)
|
||||
group = cmd.add_argument_group("Stream filtering")
|
||||
group.add_argument("-t", "--type", metavar="GLOB", default="*",
|
||||
group.add_argument("-t", "--type", default="*",
|
||||
help="Match only this stream type")
|
||||
group.add_argument("-p", "--path", metavar="GLOB", default="*",
|
||||
group.add_argument("-p", "--path", default="*",
|
||||
help="Match only this path")
|
||||
|
||||
# group.add_argument(
|
||||
# create
|
||||
cmd = sub.add_parser("create", help="Create a new stream",
|
||||
formatter_class = formatter,
|
||||
description="""
|
||||
Create a new empty stream at the
|
||||
specified path and with the specifed
|
||||
layout type.
|
||||
""")
|
||||
cmd.set_defaults(handler = self.cmd_create)
|
||||
group = cmd.add_argument_group("Required arguments")
|
||||
group.add_argument("path",
|
||||
help="Path of new stream, e.g. /foo/bar")
|
||||
group.add_argument("type",
|
||||
help="Layout type for new stream, e.g. RawData")
|
||||
|
||||
# parser.add_argument_group(group)
|
||||
# metadata
|
||||
cmd = sub.add_parser("metadata", help="Get or set stream metadata",
|
||||
description="""
|
||||
Get or set key=value metadata associated with
|
||||
a stream.
|
||||
""",
|
||||
usage="%(prog)s path [-g [key ...] | "
|
||||
"-s key=value [...] | -u key=value [...]]")
|
||||
cmd.set_defaults(handler = self.cmd_metadata)
|
||||
|
||||
# group = OptionGroup(parser, "Stream Operations")
|
||||
# group.add_argument("-l", "--list", action="store_true", default=False,
|
||||
# action="store", dest="url",
|
||||
# default="http://localhost:12380/",
|
||||
# help="NilmDB server URL (default: %default)")
|
||||
# parser.add_argument_group(group)
|
||||
group = cmd.add_argument_group("Required arguments")
|
||||
group.add_argument("path",
|
||||
help="Path of stream, e.g. /foo/bar")
|
||||
|
||||
group = cmd.add_argument_group("Actions")
|
||||
exc = group.add_mutually_exclusive_group()
|
||||
exc.add_argument("-g", "--get", nargs="*", metavar="key",
|
||||
help="Get metadata for specified keys (default all)")
|
||||
exc.add_argument("-s", "--set", nargs="+", metavar="key=value",
|
||||
help="Replace all metadata with provided "
|
||||
"key=value pairs")
|
||||
exc.add_argument("-u", "--update", nargs="+", metavar="key=value",
|
||||
help="Update metadata using provided "
|
||||
"key=value pairs")
|
||||
|
||||
# parse it
|
||||
self.args = parser.parse_args(self.argv)
|
||||
|
||||
def die(self, formatstr, *args):
|
||||
|
@ -97,13 +129,62 @@ class Cmdline(object):
|
|||
# Now dispatch client request to appropriate function. Parser
|
||||
# should have ensured that we don't have any unknown commands
|
||||
# here.
|
||||
getattr(self,"cmd_" + self.args.command)()
|
||||
self.args.handler()
|
||||
|
||||
def cmd_info(self):
|
||||
"""Print info about the server"""
|
||||
printf("Client library version: %s\n", self.client.client_version)
|
||||
printf("Server version: %s\n", self.client.version())
|
||||
printf("Server URL: %s\n", self.client.geturl())
|
||||
printf("Server database: %s\n", self.client.dbpath())
|
||||
|
||||
# if not opt.quiet:
|
||||
# printf("Server URL: %s\n", opt.url)
|
||||
def cmd_list(self):
|
||||
"""List available streams"""
|
||||
streams = self.client.stream_list()
|
||||
for (path, type) in streams:
|
||||
if (fnmatch.fnmatch(path, self.args.path) and
|
||||
fnmatch.fnmatch(type, self.args.type)):
|
||||
printf("%s %s\n", path, type)
|
||||
|
||||
def cmd_create(self):
|
||||
"""Create new stream"""
|
||||
try:
|
||||
self.client.stream_create(self.args.path, self.args.type)
|
||||
except nilmdb.client.ClientError as e:
|
||||
self.die("Error creating stream: %s\n", str(e))
|
||||
|
||||
def cmd_metadata(self):
|
||||
"""Manipulate metadata"""
|
||||
if self.args.set is not None or self.args.update is not None:
|
||||
# Either a set or an update
|
||||
if self.args.set is not None:
|
||||
keyvals = self.args.set
|
||||
handler = self.client.stream_set_metadata
|
||||
else:
|
||||
keyvals = self.args.update
|
||||
handler = self.client.stream_update_metadata
|
||||
|
||||
# Extract key=value pairs
|
||||
data = {}
|
||||
for keyval in keyvals:
|
||||
kv = keyval.split('=', 1)
|
||||
if len(kv) != 2 or kv[0] == "":
|
||||
self.die("Error parsing key=value argument '%s'\n", keyval)
|
||||
data[kv[0]] = kv[1]
|
||||
|
||||
# Make the call
|
||||
try:
|
||||
handler(self.args.path, data)
|
||||
except nilmdb.client.ClientError as e:
|
||||
self.die("Error setting/updating metadata: %s\n", str(e))
|
||||
else:
|
||||
# Get (or unspecified)
|
||||
keys = self.args.get or None
|
||||
try:
|
||||
data = self.client.stream_get_metadata(self.args.path, keys)
|
||||
except nilmdb.client.ClientError as e:
|
||||
self.die("Error getting metadata: %s\n", str(e))
|
||||
for key, value in sorted(data.items()):
|
||||
if value is None:
|
||||
value = ""
|
||||
printf("%s=%s\n", key, value)
|
||||
|
|
|
@ -196,7 +196,12 @@ class NilmDB(object):
|
|||
def stream_create(self, path, layout_name, index = None):
|
||||
"""Create a new table in the database.
|
||||
|
||||
path: path to the data (e.g. '/newton/prep')
|
||||
path: path to the data (e.g. '/newton/prep').
|
||||
Paths must contain at least two elements, e.g.:
|
||||
/newton/prep
|
||||
/newton/raw
|
||||
/newton/upstairs/prep
|
||||
/newton/upstairs/raw
|
||||
|
||||
layout_name: one of the nilmdb.layout.layouts keys, e.g. 'PrepData'
|
||||
|
||||
|
@ -208,7 +213,7 @@ class NilmDB(object):
|
|||
raise ValueError("paths must start with /")
|
||||
[ group, node ] = path.rsplit("/", 1)
|
||||
if group == '':
|
||||
raise ValueError("Invalid path")
|
||||
raise ValueError("invalid path")
|
||||
|
||||
# Make the group structure, one element at a time
|
||||
group_path = group.lstrip('/').split("/")
|
||||
|
@ -221,7 +226,10 @@ class NilmDB(object):
|
|||
pass
|
||||
|
||||
# Get description
|
||||
desc = nilmdb.layout.named[layout_name].description()
|
||||
try:
|
||||
desc = nilmdb.layout.named[layout_name].description()
|
||||
except KeyError:
|
||||
raise ValueError("no such layout")
|
||||
|
||||
# Estimated table size (for PyTables optimization purposes): assume
|
||||
# 3 months worth of data. It's OK if this is wrong.
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
import nilmdb
|
||||
import sys
|
||||
|
||||
nilmdb.cmdline.run(sys.argv[1:])
|
||||
nilmdb.cmdline.Cmdline(sys.argv[1:]).run()
|
||||
|
|
|
@ -114,6 +114,12 @@ class TestClient(object):
|
|||
eq_(client.stream_get_metadata("/newton/raw", [ "description",
|
||||
"v_scale" ] ), meta1)
|
||||
|
||||
# missing key
|
||||
eq_(client.stream_get_metadata("/newton/raw", "descr"),
|
||||
{ "descr": None })
|
||||
eq_(client.stream_get_metadata("/newton/raw", [ "descr" ]),
|
||||
{ "descr": None })
|
||||
|
||||
# test wrong types (list instead of dict)
|
||||
with assert_raises(ClientError):
|
||||
client.stream_set_metadata("/newton/prep", [1,2,3])
|
||||
|
|
|
@ -41,7 +41,7 @@ def teardown_module():
|
|||
|
||||
class TestCmdline(object):
|
||||
|
||||
def run(self, arg_string, input_string = ""):
|
||||
def run(self, arg_string, input_string = "", capture_stderr=True):
|
||||
"""Run a cmdline client with the specified argument string,
|
||||
passing the given input. Returns a tuple with the output and
|
||||
exit code"""
|
||||
|
@ -55,7 +55,11 @@ class TestCmdline(object):
|
|||
( sys.stdin, sys.stdout, sys.stderr ) = self.saved
|
||||
infile = cStringIO.StringIO(input_string)
|
||||
outfile = cStringIO.StringIO()
|
||||
with stdio_wrapper(infile, outfile, outfile) as s:
|
||||
if capture_stderr:
|
||||
errfile = outfile
|
||||
else:
|
||||
errfile = sys.stderr
|
||||
with stdio_wrapper(infile, outfile, errfile) as s:
|
||||
try:
|
||||
nilmdb.cmdline.Cmdline(shlex.split(arg_string)).run()
|
||||
sys.exit(0)
|
||||
|
@ -80,12 +84,13 @@ class TestCmdline(object):
|
|||
def check(self, checkstring):
|
||||
in_(checkstring, self.output)
|
||||
|
||||
def dump(self):
|
||||
print '-----dump start-----'
|
||||
print self.output[:-1]
|
||||
print '-----dump end-----'
|
||||
def match(self, checkstring):
|
||||
eq_(checkstring, self.output)
|
||||
|
||||
def test_cmdline_basic(self):
|
||||
def dump(self):
|
||||
printf("-----dump start-----\n%s-----dump end-----\n", self.output)
|
||||
|
||||
def test_cmdline_1_basic(self):
|
||||
|
||||
# help
|
||||
self.ok("--help")
|
||||
|
@ -116,8 +121,155 @@ class TestCmdline(object):
|
|||
self.ok("-u localhost:12380 info")
|
||||
self.ok("info")
|
||||
|
||||
def test_cmdline_info(self):
|
||||
def test_cmdline_2_info(self):
|
||||
self.ok("info")
|
||||
self.check("Server URL: http://localhost:12380/")
|
||||
self.check("Server version: " + test_server.version)
|
||||
|
||||
def test_cmdline_3_misc(self):
|
||||
# Basic stream tests, like those in test_client.
|
||||
|
||||
# BUG: for some reason these start to hang up! what's going on?
|
||||
for i in range(100):
|
||||
print i
|
||||
self.ok("list")
|
||||
eq_(1,0)
|
||||
|
||||
# No streams
|
||||
self.ok("list")
|
||||
self.match("")
|
||||
|
||||
# Bad paths
|
||||
self.fail("create foo/bar/baz PrepData")
|
||||
self.check("paths must start with /")
|
||||
|
||||
self.fail("create /foo PrepData")
|
||||
self.check("invalid path")
|
||||
|
||||
# Bad layout type
|
||||
self.fail("create /newton/prep NoSuchLayout")
|
||||
self.check("no such layout")
|
||||
|
||||
# Create a few streams
|
||||
self.ok("create /newton/prep PrepData")
|
||||
self.ok("create /newton/raw RawData")
|
||||
self.ok("create /newton/zzz/rawnotch RawNotchedData")
|
||||
|
||||
# Verify we got those 3 streams
|
||||
self.ok("list")
|
||||
self.match("/newton/prep PrepData\n"
|
||||
"/newton/raw RawData\n"
|
||||
"/newton/zzz/rawnotch RawNotchedData\n")
|
||||
|
||||
# Match just one type or one path
|
||||
self.ok("list --path /newton/raw")
|
||||
self.match("/newton/raw RawData\n")
|
||||
|
||||
self.ok("list --type RawData")
|
||||
self.match("/newton/raw RawData\n")
|
||||
|
||||
# Wildcard matches
|
||||
self.ok("list --type Raw*")
|
||||
self.match("/newton/raw RawData\n"
|
||||
"/newton/zzz/rawnotch RawNotchedData\n")
|
||||
|
||||
self.ok("list --path *zzz* --type Raw*")
|
||||
self.match("/newton/zzz/rawnotch RawNotchedData\n")
|
||||
|
||||
self.ok("list --path *zzz* --type Prep*")
|
||||
self.match("")
|
||||
|
||||
# Set / get metadata
|
||||
self.fail("metadata")
|
||||
self.fail("metadata --get")
|
||||
|
||||
self.ok("metadata /newton/prep")
|
||||
self.match("")
|
||||
|
||||
self.ok("metadata /newton/raw --get")
|
||||
self.match("")
|
||||
|
||||
self.ok("metadata /newton/prep --set "
|
||||
"'description=The Data' "
|
||||
"v_scale=1.234")
|
||||
self.ok("metadata /newton/raw --update "
|
||||
"'description=The Data'")
|
||||
self.ok("metadata /newton/raw --update "
|
||||
"v_scale=1.234")
|
||||
|
||||
self.ok("metadata /newton/prep")
|
||||
self.match("description=The Data\nv_scale=1.234\n")
|
||||
self.ok("metadata /newton/prep --get")
|
||||
self.match("description=The Data\nv_scale=1.234\n")
|
||||
self.ok("metadata /newton/prep --get descr")
|
||||
self.match("")
|
||||
self.ok("metadata /newton/prep --get description")
|
||||
self.match("description=The Data\n")
|
||||
|
||||
self.ok("metadata /newton/raw")
|
||||
self.dump()
|
||||
|
||||
|
||||
# client.stream_set_metadata("/newton/prep", meta1)
|
||||
# client.stream_update_metadata("/newton/prep", {})
|
||||
# client.stream_update_metadata("/newton/raw", meta2)
|
||||
# client.stream_update_metadata("/newton/raw", meta3)
|
||||
# eq_(client.stream_get_metadata("/newton/prep"), meta1)
|
||||
# eq_(client.stream_get_metadata("/newton/raw"), meta1)
|
||||
# eq_(client.stream_get_metadata("/newton/raw", [ "description" ] ), meta2)
|
||||
# eq_(client.stream_get_metadata("/newton/raw", [ "description",
|
||||
# "v_scale" ] ), meta1)
|
||||
|
||||
# # test wrong types (list instead of dict)
|
||||
# with assert_raises(ClientError):
|
||||
# client.stream_set_metadata("/newton/prep", [1,2,3])
|
||||
# with assert_raises(ClientError):
|
||||
# client.stream_update_metadata("/newton/prep", [1,2,3])
|
||||
|
||||
# def test_client_3_insert(self):
|
||||
# client = nilmdb.Client(url = "http://localhost:12380/")
|
||||
|
||||
# datetime_tz.localtz_set("America/New_York")
|
||||
|
||||
# testfile = "tests/data/prep-20120323T1000"
|
||||
# start = datetime_tz.datetime_tz.smartparse("20120323T1000")
|
||||
# rate = 120
|
||||
|
||||
# # First try a nonexistent path
|
||||
# data = nilmdb.timestamper.TimestamperRate(testfile, start, 120)
|
||||
# with assert_raises(ClientError) as e:
|
||||
# result = client.stream_insert("/newton/no-such-path", data)
|
||||
# in_("404 Not Found", str(e.exception))
|
||||
|
||||
# # Now try reversed timestamps
|
||||
# data = nilmdb.timestamper.TimestamperRate(testfile, start, 120)
|
||||
# data = reversed(list(data))
|
||||
# with assert_raises(ClientError) as e:
|
||||
# result = client.stream_insert("/newton/prep", data)
|
||||
# in_("400 Bad Request", str(e.exception))
|
||||
# in_("timestamp is not monotonically increasing", str(e.exception))
|
||||
|
||||
# # Now try empty data (no server request made)
|
||||
# empty = cStringIO.StringIO("")
|
||||
# data = nilmdb.timestamper.TimestamperRate(empty, start, 120)
|
||||
# result = client.stream_insert("/newton/prep", data)
|
||||
# eq_(result, None)
|
||||
|
||||
# # Try forcing a server request with empty data
|
||||
# with assert_raises(ClientError) as e:
|
||||
# client.curl.putjson("stream/insert", "", { "path": "/newton/prep" })
|
||||
# in_("400 Bad Request", str(e.exception))
|
||||
# in_("no data provided", str(e.exception))
|
||||
|
||||
# # Now do the real load
|
||||
# data = nilmdb.timestamper.TimestamperRate(testfile, start, 120)
|
||||
# result = client.stream_insert("/newton/prep", data)
|
||||
# eq_(result, "ok")
|
||||
|
||||
# # Try some overlapping data -- just insert it again
|
||||
# data = nilmdb.timestamper.TimestamperRate(testfile, start, 120)
|
||||
# with assert_raises(ClientError) as e:
|
||||
# result = client.stream_insert("/newton/prep", data)
|
||||
# in_("400 Bad Request", str(e.exception))
|
||||
# in_("OverlapError", str(e.exception))
|
||||
|
||||
|
|
|
@ -2,25 +2,23 @@
|
|||
|
||||
import shutil, os
|
||||
|
||||
def myrepr(x):
|
||||
if isinstance(x, basestring):
|
||||
return '"' + x + '"'
|
||||
else:
|
||||
return repr(x)
|
||||
|
||||
def eq_(a, b):
|
||||
if not a == b:
|
||||
raise AssertionError("%r != %r" % (a, b))
|
||||
raise AssertionError("%s != %s" % (myrepr(a), myrepr(b)))
|
||||
|
||||
def in_(a, b):
|
||||
if a not in b:
|
||||
if not isinstance(a, basestring):
|
||||
a = repr(a)
|
||||
else:
|
||||
a = '"' + a + '"'
|
||||
if not isinstance(b, basestring):
|
||||
b = repr(b)
|
||||
else:
|
||||
b = '"' + b + '"'
|
||||
raise AssertionError("%s not in %s" % (a, b))
|
||||
raise AssertionError("%s not in %s" % (myrepr(a), myrepr(b)))
|
||||
|
||||
def ne_(a, b):
|
||||
if not a != b:
|
||||
raise AssertionError("unexpected %r == %r" % (a, b))
|
||||
raise AssertionError("unexpected %s == %s" % (myrepr(a), myrepr(b)))
|
||||
|
||||
def recursive_unlink(path):
|
||||
try:
|
||||
|
|
Loading…
Reference in New Issue
Block a user