Statistics
| Branch: | Tag: | Revision:

root / qa / rapi-workload.py @ 56803e14

History | View | Annotate | Download (4.6 kB)

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()