From 039b3b4eb959a354a2ab2b4ab4108ce2be061c7f Mon Sep 17 00:00:00 2001
From: Nikolay Stanchev <ns17@it-innovation.soton.ac.uk>
Date: Thu, 28 Feb 2019 11:26:02 +0000
Subject: [PATCH] Removes the hardcoded list of UEs from pipeline script

---
 scripts/clmc-service/graph-pipeline.sh    |  8 ++++--
 src/service/clmcservice/graphapi/tests.py | 32 ++++++++++++++++++-----
 src/service/clmcservice/graphapi/views.py | 19 +++++++++++---
 3 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/scripts/clmc-service/graph-pipeline.sh b/scripts/clmc-service/graph-pipeline.sh
index dfd348f..2b8700c 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 3abc635..0b07a87 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 78fea3f..582b731 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
 
-- 
GitLab