Revision 56803e14

b/Makefile.am
972 972
	qa/qa_os.py \
973 973
	qa/qa_rapi.py \
974 974
	qa/qa_tags.py \
975
	qa/qa_utils.py
975
	qa/qa_utils.py \
976
        qa/rapi-workload.py
976 977

  
977 978
bin_SCRIPTS =
978 979
if WANT_HTOOLS
b/qa/rapi-workload.py
1
#!/usr/bin/python -u
2
#
3

  
4
# Copyright (C) 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21

  
22
"""Script for providing a large amount of RAPI calls to Ganeti.
23

  
24
"""
25

  
26
# pylint: disable=C0103
27
# due to invalid name
28

  
29

  
30
import sys
31

  
32
from ganeti.rapi.client import GanetiApiError
33

  
34
import qa_config
35
import qa_node
36
import qa_rapi
37

  
38

  
39
# The purpose of this file is to provide a stable and extensive RAPI workload
40
# that manipulates the cluster only using RAPI commands, with the assumption
41
# that an empty cluster was set up beforehand. All the nodes that can be added
42
# to the cluster should be a part of it, and no instances should be present.
43
#
44
# Its intended use is in RAPI compatibility tests, where different versions with
45
# possibly vastly different QAs must be compared. Running the QA on both
46
# versions of the cluster will produce RAPI calls, but there is no guarantee
47
# that they will match, or that functions invoked in between will not change the
48
# results.
49
#
50
# By using only RAPI functions, we are sure to be able to capture and log all
51
# the changes in cluster state, and be able to compare them afterwards.
52
#
53
# The functionality of the QA is still used to generate a functioning,
54
# RAPI-enabled cluster, and to set up a C{GanetiRapiClient} capable of issuing
55
# commands to the cluster.
56
#
57
# Due to the fact that not all calls issued as a part of the workload might be
58
# implemented in the different versions of Ganeti, the client does not halt or
59
# produce a non-zero exit code upon encountering a RAPI error. Instead, it
60
# reports it and moves on. Any utility comparing the requests should account for
61
# this.
62

  
63

  
64
def MockMethod(*_args, **_kwargs):
65
  """ Absorbs all arguments, does nothing, returns None.
66

  
67
  """
68
  return None
69

  
70

  
71
def InvokerCreator(fn, name):
72
  """ Returns an invoker function that will invoke the given function
73
  with any arguments passed to the invoker at a later time, while
74
  catching any specific non-fatal errors we would like to know more
75
  about.
76

  
77
  @type fn arbitrary function
78
  @param fn The function to invoke later.
79
  @type name string
80
  @param name The name of the function, for debugging purposes.
81
  @rtype function
82

  
83
  """
84
  def decoratedFn(*args, **kwargs):
85
    result = None
86
    try:
87
      print "Using method %s" % name
88
      result = fn(*args, **kwargs)
89
    except GanetiApiError as e:
90
      print "RAPI error while performing function %s : %s" % \
91
            (name, str(e))
92
    return result
93

  
94
  return decoratedFn
95

  
96

  
97
RAPI_USERNAME = "ganeti-qa"
98

  
99

  
100
class GanetiRapiClientWrapper(object):
101
  """ Creates and initializes a GanetiRapiClient, and acts as a wrapper invoking
102
  only the methods that the version of the client actually uses.
103

  
104
  """
105
  def __init__(self):
106
    self._client = qa_rapi.Setup(RAPI_USERNAME,
107
                                 qa_rapi.LookupRapiSecret(RAPI_USERNAME))
108

  
109
  def __getattr__(self, attr):
110
    """ Fetches an attribute from the underlying client if necessary.
111

  
112
    """
113
    # Assuming that this method exposes no public methods of its own,
114
    # and that any private methods are named according to the style
115
    # guide, this will stop infinite loops in attribute fetches.
116
    if attr.startswith("_"):
117
      return self.__getattribute__(attr)
118

  
119
    try:
120
      return InvokerCreator(self._client.__getattribute__(attr), attr)
121
    except AttributeError:
122
      print "Missing method %s; supplying mock method" % attr
123
      return MockMethod
124

  
125

  
126
def Workload(client):
127
  """ The actual RAPI workload used for tests.
128

  
129
  @type client C{GanetiRapiClientWrapper}
130
  @param client A wrapped RAPI client.
131

  
132
  """
133
  print client.GetVersion()
134

  
135

  
136
def Usage():
137
  sys.stderr.write("Usage:\n\trapi-workload.py qa-config-file")
138

  
139

  
140
def Main():
141
  if len(sys.argv) < 2:
142
    Usage()
143

  
144
  qa_config.Load(sys.argv[1])
145

  
146
  # Only the master will be present after a fresh QA cluster setup, so we have
147
  # to invoke this to get all the other nodes.
148
  qa_node.TestNodeAddAll()
149

  
150
  client = GanetiRapiClientWrapper()
151

  
152
  Workload(client)
153

  
154
  qa_node.TestNodeRemoveAll()
155

  
156

  
157
if __name__ == "__main__":
158
  Main()

Also available in: Unified diff