Browse Source

Destroy now requires that all data has been previously removed.

Added new flag "-R" to command line to perform an automatic removal.
This should be the last of the ways in which a single command could
block the nilmdb thread for a long time.
tags/nilmdb-1.4.2^0
Jim Paris 11 years ago
parent
commit
4cdef3285d
6 changed files with 51 additions and 20 deletions
  1. +1
    -1
      nilmdb/client/client.py
  2. +11
    -3
      nilmdb/cmdline/destroy.py
  3. +10
    -5
      nilmdb/server/nilmdb.py
  4. +1
    -1
      nilmdb/server/server.py
  5. +9
    -1
      tests/test_client.py
  6. +19
    -9
      tests/test_cmdline.py

+ 1
- 1
nilmdb/client/client.py View File

@@ -97,7 +97,7 @@ class Client(object):
return self.http.post("stream/create", params)

def stream_destroy(self, path):
"""Delete stream and its contents"""
"""Delete stream. Fails if any data is still present."""
params = { "path": path }
return self.http.post("stream/destroy", params)



+ 11
- 3
nilmdb/cmdline/destroy.py View File

@@ -7,11 +7,14 @@ def setup(self, sub):
cmd = sub.add_parser("destroy", help="Delete a stream and all data",
formatter_class = def_form,
description="""
Destroy the stream at the specified path. All
data and metadata related to the stream is
permanently deleted.
Destroy the stream at the specified path.
The stream must be empty. All metadata
related to the stream is permanently deleted.
""")
cmd.set_defaults(handler = cmd_destroy)
group = cmd.add_argument_group("Options")
group.add_argument("-R", "--remove", action="store_true",
help="Remove all data before destroying stream")
group = cmd.add_argument_group("Required arguments")
group.add_argument("path",
help="Path of the stream to delete, e.g. /foo/bar",
@@ -20,6 +23,11 @@ def setup(self, sub):

def cmd_destroy(self):
"""Destroy stream"""
if self.args.remove:
try:
count = self.client.stream_remove(self.args.path)
except nilmdb.client.ClientError as e:
self.die("error removing data: %s", str(e))
try:
self.client.stream_destroy(self.args.path)
except nilmdb.client.ClientError as e:


+ 10
- 5
nilmdb/server/nilmdb.py View File

@@ -450,17 +450,22 @@ class NilmDB(object):
(newpath, stream_id))

def stream_destroy(self, path):
"""Fully remove a table and all of its data from the database.
No way to undo it! Metadata is removed."""
"""Fully remove a table from the database. Fails if there are
any intervals data present; remove them first. Metadata is
also removed."""
stream_id = self._stream_id(path)

# Delete the cached interval data (if it was cached)
# Verify that no intervals are present, and clear the cache
iset = self._get_intervals(stream_id)
if len(iset):
raise NilmDBError("all intervals must be removed before "
"destroying a stream")
self._get_intervals.cache_remove(self, stream_id)

# Delete the data
# Delete the bulkdata storage
self.data.destroy(path)

# Delete metadata, stream, intervals
# Delete metadata, stream, intervals (should be none)
with self.con as con:
con.execute("DELETE FROM metadata WHERE stream_id=?", (stream_id,))
con.execute("DELETE FROM ranges WHERE stream_id=?", (stream_id,))


+ 1
- 1
nilmdb/server/server.py View File

@@ -210,7 +210,7 @@ class Stream(NilmApp):
@exception_to_httperror(NilmDBError)
@cherrypy.tools.CORS_allow(methods = ["POST"])
def destroy(self, path):
"""Delete a stream and its associated data."""
"""Delete a stream. Fails if any data is still present."""
return self.db.stream_destroy(path)

# /stream/rename?oldpath=/newton/prep&newpath=/newton/prep/1


+ 9
- 1
tests/test_client.py View File

@@ -375,6 +375,7 @@ class TestClient(object):

# Delete streams that exist
for stream in client.stream_list():
client.stream_remove(stream[0])
client.stream_destroy(stream[0])

# Database is empty
@@ -506,6 +507,10 @@ class TestClient(object):
[ 109, 118 ],
[ 200, 300 ] ])

# destroy stream (try without removing data first)
with assert_raises(ClientError):
client.stream_destroy("/context/test")
client.stream_remove("/context/test")
client.stream_destroy("/context/test")
client.close()

@@ -600,6 +605,7 @@ class TestClient(object):
])

# Clean up
client.stream_remove("/empty/test")
client.stream_destroy("/empty/test")
client.close()

@@ -635,8 +641,9 @@ class TestClient(object):
eq_(connections(), (1, 5))

# Clean up
c.stream_remove("/persist/test")
c.stream_destroy("/persist/test")
eq_(connections(), (1, 6))
eq_(connections(), (1, 7))

def test_client_13_timestamp_rounding(self):
# Test potentially bad timestamps (due to floating point
@@ -661,5 +668,6 @@ class TestClient(object):
# Server will round this and give an error on finalize()
ctx.insert("299999999.99 1\n")

client.stream_remove("/rounding/test")
client.stream_destroy("/rounding/test")
client.close()

+ 19
- 9
tests/test_cmdline.py View File

@@ -716,6 +716,9 @@ class TestCmdline(object):
self.fail("destroy /no/such/stream")
self.contain("No stream at path")

self.fail("destroy -R /no/such/stream")
self.contain("No stream at path")

self.fail("destroy asdfasdf")
self.contain("No stream at path")

@@ -729,8 +732,14 @@ class TestCmdline(object):
self.ok("list --detail")
lines_(self.captured, 7)

# Delete some
self.ok("destroy /newton/prep")
# Fail to destroy because intervals still present
self.fail("destroy /newton/prep")
self.contain("all intervals must be removed")
self.ok("list --detail")
lines_(self.captured, 7)

# Destroy for real
self.ok("destroy -R /newton/prep")
self.ok("list")
self.match("/newton/raw uint16_6\n"
"/newton/zzz/rawnotch uint16_9\n")
@@ -741,7 +750,8 @@ class TestCmdline(object):

self.ok("destroy /newton/raw")
self.ok("create /newton/raw uint16_6")
self.ok("destroy /newton/raw")
# Specify --remove with no data
self.ok("destroy --remove /newton/raw")
self.ok("list")
self.match("")

@@ -816,7 +826,7 @@ class TestCmdline(object):

# Now recreate the data one more time and make sure there are
# fewer files.
self.ok("destroy /newton/prep")
self.ok("destroy --remove /newton/prep")
self.fail("destroy /newton/prep") # already destroyed
self.ok("create /newton/prep float32_8")
os.environ['TZ'] = "UTC"
@@ -827,7 +837,7 @@ class TestCmdline(object):
for (dirpath, dirnames, filenames) in os.walk(testdb):
nfiles += len(filenames)
lt_(nfiles, 50)
self.ok("destroy /newton/prep") # destroy again
self.ok("destroy -R /newton/prep") # destroy again

def test_14_remove_files(self):
# Test BulkData's ability to remove when data is split into
@@ -977,8 +987,8 @@ class TestCmdline(object):
self.match("[ Thu, 01 Jan 2004 00:00:00.000000 +0000 -"
"> Sat, 01 Jan 2005 00:00:00.000000 +0000 ]\n")

self.ok("destroy /diff/1")
self.ok("destroy /diff/2")
self.ok("destroy -R /diff/1")
self.ok("destroy -R /diff/2")

def test_16_rename(self):
# Test renaming. Force file size smaller so we get more files
@@ -1042,7 +1052,7 @@ class TestCmdline(object):
self.fail("rename /foo/bar /xxx/yyy/zzz/www")
self.contain("path is subdir of existing node")
self.ok("rename /foo/bar /xxx/yyy/mmm")
self.ok("destroy /xxx/yyy/zzz")
self.ok("destroy -R /xxx/yyy/zzz")
check_path("xxx", "yyy", "mmm")

# Extract it at the final path
@@ -1050,7 +1060,7 @@ class TestCmdline(object):
"--end '2012-03-23 10:04:01'")
eq_(self.captured, extract_before)

self.ok("destroy /xxx/yyy/mmm")
self.ok("destroy -R /xxx/yyy/mmm")

# Make sure temporary rename dirs weren't left around
for (dirpath, dirnames, filenames) in os.walk(testdb):


Loading…
Cancel
Save