From 73efd195b7d0876b76af6caa7ba27fa5bb43fcb2 Mon Sep 17 00:00:00 2001 From: stevenvanrossem Date: Wed, 29 Jun 2016 01:44:07 +0200 Subject: [PATCH] add rest api for network and monitoring --- src/emuvim/api/rest/__init__.py | 0 src/emuvim/api/rest/compute.py | 56 +-------- src/emuvim/api/rest/monitor.py | 72 ++++++++++++ src/emuvim/api/rest/network.py | 60 ++++++++++ src/emuvim/api/rest/rest_api_endpoint.py | 73 ++++++++++++ src/emuvim/cli/prometheus.py | 1 + src/emuvim/cli/rest/__init__.py | 0 src/emuvim/cli/rest/compute.py | 9 +- src/emuvim/cli/rest/datacenter.py | 0 src/emuvim/cli/rest/monitor.py | 110 ++++++++++++++++++ src/emuvim/cli/rest/network.py | 109 +++++++++++++++++ src/emuvim/cli/son_emu_cli.py | 11 +- src/emuvim/dcemulator/net.py | 2 +- .../examples/simple_topology_restapi.py | 18 ++- src/emuvim/test/api_base.py | 2 +- src/emuvim/test/unittests/test_restapi.py | 9 +- utils/ci/son-analyze_test.sh | 3 + utils/ci/stop_sdk_monitor.sh | 2 +- utils/ci/test_sdk_monitor.sh | 10 +- 19 files changed, 478 insertions(+), 69 deletions(-) mode change 100644 => 100755 src/emuvim/api/rest/__init__.py mode change 100644 => 100755 src/emuvim/api/rest/compute.py create mode 100755 src/emuvim/api/rest/monitor.py create mode 100755 src/emuvim/api/rest/network.py create mode 100755 src/emuvim/api/rest/rest_api_endpoint.py mode change 100644 => 100755 src/emuvim/cli/rest/__init__.py mode change 100644 => 100755 src/emuvim/cli/rest/compute.py mode change 100644 => 100755 src/emuvim/cli/rest/datacenter.py create mode 100755 src/emuvim/cli/rest/monitor.py create mode 100755 src/emuvim/cli/rest/network.py mode change 100644 => 100755 src/emuvim/examples/simple_topology_restapi.py mode change 100644 => 100755 src/emuvim/test/api_base.py mode change 100644 => 100755 src/emuvim/test/unittests/test_restapi.py create mode 100755 utils/ci/son-analyze_test.sh diff --git a/src/emuvim/api/rest/__init__.py b/src/emuvim/api/rest/__init__.py old mode 100644 new mode 100755 diff --git a/src/emuvim/api/rest/compute.py b/src/emuvim/api/rest/compute.py old mode 100644 new mode 100755 index 02ca9f2..1c9c892 --- a/src/emuvim/api/rest/compute.py +++ b/src/emuvim/api/rest/compute.py @@ -1,59 +1,12 @@ import logging -import threading -from flask import Flask, request -from flask_restful import Resource,Api +from flask_restful import Resource +from flask import request import json - - logging.basicConfig(level=logging.INFO) - dcs = {} -class RestApiEndpoint(object): - - """ - Simple API endpoint that offers a REST - interface. This interface will be used by the - default command line client. - """ - global dcs - - def __init__(self, listenip, port): - self.ip = listenip - self.port = port - - # setup Flask - self.app = Flask(__name__) - self.api = Api(self.app) - - # setup endpoints - self.api.add_resource(ComputeList, "/restapi/compute/") - self.api.add_resource(ComputeStart, "/restapi/compute///start") - self.api.add_resource(ComputeStop, "/restapi/compute///stop") - self.api.add_resource(ComputeStatus, "/restapi/compute//") - self.api.add_resource(DatacenterList, "/restapi/datacenter") - self.api.add_resource(DatacenterStatus, "/restapi/datacenter/") - - logging.debug("Created API endpoint %s(%s:%d)" % (self.__class__.__name__, self.ip, self.port)) - - - def connectDatacenter(self, dc): - dcs[dc.label] = dc - logging.info("Connected DC(%s) to API endpoint %s(%s:%d)" % (dc.label, self.__class__.__name__, self.ip, self.port)) - - def start(self): - thread = threading.Thread(target= self._start_flask, args=()) - thread.daemon = True - thread.start() - logging.info("Started API endpoint @ http://%s:%d" % (self.ip, self.port)) - - - def _start_flask(self): - self.app.run(self.ip, self.port, debug=True, use_reloader=False) - - class ComputeStart(Resource): """ Start a new compute instance: A docker container (note: zerorpc does not support keyword arguments) @@ -62,7 +15,8 @@ class ComputeStart(Resource): :param image: image name :param command: command to execute :param network: list of all interface of the vnf, with their parameters (id=id1,ip=x.x.x.x/x),... - :return: networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"}) + example networks list({"id":"input","ip": "10.0.0.254/8"}, {"id":"output","ip": "11.0.0.254/24"}) + :return: docker inspect dict of deployed docker """ global dcs @@ -76,7 +30,7 @@ class ComputeStart(Resource): c = dcs.get(dc_label).startCompute( compute_name, image= image, command= command, network= network) # return docker inspect dict - return c. getStatus(), 200 + return c.getStatus(), 200 except Exception as ex: logging.exception("API error.") return ex.message, 500 diff --git a/src/emuvim/api/rest/monitor.py b/src/emuvim/api/rest/monitor.py new file mode 100755 index 0000000..b1edbc3 --- /dev/null +++ b/src/emuvim/api/rest/monitor.py @@ -0,0 +1,72 @@ +import logging +from flask_restful import Resource +from flask import request +import json + +logging.basicConfig(level=logging.INFO) + +net = None + + + +class MonitorInterfaceAction(Resource): + """ + Monitor the counters of a VNF interface + :param vnf_name: name of the VNF to be monitored + :param vnf_interface: name of the VNF interface to be monitored + :param metric: tx_bytes, rx_bytes, tx_packets, rx_packets + :return: message string indicating if the monitor action is succesful or not + """ + global net + + def put(self, vnf_name, vnf_interface, metric): + logging.debug("REST CALL: start monitor VNF interface") + try: + c = net.monitor_agent.setup_metric(vnf_name, vnf_interface, metric) + # return monitor message response + return str(c), 200 + except Exception as ex: + logging.exception("API error.") + return ex.message, 500 + + def delete(self, vnf_name, vnf_interface, metric): + logging.debug("REST CALL: stop monitor VNF interface") + try: + c = net.monitor_agent.stop_metric(vnf_name, vnf_interface, metric) + # return monitor message response + return str(c), 200 + except Exception as ex: + logging.exception("API error.") + return ex.message, 500 + + +class MonitorFlowAction(Resource): + """ + Monitor the counters of a specific flow + :param vnf_name: name of the VNF to be monitored + :param vnf_interface: name of the VNF interface to be monitored + :param metric: tx_bytes, rx_bytes, tx_packets, rx_packets + :param cookie: specific identifier of flows to monitor + :return: message string indicating if the monitor action is succesful or not + """ + global net + + def put(self, vnf_name, vnf_interface, metric, cookie): + logging.debug("REST CALL: start monitor VNF interface") + try: + c = net.monitor_agent.setup_flow(vnf_name, vnf_interface, metric, cookie) + # return monitor message response + return str(c), 200 + except Exception as ex: + logging.exception("API error.") + return ex.message, 500 + + def delete(self, vnf_name, vnf_interface, metric, cookie): + logging.debug("REST CALL: stop monitor VNF interface") + try: + c = net.monitor_agent.stop_flow(vnf_name, vnf_interface, metric, cookie) + # return monitor message response + return str(c), 200 + except Exception as ex: + logging.exception("API error.") + return ex.message, 500 \ No newline at end of file diff --git a/src/emuvim/api/rest/network.py b/src/emuvim/api/rest/network.py new file mode 100755 index 0000000..4a0214f --- /dev/null +++ b/src/emuvim/api/rest/network.py @@ -0,0 +1,60 @@ +import logging +from flask_restful import Resource +from flask import request +import json + +logging.basicConfig(level=logging.INFO) + +net = None + + +class NetworkAction(Resource): + """ + Add or remove chains between VNFs. These chain links are implemented as flow entries in the networks' SDN switches. + :param vnf_src_name: VNF name of the source of the link + :param vnf_dst_name: VNF name of the destination of the link + :param vnf_src_interface: VNF interface name of the source of the link + :param vnf_dst_interface: VNF interface name of the destination of the link + :param weight: weight of the link (can be useful for routing calculations) + :param match: OpenFlow match format of the flow entry + :param bidirectional: boolean value if the link needs to be implemented from src to dst and back + :param cookie: cookie value, identifier of the flow entry to be installed. + :return: message string indicating if the chain action is succesful or not + """ + + global net + + def put(self, vnf_src_name, vnf_dst_name): + logging.debug("REST CALL: network chain add") + command = 'add-flow' + return self._NetworkAction(vnf_src_name, vnf_dst_name, command=command) + + def delete(self, vnf_src_name, vnf_dst_name): + logging.debug("REST CALL: network chain remove") + command = 'del-flows' + return self._NetworkAction(vnf_src_name, vnf_dst_name, command=command) + + def _NetworkAction(self, vnf_src_name, vnf_dst_name, command=None): + # call DCNetwork method, not really datacenter specific API for now... + # no check if vnfs are really connected to this datacenter... + try: + vnf_src_interface = json.loads(request.json).get("vnf_src_interface") + vnf_dst_interface = json.loads(request.json).get("vnf_dst_interface") + weight = json.loads(request.json).get("weight") + match = json.loads(request.json).get("match") + bidirectional = json.loads(request.json).get("bidirectional") + cookie = json.loads(request.json).get("cookie") + c = net.setChain( + vnf_src_name, vnf_dst_name, + vnf_src_interface=vnf_src_interface, + vnf_dst_interface=vnf_dst_interface, + cmd=command, + weight=weight, + match=match, + bidirectional=bidirectional, + cookie=cookie) + # return setChain response + return str(c), 200 + except Exception as ex: + logging.exception("API error.") + return ex.message, 500 \ No newline at end of file diff --git a/src/emuvim/api/rest/rest_api_endpoint.py b/src/emuvim/api/rest/rest_api_endpoint.py new file mode 100755 index 0000000..6c69a03 --- /dev/null +++ b/src/emuvim/api/rest/rest_api_endpoint.py @@ -0,0 +1,73 @@ +import logging +import threading +from flask import Flask +from flask_restful import Api + +# need to import total module to set its global variable dcs +import compute +from compute import dcs, ComputeList, ComputeStart, ComputeStatus, ComputeStop, DatacenterList, DatacenterStatus + +# need to import total module to set its global variable net +import network +from network import NetworkAction + +import monitor +from monitor import MonitorInterfaceAction, MonitorFlowAction + +logging.basicConfig(level=logging.INFO) + + + +class RestApiEndpoint(object): + + """ + Simple API endpoint that offers a REST + interface. This interface will be used by the + default command line client. + """ + + def __init__(self, listenip, port): + self.ip = listenip + self.port = port + + # setup Flask + self.app = Flask(__name__) + self.api = Api(self.app) + + # setup endpoints + self.api.add_resource(ComputeList, "/restapi/compute/") + self.api.add_resource(ComputeStart, "/restapi/compute///start") + self.api.add_resource(ComputeStop, "/restapi/compute///stop") + self.api.add_resource(ComputeStatus, "/restapi/compute//") + self.api.add_resource(DatacenterList, "/restapi/datacenter") + self.api.add_resource(DatacenterStatus, "/restapi/datacenter/") + + self.api.add_resource(NetworkAction, "/restapi/network//") + + self.api.add_resource(MonitorInterfaceAction, "/restapi/monitor///") + self.api.add_resource(MonitorFlowAction, "/restapi/monitor////") + + logging.debug("Created API endpoint %s(%s:%d)" % (self.__class__.__name__, self.ip, self.port)) + + + def connectDatacenter(self, dc): + compute.dcs[dc.label] = dc + logging.info("Connected DC(%s) to API endpoint %s(%s:%d)" % (dc.label, self.__class__.__name__, self.ip, self.port)) + + def connectDCNetwork(self, DCnetwork): + + network.net = DCnetwork + monitor.net = DCnetwork + + logging.info("Connected DCNetwork to API endpoint %s(%s:%d)" % ( + self.__class__.__name__, self.ip, self.port)) + + def start(self): + thread = threading.Thread(target= self._start_flask, args=()) + thread.daemon = True + thread.start() + logging.info("Started API endpoint @ http://%s:%d" % (self.ip, self.port)) + + + def _start_flask(self): + self.app.run(self.ip, self.port, debug=True, use_reloader=False) \ No newline at end of file diff --git a/src/emuvim/cli/prometheus.py b/src/emuvim/cli/prometheus.py index 9432408..6250fb2 100755 --- a/src/emuvim/cli/prometheus.py +++ b/src/emuvim/cli/prometheus.py @@ -9,6 +9,7 @@ import requests # set this to localhost for now # this is correct for son-emu started outside of a container or as a container with net=host +#TODO prometheus sdk DB is started outside of emulator, place these globals in an external SDK config file? prometheus_ip = '127.0.0.1' prometheus_port = '9090' prometheus_REST_api = 'http://{0}:{1}'.format(prometheus_ip, prometheus_port) diff --git a/src/emuvim/cli/rest/__init__.py b/src/emuvim/cli/rest/__init__.py old mode 100644 new mode 100755 diff --git a/src/emuvim/cli/rest/compute.py b/src/emuvim/cli/rest/compute.py old mode 100644 new mode 100755 index bdef0ec..29e65dc --- a/src/emuvim/cli/rest/compute.py +++ b/src/emuvim/cli/rest/compute.py @@ -27,19 +27,20 @@ class RestApiClient(): 'command':args.get("docker_command"), 'network':nw_list} - responce = put("%s/restapi/compute/%s/%s/start" % + response = put("%s/restapi/compute/%s/%s/start" % (args.get("endpoint"), args.get("datacenter"), args.get("name")), json = json.dumps(req)) - pp.pprint(responce.json()) + pp.pprint(response.json()) + def stop(self, args): - responce = get("%s/restapi/compute/%s/%s/stop" % + response = get("%s/restapi/compute/%s/%s/stop" % (args.get("endpoint"), args.get("datacenter"), args.get("name"))) - pp.pprint(responce.json()) + pp.pprint(response.json()) def list(self,args): diff --git a/src/emuvim/cli/rest/datacenter.py b/src/emuvim/cli/rest/datacenter.py old mode 100644 new mode 100755 diff --git a/src/emuvim/cli/rest/monitor.py b/src/emuvim/cli/rest/monitor.py new file mode 100755 index 0000000..2ed1402 --- /dev/null +++ b/src/emuvim/cli/rest/monitor.py @@ -0,0 +1,110 @@ +from requests import get, put, delete +from tabulate import tabulate +import pprint +import argparse +import json +from emuvim.cli import prometheus + +pp = pprint.PrettyPrinter(indent=4) + +class RestApiClient(): + + def __init__(self): + self.cmds = {} + + def execute_command(self, args): + if getattr(self, args["command"]) is not None: + # call the local method with the same name as the command arg + getattr(self, args["command"])(args) + else: + print("Command not implemented.") + + def setup_metric(self, args): + vnf_name = self._parse_vnf_name(args.get("vnf_name")) + vnf_interface = self._parse_vnf_interface(args.get("vnf_name")) + + response = put("%s/restapi/monitor/%s/%s/%s" % + (args.get("endpoint"), + vnf_name, + vnf_interface, + args.get("metric"))) + pp.pprint(response.json()) + + def stop_metric(self, args): + vnf_name = self._parse_vnf_name(args.get("vnf_name")) + vnf_interface = self._parse_vnf_interface(args.get("vnf_name")) + + response = delete("%s/restapi/monitor/%s/%s/%s" % + (args.get("endpoint"), + vnf_name, + vnf_interface, + args.get("metric"))) + pp.pprint(response.json()) + + def setup_flow(self, args): + vnf_name = self._parse_vnf_name(args.get("vnf_name")) + vnf_interface = self._parse_vnf_interface(args.get("vnf_name")) + + response = put("%s/restapi/monitor/%s/%s/%s/%s" % + (args.get("endpoint"), + vnf_name, + vnf_interface, + args.get("metric"), + args.get("cookie"))) + + pp.pprint(response.json()) + + def stop_flow(self, args): + vnf_name = self._parse_vnf_name(args.get("vnf_name")) + vnf_interface = self._parse_vnf_interface(args.get("vnf_name")) + + response = delete("%s/restapi/monitor/%s/%s/%s/%s" % + (args.get("endpoint"), + vnf_name, + vnf_interface, + args.get("metric"), + args.get("cookie"))) + + pp.pprint(response.json()) + + def _parse_vnf_name(self, vnf_name_str): + vnf_name = vnf_name_str.split(':')[0] + return vnf_name + + def _parse_vnf_interface(self, vnf_name_str): + try: + vnf_interface = vnf_name_str.split(':')[1] + except: + vnf_interface = None + + return vnf_interface + +parser = argparse.ArgumentParser(description='son-emu monitor') +parser.add_argument( + "command", + choices=['setup_metric', 'stop_metric', 'setup_flow', 'stop_flow','prometheus'], + help="setup/stop a metric/flow to be monitored or query Prometheus") +parser.add_argument( + "--vnf_name", "-vnf", dest="vnf_name", + help="vnf name:interface to be monitored") +parser.add_argument( + "--metric", "-m", dest="metric", + help="tx_bytes, rx_bytes, tx_packets, rx_packets") +parser.add_argument( + "--cookie", "-c", dest="cookie", + help="flow cookie to monitor") +parser.add_argument( + "--query", "-q", dest="query", + help="prometheus query") +parser.add_argument( + "--datacenter", "-d", dest="datacenter", + help="Data center where the vnf is deployed") +parser.add_argument( + "--endpoint", "-e", dest="endpoint", + default="http://127.0.0.1:5000", + help="UUID of the plugin to be manipulated.") + +def main(argv): + args = vars(parser.parse_args(argv)) + c = RestApiClient() + c.execute_command(args) \ No newline at end of file diff --git a/src/emuvim/cli/rest/network.py b/src/emuvim/cli/rest/network.py new file mode 100755 index 0000000..e7687f6 --- /dev/null +++ b/src/emuvim/cli/rest/network.py @@ -0,0 +1,109 @@ +from requests import get,put, delete +from tabulate import tabulate +import pprint +import argparse +import json + +pp = pprint.PrettyPrinter(indent=4) + +class RestApiClient(): + + def __init__(self): + self.cmds = {} + + def execute_command(self, args): + if getattr(self, args["command"]) is not None: + # call the local method with the same name as the command arg + getattr(self, args["command"])(args) + else: + print("Command not implemented.") + + def add(self, args): + vnf_src_name = self._parse_vnf_name(args.get("source")) + vnf_dst_name = self._parse_vnf_name(args.get("destination")) + + params = self._create_dict( + vnf_src_interface=self._parse_vnf_interface(args.get("source")), + vnf_dst_interface=self._parse_vnf_interface(args.get("destination")), + weight=args.get("weight"), + match=args.get("match"), + bidirectional=args.get("bidirectional"), + cookie=args.get("cookie")) + + response = put("%s/restapi/network/%s/%s" % + (args.get("endpoint"), + vnf_src_name, + vnf_dst_name), + json=json.dumps(params)) + pp.pprint(response.json()) + + def remove(self, args): + vnf_src_name = self._parse_vnf_name(args.get("source")) + vnf_dst_name = self._parse_vnf_name(args.get("destination")) + + params = self._create_dict( + vnf_src_interface=self._parse_vnf_interface(args.get("source")), + vnf_dst_interface=self._parse_vnf_interface(args.get("destination")), + weight=args.get("weight"), + match=args.get("match"), + bidirectional=args.get("bidirectional"), + cookie=args.get("cookie")) + + response = delete("%s/restapi/network/%s/%s" % + (args.get("endpoint"), + vnf_src_name, + vnf_dst_name), + json=json.dumps(params)) + pp.pprint(response.json()) + + def _parse_vnf_name(self, vnf_name_str): + vnf_name = vnf_name_str.split(':')[0] + return vnf_name + + def _parse_vnf_interface(self, vnf_name_str): + try: + vnf_interface = vnf_name_str.split(':')[1] + except: + vnf_interface = None + + return vnf_interface + + def _create_dict(self, **kwargs): + return kwargs + +parser = argparse.ArgumentParser(description='son-emu network') +parser.add_argument( + "command", + choices=['add', 'remove'], + help="Action to be executed.") +parser.add_argument( + "--datacenter", "-d", dest="datacenter", + help="Data center to in which the network action should be initiated") +parser.add_argument( + "--source", "-src", dest="source", + help="vnf name of the source of the chain") +parser.add_argument( + "--destination", "-dst", dest="destination", + help="vnf name of the destination of the chain") +parser.add_argument( + "--weight", "-w", dest="weight", + help="weight metric to calculate the path") +parser.add_argument( + "--match", "-m", dest="match", + help="string holding extra matches for the flow entries") +parser.add_argument( + "--bidirectional", "-b", dest="bidirectional", + action='store_true', + help="add/remove the flow entries from src to dst and back") +parser.add_argument( + "--cookie", "-c", dest="cookie", + help="cookie for this flow, as easy to use identifier (eg. per tenant/service)") +parser.add_argument( + "--endpoint", "-e", dest="endpoint", + default="http://127.0.0.1:5000", + help="UUID of the plugin to be manipulated.") + +def main(argv): + args = vars(parser.parse_args(argv)) + c = RestApiClient() + c.execute_command(args) \ No newline at end of file diff --git a/src/emuvim/cli/son_emu_cli.py b/src/emuvim/cli/son_emu_cli.py index dc3e73a..5c0ae72 100755 --- a/src/emuvim/cli/son_emu_cli.py +++ b/src/emuvim/cli/son_emu_cli.py @@ -20,7 +20,8 @@ from emuvim.cli import monitor from emuvim.cli import network from emuvim.cli.rest import compute as restcom from emuvim.cli.rest import datacenter as restdc - +from emuvim.cli.rest import monitor as restmon +from emuvim.cli.rest import network as restnetw def main(): @@ -29,12 +30,16 @@ def main(): exit(0) if sys.argv[1] == "compute-zapi": compute.main(sys.argv[2:]) - elif sys.argv[1] == "network": + elif sys.argv[1] == "network-zapi": network.main(sys.argv[2:]) elif sys.argv[1] == "datacenter-zapi": datacenter.main(sys.argv[2:]) - elif sys.argv[1] == "monitor": + elif sys.argv[1] == "monitor-zapi": monitor.main(sys.argv[2:]) + elif sys.argv[1] == "monitor": + restmon.main(sys.argv[2:]) + elif sys.argv[1] == "network": + restnetw.main(sys.argv[2:]) elif sys.argv[1] == "compute": restcom.main(sys.argv[2:]) elif sys.argv[1] == "datacenter": diff --git a/src/emuvim/dcemulator/net.py b/src/emuvim/dcemulator/net.py index 39c4a96..1adc8bc 100755 --- a/src/emuvim/dcemulator/net.py +++ b/src/emuvim/dcemulator/net.py @@ -470,7 +470,7 @@ class DCNetwork(Containernet): if learning_switch: self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) else: - # no learning switch + # no learning switch, but with rest api self.ryu_process = Popen([ryu_cmd, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL) time.sleep(1) diff --git a/src/emuvim/examples/simple_topology_restapi.py b/src/emuvim/examples/simple_topology_restapi.py old mode 100644 new mode 100755 index 121e2ef..f3125f1 --- a/src/emuvim/examples/simple_topology_restapi.py +++ b/src/emuvim/examples/simple_topology_restapi.py @@ -19,8 +19,9 @@ script. import logging from mininet.log import setLogLevel from emuvim.dcemulator.net import DCNetwork -from emuvim.api.rest.compute import RestApiEndpoint -#from emuvim.api.zerorpc.compute import ZeroRpcApiEndpoint +from emuvim.api.rest.rest_api_endpoint import RestApiEndpoint + +from emuvim.api.zerorpc.compute import ZeroRpcApiEndpoint from emuvim.api.zerorpc.network import ZeroRpcApiEndpointDCNetwork logging.basicConfig(level=logging.INFO) @@ -30,11 +31,12 @@ def create_topology1(): """ 1. Create a data center network object (DCNetwork) """ - net = DCNetwork() + net = DCNetwork(monitor=True, enable_learning=False) """ 1b. add a monitoring agent to the DCNetwork """ + #keep old zeroRPC interface to test the prometheus metric query mon_api = ZeroRpcApiEndpointDCNetwork("0.0.0.0", 5151) mon_api.connectDCNetwork(net) mon_api.start() @@ -76,6 +78,14 @@ def create_topology1(): one or more data centers to it, which can then be controlled through this API, e.g., start/stop/list compute instances. """ + # keep the old zeroRPC interface for the prometheus metric query test + zapi1 = ZeroRpcApiEndpoint("0.0.0.0", 4242) + # connect data centers to this endpoint + zapi1.connectDatacenter(dc1) + zapi1.connectDatacenter(dc2) + # run API endpoint server (in another thread, don't block) + zapi1.start() + # create a new instance of a endpoint implementation api1 = RestApiEndpoint("127.0.0.1", 5000) # connect data centers to this endpoint @@ -83,6 +93,8 @@ def create_topology1(): api1.connectDatacenter(dc2) api1.connectDatacenter(dc3) api1.connectDatacenter(dc4) + # connect total network also, needed to do the chaining and monitoring + api1.connectDCNetwork(net) # run API endpoint server (in another thread, don't block) api1.start() diff --git a/src/emuvim/test/api_base.py b/src/emuvim/test/api_base.py old mode 100644 new mode 100755 index 54ffde9..e7ab297 --- a/src/emuvim/test/api_base.py +++ b/src/emuvim/test/api_base.py @@ -7,7 +7,7 @@ import os import subprocess import docker from emuvim.dcemulator.net import DCNetwork -from emuvim.api.rest.compute import RestApiEndpoint +from emuvim.api.rest.rest_api_endpoint import RestApiEndpoint from mininet.clean import cleanup from mininet.node import Controller diff --git a/src/emuvim/test/unittests/test_restapi.py b/src/emuvim/test/unittests/test_restapi.py old mode 100644 new mode 100755 index c395220..67e97f2 --- a/src/emuvim/test/unittests/test_restapi.py +++ b/src/emuvim/test/unittests/test_restapi.py @@ -7,7 +7,6 @@ import unittest from emuvim.test.api_base import SimpleTestTopology import subprocess - class testRestApi( SimpleTestTopology ): """ Tests to check the REST API endpoints of the emulator. @@ -37,6 +36,14 @@ class testRestApi( SimpleTestTopology ): print('compute list ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') subprocess.call("son-emu-cli compute list", shell=True) + + print('network add vnf1 vnf2->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') + print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') + subprocess.call("son-emu-cli network add -src vnf1 -dst vnf2 -b -c 10", shell=True) + print('network remove vnf1 vnf2->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') + print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') + subprocess.call("son-emu-cli network remove -src vnf1 -dst vnf2 -b", shell=True) + print('compute stop datacenter0, vnf2 ->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') print('->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') subprocess.call("son-emu-cli compute stop -d datacenter0 -n vnf2", shell=True) diff --git a/utils/ci/son-analyze_test.sh b/utils/ci/son-analyze_test.sh new file mode 100755 index 0000000..45aafad --- /dev/null +++ b/utils/ci/son-analyze_test.sh @@ -0,0 +1,3 @@ +vnf1_uuid=$(docker inspect --format="{{.Id}}" mn.vnf1) + + diff --git a/utils/ci/stop_sdk_monitor.sh b/utils/ci/stop_sdk_monitor.sh index b162f20..c61e3f6 100755 --- a/utils/ci/stop_sdk_monitor.sh +++ b/utils/ci/stop_sdk_monitor.sh @@ -6,6 +6,6 @@ son-emu-cli monitor stop_metric -vnf vnf1:output --metric tx_packets sleep 1 #stop the vnf -son-emu-cli compute-zapi stop -d datacenter1 -n vnf1 +son-emu-cli compute stop -d datacenter1 -n vnf1 diff --git a/utils/ci/test_sdk_monitor.sh b/utils/ci/test_sdk_monitor.sh index 08e52e5..e5cd13b 100755 --- a/utils/ci/test_sdk_monitor.sh +++ b/utils/ci/test_sdk_monitor.sh @@ -5,18 +5,20 @@ #python src/emuvim/examples/monitoring_demo_topology.py & # start a vnf -son-emu-cli compute-zapi start -d datacenter1 -n vnf1 --net '(id=input,ip=10.0.10.3/24),(id=output,ip=10.0.10.4/24)' +son-emu-cli compute start -d datacenter1 -n vnf1 --net '(id=input,ip=10.0.10.3/24),(id=output,ip=10.0.10.4/24)' sleep 1 # monitor a metric son-emu-cli monitor setup_metric -vnf vnf1:output --metric tx_packets -sleep 5 +# allow some time to gather metrics +sleep 20 # check if metric is monitored as expected (exported by son-emu, has vnf name as metric id) -tx_rate=$(son-emu-cli monitor prometheus -d datacenter1 -vnf vnf1 -q 'rate(sonemu_tx_count_packets{vnf_name="vnf1"}[1m])') +tx_rate=$(son-emu-cli monitor-zapi prometheus -d datacenter1 -vnf vnf1 -q 'rate(sonemu_tx_count_packets{vnf_name="vnf1"}[10s])') +sleep 1 # test if prometheus query worked echo $tx_rate @@ -32,7 +34,7 @@ fi # check if cpu load can be monitored (exported by cAdvisor, needs uuid) -cpu_load=$(son-emu-cli monitor prometheus -d datacenter1 -vnf vnf1 -q 'sum(rate(container_cpu_usage_seconds_total{id="/docker/"}[10s]))') +cpu_load=$(son-emu-cli monitor-zapi prometheus -d datacenter1 -vnf vnf1 -q 'sum(rate(container_cpu_usage_seconds_total{id="/docker/"}[10s]))') sleep 1 -- 2.25.1