Skip to content
Snippets Groups Projects
Commit f6ce0ba2 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

rlib2: Declare all opcodes and equivalents


By declaring all used opcodes or opcodes equivalent to the operations
executed in a resource we will be able to ensure all opcodes are covered
by RAPI (with some exceptions).

Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent b8ab1c7f
No related branches found
No related tags found
No related merge requests found
...@@ -440,22 +440,23 @@ class _MetaOpcodeResource(type): ...@@ -440,22 +440,23 @@ class _MetaOpcodeResource(type):
obj = type.__call__(mcs, *args, **kwargs) obj = type.__call__(mcs, *args, **kwargs)
for (method, op_attr, rename_attr, fn_attr) in _OPCODE_ATTRS: for (method, op_attr, rename_attr, fn_attr) in _OPCODE_ATTRS:
try: if hasattr(obj, method):
opcode = getattr(obj, op_attr) # If the method handler is already defined, "*_RENAME" or "Get*OpInput"
except AttributeError: # shouldn't be (they're only used by the automatically generated
# If the "*_OPCODE" attribute isn't set, "*_RENAME" or "Get*OpInput" # handler)
# shouldn't either
assert not hasattr(obj, rename_attr) assert not hasattr(obj, rename_attr)
assert not hasattr(obj, fn_attr) assert not hasattr(obj, fn_attr)
continue else:
# Try to generate handler method on handler instance
assert not hasattr(obj, method) try:
opcode = getattr(obj, op_attr)
# Generate handler method on handler instance except AttributeError:
setattr(obj, method, pass
compat.partial(obj._GenericHandler, opcode, else:
getattr(obj, rename_attr, None), setattr(obj, method,
getattr(obj, fn_attr, obj._GetDefaultData))) compat.partial(obj._GenericHandler, opcode,
getattr(obj, rename_attr, None),
getattr(obj, fn_attr, obj._GetDefaultData)))
return obj return obj
......
...@@ -182,10 +182,12 @@ class R_version(baserlib.ResourceBase): ...@@ -182,10 +182,12 @@ class R_version(baserlib.ResourceBase):
return constants.RAPI_VERSION return constants.RAPI_VERSION
class R_2_info(baserlib.ResourceBase): class R_2_info(baserlib.OpcodeResource):
"""/2/info resource. """/2/info resource.
""" """
GET_OPCODE = opcodes.OpClusterQuery
def GET(self): def GET(self):
"""Returns cluster information. """Returns cluster information.
...@@ -206,10 +208,12 @@ class R_2_features(baserlib.ResourceBase): ...@@ -206,10 +208,12 @@ class R_2_features(baserlib.ResourceBase):
return list(ALL_FEATURES) return list(ALL_FEATURES)
class R_2_os(baserlib.ResourceBase): class R_2_os(baserlib.OpcodeResource):
"""/2/os resource. """/2/os resource.
""" """
GET_OPCODE = opcodes.OpOsDiagnose
def GET(self): def GET(self):
"""Return a list of all OSes. """Return a list of all OSes.
...@@ -351,10 +355,12 @@ class R_2_jobs_id_wait(baserlib.ResourceBase): ...@@ -351,10 +355,12 @@ class R_2_jobs_id_wait(baserlib.ResourceBase):
} }
class R_2_nodes(baserlib.ResourceBase): class R_2_nodes(baserlib.OpcodeResource):
"""/2/nodes resource. """/2/nodes resource.
""" """
GET_OPCODE = opcodes.OpNodeQuery
def GET(self): def GET(self):
"""Returns a list of all nodes. """Returns a list of all nodes.
...@@ -371,10 +377,12 @@ class R_2_nodes(baserlib.ResourceBase): ...@@ -371,10 +377,12 @@ class R_2_nodes(baserlib.ResourceBase):
uri_fields=("id", "uri")) uri_fields=("id", "uri"))
class R_2_nodes_name(baserlib.ResourceBase): class R_2_nodes_name(baserlib.OpcodeResource):
"""/2/nodes/[node_name] resource. """/2/nodes/[node_name] resource.
""" """
GET_OPCODE = opcodes.OpNodeQuery
def GET(self): def GET(self):
"""Send information about a node. """Send information about a node.
...@@ -582,6 +590,7 @@ class R_2_groups(baserlib.OpcodeResource): ...@@ -582,6 +590,7 @@ class R_2_groups(baserlib.OpcodeResource):
"""/2/groups resource. """/2/groups resource.
""" """
GET_OPCODE = opcodes.OpGroupQuery
POST_OPCODE = opcodes.OpGroupAdd POST_OPCODE = opcodes.OpGroupAdd
POST_RENAME = { POST_RENAME = {
"name": "group_name", "name": "group_name",
...@@ -697,6 +706,7 @@ class R_2_instances(baserlib.OpcodeResource): ...@@ -697,6 +706,7 @@ class R_2_instances(baserlib.OpcodeResource):
"""/2/instances resource. """/2/instances resource.
""" """
GET_OPCODE = opcodes.OpInstanceQuery
POST_OPCODE = opcodes.OpInstanceCreate POST_OPCODE = opcodes.OpInstanceCreate
POST_RENAME = { POST_RENAME = {
"os": "os_type", "os": "os_type",
...@@ -750,6 +760,7 @@ class R_2_instances_name(baserlib.OpcodeResource): ...@@ -750,6 +760,7 @@ class R_2_instances_name(baserlib.OpcodeResource):
"""/2/instances/[instance_name] resource. """/2/instances/[instance_name] resource.
""" """
GET_OPCODE = opcodes.OpInstanceQuery
DELETE_OPCODE = opcodes.OpInstanceRemove DELETE_OPCODE = opcodes.OpInstanceRemove
def GET(self): def GET(self):
...@@ -885,12 +896,14 @@ def _ParseInstanceReinstallRequest(name, data): ...@@ -885,12 +896,14 @@ def _ParseInstanceReinstallRequest(name, data):
return ops return ops
class R_2_instances_name_reinstall(baserlib.ResourceBase): class R_2_instances_name_reinstall(baserlib.OpcodeResource):
"""/2/instances/[instance_name]/reinstall resource. """/2/instances/[instance_name]/reinstall resource.
Implements an instance reinstall. Implements an instance reinstall.
""" """
POST_OPCODE = opcodes.OpInstanceReinstall
def POST(self): def POST(self):
"""Reinstall an instance. """Reinstall an instance.
...@@ -1097,6 +1110,7 @@ class R_2_instances_name_console(baserlib.ResourceBase): ...@@ -1097,6 +1110,7 @@ class R_2_instances_name_console(baserlib.ResourceBase):
""" """
GET_ACCESS = [rapi.RAPI_ACCESS_WRITE] GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
GET_OPCODE = opcodes.OpInstanceConsole
def GET(self): def GET(self):
"""Request information for connecting to instance's console. """Request information for connecting to instance's console.
...@@ -1141,6 +1155,8 @@ class R_2_query(baserlib.ResourceBase): ...@@ -1141,6 +1155,8 @@ class R_2_query(baserlib.ResourceBase):
""" """
# Results might contain sensitive information # Results might contain sensitive information
GET_ACCESS = [rapi.RAPI_ACCESS_WRITE] GET_ACCESS = [rapi.RAPI_ACCESS_WRITE]
GET_OPCODE = opcodes.OpQuery
PUT_OPCODE = opcodes.OpQuery
def _Query(self, fields, filter_): def _Query(self, fields, filter_):
return self.GetClient().Query(self.items[0], fields, filter_).ToDict() return self.GetClient().Query(self.items[0], fields, filter_).ToDict()
...@@ -1175,6 +1191,8 @@ class R_2_query_fields(baserlib.ResourceBase): ...@@ -1175,6 +1191,8 @@ class R_2_query_fields(baserlib.ResourceBase):
"""/2/query/[resource]/fields resource. """/2/query/[resource]/fields resource.
""" """
GET_OPCODE = opcodes.OpQueryFields
def GET(self): def GET(self):
"""Retrieves list of available fields for a resource. """Retrieves list of available fields for a resource.
...@@ -1199,6 +1217,7 @@ class _R_Tags(baserlib.OpcodeResource): ...@@ -1199,6 +1217,7 @@ class _R_Tags(baserlib.OpcodeResource):
""" """
TAG_LEVEL = None TAG_LEVEL = None
GET_OPCODE = opcodes.OpTagsGet
PUT_OPCODE = opcodes.OpTagsSet PUT_OPCODE = opcodes.OpTagsSet
DELETE_OPCODE = opcodes.OpTagsDel DELETE_OPCODE = opcodes.OpTagsDel
......
...@@ -22,11 +22,13 @@ ...@@ -22,11 +22,13 @@
"""Script for testing ganeti.rapi.baserlib""" """Script for testing ganeti.rapi.baserlib"""
import unittest import unittest
import itertools
from ganeti import errors from ganeti import errors
from ganeti import opcodes from ganeti import opcodes
from ganeti import ht from ganeti import ht
from ganeti import http from ganeti import http
from ganeti import compat
from ganeti.rapi import baserlib from ganeti.rapi import baserlib
import testutils import testutils
...@@ -98,19 +100,45 @@ class TestFillOpcode(unittest.TestCase): ...@@ -98,19 +100,45 @@ class TestFillOpcode(unittest.TestCase):
class TestOpcodeResource(unittest.TestCase): class TestOpcodeResource(unittest.TestCase):
def testDoubleDefinition(self): @staticmethod
class _TClass(baserlib.OpcodeResource): def _MakeClass(method, attrs):
GET_OPCODE = opcodes.OpTestDelay return type("Test%s" % method, (baserlib.OpcodeResource, ), attrs)
def GET(self): pass
@staticmethod
self.assertRaises(AssertionError, _TClass, None, None, None) def _GetMethodAttributes(method):
attrs = ["%s_OPCODE" % method, "%s_RENAME" % method,
def testNoOpCode(self): "Get%sOpInput" % method.capitalize()]
class _TClass(baserlib.OpcodeResource): assert attrs == dict((opattrs[0], list(opattrs[1:]))
POST_OPCODE = None for opattrs in baserlib._OPCODE_ATTRS)[method]
def POST(self): pass return attrs
self.assertRaises(AssertionError, _TClass, None, None, None) def test(self):
for method in baserlib._SUPPORTED_METHODS:
# Empty handler
obj = self._MakeClass(method, {})(None, None, None)
for attr in itertools.chain(*baserlib._OPCODE_ATTRS):
self.assertFalse(hasattr(obj, attr))
# Direct handler function
obj = self._MakeClass(method, {
method: lambda _: None,
})(None, None, None)
self.assertFalse(compat.all(hasattr(obj, attr)
for i in baserlib._SUPPORTED_METHODS
for attr in self._GetMethodAttributes(i)))
# Let metaclass define handler function
for opcls in [None, object()]:
obj = self._MakeClass(method, {
"%s_OPCODE" % method: opcls,
})(None, None, None)
self.assertTrue(callable(getattr(obj, method)))
self.assertEqual(getattr(obj, "%s_OPCODE" % method), opcls)
self.assertFalse(hasattr(obj, "%s_RENAME" % method))
self.assertFalse(compat.any(hasattr(obj, attr)
for i in baserlib._SUPPORTED_METHODS
if i != method
for attr in self._GetMethodAttributes(i)))
def testIllegalRename(self): def testIllegalRename(self):
class _TClass(baserlib.OpcodeResource): class _TClass(baserlib.OpcodeResource):
...@@ -124,8 +152,8 @@ class TestOpcodeResource(unittest.TestCase): ...@@ -124,8 +152,8 @@ class TestOpcodeResource(unittest.TestCase):
pass pass
obj = _Empty(None, None, None) obj = _Empty(None, None, None)
for attr in ["GetPostOpInput", "GetPutOpInput", "GetGetOpInput",
"GetDeleteOpInput"]: for attr in itertools.chain(*baserlib._OPCODE_ATTRS):
self.assertFalse(hasattr(obj, attr)) self.assertFalse(hasattr(obj, attr))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment