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