import unittest
import re
+import itertools
+import operator
from ganeti import _autoconf
from ganeti import utils
from ganeti import build
from ganeti import compat
from ganeti import mcpu
+from ganeti import opcodes
+from ganeti import constants
+from ganeti.rapi import baserlib
+from ganeti.rapi import rlib2
from ganeti.rapi import connector
import testutils
VALID_URI_RE = re.compile(r"^[-/a-z0-9]*$")
-
-class TestDocs(unittest.TestCase):
- """Documentation tests"""
-
- @staticmethod
- def _ReadDocFile(filename):
- return utils.ReadFile("%s/doc/%s" %
- (testutils.GetSourceDir(), filename))
-
- def testHookDocs(self):
+RAPI_OPCODE_EXCLUDE = frozenset([
+ # Not yet implemented
+ opcodes.OpBackupQuery,
+ opcodes.OpBackupRemove,
+ opcodes.OpClusterConfigQuery,
+ opcodes.OpClusterRepairDiskSizes,
+ opcodes.OpClusterVerify,
+ opcodes.OpClusterVerifyDisks,
+ opcodes.OpInstanceChangeGroup,
+ opcodes.OpInstanceMove,
+ opcodes.OpNodeQueryvols,
+ opcodes.OpOobCommand,
+ opcodes.OpTagsSearch,
+ opcodes.OpClusterActivateMasterIp,
+ opcodes.OpClusterDeactivateMasterIp,
+
+ # Difficult if not impossible
+ opcodes.OpClusterDestroy,
+ opcodes.OpClusterPostInit,
+ opcodes.OpClusterRename,
+ opcodes.OpNodeAdd,
+ opcodes.OpNodeRemove,
+
+ # Helper opcodes (e.g. submitted by LUs)
+ opcodes.OpClusterVerifyConfig,
+ opcodes.OpClusterVerifyGroup,
+ opcodes.OpGroupEvacuate,
+ opcodes.OpGroupVerifyDisks,
+
+ # Test opcodes
+ opcodes.OpTestAllocator,
+ opcodes.OpTestDelay,
+ opcodes.OpTestDummy,
+ opcodes.OpTestJqueue,
+ ])
+
+
+def _ReadDocFile(filename):
+ return utils.ReadFile("%s/doc/%s" %
+ (testutils.GetSourceDir(), filename))
+
+
+class TestHooksDocs(unittest.TestCase):
+ def test(self):
"""Check whether all hooks are documented.
"""
- hooksdoc = self._ReadDocFile("hooks.rst")
+ hooksdoc = _ReadDocFile("hooks.rst")
# Reverse mapping from LU to opcode
lu2opcode = dict((lu, op)
msg=("Missing documentation for hook %s/%s" %
(lucls.HTYPE, lucls.HPATH)))
+
+class TestRapiDocs(unittest.TestCase):
def _CheckRapiResource(self, uri, fixup, handler):
docline = "%s resource." % uri
self.assertEqual(handler.__doc__.splitlines()[0].strip(), docline,
self.assertTrue(VALID_URI_RE.match(uri), msg="Invalid URI %r" % uri)
- def testRapiDocs(self):
+ def test(self):
"""Check whether all RAPI resources are documented.
"""
- rapidoc = self._ReadDocFile("rapi.rst")
+ rapidoc = _ReadDocFile("rapi.rst")
node_name = re.escape("[node_name]")
instance_name = re.escape("[instance_name]")
msg=("URIs matched by more than one resource: %s" %
utils.CommaJoin(uri_dups)))
+ self._FindRapiMissing(resources.values())
+ self._CheckTagHandlers(resources.values())
+
+ def _FindRapiMissing(self, handlers):
+ used = frozenset(itertools.chain(*map(baserlib.GetResourceOpcodes,
+ handlers)))
+
+ unexpected = used & RAPI_OPCODE_EXCLUDE
+ self.assertFalse(unexpected,
+ msg=("Found RAPI resources for excluded opcodes: %s" %
+ utils.CommaJoin(_GetOpIds(unexpected))))
+
+ missing = (frozenset(opcodes.OP_MAPPING.values()) - used -
+ RAPI_OPCODE_EXCLUDE)
+ self.assertFalse(missing,
+ msg=("Missing RAPI resources for opcodes: %s" %
+ utils.CommaJoin(_GetOpIds(missing))))
+
+ def _CheckTagHandlers(self, handlers):
+ tag_handlers = filter(lambda x: issubclass(x, rlib2._R_Tags), handlers)
+ self.assertEqual(frozenset(map(operator.attrgetter("TAG_LEVEL"),
+ tag_handlers)),
+ constants.VALID_TAG_TYPES)
+
+
+def _GetOpIds(ops):
+ """Returns C{OP_ID} for all opcodes in passed sequence.
+
+ """
+ return sorted(opcls.OP_ID for opcls in ops)
+
class TestManpages(unittest.TestCase):
"""Manpage tests"""