From: Marcin Siodelski Date: Fri, 25 May 2012 12:55:42 +0000 (+0200) Subject: [1954] Updated with suggestions from second round of review X-Git-Tag: trac2351_base~226^2~76^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3f32b68d6f2d0f02af6cebac046a604751ad4dcc;p=thirdparty%2Fkea.git [1954] Updated with suggestions from second round of review --- diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc index 305621c81c..154a34027c 100644 --- a/tests/tools/perfdhcp/command_options.cc +++ b/tests/tools/perfdhcp/command_options.cc @@ -39,11 +39,11 @@ CommandOptions::instance() { void CommandOptions::reset() { - // default mac address used in DHCP messages + // Default mac address used in DHCP messages // if -b mac= was not specified uint8_t mac[6] = { 0x0, 0xC, 0x1, 0x2, 0x3, 0x4 }; - // default packet drop time if -D parameter + // Default packet drop time if -D parameter // was not specified double dt[2] = { 1., 1. }; @@ -53,15 +53,14 @@ CommandOptions::reset() { exchange_mode_ = DORA_SARR; rate_ = 0; report_delay_ = 0; - random_range_ = 0; - max_random_ = 0; + clients_num_ = 0; + clients_num_ = 0; mac_prefix_.assign(mac, mac + 6); base_.resize(0); num_request_.resize(0); period_ = 0; drop_time_set_ = 0; drop_time_.assign(dt, dt + 2); - max_drop_set_ = 0; max_drop_.clear(); max_pdrop_.clear(); localname_.clear(); @@ -92,33 +91,37 @@ CommandOptions::parse(int argc, char** const argv) { // parsing different command lines multiple times optind = 1; opterr = 0; + + // Reset values of class members + reset(); + initialize(argc, argv); validate(); } void CommandOptions::initialize(int argc, char** argv) { - char opt = 0; // subsequent options returned by getopt() - std::string dropArg; // value of -Dargument - size_t percentLoc = 0; // location of % sign in -D - double dropPercent = 0; // % value (1..100) in -D<%value> - int numDrops = 0; // max number of drops specified in -D - int numReq = 0; // max number of dropped requests in -n - int offsetArg = 0; // temp variable holiding offset arguments - std::string sarg; // temp variable for string args - - // in this section we collect argument values from command line + char opt = 0; // Subsequent options returned by getopt() + std::string drop_arg; // Value of -Dargument + size_t percent_loc = 0; // Location of % sign in -D + double drop_percent = 0; // % value (1..100) in -D<%value> + int num_drops = 0; // Max number of drops specified in -D + int num_req = 0; // Max number of dropped requests in -n + int offset_arg = 0; // Temporary variable holding offset arguments + std::string sarg; // Temporary variable for string args + + // In this section we collect argument values from command line // they will be tuned and validated elsewhere while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:")) != -1) { switch (opt) { - case 'h': - usage(); - return; - case 'v': version(); return; + case '1': + use_first_ = true; + break; + case '4': check(ipversion_ == 6, "IP version already set to 6"); ipversion_ = 4; @@ -129,38 +132,27 @@ CommandOptions::initialize(int argc, char** argv) { ipversion_ = 6; break; - case 'r': - rate_ = positiveInteger("rate_ must be a positive integer"); - break; - - case 't': - report_delay_ = positiveInteger("report_delay_ must be a positive integer"); - break; - - case 'R': - initRandomRange(optarg); + case 'a': + aggressivity_ = positiveInteger("value of aggressivity: -a must be a positive integer"); break; case 'b': - check(base_.size() > 3, "too many bases"); + check(base_.size() > 3, "-b already specified, unexpected occurence of 5th -b"); base_.push_back(optarg); decodeBase(base_.back()); break; - case 'n': - numReq = positiveInteger("num-request must be a positive integer"); - if (num_request_.size() >= 2) { - isc_throw(isc::InvalidParameter, - "too many num-request values"); - } - num_request_.push_back(numReq); + case 'B': + broadcast_ = true; break; - case 'p': - period_ = positiveInteger("test-period must be a positive integer"); + case 'c': + rapid_commit_ = true; break; case 'd': + check(drop_time_set_ > 1, "maximum number of drops already specified, " + "unexpected 3rd occurence of -d"); try { drop_time_[drop_time_set_] = boost::lexical_cast(optarg); } catch (boost::bad_lexical_cast&) { @@ -168,133 +160,152 @@ CommandOptions::initialize(int argc, char** argv) { "value of drop time: -d must be positive number"); } check(drop_time_[drop_time_set_] <= 0., "drop-time must be a positive number"); - drop_time_set_ = 1; + drop_time_set_ = true; break; case 'D': - dropArg = std::string(optarg); - percentLoc = dropArg.find('%'); - if ((percentLoc) != std::string::npos) { - dropPercent = boost::lexical_cast(dropArg.substr(percentLoc + 1)); - check((dropPercent <= 0) || (dropPercent >= 100), + drop_arg = std::string(optarg); + percent_loc = drop_arg.find('%'); + check(max_pdrop_.size() > 1 || max_drop_.size() > 1, "values of maximum drops: -D already " + "specified, unexpected 3rd occurence of -D,value>"); + if ((percent_loc) != std::string::npos) { + try { + drop_percent = boost::lexical_cast(drop_arg.substr(0, percent_loc)); + } catch (boost::bad_lexical_cast&) { + isc_throw(isc::InvalidParameter, + "value of drop percentage: -D<%value> must be 0..100"); + } + check((drop_percent <= 0) || (drop_percent >= 100), "value of drop percentage: -D<%value> must be 0..100"); - max_pdrop_.push_back(dropPercent); - break; + max_pdrop_.push_back(drop_percent); + } else { + num_drops = positiveInteger("value of max drops number: -d must be a positive integer"); + max_drop_.push_back(num_drops); } - numDrops = positiveInteger("value of max drops number: -d must be a positive integer"); - max_drop_.push_back(numDrops); break; - case 'l': - localname_ = std::string(optarg); + case 'E': + elp_offset_ = nonNegativeInteger("value of time-offset: -E must not be a negative integer"); break; - case 'P': - preload_ = nonNegativeInteger("number of preload packets: -P must not be " - "a negative integer"); + case 'h': + usage(); + return; + + case 'i': + exchange_mode_ = DO_SA; break; - case 'a': - aggressivity_ = positiveInteger("value of aggressivity: -a must be a positive integer"); + case 'I': + rip_offset_ = positiveInteger("value of ip address offset: -I must be a positive integer"); + break; + + case 'l': + localname_ = std::string(optarg); break; case 'L': local_port_ = nonNegativeInteger("value of local port: -L must not be a negative integer"); - check(local_port_ > static_cast( std::numeric_limits::max()), + check(local_port_ > static_cast(std::numeric_limits::max()), "local-port must be lower than " + boost::lexical_cast(std::numeric_limits::max())); break; - case 's': - seed_ = static_cast - (nonNegativeInteger("value of -s must be non-negative integer")); - seeded_ = seed_ > 0 ? true : false; + case 'n': + num_req = positiveInteger("value of num-request: -n must be a positive integer"); + if (num_request_.size() >= 2) { + isc_throw(isc::InvalidParameter,"value of maximum number of requests: -n " + "already specified, unexpected 3rd occurence of -n"); + } + num_request_.push_back(num_req); break; - case 'i': - exchange_mode_ = DO_SA; + case 'O': + if (rnd_offset_.size() < 2) { + offset_arg = positiveInteger("value of random offset: -O must be greater than 3"); + } else { + isc_throw(isc::InvalidParameter, + "random offsets already specified, unexpected 3rd occurence of -O"); + } + check(offset_arg < 3, "value of random random-offset: -O must be greater than 3 "); + rnd_offset_.push_back(offset_arg); break; - case 'B': - broadcast_ = true; + case 'p': + period_ = positiveInteger("value of test period: -p must be a positive integer"); break; - case 'c': - rapid_commit_ = true; + case 'P': + preload_ = nonNegativeInteger("number of preload packets: -P must not be " + "a negative integer"); break; - case '1': - use_first_ = true; + case 'r': + rate_ = positiveInteger("value of rate: -r must be a positive integer"); break; - case 'T': - switch (template_file_.size()) { - case 0: - case 1: - sarg = nonEmptyString("template file name not specified, expected -T"); - template_file_.push_back(sarg); - break; - default: - isc_throw(isc::InvalidParameter, - "template-files are already set"); - } + case 'R': + initClientsNum(); break; - case 'X': - offsetArg = positiveInteger("value of transaction id: -X must be a positive integer"); - if (xid_offset_.size() >= 2) { - xid_offset_.clear(); - } - xid_offset_.push_back(offsetArg); + case 's': + seed_ = static_cast + (nonNegativeInteger("value of seed: -s must be non-negative integer")); + seeded_ = seed_ > 0 ? true : false; break; - case 'O': - try { - offsetArg = boost::lexical_cast(optarg); - } catch (boost::bad_lexical_cast&) { - isc_throw(isc::InvalidParameter, "value of random random-offset: -O " - "must be greater than 3"); - } - check(offsetArg < 3, "value of random random-offset: -O must be greater than 3 "); - if (rnd_offset_.size() >= 2) { - rnd_offset_.clear(); - } - rnd_offset_.push_back(offsetArg); + case 'S': + sid_offset_ = positiveInteger("value of server id offset: -S must be a positive integer"); break; - case 'E': - elp_offset_ = nonNegativeInteger("value of time-offset: -E must not be a negative integer"); + case 't': + report_delay_ = positiveInteger("value of report delay: -t must be a positive integer"); break; - case 'S': - sid_offset_ = positiveInteger("value of server id offset: -S must be a positive integer"); + case 'T': + if (template_file_.size() < 2) { + sarg = nonEmptyString("template file name not specified, expected -T"); + template_file_.push_back(sarg); + } else { + isc_throw(isc::InvalidParameter, + "template files are already specified, unexpected 3rd -T occurence"); + } break; - case 'I': - rip_offset_ = positiveInteger("value of ip addr offset: -I must be a positive integer"); + case 'w': + wrapped_ = nonEmptyString("command for wrapped mode: -w must be specified"); break; case 'x': diags_ = nonEmptyString("value of diagnostics selectors: -x must be specified"); break; - case 'w': - wrapped_ = nonEmptyString("command for wrapped mode: -w must be specified"); + case 'X': + if (xid_offset_.size() < 2) { + offset_arg = positiveInteger("value of transaction id: -X must be a positive integer"); + } else { + isc_throw(isc::InvalidParameter, + "transaction ids already specified, unexpected 3rd -X occurence"); + } + xid_offset_.push_back(offset_arg); break; default: - isc_throw(isc::InvalidParameter, - "unknown command line option"); + isc_throw(isc::InvalidParameter, "unknown command line option"); } } - // Tune ip version as it still might be zero - // if not specified in command line was used + // If the IP version was not specified in the + // command line, assume IPv4. if (ipversion_ == 0) { ipversion_ = 4; } - // Tune up template file lists + // If template packet files specified for both DISCOVER/SOLICIT + // and REQUEST/REPLY exchanges make sure we have transaction id + // and random duid offsets for both exchanges. We will duplicate + // value specified as -X and -R for second + // exchange if user did not specified otherwise. if (template_file_.size() > 1) { if (xid_offset_.size() == 1) { xid_offset_.push_back(xid_offset_[0]); @@ -305,12 +316,16 @@ CommandOptions::initialize(int argc, char** argv) { } // Get server argument + // NoteFF02::1:2 and FF02::1:3 are defined in RFC3315 as + // All_DHCP_Relay_Agents_and_Servers and All_DHCP_Servers + // addresses check(optind < argc -1, "extra arguments?"); if (optind == argc - 1) { server_name_ = argv[optind]; // Decode special cases if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) { broadcast_ = 1; + // 255.255.255.255 is IPv4 broadcast address server_name_ = "255.255.255.255"; } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) { server_name_ = "FF02::1:2"; @@ -323,38 +338,25 @@ CommandOptions::initialize(int argc, char** argv) { } void -CommandOptions::initRandomRange(const std::string& opt) { +CommandOptions::initClientsNum() { const std::string errmsg = "value of -R must be non-negative integer"; - long long randomRange = 0; + + // Declare clients_num as as 64-bit signed value to + // be able to detect negative values provided + // by user. We would not detect negative values + // if we casted directly to unsigned value. + long long clients_num = 0; try { - randomRange = boost::lexical_cast(opt); + clients_num = boost::lexical_cast(optarg); } catch (boost::bad_lexical_cast&) { isc_throw(isc::InvalidParameter, errmsg.c_str()); } - check(randomRange < 0, errmsg.c_str()); + check(clients_num < 0, errmsg); try { - random_range_ = boost::lexical_cast(randomRange); + clients_num_ = boost::lexical_cast(optarg); } catch (boost::bad_lexical_cast&) { isc_throw(isc::InvalidParameter, errmsg); } - - if ((random_range_ != 0) && (random_range_ != std::numeric_limits::max())) { - // randomization range is restricted by number of clients - uint32_t maxTargetRand = random_range_ + 1; - // randomization with random() function is within uint32_t range - uint64_t maxRandom = std::numeric_limits::max() + 1; - // restrict max randomized value to - // multiple of number of clients - uint64_t restrictedRandom = (maxRandom / maxTargetRand) * maxTargetRand; - if (restrictedRandom == maxRandom) { - // no need to restrict max random value - // uint32_t range is already multiple - // of number of clients range - max_random_ = 0; - } else { - max_random_ = static_cast (restrictedRandom); - } - } } void @@ -384,20 +386,24 @@ CommandOptions::decodeMac(const std::string& base) { std::istringstream s1(base.substr(found + 1)); std::string token; mac_prefix_.clear(); + // Get pieces of MAC address separated with : (or even ::) while (std::getline(s1, token, ':')) { unsigned int ui = 0; - std::istringstream s2(token); + // Convert token to byte value using std::istringstream if (token.length() > 0) { try { + // Do actual conversion ui = convertHexString(token); } catch (isc::InvalidParameter&) { isc_throw(isc::InvalidParameter, "invalid characters in MAC provided"); } + // If conversion succeeded store byte value mac_prefix_.push_back(ui); } } + // MAC address must consist of 6 octets, otherwise it is invalid check(mac_prefix_.size() != 6, errmsg); } @@ -408,15 +414,16 @@ CommandOptions::decodeDuid(const std::string& base) { check(found == std::string::npos, "expected -b format for duid is -b duid="); std::string b = base.substr(found + 1); - // duid must have even number of digits and must not be longer than 64 bytes + // DUID must have even number of digits and must not be longer than 64 bytes check(b.length() & 1, "odd number of hexadecimal digits in duid"); check(b.length() > 128, "duid too large"); check(b.length() == 0, "no duid specified"); - // Turn pairs of hex digits into vector of bytes + // Turn pairs of hexadecimal digits into vector of octets for (int i = 0; i < b.length(); i += 2) { unsigned int ui = 0; try { + // Do actual conversion ui = convertHexString(b.substr(i, 2)); } catch (isc::InvalidParameter&) { isc_throw(isc::InvalidParameter, @@ -427,17 +434,20 @@ CommandOptions::decodeDuid(const std::string& base) { } uint8_t -CommandOptions::convertHexString(const std::string& s) { +CommandOptions::convertHexString(const std::string& text) const { unsigned int ui = 0; - for (int i = 0; i < s.length(); ++i) { - if (!std::isxdigit(s[i])) { + // First, check if we are dealing with hexadecimal digits only + for (int i = 0; i < text.length(); ++i) { + if (!std::isxdigit(text[i])) { isc_throw(isc::InvalidParameter, - "The following digit: " << s[i] << " in " - << s << "is not hexadecimal"); + "The following digit: " << text[i] << " in " + << text << "is not hexadecimal"); } } - std::istringstream ss(s); - ss >> std::hex >> ui >> std::dec; + // If we are here, we have valid string to convert to octet + std::istringstream text_stream(text); + text_stream >> std::hex >> ui >> std::dec; + // Check if for some reason we have overflow - this should never happen! if (ui > 0xFF) { isc_throw(isc::InvalidParameter, "Can't convert more than two hex digits to byte"); } @@ -476,7 +486,7 @@ CommandOptions::validate() const { check((getRate() == 0) && (getReportDelay() != 0), "-r must be set to use -t\n"); check((getRate() == 0) && (getNumRequests().size() > 0), - "-r must be set to use -n\n"); + "-r must be set to use -n\n"); check((getRate() == 0) && (getPeriod() != 0), "-r must be set to use -p\n"); check((getRate() == 0) && @@ -507,7 +517,7 @@ CommandOptions::check(bool condition, const std::string& errmsg) const { } int -CommandOptions::positiveInteger(const std::string& errmsg) { +CommandOptions::positiveInteger(const std::string& errmsg) const { try { int value = boost::lexical_cast(optarg); check(value <= 0, errmsg); @@ -518,7 +528,7 @@ CommandOptions::positiveInteger(const std::string& errmsg) { } int -CommandOptions::nonNegativeInteger(const std::string& errmsg) { +CommandOptions::nonNegativeInteger(const std::string& errmsg) const { try { int value = boost::lexical_cast(optarg); check(value < 0, errmsg); @@ -529,7 +539,7 @@ CommandOptions::nonNegativeInteger(const std::string& errmsg) { } std::string -CommandOptions::nonEmptyString(const std::string& errmsg) { +CommandOptions::nonEmptyString(const std::string& errmsg) const { std::string sarg = optarg; if (sarg.length() == 0) { isc_throw(isc::InvalidParameter, errmsg); diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h index 6c7c4b6592..a0e99af6c2 100644 --- a/tests/tools/perfdhcp/command_options.h +++ b/tests/tools/perfdhcp/command_options.h @@ -44,17 +44,19 @@ public: /// \brief Reset to defaults /// - /// Resets the CommandOptions object to default values. + /// Reset data members to default values. This is specifically + /// useful when unit tests are performed using different + /// command line options. void reset(); /// \brief Parse command line /// - /// Parses the command line and stores the selected options. + /// Parses the command line and stores the selected options + /// in class data members. /// /// \param argc Argument count passed to main(). /// \param argv Argument value array passed to main(). - /// \param force_reset Force reset of state variables - /// \throws isc::InvalidParameter if parsing fails + /// \throws isc::InvalidParameter if parse fails void parse(int argc, char** const argv); /// \brief Returns IP version @@ -72,64 +74,76 @@ public: /// \return exchange rate per second int getRate() const { return rate_; } - /// \brief Returns report delay + /// \brief Returns delay between two performance reports /// - /// \return delay between two consecutive reports + /// \return delay between two consecutive performance reports int getReportDelay() const { return report_delay_; } - /// \brief Returns randomization range + /// \brief Returns number of simulated clients /// - /// \return randomization range - uint32_t getRandomRange() const { return random_range_; } + /// \return number of simulated clients + uint32_t getClientsNum() const { return clients_num_; } - /// \brief Returns MAC addr prefix + /// \brief Returns MAC address prefix /// - /// \ return MAC addr prefix to generate different clients + /// \ return MAC address prefix to simulate different clients std::vector getMacPrefix() const { return mac_prefix_; } /// \brief Returns DUID prefix /// - /// \return DUID prefix to generate different clients + /// \return DUID prefix to simulate different clients std::vector getDuidPrefix() const { return duid_prefix_; } /// \brief Returns base values /// - /// \return base values for xid generation + /// \return all base values specified std::vector getBase() const { return base_; } - /// \brief Returns number of exchanges + /// \brief Returns maximum number of exchanges /// - /// \return number of exchange requests + /// \return number of exchange requests before test is aborted std::vector getNumRequests() const { return num_request_; } /// \brief Returns test period /// - /// \return test period + /// \return test period before it is aborted int getPeriod() const { return period_; } - /// \brief Returns lost time + /// \brief Returns drop time /// - /// \return return time before request is lost + /// The method returns maximum time elapsed from + /// sending the packet before it is assumed dropped. + /// + /// \return return time before request is assumed dropped std::vector getDropTime() const { return drop_time_; } - /// \brief Returns max drops number + /// \brief Returns maximum drops number + /// + /// Returns maximum number of packet drops before + /// aborting a test. /// - /// \return maximum number of lost requests + /// \return maximum number of dropped requests std::vector getMaxDrop() const { return max_drop_; } - /// \brief Returns max percentage of drops + /// \brief Returns maximal percentage of drops + /// + /// Returns maximal percentage of packet drops + /// before aborting a test. /// /// \return maximum percentage of lost requests std::vector getMaxDropPercentage() const { return max_pdrop_; } - /// \brief Returns local address or interface + /// \brief Returns local address or interface name /// - /// \return local address or interface + /// \return local address or interface name std::string getLocalName() const { return localname_; } - /// \brief Checks if interface name is used + /// \brief Checks if interface name was used /// - /// \return true if interface name was specified + /// The method checks if interface name was used + /// rather than address. + /// + /// \return true if interface name was used bool isInterface() const { return is_interface_; } /// \brief Returns number of preload exchanges @@ -157,17 +171,17 @@ public: /// \return random seed uint32_t getSeed() const { return seed_; } - /// \brief Checks if broadcast + /// \brief Checks if broadcast address is to be used /// - /// \return true if broadcast handling + /// \return true if broadcast address is to be used bool isBroadcast() const { return broadcast_; } - /// \brief Check if rapid commit used + /// \brief Check if rapid commit option used /// - /// \return true if rapid commit is used + /// \return true if rapid commit option is used bool isRapidCommit() const { return rapid_commit_; } - /// \brief Check if server-ID taken from first package + /// \brief Check if server-ID to be taken from first package /// /// \return true if server-iD to be taken from first package bool isUseFirst() const { return use_first_; } @@ -204,12 +218,12 @@ public: /// \brief Returns diagnostic selectors /// - /// \return diagnostic selector + /// \return diagnostics selector std::string getDiags() const { return diags_; } /// \brief Returns wrapped command /// - /// \return wrapped command + /// \return wrapped command (start/stop) std::string getWrapped() const { return wrapped_; } /// \brief Returns server name @@ -231,13 +245,12 @@ private: /// \brief Default Constructor /// - /// Protected constructor as this is a singleton class. + /// Private constructor as this is a singleton class. /// Use CommandOptions::instance() to get instance of it. CommandOptions() { reset(); } -private: /// \brief Initializes class members based command line /// /// Reads each command line parameter and sets class member values @@ -254,7 +267,7 @@ private: /// \brief Throws !InvalidParameter exception if condition is true /// - /// Convenience function that throws an !InvalidParameter exception if + /// Convenience function that throws an InvalidParameter exception if /// the condition argument is true /// /// \param condition Condition to be checked @@ -262,38 +275,41 @@ private: /// \throws isc::InvalidParameter if condition argument true inline void check(bool condition, const std::string& errmsg) const; - /// \brief Casts command line arg to positive int + /// \brief Casts command line argument to positive integer /// /// \param errmsg Error message if lexical cast fails /// \throw InvalidParameter if lexical cast fails - int positiveInteger(const std::string& errmsg); + int positiveInteger(const std::string& errmsg) const; - /// \brief Casts command line arg to non-negative int + /// \brief Casts command line argument to non-negative integer /// /// \param errmsg Error message if lexical cast fails /// \throw InvalidParameter if lexical cast fails - int nonNegativeInteger(const std::string& errmsg); + int nonNegativeInteger(const std::string& errmsg) const; - /// \brief Returns command line string if it is non-empty + /// \brief Returns command line string if it is not empty /// /// \param errmsg Error message if string is empty /// \throw InvalidParameter if string is empty - std::string nonEmptyString(const std::string& errmsg); + std::string nonEmptyString(const std::string& errmsg) const; /// \brief Calculates max_random_ and random_range_ /// /// \param opt Value of -R option /// \throw InvalidParameter if -R is wrong - void initRandomRange(const std::string& opt); + void initClientsNum(); /// \brief Decodes base provided with -b /// - /// Function decodes parameters given with -b - /// parameter. Currently it supports the following command line options e.g.: - /// -b mac=00:01:02:03:04:05 - /// -b duid=0F1234 (duid can be up to 128 hex digits) + /// Function decodes argument of -b switch, which + /// specifies a base value used to generate unique + /// mac or duid values in packets sent to system + /// under test. + /// The following forms of switch arguments are supported: + /// - -b mac=00:01:02:03:04:05 + /// - -b duid=0F1234 (duid can be up to 128 hex digits) // Function will decode 00:01:02:03:04:05 and/or - /// 0F1234 repsectively and initialize mac_prefix_ + /// 0F1234 respectively and initialize mac_prefix_ /// and/or duid_prefix_ members /// /// \param base Base in string format @@ -303,7 +319,7 @@ private: /// \brief Decodes base MAC address provided with -b /// /// Function decodes parameter given as -b mac=00:01:02:03:04:05 - /// Function will decode 00:01:02:03:04:05 initialize mac_prefix_ + /// The function will decode 00:01:02:03:04:05 initialize mac_prefix_ /// class member. /// Provided MAC address is for example only /// @@ -314,55 +330,78 @@ private: /// \brief Decodes base DUID provided with -b /// /// Function decodes parameter given as -b duid=0F1234 - /// Function will decode 0F1234 and initialize duid_prefix_ + /// The function will decode 0F1234 and initialize duid_prefix_ /// class member. /// Provided DUID is for example only. /// - /// \param base Base string given as -b mac=00:01:02:03:04:05 + /// \param base Base string given as -b duid=0F1234 /// \throws isc::InvalidParameter if DUID is invalid void decodeDuid(const std::string& base); - /// \brief converts two digit hex string to byte + /// \brief Converts two-digit hexadecimal string to a byte /// - /// \param s hex string e.g. AF + /// \param hex_text Hexadecimal string e.g. AF /// \throw isc::InvalidParameter if string does not represent hex byte - uint8_t convertHexString(const std::string& s); - - uint8_t ipversion_; ///< IP version - ExchangeMode exchange_mode_ ; ///< Packet exchange mode (e.g. DORR/SARR) - int rate_; ///< rate in exchange per second - int report_delay_; ///< delay between two reports - uint32_t random_range_; ///< number of simulated clients - uint32_t max_random_; ///< maximum random value - std::vector mac_prefix_; ///< MAC addr prefix - std::vector duid_prefix_; ///< DUID prefix - std::vector base_; ///< base values for xid - std::vector num_request_; ///< number of exchanges - int period_; ///< test period - int drop_time_set_; ///< losttime[0] was set - std::vector drop_time_; ///< time before a request is lost - int max_drop_set_; ///< max{p}drop[0] was set - std::vector max_drop_; ///< maximum number of lost requests - std::vector max_pdrop_; ///< maximum percentage - std::string localname_; ///< local address or interface - bool is_interface_; ///< interface vs local address - int preload_; ///< preload exchanges - int aggressivity_; ///< back to back exchanges - int local_port_; ///< local port number (host endian) - bool seeded_; ///< is a seed provided - uint32_t seed_; ///< randomization seed - bool broadcast_; ///< use broadcast - bool rapid_commit_; ///< add rapid commit option - bool use_first_; ///< where to take the server-ID - std::vector template_file_; ///< template file name - std::vector xid_offset_; ///< template offsets (xid)*/ - std::vector rnd_offset_; ///< template offsets (random) - int elp_offset_; ///< template offset (elapsed time) - int sid_offset_; ///< template offset (server ID) - int rip_offset_; ///< template offset (requested IP) - std::string diags_; ///< diagnostic selectors - std::string wrapped_; ///< wrapped command - std::string server_name_; ///< server + uint8_t convertHexString(const std::string& hex_text) const; + + uint8_t ipversion_; ///< IP protocol version to be used, expected values are: + ///< 4 for IPv4 and 6 for IPv6, default value 0 means "not set" + ExchangeMode exchange_mode_; ///< Packet exchange mode (e.g. DORA/SARR) + int rate_; ///< Rate in exchange per second + int report_delay_; ///< Delay between generation of two consecutive + ///< performance reports + uint32_t clients_num_; ///< Number of simulated clients (aka randomization range). + std::vector mac_prefix_; ///< MAC address prefix used to generate unique DUIDs + ///< for simulated clients. + std::vector duid_prefix_; ///< DUID prefix used to generate unique DUIDs for + ///< simulated clients + std::vector base_; ///< Collection of base values specified with -b + ///< options. Supported "bases" are mac= and duid= + std::vector num_request_; ///< Number of 2 or 4-way exchanges to perform + int period_; ///< Test period in seconds + uint8_t drop_time_set_; ///< Indicates number of -d parameters specified by user. + ///< If this value goes above 2, command line parsing fails. + std::vector drop_time_; ///< Time to elapse before request is lost. The fisrt value of + ///< two-element vector refers to DO/SA exchanges, + ///< second value refers to RA/RR. Default values are { 1, 1 } + std::vector max_drop_; ///< Maximum number of drops request before aborting test. + ///< First value of two-element vector specifies maximum + ///< number of drops for DO/SA exchange, second value + ///< specifies maximum number of drops for RA/RR. + std::vector max_pdrop_; ///< Maximal percentage of lost requests before aborting test. + ///< First value of two-element vector specifies percentage for + ///< DO/SA exchanges, second value for RA/RR. + std::string localname_; ///< Local address or interface specified with -l option. + bool is_interface_; ///< Indicates that specified value with -l is + ///< rather interface (not address) + int preload_; ///< Number of preload packets. Preload packets are used to + ///< initiate communication with server before doing performance + ///< measurements. + int aggressivity_; ///< Number of exchanges sent before next pause. + int local_port_; ///< Local port number (host endian) + bool seeded_; ///< Indicates that randomization seed was provided. + uint32_t seed_; ///< Randomization seed. + bool broadcast_; ///< Indicates that we use broadcast address. + bool rapid_commit_; ///< Indicates that we do rapid commit option. + bool use_first_; ///< Indicates that we take server id from first received packet. + std::vector template_file_; ///< Packet template file names. These files store template packets + ///< that are used for initiating echanges. Template packets + ///< read from files are later tuned with variable data. + std::vector xid_offset_; ///< Offset of transaction id in template files. First vector + ///< element points to offset for DISCOVER/SOLICIT messages, + ///< second element points to trasaction id offset for + ///< REQUEST messages + std::vector rnd_offset_; ///< Random value offset in templates. Random value offset + ///< points to last octet of DUID. Up to 4 last octets of + ///< DUID are randomized to simulate differnt clients. + int elp_offset_; ///< Offset of elapsed time option in template packet. + int sid_offset_; ///< Offset of server id option in template packet. + int rip_offset_; ///< Offset of requested ip data in template packet/ + std::string diags_; ///< String representing diagnostic selectors specified + ///< by user with -x. + std::string wrapped_; ///< Wrapped command specified as -w. Expected + ///< values are start and stop. + std::string server_name_; ///< Server name specified as last argument of command line. }; } // namespace perfdhcp diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc index 9119b3229b..c92edd0a04 100644 --- a/tests/tools/perfdhcp/tests/command_options_unittest.cc +++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc @@ -26,6 +26,9 @@ using namespace isc; using namespace isc::perfdhcp; /// \brief Test Fixture Class +/// +/// This test fixture class is used to perform +/// unit tests on perfdhcp CommandOptions class. class CommandOptionsTest : public virtual ::testing::Test { public: @@ -33,18 +36,18 @@ public: CommandOptionsTest() { } protected: - /// \brief Parse command line and cleanup /// - /// Tokenizes command line to array of C-strings, - /// parses arguments and de-allocates C-strings + /// The method tokenizes command line to array of C-strings, + /// parses arguments using CommandOptions class to set + /// its data members and de-allocates array of C-strings. /// - /// \param s Command line to parse - /// \return non-zero if parsing failed - void process(const std::string& s) { + /// \param cmdline Command line to parse + /// \throws std::bad allocation if tokenization failed + void process(const std::string& cmdline) { CommandOptions& opt = CommandOptions::instance(); int argc = 0; - char** argv = tokenizeString(s, &argc); + char** argv = tokenizeString(cmdline, &argc); opt.reset(); opt.parse(argc, argv); for(int i = 0; i < argc; ++i) { @@ -54,7 +57,7 @@ protected: free(argv); } - /// \brief Check initialized values + /// \brief Check default initialized values /// /// Check if initialized values are correct void checkDefaults() { @@ -64,7 +67,7 @@ protected: EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode()); EXPECT_EQ(0, opt.getRate()); EXPECT_EQ(0, opt.getReportDelay()); - EXPECT_EQ(0, opt.getRandomRange()); + EXPECT_EQ(0, opt.getClientsNum()); // default mac uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 }; @@ -109,28 +112,33 @@ protected: /// \param s String to split (tokenize) /// \param num Number of tokens returned /// \return array of C-strings (tokens) - char** tokenizeString(const std::string& s, int* num) const { + char** tokenizeString(const std::string& text_to_split, int* num) const { char** results = NULL; - std::stringstream ss(s); - std::istream_iterator sit(ss); - std::istream_iterator send; - std::vector tokens(sit, send); + // Tokenization with std streams + std::stringstream text_stream(text_to_split); + // Iterators to be used for tokenization + std::istream_iterator text_iterator(text_stream); + std::istream_iterator text_end; + // Tokenize string (space is a separator) using begin and end iteratos + std::vector tokens(text_iterator, text_end); if (tokens.size() > 0) { + // Allocate array of C-strings where we will store tokens results = static_cast(malloc(tokens.size() * sizeof(char*))); if (results == NULL) { throw std::bad_alloc(); } + // Store tokens in C-strings array for (int i = 0; i < tokens.size(); ++i) { char* cs = static_cast(malloc(tokens[i].length() + 1)); strcpy(cs, tokens[i].c_str()); results[i] = cs; } + // Return number of tokens to calling function if (num != NULL) { *num = tokens.size(); } } - return results; } @@ -159,7 +167,11 @@ TEST_F(CommandOptionsTest, IpVersion) { EXPECT_FALSE(opt.isRapidCommit()); // Negative test cases + // -4 and -6 must not coexist + EXPECT_THROW(process("perfdhcp -4 -6 -l ethx"), isc::InvalidParameter); + // -6 and -B must not coexist EXPECT_THROW(process("perfdhcp -6 -B -l ethx"), isc::InvalidParameter); + // -c and -4 (default) must not coexist EXPECT_THROW(process("perfdhcp -c -l ethx"), isc::InvalidParameter); } @@ -169,7 +181,9 @@ TEST_F(CommandOptionsTest, Rate) { EXPECT_EQ(10, opt.getRate()); // Negative test cases + // Rate must not be 0 EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx"), isc::InvalidParameter); + // -r must be specified to use -n, -p and -D EXPECT_THROW(process("perfdhcp -6 -t 5 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -4 -n 150 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -6 -p 120 -l ethx"), isc::InvalidParameter); @@ -182,19 +196,21 @@ TEST_F(CommandOptionsTest, ReportDelay) { EXPECT_EQ(17, opt.getReportDelay()); // Negative test cases - EXPECT_THROW(process("perfdhcp -r -3 -t 17 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -r 0 -t 17 -l ethx"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -r s -t 17 -l ethx"), isc::InvalidParameter); + // -t must be positive integer + EXPECT_THROW(process("perfdhcp -r 10 -t -8 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r 10 -t 0 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -r 10 -t s -l ethx"), isc::InvalidParameter); } -TEST_F(CommandOptionsTest, RandomRange) { +TEST_F(CommandOptionsTest, ClientsNum) { CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -R 200 -l ethx"); - EXPECT_EQ(200, opt.getRandomRange()); + EXPECT_EQ(200, opt.getClientsNum()); process("perfdhcp -R 0 -l ethx"); - EXPECT_EQ(0, opt.getRandomRange()); + EXPECT_EQ(0, opt.getClientsNum()); // Negative test cases + // Number of clients must be non-negative integer EXPECT_THROW(process("perfdhcp -R -5 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -R gs -l ethx"), isc::InvalidParameter); } @@ -209,16 +225,20 @@ TEST_F(CommandOptionsTest, Base) { std::vector v1 = opt.getMacPrefix(); ASSERT_EQ(6, v1.size()); EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac)); + // "3x" is invalid value in MAC address EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx"), isc::InvalidParameter); // Test DUID std::vector v2 = opt.getDuidPrefix(); ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size()); EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid)); + // "t" is invalid digit in DUID EXPECT_THROW(process("perfdhcp -6 -l ethx -b duiD=1AB7Ft670901FF"), isc::InvalidParameter); // Some more negative test cases + // Base is not specified EXPECT_THROW(process("perfdhcp -b -l ethx"), isc::InvalidParameter); + // Typo: should be mac= instead of mc= EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05"), isc::InvalidParameter); } @@ -235,6 +255,7 @@ TEST_F(CommandOptionsTest, DropTime) { EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]); // Negative test cases + // Drop time must not be negative EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0"), isc::InvalidParameter); } @@ -245,9 +266,12 @@ TEST_F(CommandOptionsTest, TimeOffset) { EXPECT_EQ(4, opt.getElapsedTimeOffset()); // Negative test cases + // Argument -E must be used with -T EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -E -i"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -l ethx -E -3"), isc::InvalidParameter); + // Value in -E not specified + EXPECT_THROW(process("perfdhcp -l ethx -T file.x -E -i"), isc::InvalidParameter); + // Value for -E must not be negative + EXPECT_THROW(process("perfdhcp -l ethx -E -3 -T file.x"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, ExchangeMode) { @@ -256,11 +280,13 @@ TEST_F(CommandOptionsTest, ExchangeMode) { EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode()); // Negative test cases + // No template file specified EXPECT_THROW(process("perfdhcp -i -l ethx -X 3"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -O 2"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -E 3"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -S 1"), isc::InvalidParameter); - EXPECT_THROW(process("perfdhcp -i -l ethx -I 2"), isc::InvalidParameter); + // Offsets can't be used in simple exchanges (-i) + EXPECT_THROW(process("perfdhcp -i -l ethx -O 2 -T file.x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -E 3 -T file.x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -S 1 -T file.x"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -i -l ethx -I 2 -T file.x"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Offsets) { @@ -277,6 +303,7 @@ TEST_F(CommandOptionsTest, Offsets) { EXPECT_EQ(3, opt.getTransactionIdOffset()[1]); // Negative test cases + // IP offset/IA_NA offset must be positive EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx"), isc::InvalidParameter); @@ -289,8 +316,10 @@ TEST_F(CommandOptionsTest, LocalPort) { EXPECT_EQ(2000, opt.getLocalPort()); // Negative test cases + // Local port must be between 0..65535 EXPECT_THROW(process("perfdhcp -l ethx -L -2"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -l ethx -L"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -L 65540"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Preload) { @@ -299,7 +328,9 @@ TEST_F(CommandOptionsTest, Preload) { EXPECT_EQ(3, opt.getPreload()); // Negative test cases + // Number of preload packages must not be negative integer EXPECT_THROW(process("perfdhcp -P -1 -l ethx"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -P -3 -l ethx"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Seed) { @@ -313,6 +344,7 @@ TEST_F(CommandOptionsTest, Seed) { EXPECT_FALSE(opt.isSeeded()); // Negtaive test cases + // Seed must be non-negative integer EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx"), isc::InvalidParameter); EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx"), isc::InvalidParameter); } @@ -329,7 +361,10 @@ TEST_F(CommandOptionsTest, TemplateFiles) { EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]); // Negative test cases + // No template file specified EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T"), isc::InvalidParameter); + // Too many template files specified + EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T file.x -T file.x -T file.x"), isc::InvalidParameter); } TEST_F(CommandOptionsTest, Wrapped) { @@ -338,6 +373,7 @@ TEST_F(CommandOptionsTest, Wrapped) { EXPECT_EQ("start", opt.getWrapped()); // Negative test cases + // Missing command after -w, expected start/stop EXPECT_THROW(process("perfdhcp -B -i -l ethx -w"), isc::InvalidParameter); } @@ -345,5 +381,74 @@ TEST_F(CommandOptionsTest, Diagnostics) { CommandOptions& opt = CommandOptions::instance(); process("perfdhcp -l ethx -i -x asTe"); EXPECT_EQ("asTe", opt.getDiags()); + + // Negative test cases + // No diagnostics string specified + EXPECT_THROW(process("perfdhcp -l ethx -i -x"), isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Aggressivity) { + CommandOptions& opt = CommandOptions::instance(); + process("perfdhcp -a 10 -l 192.168.0.1"); + EXPECT_EQ(10, opt.getAggressivity()); + + // Negative test cases + // Aggressivity must be non negative integer + EXPECT_THROW(process("perfdhcp -l ethx -a 0"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -l ethx -a"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -a -2 -l ethx -a 3"), isc::InvalidParameter); } +TEST_F(CommandOptionsTest, MaxDrop) { + CommandOptions& opt = CommandOptions::instance(); + process("perfdhcp -D 25 -l ethx -r 10"); + EXPECT_EQ(25, opt.getMaxDrop()[0]); + process("perfdhcp -D 25 -l ethx -D 15 -r 10"); + EXPECT_EQ(25, opt.getMaxDrop()[0]); + EXPECT_EQ(15, opt.getMaxDrop()[1]); + + process("perfdhcp -D 15% -l ethx -r 10"); + EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); + process("perfdhcp -D 15% -D25% -l ethx -r 10"); + EXPECT_EQ(15, opt.getMaxDropPercentage()[0]); + EXPECT_EQ(25, opt.getMaxDropPercentage()[1]); + process("perfdhcp -D 1% -D 99% -l ethx -r 10"); + EXPECT_EQ(1, opt.getMaxDropPercentage()[0]); + EXPECT_EQ(99, opt.getMaxDropPercentage()[1]); + + // Negative test cases + // Too many -D options + EXPECT_THROW(process("perfdhcp -D 0% -D 1 -l ethx -r20 -D 3"), isc::InvalidParameter); + // Too many -D options + EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -r20 -D 10%"), isc::InvalidParameter); + // Percentage is out of bounds + EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx -r20"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx -r20"), isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, NumRequest) { + CommandOptions& opt = CommandOptions::instance(); + process("perfdhcp -n 1000 -r 10 -l ethx"); + EXPECT_EQ(1000, opt.getNumRequests()[0]); + process("perfdhcp -n 5 -r 10 -n 500 -l ethx"); + EXPECT_EQ(5, opt.getNumRequests()[0]); + EXPECT_EQ(500, opt.getNumRequests()[1]); + + // Negative test cases + // Too many -n parameters, expected maximum 2 + EXPECT_THROW(process("perfdhcp -n 1 -n 2 -l ethx -n3 -r 20"), isc::InvalidParameter); + // Num request must be positive integer + EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx -r 10"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -n 0 -l ethx -r 10"), isc::InvalidParameter); +} + +TEST_F(CommandOptionsTest, Period) { + CommandOptions& opt = CommandOptions::instance(); + process("perfdhcp -p 120 -l ethx -r 100"); + EXPECT_EQ(120, opt.getPeriod()); + + // Negative test cases + // Test period must be positive integer + EXPECT_THROW(process("perfdhcp -p 0 -l ethx -r 50"), isc::InvalidParameter); + EXPECT_THROW(process("perfdhcp -p -3 -l ethx -r 50"), isc::InvalidParameter); +}