first pass at deep-checker, no webapi yet, probably big problems with it, only minimal tests

This commit is contained in:
Brian Warner 2008-07-16 18:20:57 -07:00
parent 7b9fff388d
commit 9289433ba3
6 changed files with 154 additions and 2 deletions

View File

@ -9,6 +9,7 @@ from allmydata.mutable.node import MutableFileNode
from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\
IURI, IFileNode, IMutableFileURI, IVerifierURI, IFilesystemNode, \
ExistingChildError, ICheckable
from allmydata.immutable.checker import DeepCheckResults
from allmydata.util import hashutil, mathutil
from allmydata.util.hashutil import netstring
from allmydata.util.limiter import ConcurrencyLimiter
@ -533,6 +534,48 @@ class NewDirectoryNode:
d.addCallback(_got_list)
return d
def deep_check(self, verify=False, repair=False):
results = DeepCheckResults()
found = set()
found.add(self.get_verifier())
limiter = ConcurrencyLimiter(10)
d = self._add_check_from_node(self, results, limiter, verify, repair)
d.addCallback(lambda res:
self._add_deepcheck_from_dirnode(self,
found, results, limiter,
verify, repair))
d.addCallback(lambda res: results)
return d
def _add_check_from_node(self, node, results, limiter, verify, repair):
d = limiter.add(node.check, verify, repair)
d.addCallback(results.add_check)
return d
def _add_deepcheck_from_dirnode(self, node, found, results, limiter,
verify, repair):
d = limiter.add(node.list)
def _got_list(children):
dl = []
for name, (child, metadata) in children.iteritems():
verifier = child.get_verifier()
if verifier in found:
# avoid loops
continue
dl.append(self._add_check_from_node(child,
results, limiter,
verify, repair))
if IDirectoryNode.providedBy(child):
dl.append(self._add_deepcheck_from_node(child, found,
results, limiter,
verify, repair))
if dl:
return defer.DeferredList(dl)
d.addCallback(_got_list)
return d
class DeepStats:
def __init__(self):
self.stats = {}

View File

@ -10,7 +10,8 @@ from zope.interface import implements
from twisted.internet import defer
from twisted.python import log
from allmydata import storage
from allmydata.interfaces import IVerifierURI, ICheckerResults
from allmydata.interfaces import IVerifierURI, \
ICheckerResults, IDeepCheckResults
from allmydata.immutable import download
from allmydata.util import hashutil, base32
@ -44,6 +45,42 @@ class Results:
s += "Not Healthy!\n"
return s
class DeepCheckResults:
implements(IDeepCheckResults)
def __init__(self):
self.objects_checked = 0
self.objects_healthy = 0
self.repairs_attempted = 0
self.repairs_successful = 0
self.problems = []
self.server_problems = {}
def add_check(self, r):
self.objects_checked += 1
if r.is_healthy:
self.objects_healthy += 1
else:
self.problems.append(r)
def add_repair(self, is_successful):
self.repairs_attempted += 1
if is_successful:
self.repairs_successful += 1
def count_objects_checked(self):
return self.objects_checked
def count_objects_healthy(self):
return self.objects_healthy
def count_repairs_attempted(self):
return self.repairs_attempted
def count_repairs_successful(self):
return self.repairs_successful
def get_server_problems(self):
return self.server_problems
def get_problems(self):
return self.problems
class SimpleCHKFileChecker:
"""Return a list of (needed, total, found, sharemap), where sharemap maps

View File

@ -3,7 +3,7 @@ from zope.interface import implements
from twisted.internet import defer
from allmydata.interfaces import IFileNode, IFileURI, IURI, ICheckable
from allmydata import uri
from allmydata.immutable.checker import Results, \
from allmydata.immutable.checker import Results, DeepCheckResults, \
SimpleCHKFileChecker, SimpleCHKFileVerifier
class FileNode:
@ -52,6 +52,15 @@ class FileNode:
v = SimpleCHKFileChecker(peer_getter, vcap)
return v.check()
def deep_check(self, verify=False, repair=False):
d = self.check(verify, repair)
def _done(r):
dr = DeepCheckResults()
dr.add_check(r)
return dr
d.addCallback(_done)
return d
def download(self, target):
downloader = self._client.getServiceNamed("downloader")
return downloader.download(self.uri, target)

View File

@ -1473,6 +1473,15 @@ class ICheckable(Interface):
taken.
"""
def deep_check(verify=False, repair=False):
"""Check upon the health of me and everything I can reach.
This is a recursive form of check(), useable on dirnodes. (it can be
called safely on filenodes too, but only checks the one object).
I return a Deferred that fires with an IDeepCheckResults object.
"""
class ICheckerResults(Interface):
"""I contain the detailed results of a check/verify/repair operation.
@ -1506,6 +1515,27 @@ class ICheckerResults(Interface):
# same nodeid, they will fail as a pair, and overall reliability is
# decreased.
class IDeepCheckResults(Interface):
"""I contain the results of a deep-check operation.
This is returned by a call to ICheckable.deep_check().
"""
def count_objects_checked():
"""Return the number of objects that were checked."""
def count_objects_healthy():
"""Return the number of objects that were fully healthy."""
def count_repairs_attempted():
"""Return the number of repair operations that were attempted."""
def count_repairs_successful():
"""Return the number of repair operations that succeeded in bringing
the object back up to full health."""
def get_server_problems():
"""Return a dict, mapping server nodeid to a count of how many
problems involved that server."""
def get_problems():
"""Return a list of ICheckerResults, one for each object that
was not fully healthy."""
class IClient(Interface):

View File

@ -11,6 +11,7 @@ from allmydata.util import hashutil
from allmydata.util.assertutil import precondition
from allmydata.uri import WriteableSSKFileURI
from allmydata.immutable.encode import NotEnoughSharesError
from allmydata.immutable.checker import DeepCheckResults
from pycryptopp.publickey import rsa
from pycryptopp.cipher.aes import AES
@ -240,6 +241,15 @@ class MutableFileNode:
checker = MutableChecker(self)
return checker.check(verify, repair)
def deep_check(self, verify=False, repair=False):
d = self.check(verify, repair)
def _done(r):
dr = DeepCheckResults()
dr.add_check(r)
return dr
d.addCallback(_done)
return d
# allow the use of IDownloadTarget
def download(self, target):
# fake it. TODO: make this cleaner.

View File

@ -117,6 +117,29 @@ class Dirnode(unittest.TestCase, testutil.ShouldFailMixin, testutil.StallMixin):
d.addCallback(_done)
return d
def _test_deepcheck_create(self):
d = self.client.create_empty_dirnode()
def _created_root(rootnode):
self._rootnode = rootnode
d.addCallback(_created_root)
def _done(res):
return self._rootnode
d.addCallback(_done)
return d
def test_deepcheck(self):
d = self._test_deepcheck_create()
d.addCallback(lambda rootnode: rootnode.deep_check())
def _check_results(r):
self.failUnlessEqual(r.count_objects_checked(), 1)
self.failUnlessEqual(r.count_objects_healthy(), 1)
self.failUnlessEqual(r.count_repairs_attempted(), 0)
self.failUnlessEqual(r.count_repairs_successful(), 0)
self.failUnlessEqual(len(r.get_server_problems()), 0)
self.failUnlessEqual(len(r.get_problems()), 0)
d.addCallback(_check_results)
return d
def test_readonly(self):
fileuri = make_chk_file_uri(1234)
filenode = self.client.create_node_from_uri(fileuri)