From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id AE01DA04DD; Thu, 2 Jan 2020 04:29:17 +0100 (CET) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id A0D341BFA0; Thu, 2 Jan 2020 04:29:17 +0100 (CET) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by dpdk.org (Postfix) with ESMTP id BF5B51BF9B for ; Thu, 2 Jan 2020 04:29:15 +0100 (CET) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by orsmga105.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 01 Jan 2020 19:29:14 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.69,385,1571727600"; d="scan'208";a="419618077" Received: from fmsmsx103.amr.corp.intel.com ([10.18.124.201]) by fmsmga005.fm.intel.com with ESMTP; 01 Jan 2020 19:29:14 -0800 Received: from fmsmsx156.amr.corp.intel.com (10.18.116.74) by FMSMSX103.amr.corp.intel.com (10.18.124.201) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 1 Jan 2020 19:29:14 -0800 Received: from shsmsx107.ccr.corp.intel.com (10.239.4.96) by fmsmsx156.amr.corp.intel.com (10.18.116.74) with Microsoft SMTP Server (TLS) id 14.3.439.0; Wed, 1 Jan 2020 19:29:14 -0800 Received: from shsmsx104.ccr.corp.intel.com ([169.254.5.197]) by SHSMSX107.ccr.corp.intel.com ([169.254.9.210]) with mapi id 14.03.0439.000; Thu, 2 Jan 2020 11:29:12 +0800 From: "Chen, Zhaoyan" To: "Mo, YufengX" , "dts@dpdk.org" , "Wang, Yinan" , "Ma, LihongX" CC: "Mo, YufengX" , "Chen, Zhaoyan" Thread-Topic: [dts] [PATCH V3 3/5] framework/pktgen_base: add new features Thread-Index: AQHVppO6vfP30oqbUUuAj4542Z1ltKfW7GyQ Date: Thu, 2 Jan 2020 03:29:12 +0000 Message-ID: <9DEEADBC57E43F4DA73B571777FECECA41E76D0B@SHSMSX104.ccr.corp.intel.com> References: <20191129090315.60124-1-yufengx.mo@intel.com> <20191129090315.60124-4-yufengx.mo@intel.com> In-Reply-To: <20191129090315.60124-4-yufengx.mo@intel.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dts] [PATCH V3 3/5] framework/pktgen_base: add new features X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" Acked-by: Zhaoyan Chen Regards, Zhaoyan Chen > -----Original Message----- > From: dts On Behalf Of yufengmx > Sent: Friday, November 29, 2019 5:03 PM > To: dts@dpdk.org; Wang, Yinan ; Ma, LihongX > > Cc: Mo, YufengX > Subject: [dts] [PATCH V3 3/5] framework/pktgen_base: add new features >=20 >=20 > *. add test method(latency/loss/throughput) delay/duration options new us= age > definition and relevant process source code. > *. set delay option to be the warm up time after start transmission. > *. add __get_single_throughput_statistic/__get_multi_throughput_statistic= methods to > realize > measure_throughput support return several throughput numbers in a dura= tion. > *. set duration option default value to 10 second. > *. add test method(latency/loss/throughput/rfc2544) options parameter usa= ge > comment, > which is the same as doc pktgen_prog_guide.rst. > *. add a callback to deal with executing other tools query actions in thr= oughput traffic > lasting status. > *. add measure_rfc2544_dichotomy method. >=20 > Signed-off-by: yufengmx > --- > framework/pktgen_base.py | 244 ++++++++++++++++++++++++++++++++++++--- > 1 file changed, 228 insertions(+), 16 deletions(-) >=20 > diff --git a/framework/pktgen_base.py b/framework/pktgen_base.py index > 7855f6f..75c3036 100644 > --- a/framework/pktgen_base.py > +++ b/framework/pktgen_base.py > @@ -81,7 +81,7 @@ class PacketGenerator(object): > return port_idx > else: > port =3D -1 > - except: > + except Exception as e: > port =3D -1 >=20 > return port > @@ -100,7 +100,7 @@ class PacketGenerator(object): > port =3D self._get_gen_port(tester_pci) > msg =3D "test port {0} map gen port {1}".format(port_id, por= t) > self.logger.debug(msg) > - except: > + except Exception as e: > port =3D -1 >=20 > return port > @@ -157,39 +157,120 @@ class PacketGenerator(object): > def reset_streams(self): > self.__streams =3D [] >=20 > - def measure_throughput(self, stream_ids=3D[], options=3D{}): > - """ > - Measure throughput on each tx ports > - """ > - bps_rx =3D [] > - pps_rx =3D [] > - self._prepare_transmission(stream_ids=3Dstream_ids) > + def __warm_up_pktgen(self, stream_ids, options, delay): > + ''' run warm up traffic before start main traffic ''' > + if not delay: > + return > + msg =3D '{1} packet generator: run traffic {0}s to warm up ... '= .format( > + delay, self.pktgen_type) > + self.logger.info(msg) > self._start_transmission(stream_ids, options) > - > - delay =3D options.get('delay') or 5 > time.sleep(delay) > + self._stop_transmission(stream_ids) > + self._clear_streams() > + > + def __get_single_throughput_statistic(self, stream_ids): > + bps_rx =3D [] > + pps_rx =3D [] > used_rx_port =3D [] > + msg =3D 'begin get port statistic ...' > + self.logger.info(msg) > for stream_id in stream_ids: > if self.__streams[stream_id]['rx_port'] not in used_rx_port: > rxbps_rates, rxpps_rates =3D self._retrieve_port_statist= ic( > - stream_id, 'thro= ughput') > + stream_id, 'throughput') > used_rx_port.append(self.__streams[stream_id]['rx_port']= ) > bps_rx.append(rxbps_rates) > pps_rx.append(rxpps_rates) > - self._stop_transmission(stream_id) > - > bps_rx_total =3D self._summary_statistic(bps_rx) > pps_rx_total =3D self._summary_statistic(pps_rx) > - self.logger.info("throughput: pps_rx %f, bps_rx %f" % (pps_rx_to= tal, > bps_rx_total)) > + self.logger.info( > + "throughput: pps_rx %f, bps_rx %f" % (pps_rx_total, > + bps_rx_total)) >=20 > return bps_rx_total, pps_rx_total >=20 > + def __get_multi_throughput_statistic( > + self, stream_ids, duration, interval, callback=3DNone): > + """ > + duration: traffic duration (second) > + interval: interval of get throughput statistics (second) > + callback: a callback method of suite, which is used to do some a= ctions > + during traffic lasting. > + > + Return: a list of throughput instead of a single tuple of pps/bp= s rate > + """ > + time_elapsed =3D 0 > + stats =3D [] > + while time_elapsed < duration: > + time.sleep(interval) > + stats.append(self.__get_single_throughput_statistic(stream_i= ds)) > + if callback and callable(callback): > + callback() > + time_elapsed +=3D interval > + return stats > + > + def measure_throughput(self, stream_ids=3D[], options=3D{}): > + """ > + Measure throughput on each tx ports > + > + options usage: > + rate: > + port rate percent, float(0--100). Default value is 100. > + > + delay: > + warm up time before start main traffic. If it is set, it= will start > + a delay time traffic to make sure packet generator under= good status. > + Warm up flow is ignored by default. > + > + interval: > + a interval time of get throughput statistic (second) > + If set this key value, pktgen will return several throug= hput statistic > + data within a duration traffic. If not set this key valu= e, only > + return one statistic data. It is ignored by default. > + > + callback: > + this key works with ``interval`` key. If it is set, the = callback > + of suite level will be executed after getting throughput= statistic. > + callback method should define as below, don't add sleep = in this method. > + > + def callback(self): > + xxxx() > + > + duration: > + traffic lasting time(second). Default value is 10 second= . > + """ > + interval =3D options.get('interval') > + callback =3D options.get('callback') > + duration =3D options.get('duration') or 10 > + delay =3D options.get('delay') > + self._prepare_transmission(stream_ids=3Dstream_ids) > + # start warm up traffic > + self.__warm_up_pktgen(stream_ids, options, delay) > + # main traffic > + self._start_transmission(stream_ids) > + # keep traffic within a duration time and get throughput statist= ic > + if interval and duration: > + stats =3D self.__get_multi_throughput_statistic( > + stream_ids, duration, interval, callback) > + else: > + time.sleep(duration) > + stats =3D self.__get_single_throughput_statistic(stream_ids) > + self._stop_transmission(stream_ids) > + return stats > + > def _measure_loss(self, stream_ids=3D[], options=3D{}): > """ > Measure lost rate on each tx/rx ports > """ > + delay =3D options.get('delay') > + duration =3D options.get('duration') or 10 > self._prepare_transmission(stream_ids=3Dstream_ids) > + # start warm up traffic > + self.__warm_up_pktgen(stream_ids, options, delay) > + # main traffic > self._start_transmission(stream_ids, options) > + # keep traffic within a duration time > + time.sleep(duration) > self._stop_transmission(None) > result =3D {} > used_rx_port =3D [] > @@ -210,6 +291,19 @@ class PacketGenerator(object): > return result >=20 > def measure_loss(self, stream_ids=3D[], options=3D{}): > + ''' > + options usage: > + rate: > + port rate percent, float(0--100). Default value is 100. > + > + delay: > + warm up time before start main traffic. If it is set, it= will > + start a delay time traffic to make sure packet generator > + under good status. Warm up flow is ignored by default. > + > + duration: > + traffic lasting time(second). Default value is 10 second= . > + ''' > result =3D self._measure_loss(stream_ids, options) > # here only to make sure that return value is the same as dts/et= gen format > # In real testing scenario, this method can offer more data than= it @@ -218,9 > +312,28 @@ class PacketGenerator(object): > def measure_latency(self, stream_ids=3D[], options=3D{}): > """ > Measure latency on each tx/rx ports > + > + options usage: > + rate: > + port rate percent, float(0--100). Default value is 100. > + > + delay: > + warm up time before start main traffic. If it is set, it= will > + start a delay time transmission to make sure packet gene= rator > + under correct status. Warm up flow is ignored by default= . > + > + duration: > + traffic lasting time(second). Default value is 10 second= . > """ > + delay =3D options.get('delay') > + duration =3D options.get('duration') or 10 > self._prepare_transmission(stream_ids=3Dstream_ids, latency=3DTr= ue) > + # start warm up traffic > + self.__warm_up_pktgen(stream_ids, options, delay) > + # main traffic > self._start_transmission(stream_ids, options) > + # keep traffic within a duration time > + time.sleep(duration) > self._stop_transmission(None) >=20 > result =3D {} > @@ -248,7 +361,26 @@ class PacketGenerator(object): > return True >=20 > def measure_rfc2544(self, stream_ids=3D[], options=3D{}): > - """ check loss rate with rate percent dropping """ > + """ check loss rate with rate percent dropping > + > + options usage: > + rate: > + port rate percent at first round testing(0 ~ 100), defau= lt is 100. > + > + pdr: > + permit packet drop rate, , default is 0. > + > + drop_step: > + port rate percent drop step(0 ~ 100), default is 1. > + > + delay: > + warm up time before start main traffic. If it is set, it= will > + start a delay time traffic to make sure packet generator > + under good status. Warm up flow is ignored by default. > + > + duration: > + traffic lasting time(second). Default value is 10 second= . > + """ > loss_rate_table =3D [] > rate_percent =3D options.get('rate') or float(100) > permit_loss_rate =3D options.get('pdr') or 0 @@ -265,6 +397,9 @@= class > PacketGenerator(object): > tx_num, rx_num =3D result.values()[0][1:] > return rate_percent, tx_num, rx_num > _options =3D deepcopy(options) > + # if warm up option 'delay' is set, ignore it in next work flow > + if 'delay' in _options: > + _options.pop('delay') > if 'rate' in _options: > _options.pop('rate') > while not status and rate_percent > 0: > @@ -328,6 +463,81 @@ class PacketGenerator(object): > # here only pick one > return loss_pps_table[-1][1].values()[0] >=20 > + def measure_rfc2544_dichotomy(self, stream_ids=3D[], options=3D{}): > + """ check loss rate using dichotomy algorithm > + > + options usage: > + delay: > + warm up time before start main traffic. If it is set, it= will > + start a delay time traffic to make sure packet generator > + under good status. Warm up flow is ignored by default. > + > + duration: > + traffic lasting time(second). Default value is 10 second= . > + > + min_rate: > + lower bound rate percent , default is 0. > + > + max_rate: > + upper bound rate percent , default is 100. > + > + pdr: > + permit packet drop rate(<1.0), default is 0. > + > + accuracy : > + dichotomy algorithm accuracy, default 0.001. > + """ > + max_rate =3D options.get('max_rate') or 100.0 > + min_rate =3D options.get('min_rate') or 0.0 > + accuracy =3D options.get('accuracy') or 0.001 > + permit_loss_rate =3D options.get('pdr') or 0.0 > + duration =3D options.get('duration') or 10.0 > + # start warm up traffic > + delay =3D options.get('delay') > + _options =3D {'duration': duration} > + self.__warm_up_pktgen(stream_ids, _options, delay) > + # traffic parameters for dichotomy algorithm > + loss_rate_table =3D [] > + hit_result =3D None > + rate =3D traffic_rate_max =3D max_rate > + traffic_rate_min =3D min_rate > + while True: > + # run loss rate testing > + _options =3D {'duration': duration} > + result =3D self._measure_loss(stream_ids, _options) > + loss_rate_table.append([rate, result]) > + status =3D self._check_loss_rate(result, permit_loss_rate) > + # if upper bound rate percent hit, quit the left flow > + if rate =3D=3D max_rate and status: > + hit_result =3D result > + break > + # if lower bound rate percent not hit, quit the left flow > + if rate =3D=3D min_rate and not status: > + break > + if status: > + traffic_rate_min =3D rate > + hit_result =3D result > + else: > + traffic_rate_max =3D rate > + if traffic_rate_max - traffic_rate_min < accuracy: > + break > + rate =3D (traffic_rate_max - traffic_rate_min)/2 + traffic_r= ate_min > + self._clear_streams() > + # set stream rate percent to custom value > + self._set_stream_rate_percent(rate) > + > + if not hit_result: > + msg =3D ('expected permit loss rate <{0}>' > + 'not between rate {1} and rate {2}').format( > + permit_loss_rate, max_rate, min_rate) > + self.logger.error(msg) > + self.logger.info(pformat(loss_rate_table)) > + else: > + self.logger.debug(pformat(loss_rate_table)) > + self.logger.info("zero loss rate is %f" % rate) > + > + return hit_result > + > def measure(self, stream_ids, traffic_opt): > ''' > use as an unify interface method for packet generator @@ -343,6 = +553,8 @@ > class PacketGenerator(object): > result =3D self.measure_rfc2544(stream_ids, traffic_opt) > elif method =3D=3D 'rfc2544_with_pps': > result =3D self.measure_rfc2544_with_pps(stream_ids, traffic= _opt) > + elif method =3D=3D 'rfc2544_dichotomy': > + result =3D self.measure_rfc2544_dichotomy(stream_ids, > + traffic_opt) > else: > result =3D None >=20 > -- > 2.21.0