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