Revision 396c5dfb qa/rapi-workload.py
b/qa/rapi-workload.py | ||
---|---|---|
26 | 26 |
# pylint: disable=C0103 |
27 | 27 |
# due to invalid name |
28 | 28 |
|
29 |
|
|
29 |
import inspect |
|
30 | 30 |
import sys |
31 |
import types |
|
31 | 32 |
|
32 | 33 |
import ganeti.constants as constants |
33 | 34 |
from ganeti.rapi.client import GanetiApiError |
... | ... | |
69 | 70 |
return None |
70 | 71 |
|
71 | 72 |
|
72 |
def InvokerCreator(fn, name): |
|
73 |
""" Returns an invoker function that will invoke the given function |
|
74 |
with any arguments passed to the invoker at a later time, while |
|
75 |
catching any specific non-fatal errors we would like to know more |
|
76 |
about. |
|
77 |
|
|
78 |
@type fn arbitrary function |
|
79 |
@param fn The function to invoke later. |
|
80 |
@type name string |
|
81 |
@param name The name of the function, for debugging purposes. |
|
82 |
@rtype function |
|
83 |
|
|
84 |
""" |
|
85 |
def decoratedFn(*args, **kwargs): |
|
86 |
result = None |
|
87 |
try: |
|
88 |
print "Using method %s" % name |
|
89 |
result = fn(*args, **kwargs) |
|
90 |
except GanetiApiError as e: |
|
91 |
print "RAPI error while performing function %s : %s" % \ |
|
92 |
(name, str(e)) |
|
93 |
return result |
|
94 |
|
|
95 |
return decoratedFn |
|
96 |
|
|
97 |
|
|
98 | 73 |
RAPI_USERNAME = "ganeti-qa" |
99 | 74 |
|
100 | 75 |
|
... | ... | |
107 | 82 |
self._client = qa_rapi.Setup(RAPI_USERNAME, |
108 | 83 |
qa_rapi.LookupRapiSecret(RAPI_USERNAME)) |
109 | 84 |
|
85 |
self._method_invocations = {} |
|
86 |
|
|
87 |
def _RecordMethodInvocation(self, name, arg_dict): |
|
88 |
""" Records the invocation of a C{GanetiRAPIClient} method, noting the |
|
89 |
argument and the method names. |
|
90 |
|
|
91 |
""" |
|
92 |
if name not in self._method_invocations: |
|
93 |
self._method_invocations[name] = set() |
|
94 |
|
|
95 |
for named_arg in arg_dict: |
|
96 |
self._method_invocations[name].add(named_arg) |
|
97 |
|
|
98 |
def _InvokerCreator(self, fn, name): |
|
99 |
""" Returns an invoker function that will invoke the given function |
|
100 |
with any arguments passed to the invoker at a later time, while |
|
101 |
catching any specific non-fatal errors we would like to know more |
|
102 |
about. |
|
103 |
|
|
104 |
@type fn arbitrary function |
|
105 |
@param fn The function to invoke later. |
|
106 |
@type name string |
|
107 |
@param name The name of the function, for debugging purposes. |
|
108 |
@rtype function |
|
109 |
|
|
110 |
""" |
|
111 |
def decoratedFn(*args, **kwargs): |
|
112 |
result = None |
|
113 |
try: |
|
114 |
print "Using method %s" % name |
|
115 |
self._RecordMethodInvocation(name, kwargs) |
|
116 |
result = fn(*args, **kwargs) |
|
117 |
except GanetiApiError as e: |
|
118 |
print "RAPI error while performing function %s : %s" % \ |
|
119 |
(name, str(e)) |
|
120 |
return result |
|
121 |
|
|
122 |
return decoratedFn |
|
123 |
|
|
110 | 124 |
def __getattr__(self, attr): |
111 | 125 |
""" Fetches an attribute from the underlying client if necessary. |
112 | 126 |
|
... | ... | |
116 | 130 |
# guide, this will stop infinite loops in attribute fetches. |
117 | 131 |
if attr.startswith("_"): |
118 | 132 |
return self.__getattribute__(attr) |
133 |
|
|
134 |
# We also want to expose non-methods |
|
135 |
if hasattr(self._client, attr) and \ |
|
136 |
not isinstance(getattr(self._client, attr), types.MethodType): |
|
137 |
return getattr(self._client, attr) |
|
138 |
|
|
119 | 139 |
try: |
120 |
return InvokerCreator(self._client.__getattribute__(attr), attr) |
|
140 |
return self._InvokerCreator(self._client.__getattribute__(attr), attr)
|
|
121 | 141 |
except AttributeError: |
122 | 142 |
print "Missing method %s; supplying mock method" % attr |
123 | 143 |
return MockMethod |
124 | 144 |
|
145 |
def _OutputMethodInvocationDetails(self): |
|
146 |
""" Attempts to output as much information as possible about the methods |
|
147 |
that have and have not been invoked, including which arguments have not |
|
148 |
been used. |
|
149 |
|
|
150 |
""" |
|
151 |
print "\nMethod usage:\n" |
|
152 |
for method in [n for n in dir(self._client) |
|
153 |
if not n.startswith('_') and |
|
154 |
isinstance(self.__getattr__(n), types.FunctionType)]: |
|
155 |
if method not in self._method_invocations: |
|
156 |
print "Method unused: %s" % method |
|
157 |
else: |
|
158 |
arg_spec, _, _, default_arg_spec = \ |
|
159 |
inspect.getargspec(getattr(self._client, method)) |
|
160 |
default_args = [] |
|
161 |
if default_arg_spec is not None: |
|
162 |
default_args = arg_spec[-len(default_arg_spec):] |
|
163 |
used_arg_set = self._method_invocations[method] |
|
164 |
unused_args = [arg for arg in default_args if arg not in used_arg_set] |
|
165 |
if unused_args: |
|
166 |
print "Method %s used, but arguments unused: %s" % \ |
|
167 |
(method, ", ".join(unused_args)) |
|
168 |
|
|
125 | 169 |
|
126 | 170 |
def Finish(client, fn, *args, **kwargs): |
127 | 171 |
""" When invoked with a job-starting RAPI client method, it passes along any |
... | ... | |
663 | 707 |
|
664 | 708 |
qa_node.TestNodeRemoveAll() |
665 | 709 |
|
710 |
# The method invoked has the naming of the protected method, and pylint does |
|
711 |
# not like this. Disabling the warning is healthier than explicitly adding and |
|
712 |
# maintaining an exception for this method in the wrapper. |
|
713 |
# pylint: disable=W0212 |
|
714 |
client._OutputMethodInvocationDetails() |
|
715 |
# pylint: enable=W0212 |
|
666 | 716 |
|
667 | 717 |
if __name__ == "__main__": |
668 | 718 |
Main() |
Also available in: Unified diff