On Fri, Dec 20, 2013 at 10:47 AM, Helga Velroyen <[email protected]> wrote:
> > > > On Fri, Dec 20, 2013 at 10:09 AM, Hrvoje Ribicic <[email protected]> wrote: > >> The RAPI workload script supplies work for the RAPI compatibility >> tests. The initial version does very little, but can be expanded >> as needed. >> >> Signed-off-by: Hrvoje Ribicic <[email protected]> >> --- >> Makefile.am | 3 +- >> qa/rapi-workload.py | 158 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 2 files changed, 160 insertions(+), 1 deletion(-) >> create mode 100755 qa/rapi-workload.py >> >> diff --git a/Makefile.am b/Makefile.am >> index e74f58b..14cb774 100644 >> --- a/Makefile.am >> +++ b/Makefile.am >> @@ -970,7 +970,8 @@ qa_scripts = \ >> qa/qa_os.py \ >> qa/qa_rapi.py \ >> qa/qa_tags.py \ >> - qa/qa_utils.py >> + qa/qa_utils.py \ >> + qa/rapi-workload.py >> >> bin_SCRIPTS = >> if WANT_HTOOLS >> diff --git a/qa/rapi-workload.py b/qa/rapi-workload.py >> new file mode 100755 >> index 0000000..f936d95 >> --- /dev/null >> +++ b/qa/rapi-workload.py >> @@ -0,0 +1,158 @@ >> +#!/usr/bin/python -u >> +# >> + >> +# Copyright (C) 2013 Google Inc. >> +# >> +# This program is free software; you can redistribute it and/or modify >> +# it under the terms of the GNU General Public License as published by >> +# the Free Software Foundation; either version 2 of the License, or >> +# (at your option) any later version. >> +# >> +# This program is distributed in the hope that it will be useful, but >> +# WITHOUT ANY WARRANTY; without even the implied warranty of >> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >> +# General Public License for more details. >> +# >> +# You should have received a copy of the GNU General Public License >> +# along with this program; if not, write to the Free Software >> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA >> +# 02110-1301, USA. >> + >> + >> +"""Script for providing a large amount of RAPI calls to Ganeti. >> + >> +""" >> + >> +# pylint: disable=C0103 >> +# due to invalid name >> + >> + >> +import sys >> + >> +from ganeti.rapi.client import GanetiApiError >> + >> +import qa_config >> +import qa_node >> +import qa_rapi >> + >> + >> +# The purpose of this file is to provide a stable and extensive RAPI >> workload >> +# that manipulates the cluster only using RAPI commands, with the >> assumption >> +# that an empty cluster was set up beforehand. All the nodes that can be >> added >> +# to the cluster should be a part of it, and no instances should be >> present. >> +# >> +# Its intended use is in RAPI compatibility tests, where different >> versions with >> +# possibly vastly different QAs must be compared. Running the QA on both >> +# versions of the cluster will produce RAPI calls, but there is no >> guarantee >> +# that they will match, or that functions invoked in between will not >> change the >> +# results. >> +# >> +# By using only RAPI functions, we are sure to be able to capture and >> log all >> +# the changes in cluster state, and be able to compare them afterwards. >> +# >> +# The functionality of the QA is still used to generate a functioning, >> +# RAPI-enabled cluster, and to set up a C{GanetiRapiClient} capable of >> issuing >> +# commands to the cluster. >> +# >> +# Due to the fact that not all calls issued as a part of the workload >> might be >> +# implemented in the different versions of Ganeti, the client does not >> halt or >> +# produce a non-zero exit code upon encountering a RAPI error. Instead, >> it >> +# reports it and moves on. Any utility comparing the requests should >> account for >> +# this. >> + >> + >> +def MockMethod(*_args, **_kwargs): >> + """ Absorbs all arguments, does nothing, returns None. >> + >> + """ >> + return None >> + >> + >> +def InvokerCreator(fn, name): >> + """ Returns an invoker function that will invoke the given function >> + with any arguments passed to the invoker at a later time, while >> + catching any specific non-fatal errors we would like to know more >> + about. >> + >> + @type fn arbitrary function >> + @param fn The function to invoke later. >> + @type name string >> + @param name The name of the function, for debugging purposes. >> + @rtype function >> + >> + """ >> + def decoratedFn(*args, **kwargs): >> + result = None >> + try: >> + print "Using method %s" % name >> + result = fn(*args, **kwargs) >> + except GanetiApiError as e: >> + print "RAPI error while performing function %s : %s" % \ >> + (name, str(e)) >> + return result >> + >> + return decoratedFn >> + >> + >> +RAPI_USERNAME = "ganeti-qa" >> + >> + >> +class GanetiRapiClientWrapper(object): >> + """ Creates and initializes a GanetiRapiClient, and acts as a wrapper >> invoking >> + only the methods that the version of the client actually uses. >> + >> + """ >> + def __init__(self): >> + self._client = qa_rapi.Setup(RAPI_USERNAME, >> + qa_rapi.LookupRapiSecret(RAPI_USERNAME)) >> + >> + def __getattr__(self, attr): >> + """ Fetches an attribute from the underlying client if necessary. >> + >> + """ >> + #Assuming that this method exposes no public methods of its own, >> + #and that any private methods are named according to the style >> + #guide, this will stop infinite loops in attribute fetches. >> > > super-nit: I think it more consistent with the ganeti code to have a > whitespace after "#" > > Yup, and that is my usual style - don't know how this slipped by. Minor interdiff FYI: diff --git a/qa/rapi-workload.py b/qa/rapi-workload.py index f936d95..72eedce 100755 --- a/qa/rapi-workload.py +++ b/qa/rapi-workload.py @@ -110,9 +110,9 @@ class GanetiRapiClientWrapper(object): """ Fetches an attribute from the underlying client if necessary. """ - #Assuming that this method exposes no public methods of its own, - #and that any private methods are named according to the style - #guide, this will stop infinite loops in attribute fetches. + # Assuming that this method exposes no public methods of its own, + # and that any private methods are named according to the style + # guide, this will stop infinite loops in attribute fetches. if attr.startswith("_"): return self.__getattribute__(attr) > + if attr.startswith("_"): >> + return self.__getattribute__(attr) >> + >> + try: >> + return InvokerCreator(self._client.__getattribute__(attr), attr) >> + except AttributeError: >> + print "Missing method %s; supplying mock method" % attr >> + return MockMethod >> + >> + >> +def Workload(client): >> + """ The actual RAPI workload used for tests. >> + >> + @type client C{GanetiRapiClientWrapper} >> + @param client A wrapped RAPI client. >> + >> + """ >> + print client.GetVersion() >> + >> + >> +def Usage(): >> + sys.stderr.write("Usage:\n\trapi-workload.py qa-config-file") >> + >> + >> +def Main(): >> + if len(sys.argv) < 2: >> + Usage() >> + >> + qa_config.Load(sys.argv[1]) >> + >> + # Only the master will be present after a fresh QA cluster setup, so >> we have >> + # to invoke this to get all the other nodes. >> + qa_node.TestNodeAddAll() >> + >> + client = GanetiRapiClientWrapper() >> + >> + Workload(client) >> + >> + qa_node.TestNodeRemoveAll() >> + >> + >> +if __name__ == "__main__": >> + Main() >> -- >> 1.8.5.1 >> >> > LGTM, thanks > > > -- > -- > Helga Velroyen | Software Engineer | [email protected] | > > Google Germany GmbH > Dienerstr. 12 > 80331 München > > Registergericht und -nummer: Hamburg, HRB 86891 > Sitz der Gesellschaft: Hamburg > Geschäftsführer: Graham Law, Christine Elizabeth Flores >
