diff options
author | ahothan <ahothan@cisco.com> | 2019-03-17 22:34:25 -0700 |
---|---|---|
committer | ahothan <ahothan@cisco.com> | 2019-03-17 22:43:25 -0700 |
commit | 634764bdb133f65515088be91f3a5049d6eb027e (patch) | |
tree | 159c29c7dcf558b5ab22dc1916c2c0f1c97611bb /nfvbench | |
parent | 727e863a026fc1f4fbb427bca86829627e954488 (diff) |
NFVBENCH-126 Remove socketio support (debt reduction)
Allow REST server to start in no-openstack mode
Fix pbr version (was set to 0.0.0)
Add docker-compose support
Update documentation for REST API
Change-Id: Ib520b09283ba6a878f802365292a7a829e6ccd09
Signed-off-by: ahothan <ahothan@cisco.com>
Diffstat (limited to 'nfvbench')
-rw-r--r-- | nfvbench/nfvbench.py | 45 | ||||
-rw-r--r-- | nfvbench/nfvbenchd.py | 78 | ||||
-rw-r--r-- | nfvbench/stats_collector.py | 51 |
3 files changed, 41 insertions, 133 deletions
diff --git a/nfvbench/nfvbench.py b/nfvbench/nfvbench.py index cdb99c8..bb73d68 100644 --- a/nfvbench/nfvbench.py +++ b/nfvbench/nfvbench.py @@ -36,7 +36,7 @@ import credentials as credentials from fluentd import FluentLogHandler import log from log import LOG -from nfvbenchd import WebSocketIoServer +from nfvbenchd import WebServer from specs import ChainType from specs import Specs from summarizer import NFVBenchSummarizer @@ -71,6 +71,11 @@ class NFVBench(object): self.notifier = notifier def run(self, opts, args): + """This run() method is called for every NFVbench benchmark request. + + In CLI mode, this method is called only once per invocation. + In REST server mode, this is called once per REST POST request + """ status = NFVBench.STATUS_OK result = None message = '' @@ -82,6 +87,12 @@ class NFVBench(object): try: # recalc the running config based on the base config and options for this run self._update_config(opts) + + # check that an empty openrc file (no OpenStack) is only allowed + # with EXT chain + if not self.config.openrc_file and self.config.service_chain != ChainType.EXT: + raise Exception("openrc_file in the configuration is required for PVP/PVVP chains") + self.specs.set_run_spec(self.config_plugin.get_run_spec(self.config, self.specs.openstack)) self.chain_runner = ChainRunner(self.config, @@ -239,10 +250,8 @@ def _parse_opts_from_cli(): parser.add_argument('--server', dest='server', default=None, - action='store', - metavar='<http_root_pathname>', - help='Run nfvbench in server mode and pass' - ' the HTTP root folder full pathname') + action='store_true', + help='Run nfvbench in server mode') parser.add_argument('--host', dest='host', action='store', @@ -574,14 +583,6 @@ def main(): print json.dumps(config, sort_keys=True, indent=4) sys.exit(0) - # check that an empty openrc file (no OpenStack) is only allowed - # with EXT chain - if not config.openrc_file: - if config.service_chain == ChainType.EXT: - LOG.info('EXT chain with OpenStack mode disabled') - else: - raise Exception("openrc_file is empty in the configuration and is required") - # update the config in the config plugin as it might have changed # in a copy of the dict (config plugin still holds the original dict) config_plugin.set_config(config) @@ -599,18 +600,14 @@ def main(): nfvbench_instance = NFVBench(config, openstack_spec, config_plugin, factory) if opts.server: - if os.path.isdir(opts.server): - server = WebSocketIoServer(opts.server, nfvbench_instance, fluent_logger) - nfvbench_instance.set_notifier(server) - try: - port = int(opts.port) - except ValueError: - server.run(host=opts.host) - else: - server.run(host=opts.host, port=port) + server = WebServer(nfvbench_instance, fluent_logger) + try: + port = int(opts.port) + except ValueError: + server.run(host=opts.host) else: - print 'Invalid HTTP root directory: ' + opts.server - sys.exit(1) + server.run(host=opts.host, port=port) + # server.run() should never return else: with utils.RunLock(): run_summary_required = True diff --git a/nfvbench/nfvbenchd.py b/nfvbench/nfvbenchd.py index fa781af..ae89e7a 100644 --- a/nfvbench/nfvbenchd.py +++ b/nfvbench/nfvbenchd.py @@ -16,37 +16,31 @@ import json import Queue +from threading import Thread import uuid from flask import Flask from flask import jsonify -from flask import render_template from flask import request -from flask_socketio import emit -from flask_socketio import SocketIO from summarizer import NFVBenchSummarizer from log import LOG from utils import byteify from utils import RunLock -# this global cannot reside in Ctx because of the @app and @socketio decorators -app = None -socketio = None +from __init__ import __version__ STATUS_OK = 'OK' STATUS_ERROR = 'ERROR' STATUS_PENDING = 'PENDING' STATUS_NOT_FOUND = 'NOT_FOUND' - def result_json(status, message, request_id=None): body = { 'status': status, 'error_message': message } - if request_id is not None: body['request_id'] = request_id @@ -66,15 +60,13 @@ class Ctx(object): run_queue = Queue.Queue() busy = False result = None - request_from_socketio = False results = {} ids = [] current_id = None @staticmethod - def enqueue(config, request_id, from_socketio=False): + def enqueue(config, request_id): Ctx.busy = True - Ctx.request_from_socketio = from_socketio config['request_id'] = request_id Ctx.run_queue.put(config) @@ -129,40 +121,18 @@ class Ctx(object): return Ctx.current_id -def setup_flask(root_path): - global socketio - global app +def setup_flask(): app = Flask(__name__) - app.root_path = root_path - socketio = SocketIO(app, async_mode='threading') busy_json = result_json(STATUS_ERROR, 'there is already an NFVbench request running') not_busy_json = result_json(STATUS_ERROR, 'no pending NFVbench run') not_found_msg = 'results not found' pending_msg = 'NFVbench run still pending' - # --------- socketio requests ------------ - - @socketio.on('start_run') - def _socketio_start_run(config): - if not Ctx.is_busy(): - Ctx.enqueue(config, get_uuid(), from_socketio=True) - else: - emit('error', {'reason': 'there is already an NFVbench request running'}) - - @socketio.on('echo') - def _socketio_echo(config): - emit('echo', config) - # --------- HTTP requests ------------ - @app.route('/') - def _index(): - return render_template('index.html') - - @app.route('/echo', methods=['GET']) - def _echo(): - config = request.json - return jsonify(config) + @app.route('/version', methods=['GET']) + def _version(): + return __version__ @app.route('/start_run', methods=['POST']) def _start_run(): @@ -201,23 +171,24 @@ def setup_flask(root_path): return jsonify(res) return jsonify(not_busy_json) + return app + -class WebSocketIoServer(object): - """This class takes care of the web socketio server, accepts websocket events, and sends back - notifications using websocket events (send_ methods). Caller should simply create an instance +class WebServer(object): + """This class takes care of the web server. Caller should simply create an instance of this class and pass a runner object then invoke the run method """ - def __init__(self, http_root, runner, fluent_logger): + def __init__(self, runner, fluent_logger): self.nfvbench_runner = runner - setup_flask(http_root) + self.app = setup_flask() self.fluent_logger = fluent_logger - def run(self, host='127.0.0.1', port=7556): + def run(self, host, port): - # socketio.run will not return so we need to run it in a background thread so that + # app.run will not return so we need to run it in a background thread so that # the calling thread (main thread) can keep doing work - socketio.start_background_task(target=socketio.run, app=app, host=host, port=port) + Thread(target=self.app.run, args=(host, port)).start() # wait for run requests # the runner must be executed from the main thread (Trex client library requirement) @@ -238,11 +209,8 @@ class WebSocketIoServer(object): results = result_json(STATUS_ERROR, str(exc)) LOG.exception('NFVbench runner exception:') - if Ctx.request_from_socketio: - socketio.emit('run_end', results) - else: - # this might overwrite a previously unfetched result - Ctx.set_result(results) + # this might overwrite a previously unfetched result + Ctx.set_result(results) try: summary = NFVBenchSummarizer(results['result'], self.fluent_logger) LOG.info(str(summary)) @@ -255,13 +223,3 @@ class WebSocketIoServer(object): Ctx.release() if self.fluent_logger: self.fluent_logger.send_run_summary(True) - - def send_interval_stats(self, time_ms, tx_pps, rx_pps, drop_pct): - stats = {'time_ms': time_ms, 'tx_pps': tx_pps, 'rx_pps': rx_pps, 'drop_pct': drop_pct} - socketio.emit('run_interval_stats', stats) - - def send_ndr_found(self, ndr_pps): - socketio.emit('ndr_found', {'rate_pps': ndr_pps}) - - def send_pdr_found(self, pdr_pps): - socketio.emit('pdr_found', {'rate_pps': pdr_pps}) diff --git a/nfvbench/stats_collector.py b/nfvbench/stats_collector.py index 964d704..dc750db 100644 --- a/nfvbench/stats_collector.py +++ b/nfvbench/stats_collector.py @@ -56,9 +56,7 @@ class IntervalCollector(StatsCollector): self.notifier = notifier def add(self, stats): - if self.notifier: - current_stats = self.__compute_tx_rx_diff(stats) - self.notifier.send_interval_stats(**current_stats) + pass def reset(self): # don't reset time! @@ -66,52 +64,7 @@ class IntervalCollector(StatsCollector): self.last_tx_pkts = 0 def add_ndr_pdr(self, tag, stats): - if self.notifier: - - current_time = self._get_current_time_diff() - rx_pps = self._get_rx_pps(stats['tx_pps'], stats['drop_percentage']) - - self.last_tx_pkts = stats['tx_pps'] / 1000 * (current_time - self.last_time) - self.last_rx_pkts = rx_pps / 1000 * (current_time - self.last_time) - self.last_time = current_time - - # 'drop_pct' key is an unfortunate name, since in iteration stats it means - # number of the packets. More suitable would be 'drop_percentage'. - # FDS frontend depends on this key - current_stats = { - '{}_pps'.format(tag): stats['tx_pps'], - 'tx_pps': stats['tx_pps'], - 'rx_pps': rx_pps, - 'drop_pct': stats['drop_percentage'], - 'time_ms': current_time - } - - self.notifier.send_interval_stats(time_ms=current_stats['time_ms'], - tx_pps=current_stats['tx_pps'], - rx_pps=current_stats['rx_pps'], - drop_pct=current_stats['drop_pct']) - if tag == 'ndr': - self.notifier.send_ndr_found(stats['tx_pps']) - else: - self.notifier.send_pdr_found(stats['tx_pps']) - - def __compute_tx_rx_diff(self, stats): - current_time = self._get_current_time_diff() - tx_diff = stats['overall']['tx']['total_pkts'] - self.last_tx_pkts - tx_pps = (tx_diff * 1000) / (current_time - self.last_time) - rx_diff = stats['overall']['rx']['total_pkts'] - self.last_rx_pkts - rx_pps = (rx_diff * 1000) / (current_time - self.last_time) - - self.last_rx_pkts = stats['overall']['rx']['total_pkts'] - self.last_tx_pkts = stats['overall']['tx']['total_pkts'] - self.last_time = current_time - - return { - 'tx_pps': tx_pps, - 'rx_pps': rx_pps, - 'drop_pct': max(0.0, (1 - (float(rx_pps) / tx_pps)) * 100), - 'time_ms': current_time - } + pass class IterationCollector(StatsCollector): |