//%2006//////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.; // IBM Corp.; EMC Corporation, The Open Group. // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.; // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.; // EMC Corporation; VERITAS Software Corporation; The Open Group. // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.; // EMC Corporation; Symantec Corporation; The Open Group. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // //============================================================================== // // Author: John Alex // // Modified By: // //%///////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include "StressTestController.h" #include "StressTestControllerException.h" // //Windows // #ifdef PEGASUS_OS_TYPE_WINDOWS // for DWORD etc. #include // getpid() and others typedef DWORD pid_t; #include #elif !defined(PEGASUS_OS_OS400) #include #endif //#define STRESSTEST_DEBUG #define SIXTYSECONDS 60 #define MILLISECONDS 1000 #define CHECKUP_INTERVAL 1 #define STOP_DELAY 1 #define SHUTDOWN_DELAY 5 #define RUN_DELAY 1 #define DEFAULT_INSTANCE "5" #define convertmin2millisecs(x) (x * SIXTYSECONDS * MILLISECONDS) #define getToleranceInPercent(x,y) (100 - (((y-x)/y) * 100)) static void endAllTests(int signum); static void cleanupProcess(); static String convertUint64toString(Uint64 x); PEGASUS_NAMESPACE_BEGIN PEGASUS_USING_PEGASUS; PEGASUS_USING_STD; /** Log file descripter */ /** variable for Signal handler */ static Boolean Quit = false; /** The command name. */ const char StressTestControllerCommand::COMMAND_NAME [] = "TestStressTestController"; /** StressTest Configuration file details */ char StressTestControllerCommand::FILENAME[] = "default_stresstest.conf"; char StressTestControllerCommand::TESTDIR[] = "/test/"; char StressTestControllerCommand::STRESSTESTDIR[] = "StressTestController/"; char StressTestControllerCommand::LOGDIR[] = "log/"; char StressTestControllerCommand::BINDIR[] = "/bin/"; char StressTestControllerCommand::DEFAULT_CFGDIR[] = STRESSTEST_DEFAULTCFGDIR; char StressTestControllerCommand::DEFAULT_LOGDIR[] = "/test/StressTestController/log/"; char StressTestControllerCommand::DEFAULT_TMPDIR[] = "/test/StressTestController/tmp/"; String DEFAULT_BINDIR = String::EMPTY; static Uint32 DEFAULT_CLIENTS = 2; static Uint32 Total_Clients = DEFAULT_CLIENTS; static Uint32 Total_vClients = DEFAULT_CLIENTS; static Uint32 NEW_CLIENTS = 5; static char MODELWALK_CLIENT[] = "TestModelWalkStressClient"; static char WRAPPER_CLIENT[] = "TestWrapperStressClient"; /** StressTest Client Status types */ enum CStatus{ VALID_RESPONSE, INVALID_RESPONSE, NO_RESPONSE}; /** Temporary arrays to store client information */ /** Client PID's */ static pid_t *clientPIDs; /** Client Status */ static int *clientStatus; /** Client Status */ static int *prev_clientStatus; /** Client Instance */ static int *clientInstance; /** Indicates if client is Active */ static Boolean *clientActive; /** Client status time stamp */ static Uint64 *clientTimeStamp; /** Previous client status time stamp */ static Uint64 *prev_clientTimeStamp; /** DEFAULT VALUES: */ /** Default duration for the stress tests */ double StressTestControllerCommand::_duration = 180; /** Label for the usage string for this command. */ const char StressTestControllerCommand::_USAGE [] = "Usage: "; /** The option character used to specify the hostname. */ const char StressTestControllerCommand::_OPTION_HOSTNAME = 'h'; /** The option character used to specify the port number. */ const char StressTestControllerCommand::_OPTION_PORTNUMBER = 'p'; /** The option character used to specify SSL usage. */ const char StressTestControllerCommand::_OPTION_SSL = 's'; /** The option character used to specify the username. */ const char StressTestControllerCommand::_OPTION_USERNAME = 'u'; /** The option character used to specify the password. */ const char StressTestControllerCommand::_OPTION_PASSWORD = 'w'; /** The minimum valid portnumber. */ const Uint32 StressTestControllerCommand::_MIN_PORTNUMBER = 0; /** The maximum valid portnumber. */ const Uint32 StressTestControllerCommand::_MAX_PORTNUMBER = 65535; /** The minimum Duration. */ const Uint32 StressTestControllerCommand::_MIN_DURATION = 0; /** The minimum valid Tolerance Level. */ const Uint32 StressTestControllerCommand::_MIN_TOLERANCE = 0; /** The maximum valid Tolerance Level. */ const Uint32 StressTestControllerCommand::_MAX_TOLERANCE = 100; /** The variable used to specify the hostname. */ static const char HOSTNAME[] = "hostname"; /** The variable used to specify the port number. */ static const char PORTNUMBER[] = "port"; /** The variable used to specify SSL usage. */ static const char SSL [] = "ssl"; /** The variable used to specify the username. */ static const char USERNAME[] = "username"; /** The variable used to specify the password. */ static const char PASSWORD[] = "password"; /** The variable used to specify the duration of the tests. */ static const char DURATION[] = "duration"; /** The variable used to specify the duration of the Client tests. */ static const char CLIENTDURATION[] = "ClientDuration"; /** The variable used to specify the ToleranceLevel for the tests. */ static const char TOLERANCELEVEL[] = "TOLERANCELEVEL"; /** The variable used to specify the NameSpace for the tests. */ static const char NAMESPACE[] = "namespace"; /** The variable used to specify the ClassName for the tests. */ static const char CLASSNAME[] = "classname"; /** The variable used to specify the Name for the tests. */ static const char NAME[] = "NAME"; /** The variable used to specify the Clientname for the tests. */ static const char CLIENTNAME[] = "clientname"; /** The variable used to specify the Clientname for the tests. */ static const char OPTIONS[] = "options"; /** The variable used to specify the Clientname for the tests. */ static const char INSTANCE[] = "INSTANCE"; /** The variable used to specify the Clientname for the tests. */ static const char CLIENTWAIT[] = "CLIENTWAIT"; /** * Message resource name */ static const char PASSWORD_PROMPT [] = "Please enter your password: "; static const char PASSWORD_BLANK [] = "Password cannot be blank. Please re-enter your password."; static const char LONG_HELP[] = "help"; static const char LONG_VERSION[] = "version"; static const char LONG_VERBOSE[] = "verbose"; static Boolean IsAClient = false; static Boolean IsClientOptions = false; static Boolean IgnoreLine = false; /** Constructs a StressTestControllerCommand and initializes instance variables. */ StressTestControllerCommand::StressTestControllerCommand () { _hostName = String (); _hostNameSpecified = false; _portNumber = WBEM_DEFAULT_HTTP_PORT; _portNumberSpecified = false; char buffer[32]; sprintf(buffer, "%lu", (unsigned long) _portNumber); _portNumberStr = buffer; _timeout = DEFAULT_TIMEOUT_MILLISECONDS; _userName = String (); _userNameSpecified = false; _password = String (); _passwordSpecified = false; _useSSL = false; // // initialize // _clientCount = 0; _currClientCount = 0; // // Set up tables for client properties. // _clientTable = new Table[Total_Clients]; // // Allocate one table to collect all the common properties Thy use AutoPtr // _propertyTable = new Table; // // Client Information // _clientCommands = 0; _clientDurations = 0; _clientDelays = 0; // // Get environment variables: // pegasusHome = getenv("PEGASUS_HOME"); DEFAULT_BINDIR = String(pegasusHome); DEFAULT_BINDIR.append(BINDIR); _stressTestLogFile = String::EMPTY; _stressTestClientPIDFile = String::EMPTY; _stressTestClientLogFile = String::EMPTY; _tmpStressTestClientPIDFile = String::EMPTY; _usage = String (_USAGE); _usage.append (COMMAND_NAME); #ifndef DISABLE_SUPPORT_FOR_REMOTE_CONNECTIONS _usage.append (" [ -"); _usage.append (_OPTION_SSL); _usage.append (" ] [ -"); _usage.append (_OPTION_HOSTNAME); _usage.append (" hostname ] [ -"); _usage.append (_OPTION_PORTNUMBER); _usage.append (" portnumber ]\n [ -"); _usage.append (_OPTION_USERNAME); _usage.append (" username ] [ -"); _usage.append (_OPTION_PASSWORD); _usage.append (" password ]"); #endif _usage.append (" [ --"); _usage.append (LONG_HELP); _usage.append(" ]\n"); _usage.append(" "); _usage.append("[ --").append(LONG_VERSION).append(" ]"); _usage.append(" [ --").append(LONG_VERBOSE).append(" ]").append(\ " [] \n"); _usage.append("Options : \n"); _usage.append(" -h - Connect to CIM Server on specified "); _usage.append("hostname. \n"); _usage.append(" --help - Display this help message.\n"); _usage.append(" -p - Connect to CIM Server on specified "); _usage.append("portnumber.\n"); _usage.append(" -s - Use SSL protocol between Stress Test "); _usage.append("Client\n"); _usage.append(" and the CIM Server\n"); _usage.append(" -u - Connect to CIM Server using the specified"); _usage.append(" username\n"); _usage.append(" --version - Display CIM Server version number\n"); _usage.append(" --verbose - Display verbose information\n"); _usage.append(" -w - Connect to CIM Server using the specified"); _usage.append(" password\n"); _usage.append("\nOperands : \n"); _usage.append(" \n"); _usage.append(" - Specifies the name of the configuration "); _usage.append("file that is to be used \n"); _usage.append(" for the tests.\n"); setUsage(_usage); } /* StressTestControllerCommand */ /** Parses the command line, validates the options, and sets instance variables based on the option arguments. @param argc the number of command line arguments @param argv the string vector of command line arguments @exception CommandFormatException if an error is encountered in parsing the command line */ void StressTestControllerCommand::setCommand (Uint32 argc, char* argv []) { Uint32 i = 0; Uint32 c = 0; String GetOptString = String (); getoopt getOpts; _toleranceLevel = 0; _configFilePath = String (); _configFilePathSpecified = false; _operationType = OPERATION_TYPE_UNINITIALIZED; ofstream log_file; // // opens the log file // OpenAppend(log_file,_stressTestLogFile); if (!log_file) { if(verboseEnabled) { cout< 1) { // // More than one hostname option was found // log_file.close(); DuplicateOptionException e(_OPTION_HOSTNAME); throw e; } _hostName = getOpts [i].Value (); _hostNameSpecified = true; if (!_propertyTable->insert("hostname", _hostName)) { // shouldn't get here if(verboseEnabled) { cout< 1) { // // More than one portNumber option was found // log_file.close(); DuplicateOptionException e(_OPTION_PORTNUMBER); throw e; } _portNumberStr = getOpts [i].Value (); try { getOpts[i].Value(_portNumber); } catch (const TypeMismatchException&) { log_file.close(); InvalidOptionArgumentException e( _portNumberStr, _OPTION_PORTNUMBER); throw e; } _portNumberSpecified = true; if (!_propertyTable->insert("port", _portNumberStr)) { if(verboseEnabled) { cout<insert("port", _portNumberStr)) { if(verboseEnabled) { cout<insert("ssl", "")) { if(verboseEnabled) { cout< 1) { // // More than one username option was found // log_file.close(); DuplicateOptionException e(_OPTION_USERNAME); throw e; } _userName = getOpts[i].Value(); _userNameSpecified = true; if (!_propertyTable->insert("username", _userName)) { if(verboseEnabled) { cout< 1) { // // More than one password option was found // log_file.close(); DuplicateOptionException e(_OPTION_PASSWORD); throw e; } _password = getOpts[i].Value(); _passwordSpecified = true; if (!_propertyTable->insert("password", _password)) { if(verboseEnabled) { cout< _MAX_PORTNUMBER) { // // Portnumber out of valid range // log_file.close(); InvalidOptionArgumentException e(_portNumberStr, _OPTION_PORTNUMBER); throw e; } } log_file.close(); } /* setCommand */ /** Generates commands and its options for each of the clients. The client table is traversed to generate each of the client commands. The Commands, Duration and Delays for each client are saved in the following array's respectively: _clientCommands _clientDurations _clientDelays @param log_file The log file. @return 0 if the command is successfully generated 1 if the command cannot be generated. */ Boolean StressTestControllerCommand::generateClientCommands(ostream& log_file) { String client_command = String::EMPTY; double duration = _duration; double delay = 0; // // Array's to store client specific information // _clientCommands = new String[_clientCount]; _clientDurations = new Uint64[_clientCount]; _clientDelays = new Uint64[_clientCount]; // // Retrieve all the client options from the client table // and build commands for respective clients. // Add appropriate options to command string as required // for (Uint32 j=0; j< _clientCount; j++) { delay = 0; String clientName = String::EMPTY; String clientInst = String::EMPTY; // // Stress Client Name must exist for each client/client table // if (!_clientTable[j].lookup(NAME, clientName)) { log_file<start(); k; k++) { String propertyValue = String::EMPTY; // // Only include the common properties that are not already // listed for the clients. // if (!_clientTable[j].lookup(k.key(), propertyValue)) { // // Include options other than ToleranceLevel // clientDuration,clientwait and Duration // in the command string for the clients. // if ((!String::equalNoCase(k.key(),TOLERANCELEVEL)) && (!String::equalNoCase(k.key(),CLIENTDURATION)) && (!String::equalNoCase(k.key(),CLIENTWAIT)) && (!String::equalNoCase(k.key(),DURATION))) { client_command.append(" -"); client_command.append(k.key()); // // No values required for SSL // if (!String::equalNoCase(k.key(),SSL)) { client_command.append(" "); client_command.append(k.value()); } } } // // Use default duration if one was not specified in the Config file. // if (String::equalNoCase(k.key(),DURATION)) { duration = atof(k.value().getCString()); } else { duration = _duration; } } /* for (Table::Iterator k = _propertyTable->start(); k; k++) */ // // Looking up table while ignoring cases for Client Duration/Wait. // for (Table::Iterator k = _clientTable[j].start(); k; k++) { // // Overwrite duration if client duration set // if (String::equalNoCase(k.key(),CLIENTDURATION)) { duration = atof(k.value().getCString()); } if (String::equalNoCase(k.key(),CLIENTWAIT)) { delay = atof(k.value().getCString()); } } // // Save the generated command to corresponding element in the // clientCommand array. // _clientCommands[j] = client_command; // // Converting minutes to milliseconds // _clientDurations[j] = (Uint64)convertmin2millisecs(duration); _clientDelays[j] = (Uint64)convertmin2millisecs(delay); // // Saving logs // log_file< 0) { clientInstance = new int[_clientCount]; clientStartMilliseconds = new Uint64[_clientCount]; clientStopMilliseconds = new Uint64[_clientCount]; clientDelayMilliseconds = new Uint64[_clientCount]; clientStopped = new Boolean[_clientCount]; clientDelayed = new Boolean[_clientCount]; } else { errPrintWriter << "Stress Tests must have at least one Client." << endl; log_file< nowMilliseconds) { // // Small delay in the while loop seemed to reduce the CPU usage // considerably in Windows. (From 80% to 1%) // #ifndef PEGASUS_OS_TYPE_WINDOWS sleep(RUN_DELAY); #else Sleep(RUN_DELAY * 1000); #endif // // Quit if SIGINT, SIGABRT is caught // So the clients can be gracefully shutdown. // if(Quit) { log_file<< "Test interrupted by either SIGINT or SIGABORT."<= nextCheckupInMillisecs) { // // Set up the next tolerance time // nextCheckupInMillisecs = (Uint64)convertmin2millisecs(CHECKUP_INTERVAL) + nowMilliseconds; // // End tests when failed to acquire the Client PID or // status. // if (!rc) { outPrintWriter<< "Failed to communicate with clients."<= nextCheckupInMillisecs)*/ // // Stop clients with delay // for (Uint32 clientID=0; clientID < _clientCount; clientID++) { // // Get Current time // nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds(); // // Stop only running clients as required. // if (!clientStopped[clientID]) { // // If Client's duration is up // if (clientStopMilliseconds[clientID]<= nowMilliseconds) { // // Stop all the instances of this client // for (int instanceID =0; instanceID nowMilliseconds) */ }//try catch (const StressTestControllerException& e) { errPrintWriter << StressTestControllerCommand::COMMAND_NAME << ": " << e.getMessage () << endl; return (RC_ERROR); } // // Stress Tests should be stopped. // outPrintWriter<<"Ending tests::Preparing to stop all the clients."<start(); i; i++) { log_file<<" "<:::::: Example: 1::7582::0::4119329327 Client PID, status and Time Stamp from the PID file will be saved in the following global array's for each client. clientPIDs clientStatus clientTimeStamp @param actual_clients The actual number of clients executed by the Controller. @param log_file The log file. @return true if the status and PIDs were read successfully false Failed to read the status & PIDs of clients. */ Boolean StressTestControllerCommand::_getClientPIDs( int actual_clients, ostream& log_file) { ifstream ifs; // // Make a temp copy of the file // Boolean cTempFile = FileSystem::copyFile( _stressTestClientPIDFile, _tmpStressTestClientPIDFile); if(!cTempFile) { cout<<"Cannot copy file "<<_stressTestClientPIDFile< = - Client options/properties in Config file are represented as follows: - "[" indicates start of client options. - Client properties and values are seperated by commas. Example: [clientName=cimcli,Options=niall] @param line The line that will be parsed. @parm lineNumber The line number of the line. @parm name The property name that will be retrieved. @parm value The property value of the name. @parm log_file The log file. @return true Succesfully parsed the line. false Failed to parse the lines successfully. */ Boolean StressTestControllerCommand::_parseLine( const String & line, int lineNumber, String &name, String &value, ostream& log_file) { const Char16* p; p = line.getChar16Data(); // // Skip whitespace // while (*p && isspace(*p)) { p++; } // // Ignore empty lines // if (!*p) { IgnoreLine = true; return IgnoreLine; } // // Skip comment lines // if (*p == '#') { IgnoreLine = true; return IgnoreLine; } // // Retreive all the Client Options // "[" indicates start of client options. // if (*p == '[') { IsAClient = true; IsClientOptions = true; p++; // // Ignore spaces before client property // while (*p && isspace(*p)) { p++; } // // Invalid Client property name // if (!(isalpha(*p) || *p == '_')) { String ErrReport = String("Syntax Error with client options:"); ErrReport.append(line.getCString()); throw StressTestControllerException(ErrReport); } // // Retrieve client options // try { // // get and validate client options // _getClientOptions(p,log_file); } catch (Exception& e) { String msg(e.getMessage()); if ((name == String::EMPTY) ||(value == String::EMPTY)) { msg.append(" in "); msg.append(line.getCString()); } throw StressTestControllerException(msg); } catch (...) { String msg = String( "Unknown exception caught when geting client options."); log_file<= (2 * (Uint64)convertmin2millisecs(CHECKUP_INTERVAL)))) { if (verboseEnabled) { log_file <<" Status not updated for client (" < 0) { double curr_tolerancePercent = getToleranceInPercent( failed_count, (double)count); if (verboseEnabled) { cout<<" total running clients ="<= curr_tolerancePercent) { withinTolerance = true; } return(withinTolerance); } // // All process are stopped. // return(withinTolerance = true); } /* _checkToleranceLevel */ /* This will populate the client table with the hard coded values for the stress tests. This method is only used if the default configuration file does not exist. Default clients are 5 instances of "TestWrapperStressClient" and "TestModelWalkStressClient". @parm log_file The log_file. */ void StressTestControllerCommand::getDefaultClients(ostream& log_file) { // // Setting the client count to default client value // _clientCount = DEFAULT_CLIENTS; log_file << "Populating default configuration for stress Tests." << endl; if (verboseEnabled) { cout << "Populating default configuration for stress Tests." << endl; } // // Populating default client attributes // for (Uint32 i=0;i<_clientCount; i++) { // // Adding the default instance value to each client table // if (!_clientTable[i].insert(INSTANCE, DEFAULT_INSTANCE)) { log_file << "Duplicate name already saved: "<