diff --git a/scripts/clmc-service/graph-pipeline.sh b/scripts/clmc-service/graph-pipeline.sh index dfd348f8ec69c93f2e2f93fac909d84f301d209e..2b8700cc6984400e1221ca0f995d136e69a79537 100644 --- a/scripts/clmc-service/graph-pipeline.sh +++ b/scripts/clmc-service/graph-pipeline.sh @@ -27,14 +27,18 @@ set -euo pipefail CLMC_IP="localhost" -ues=("ue20" "ue22" "ue23" "ue24") # TODO currently the list of ues is hardcoded JSON_CONFIG=$1 # expects the JSON configuration passed to the execute_graph_pipeline API endpoint # extract and delete some of the configuration details, which are used by this script fields=$(echo ${JSON_CONFIG} | jq -r '"\(.query_period) \(.service_function_chain) \(.results_measurement_name)"') read query_period db_name results_measurement <<< ${fields} -JSON_CONFIG=$(echo ${JSON_CONFIG} | jq 'del(.query_period, .results_measurement_name)') + +# extract the list of ues +ues=($(echo ${JSON_CONFIG} | jq -r '.ues | .[]')) # convert the jq array to bash array + +# delete these fields +JSON_CONFIG=$(echo ${JSON_CONFIG} | jq 'del(.query_period, .results_measurement_name, .ues)') while true diff --git a/src/service/clmcservice/graphapi/tests.py b/src/service/clmcservice/graphapi/tests.py index 3abc635640fd48a15d34b9bd3a0c2df8e731519b..0b07a87ff40dc7c738f0008cf135560e5327df1f 100644 --- a/src/service/clmcservice/graphapi/tests.py +++ b/src/service/clmcservice/graphapi/tests.py @@ -22,9 +22,9 @@ // Created for Project : FLAME """ -from json import dumps +from json import dumps, loads from signal import SIGKILL -from unittest.mock import patch, Mock, PropertyMock +from unittest.mock import patch, Mock, MagicMock, PropertyMock import pytest from pyramid import testing from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPInternalServerError @@ -44,7 +44,8 @@ class TestGraphAPI(object): """ self.registry = testing.setUp() - self.registry.add_settings({"neo4j_host": "localhost", "neo4j_password": "admin", "influx_host": "localhost", "influx_port": 8086, "network_bandwidth": 104857600}) + self.registry.add_settings({"neo4j_host": "localhost", "neo4j_password": "admin", "influx_host": "localhost", "influx_port": 8086, "network_bandwidth": 104857600, + "network_ues_path": "/opt/clmc/src/service/resources/GraphAPI/network_ues.json"}) yield @@ -517,17 +518,21 @@ class TestGraphAPI(object): error_raised = True assert error_raised, error_msg + @patch('clmcservice.graphapi.views.load') + @patch('clmcservice.graphapi.views.open') @patch('clmcservice.graphapi.views.Popen') @patch('clmcservice.graphapi.views.uuid4') - def test_execute_graph_pipeline(self, uuid_mock, popen_mock): + def test_execute_graph_pipeline(self, uuid_mock, popen_mock, fileopen_mock, jsonload_mock): """ Tests the functionality to start a pipeline script executing the graph API workflow - build, query, delete. :param uuid_mock: mock object for the uuid generator function :param popen_mock: mock object for the process creation function + :param fileopen_mock: mock object the mimic the behaviour of opening a file + :param jsonload_mock: mock object to mimic the behaviour of the JSON load function """ - # mock the behaviour of the uuid function + # mock the behaviour of the uuid4 function uuid_mock.return_value = "monitor_test_uuid1" # mock the behaviour of the Popen class @@ -538,6 +543,11 @@ class TestGraphAPI(object): type(popen_intance_mock).returncode = returncode_property_mock # a property mock cannot be attached directly to the mock object, hence use its type object popen_mock.return_value = popen_intance_mock + # mock the behaviur of the open() and load() function + fileopen_mock.return_value = MagicMock() # a magic mock is needed so that the dunder methods __enter__ and __exit__ are generated + ues_dict = {"127.0.0.1": "ue1", "127.0.0.2": "ue2", "127.0.0.3": "ue3"} + jsonload_mock.return_value = ues_dict + # check proper behaviour service_functions = dict(nginx={"measurement_name": "nginx", "response_time_field": "mean(avg_processing_time)", "request_size_field": "mean(avg_request_size)", "response_size_field": "mean(avg_response_size)"}, @@ -552,7 +562,12 @@ class TestGraphAPI(object): response = GraphAPI(request).execute_graph_pipeline() assert response == {"uuid": uuid_mock.return_value, "database": "test_sfc"} - popen_mock.assert_called_once_with(["graph-pipeline.sh", body]) # assert that the graph pipeline script is ran with the JSON config that was received in the request + monitor_json_body["ues"] = list(ues_dict.values()) + + # assert that the graph pipeline script is ran with the JSON config that was received in the request along with the UEs + actual_call_arguments = popen_mock.call_args[0][0] # we expect exactly one call to Popen() with one argument which is a list + assert actual_call_arguments[0] == "graph-pipeline.sh", "Incorrect graph pipeline script name" + assert loads(actual_call_arguments[1]) == monitor_json_body, "Incorrect JSON configuration passed to pipeline script" pid_property_mock.assert_called_once_with() # assert that the process ID attribute was called and saved returncode_property_mock.assert_called_once_with() # assert that the process return code attribute was called to check if the process has started successfully @@ -571,7 +586,10 @@ class TestGraphAPI(object): error_raised = True assert error_raised, "Expecting a 500 HTTP error if the process terminated immediately after it was started" - popen_mock.assert_called_with(["graph-pipeline.sh", body]) # assert that the graph pipeline script is ran with the JSON config that was received in the request + # assert that the graph pipeline script is ran with the JSON config that was received in the request along with the UEs + actual_call_arguments = popen_mock.call_args[0][0] # we expect exactly one call to Popen() with one argument which is a list + assert actual_call_arguments[0] == "graph-pipeline.sh", "Incorrect graph pipeline script name" + assert loads(actual_call_arguments[1]) == monitor_json_body, "Incorrect JSON configuration passed to pipeline script" pid_property_mock.assert_called_with() # assert that the process ID attribute was called and saved returncode_property_mock.assert_called_with() # assert that the process return code attribute was called to check if the process has started successfully diff --git a/src/service/clmcservice/graphapi/views.py b/src/service/clmcservice/graphapi/views.py index 78fea3fd86e817980d0fad947efbc187d9a13bf9..582b731fee4cfbf215f0386a688cc470f34641d9 100644 --- a/src/service/clmcservice/graphapi/views.py +++ b/src/service/clmcservice/graphapi/views.py @@ -32,7 +32,7 @@ from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPServiceUnav from pyramid.view import view_defaults, view_config from requests import exceptions, get from uuid import uuid4 -from json import load +from json import load, dumps from subprocess import Popen from os import kill from signal import SIGKILL @@ -351,7 +351,7 @@ class GraphAPI(object): ues = load(fh) except Exception as e: log.error("Unexpected error: {0}".format(e)) - log.error("No service-route-to-ue mapping was found while building the network topology.") + log.error("No service-router-to-ue mapping was found while building the network topology.") ues = {} # build the network graph and retrieve the number of switch nodes and cluster nodes that were created @@ -400,7 +400,20 @@ class GraphAPI(object): request_uuid = str(uuid4()) sfc = json_queries["service_function_chain"] - process = Popen(["graph-pipeline.sh", body]) + # get the list of ues + ues_file = self.request.registry.settings["network_ues_path"] + try: + with open(ues_file) as fh: + ues = load(fh) + except Exception as e: + log.error("Unexpected error: {0}".format(e)) + log.error("No service-router-to-ue mapping was found while building the network topology.") + ues = {} + + ues_list = list(ues.values()) + json_queries["ues"] = ues_list + + process = Popen(["graph-pipeline.sh", dumps(json_queries)]) process_pid = process.pid process_return_code = process.returncode