From 04a7de082bd221eae3c7004f4e0b99dfa4f8be91 Mon Sep 17 00:00:00 2001 From: ahothan Date: Fri, 28 Jul 2017 17:08:46 -0700 Subject: Initial code drop from Cisco Change-Id: Ie2993886dc8e95c5f73ccdb871add8b96ffcc849 Signed-off-by: ahothan --- client/__init__.py | 0 client/client.py | 150 ++++++++++++++++++++++++++++++++++++++++++++++ client/nfvbench_client.py | 88 +++++++++++++++++++++++++++ client/requirements.txt | 7 +++ 4 files changed, 245 insertions(+) create mode 100644 client/__init__.py create mode 100644 client/client.py create mode 100644 client/nfvbench_client.py create mode 100644 client/requirements.txt (limited to 'client') diff --git a/client/__init__.py b/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/client/client.py b/client/client.py new file mode 100644 index 0000000..5cbc733 --- /dev/null +++ b/client/client.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python +# Copyright 2017 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import requests +import time + +from socketIO_client import SocketIO + + +class TimeOutException(Exception): + pass + + +class NfvbenchException(Exception): + pass + + +class NfvbenchClient(object): + """Python client class to control a nfvbench server + + The nfvbench server must run in background using the --server option. + Since HTML pages are not required, the path to pass to --server can be + any directory on the host. + """ + def __init__(self, nfvbench_url, use_socketio): + """Client class to send requests to the nfvbench server + + Args: + nfvbench_url: the URL of the nfvbench server (e.g. 'http://127.0.0.1:7555') + """ + self.url = nfvbench_url + self.use_socketio = use_socketio + + def socketio_send(self, send_event, receive_event, config, timeout): + class Exec(object): + socketIO = None + socketio_result = None + + def close_socketio(result): + Exec.socketio_result = result + Exec.socketIO.disconnect() + + def on_response(*args): + close_socketio(args[0]) + + def on_error(*args): + raise NfvbenchException(args[0]) + + Exec.socketIO = SocketIO(self.url) + Exec.socketIO.on(receive_event, on_response) + Exec.socketIO.on('error', on_error) + Exec.socketIO.emit(send_event, config) + Exec.socketIO.wait(seconds=timeout) + + if timeout and not Exec.socketio_result: + raise TimeOutException() + return Exec.socketio_result + + def http_get(self, command, config): + url = self.url + '/' + command + res = requests.get(url, json=config) + if res.ok: + return res.json() + res.raise_for_status() + + def http_post(self, command, config): + url = self.url + '/' + command + res = requests.post(url, json=config) + if res.ok: + return res.json() + res.raise_for_status() + + def echo_config(self, config, timeout=100): + """Send an echo event to the nfvbench server with some dummy config and expect the + config to be sent back right away. + + Args: + config: some dummy configuration - must be a valid dict + timeout: how long to wait in seconds or 0 to return immediately, + defaults to 100 seconds + + Returns: + The config as passed as a dict or None if timeout passed is 0 + + Raises: + NfvbenchException: the execution of the passed configuration failed, + the body of the exception + containes the description of the failure. + TimeOutException: the request timed out (and might still being executed + by the server) + """ + if self.use_socketio: + return self.socketio_send('echo', 'echo', config, timeout) + return self.http_get('echo', config) + + def run_config(self, config, timeout=300, poll_interval=5): + """Request an nfvbench configuration to be executed by the nfvbench server. + + This function will block the caller until the request completes or the request times out. + It can return immediately if timeout is set to 0. + Note that running a configuration may take a while depending on the amount of work + requested - so set the timeout value to an appropriate value. + + Args: + config: the nfvbench configuration to execute - must be a valid dict with + valid nfvbench attributes + timeout: how long to wait in seconds or 0 to return immediately, + defaults to 300 seconds + poll_interval: seconds between polling (http only) - defaults to every 5 seconds + + Returns: + The result of the nfvbench execution + or None if timeout passed is 0 + The function will return as soon as the request is completed or when the + timeout occurs (whichever is first). + + Raises: + NfvbenchException: the execution of the passed configuration failed, the body of + the exception contains the description of the failure. + TimeOutException: the request timed out but will still be executed by the server. + """ + if self.use_socketio: + return self.socketio_send('start_run', 'run_end', config, timeout) + res = self.http_post('start_run', config) + if res['status'] != 'PENDING': + raise NfvbenchException(res['error_message']) + + # poll until request completes + elapsed = 0 + while True: + time.sleep(poll_interval) + result = self.http_get('status', config) + if result['status'] != 'PENDING': + return result + elapsed += poll_interval + if elapsed >= timeout: + raise TimeOutException() diff --git a/client/nfvbench_client.py b/client/nfvbench_client.py new file mode 100644 index 0000000..3973b9c --- /dev/null +++ b/client/nfvbench_client.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# Copyright 2017 Cisco Systems, Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +# +# This is an example of python application controling a nfvbench server +# using the nfvbench client API. +# The nfvbench server must run in background using the --server option. +# Since HTML pages are not required, the path to pass to --server can be any directory on the host. +# +import argparse +import json +import sys + +from client import NfvbenchClient + + +# +# At the CLI, the user can either: +# - pass an nfvbench configuration as a string (-c ) +# - pass an nfvbench configuration as a file name containing the +# configuration (-f ) +# - or pass a test config (-e ) that will be echoed back by the server as is +# +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument('-f', '--file', dest='file', + action='store', + help='NFVbench config file to execute (json format)', + metavar='') + parser.add_argument('-c', '--config', dest='config', + action='store', + help='NFVbench config to execute (json format)', + metavar='') + parser.add_argument('-e', '--echo', dest='echo', + action='store', + help='NFVbench config to echo (json format)', + metavar='') + parser.add_argument('-t', '--timeout', dest='timeout', + default=900, + action='store', + help='time (seconds) to wait for NFVbench result', + metavar='') + parser.add_argument('--use-socketio', dest='use_socketio', + action='store_true', + help='NFVbench config to echo (json format)') + parser.add_argument('url', help='nfvbench server url (e.g. http://10.0.0.1:5000)') + opts = parser.parse_args() + + if not opts.file and not opts.config and not opts.echo: + print('at least one of -f or -c or -e required') + sys.exit(-1) + + nfvbench = NfvbenchClient(opts.url, opts.use_socketio) + # convert JSON into a dict + try: + timeout = int(opts.timeout) + if opts.file: + with open(opts.file) as fd: + config = json.loads(fd.read()) + result = nfvbench.run_config(config, timeout=timeout) + elif opts.config: + config = json.loads(opts.config) + result = nfvbench.run_config(config, timeout=timeout) + elif opts.echo: + config = json.loads(opts.echo) + result = nfvbench.echo_config(config, timeout=timeout) + print('Result:', result) + except ValueError as ex: + print('Input configuration is invalid: ' + str(ex)) + print() + + +if __name__ == "__main__": + main() diff --git a/client/requirements.txt b/client/requirements.txt new file mode 100644 index 0000000..80fc402 --- /dev/null +++ b/client/requirements.txt @@ -0,0 +1,7 @@ +# +# +backports.ssl-match-hostname==3.5.0.1 # via websocket-client +requests==2.13.0 # via socketio-client +six==1.10.0 # via socketio-client, websocket-client +socketIO-client==0.7.2 +websocket-client==0.40.0 # via socketio-client -- cgit 1.2.3-korg