diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c79aaa55d9708c7d8a447de7165b90a7fb4740a4..13687d939fd18e771cc990d5f4c965e193b41fb7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -35,8 +35,8 @@ build:tests:
     - python setup.py sdist --dist-dir=$CI_PROJECT_DIR/build
   artifacts:
     paths:
-    - build/clmctest-2.0.4.tar.gz
-    - build/clmcservice-2.0.4.tar.gz
+    - build/clmctest-2.1.1.tar.gz
+    - build/clmcservice-2.1.1.tar.gz
     expire_in: 1 day
 
 test:all:
@@ -50,8 +50,8 @@ test:all:
     - echo "REPO_PASS=${REPO_PASS}" >> $CI_PROJECT_DIR/reporc
     - sudo scripts/test/fixture.sh create -f src/test/clmctest/rspec.json -r $CI_PROJECT_DIR -c all
     - sudo mkdir /var/lib/lxd/containers/test-runner/rootfs/opt/clmc/build
-    - sudo cp build/clmctest-2.0.4.tar.gz /var/lib/lxd/containers/test-runner/rootfs/opt/clmc/build
-    - sudo lxc exec test-runner -- pip3 install /opt/clmc/build/clmctest-2.0.4.tar.gz
+    - sudo cp build/clmctest-2.1.1.tar.gz /var/lib/lxd/containers/test-runner/rootfs/opt/clmc/build
+    - sudo lxc exec test-runner -- pip3 install /opt/clmc/build/clmctest-2.1.1.tar.gz
     - sudo lxc exec test-runner -- pytest -s --tb=short -rfp --pyargs clmctest
   when: on_success      
   
diff --git a/IPRREGISTRY.md b/IPRREGISTRY.md
index a169553930809320aa6b024253dbda3c04778d1a..bdc19dcdf58c12f447bee02e6ccd4059ebf40236 100644
--- a/IPRREGISTRY.md
+++ b/IPRREGISTRY.md
@@ -20,6 +20,7 @@ The CLMC depends on 3rd party open source software distributed using approved op
 | tosca-parser | APACHE LICENSE v2 | https://github.com/openstack/tosca-parser/blob/master/LICENSE |
 | schema | MIT LICENSE | https://github.com/keleshev/schema/blob/master/LICENSE-MIT |	
 | requests | APACHE LICENSE v2 | https://github.com/requests/requests/blob/master/LICENSE |
+| psutil | BSD LICENSE | https://github.com/giampaolo/psutil/blob/master/LICENSE |
 | pytest | MIT LICENSE | https://github.com/pytest-dev/pytest/blob/master/LICENSE |	
 | pytest-cov | MIT LICENSE | https://github.com/pytest-dev/pytest-cov/blob/master/LICENSE |
 			
diff --git a/docs/clmc-service.md b/docs/clmc-service.md
index 8f8c1ef30dfdedf5b39a4fe5e84c25a3ec3a30a0..129b2359a1df1cab9f9660fa3dcd96f2fd2eee92 100644
--- a/docs/clmc-service.md
+++ b/docs/clmc-service.md
@@ -339,8 +339,8 @@ with **/clmc-service** so that the nginx reverse proxy server (listening on port
 
 * **DELETE** ***/graph/monitor/{request_id}*** 
 
-    This API methods instructs the CLMC service to stop running a graph monitoring pipeline script associated with the request identifier in the URL.
-    (retrieved from the response of a POST request for /graph/monitor), e.g. request sent to */graph/monitor/75df6f8d-3829-4fd8-a3e6-b3e917010141*
+    This API method instructs the CLMC service to stop running a graph monitoring pipeline script associated with the request identifier in the URL.
+    (could be retrieved from the response of a POST request for /graph/monitor), e.g. request sent to */graph/monitor/75df6f8d-3829-4fd8-a3e6-b3e917010141*
 
     * Response:
 
@@ -356,6 +356,28 @@ with **/clmc-service** so that the nginx reverse proxy server (listening on port
         }
         ```
 
+* **GET** ***/graph/monitor/{request_id}*** 
+
+    This API method fetches the status of a graph monitoring pipeline script associated with the request identifier in the URL.
+    (could be retrieved from the response of a POST request for /graph/monitor), e.g. request sent to */graph/monitor/75df6f8d-3829-4fd8-a3e6-b3e917010141*
+
+    * Response:
+    
+        The response of this request is a JSON content, which contains a single output message along with the status of the monitoring pipeline (encountering
+        a 'sleeping' status should be expected, since the background process is executing only once per given delay; a 'zombie' status would indicate that
+        the process is dead).
+
+        Returns a 404 Not Found error if the request ID is not associated with any graph monitoring process.
+    
+    * Response Body Example:
+    
+        ```json
+        {
+          "status": "sleeping",
+          "msg": "Successfully fetched status of graph pipeline process."
+        }
+        ```
+
 * **POST** ***/graph/temporal***
 
     This API method sends a request to the CLMC service to build a graph snapshot in the time range between the *from* and *to* timestamps.
@@ -573,6 +595,11 @@ with **/clmc-service** so that the nginx reverse proxy server (listening on port
         }
         ```
 
+* **PUT** ***/graph/network***
+
+    This API methods provides the same functionality as the *POST /graph/network* API endpoint with the only difference being
+    that the properties (e.g. latency) of any existing links between network nodes that already exist will be updated.
+
 * **DELETE** ***/graph/network***
 
     This API method instructs CLMC to delete the network topology from its graph database.
diff --git a/src/service/VERSION b/src/service/VERSION
index 9790358565b1ca694d3f9a6f7e2f59f2d512b9aa..b842790079f79338cf4a5f7de3efcabd6f25b0b5 100644
--- a/src/service/VERSION
+++ b/src/service/VERSION
@@ -1 +1 @@
-__version__ = "2.0.4"
\ No newline at end of file
+__version__ = "2.1.1"
\ No newline at end of file
diff --git a/src/service/clmcservice/alertsapi/alerts_specification_schema.py b/src/service/clmcservice/alertsapi/alerts_specification_schema.py
index 3db515f5e7ce8be5119edba41664f83829ee706f..022c2a38f3ff62b7cf021ce672333c8c3e230950 100644
--- a/src/service/clmcservice/alertsapi/alerts_specification_schema.py
+++ b/src/service/clmcservice/alertsapi/alerts_specification_schema.py
@@ -45,7 +45,7 @@ SFEMC = "flame_sfemc"  # describes the keyword used for the SFEMC alert handler
 
 # Influx QL functions defined in the documentation https://docs.influxdata.com/influxdb/v1.6/query_language/functions/
 INFLUX_QL_FUNCTIONS = (
-    "count", "mean", "median", "mode", "sum", "first", "last", "max", "min"
+    "count", "mean", "median", "mode", "sum", "first", "last", "max", "min", "spread", "stddev"
 )
 
 # Kapacitor Tick Script template IDs
diff --git a/src/service/clmcservice/graphapi/tests.py b/src/service/clmcservice/graphapi/tests.py
index 7830b4338cd51ebb17dcd97f1d63eb2efffa100a..871dd5e617b302a99fb7fadc21fa7c4c5dba226b 100644
--- a/src/service/clmcservice/graphapi/tests.py
+++ b/src/service/clmcservice/graphapi/tests.py
@@ -23,14 +23,15 @@
 """
 
 from json import dumps, loads
-from signal import SIGKILL
+from psutil import NoSuchProcess, STATUS_SLEEPING
 from unittest.mock import patch, Mock, MagicMock, PropertyMock
 import pytest
 from pyramid import testing
 from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPInternalServerError
 from clmcservice.graphapi.views import GraphAPI
 from clmcservice.models import MonitoringProcess
-from clmcservice.graphapi.conftest import links, sdn_switches, ues, clusters
+from clmcservice.graphapi.conftest import links, sdn_switches, switches, ues, clusters
+from clmcservice.graphapi.utilities import delete_network_graph
 
 
 class TestGraphAPI(object):
@@ -494,22 +495,21 @@ class TestGraphAPI(object):
         mock_response3.status_code = 200
         mock_response3.json.return_value = links
         # we are doing two calls to the API, hence need to repeat the responses
-        http_get_mock.side_effect = [mock_response1, mock_response2, mock_response3, mock_response1, mock_response2, mock_response3]
+        http_get_mock.side_effect = [mock_response1, mock_response2, mock_response3] * 2
 
         # mock the behaviour of reading the clusters and ues mappping from files
         file_open_mock.return_value = MagicMock()  # use magic mock so that special methods (dunders) are auto generated
         # we are doing two calls to the API, hence need to repeat the results
-        json_load_mock.side_effect = [clusters, ues, clusters, ues]
+        json_load_mock.side_effect = [clusters, ues] * 2
 
         assert set([node["name"] for node in graph_db.nodes.match("Cluster")]) == set(), "Cluster nodes must not be created before the build request"
         assert set([node["name"] for node in graph_db.nodes.match("Switch")]) == set(), "Switch nodes must not be created before the build request"
         assert set([node["name"] for node in graph_db.nodes.match("UserEquipment")]) == set(), "UE nodes must not be created before the build request"
 
-        # sent request to build the network topology
+        # sent request to build the network topology through a POST request
         request = testing.DummyRequest()
         response = GraphAPI(request).build_network_topology()
         assert response == {"new_switches_count": 6, "new_clusters_count": 6, "new_ues_count": 3}
-
         assert set([node["name"] for node in graph_db.nodes.match("Cluster")]) == set(["DC" + str(i) for i in range(1, 7)]), "Cluster nodes must have been created"
         assert set([node["name"] for node in graph_db.nodes.match("Switch")]) == set(["127.0.0." + str(i) for i in range(1, 7)]), "Switch nodes must have been created"
         assert set([node["name"] for node in graph_db.nodes.match("UserEquipment")]) == set(["ue" + str(i) for i in (2, 3, 6)]), "UE nodes must have been created"
@@ -518,11 +518,85 @@ class TestGraphAPI(object):
         request = testing.DummyRequest()
         response = GraphAPI(request).build_network_topology()
         assert response == {"new_switches_count": 0, "new_clusters_count": 0, "new_ues_count": 0}
-
         assert set([node["name"] for node in graph_db.nodes.match("Cluster")]) == set(["DC" + str(i) for i in range(1, 7)])
         assert set([node["name"] for node in graph_db.nodes.match("Switch")]) == set(["127.0.0." + str(i) for i in range(1, 7)])
         assert set([node["name"] for node in graph_db.nodes.match("UserEquipment")]) == set(["ue" + str(i) for i in (2, 3, 6)])
 
+        # clean up
+        delete_network_graph(graph_db)
+
+    @patch('clmcservice.graphapi.views.load')
+    @patch('clmcservice.graphapi.views.open')
+    @patch('clmcservice.graphapi.views.get')
+    def test_build_and_update_network(self, http_get_mock, file_open_mock, json_load_mock, db_testing_data):
+        """
+        Tests the functionality to build and update the network graph.
+
+        :param http_get_mock: mocks the HTTP GET function
+        :param file_open_mock: mocks the open file function
+        :param json_load_mock: mocks the JSON load function
+        :param db_testing_data: fixture used to get a reference to the graph DB
+        """
+
+        from_timestamp, to_timestamp, graph_db = db_testing_data  # fixture, used to get reference to the graph DB
+
+        # mock the responses from the sdn controller - 3 GET requests are executed, so we need 3 responses
+        mock_response1 = Mock()
+        mock_response1.status_code = 200
+        mock_response1.json.return_value = sdn_switches
+        mock_response2 = Mock()
+        mock_response2.status_code = 200
+        mock_response2.json.return_value = links
+        mock_response3 = Mock()
+        mock_response3.status_code = 200
+        mock_response3.json.return_value = links
+        # we are doing two calls to the API, hence need to repeat the responses
+        http_get_mock.side_effect = [mock_response1, mock_response2, mock_response3] * 2
+
+        # mock the behaviour of reading the clusters and ues mappping from files
+        file_open_mock.return_value = MagicMock()  # use magic mock so that special methods (dunders) are auto generated
+        # we are doing two calls to the API, hence need to repeat the results
+        json_load_mock.side_effect = [clusters, ues] * 2
+
+        # sent request to build the network topology through a PUT request
+        request = testing.DummyRequest()
+        response = GraphAPI(request).build_and_update_network_topology()
+        assert response == {"new_switches_count": 6, "new_clusters_count": 6, "new_ues_count": 3}
+        assert set([node["name"] for node in graph_db.nodes.match("Cluster")]) == set(["DC" + str(i) for i in range(1, 7)]), "Cluster nodes must have been created"
+        assert set([node["name"] for node in graph_db.nodes.match("Switch")]) == set(["127.0.0." + str(i) for i in range(1, 7)]), "Switch nodes must have been created"
+        assert set([node["name"] for node in graph_db.nodes.match("UserEquipment")]) == set(["ue" + str(i) for i in (2, 3, 6)]), "UE nodes must have been created"
+
+        # fetch the first network link as test link
+        test_link = links[0]
+        test_link_source = switches[test_link["src-switch"]]
+        test_link_destination = switches[test_link["dst-switch"]]
+        test_link_source_node = graph_db.nodes.match("Switch", name=test_link_source).first()
+        test_link_destination_node = graph_db.nodes.match("Switch", name=test_link_destination).first()
+        # assert the nodes exist
+        assert test_link_source_node is not None
+        assert test_link_destination_node is not None
+
+        # confirm the latency value of the edge
+        old_latency = test_link["latency"] / 1000  # convert to seconds
+        test_link_edge = graph_db.relationships.match(nodes=(test_link_source_node, test_link_destination_node), r_type="linkedTo").first()
+        # assert the edge exists
+        assert test_link_edge is not None
+        assert test_link_edge["latency"] == old_latency, "Edge was not created with the correct latency value"
+
+        # update the test link in the SDN controller mock data with a new latency
+        new_latency = 5 * old_latency
+        test_link["latency"] = new_latency * 1000  # convert to milliseconds
+
+        # send the same request again and ensure that the latency of the network edge is updated
+        request = testing.DummyRequest()
+        GraphAPI(request).build_and_update_network_topology()
+
+        graph_db.pull(test_link_edge)
+        assert test_link_edge["latency"] == new_latency, "Edge was not updated with the correct latency value"
+
+        # clean up
+        delete_network_graph(graph_db)
+
     def test_delete_network(self, graph_network_topology, db_testing_data):
         """
         Tests the delete network graph functionality.
@@ -696,12 +770,52 @@ class TestGraphAPI(object):
         assert len(popen_mock.call_args_list) == 2, "No subprocess should be started if the UE nodes list is empty (network topology not built)"
         nodes_matcher_mock.assert_called_with("UserEquipment")  # assert that the graph nodes match function has been called with "UserEquipment" as argument
 
-    @patch('clmcservice.graphapi.views.kill')
-    def test_stop_graph_pipeline(self, mock_kill):
+    @patch('clmcservice.graphapi.views.Process')
+    def test_get_graph_pipeline_status(self, mock_process):
+        """
+        Tests the functionality to fetch the status of a graph monitoring script.
+
+        :param mock_process: mock object to mimic the behavior of the psutil.Process functionality
+        """
+
+        # mock a monitoring process
+        pid = 111
+        reqid = "test_request_id"
+        MonitoringProcess.add({"request_id": reqid, "process_id": pid})
+
+        # test behaviour with not-existing request UUID
+        request = testing.DummyRequest()
+        request.matchdict["request_id"] = "unknown-request-uuid"
+        error_raised = False
+        try:
+            GraphAPI(request).get_graph_pipeline_status()
+        except HTTPNotFound:
+            error_raised = True
+        assert error_raised, "Error must have been raised for unrecognised request UUID."
+
+        # test the behaviour when the PID doesn't exist or another OSError is thrown
+        mock_process.side_effect = NoSuchProcess("error")
+        request = testing.DummyRequest()
+        request.matchdict["request_id"] = reqid
+        response = GraphAPI(request).get_graph_pipeline_status()
+        assert response == {"msg": "Monitoring process has been stopped or killed or terminated before this request was executed."}
+
+        # test behaviour with existing request UUID and existing PID
+        assert MonitoringProcess.exists(reqid)
+        mock_process.side_effect = None
+        mock_process.return_value.status = Mock(return_value=STATUS_SLEEPING)
+        request = testing.DummyRequest()
+        request.matchdict["request_id"] = reqid
+        response = GraphAPI(request).get_graph_pipeline_status()
+        assert response == {"status": STATUS_SLEEPING, "msg": "Successfully fetched status of graph pipeline process."}
+        mock_process.return_value.status.assert_called_with()
+
+    @patch('clmcservice.graphapi.views.Process')
+    def test_stop_graph_pipeline(self, mock_process):
         """
-        Tests the funcitonality to stop a graph monitoring script.
+        Tests the functionality to stop a graph monitoring script.
 
-        :param mock_kill: mock object to mimic the behavior of the os.kill functionality
+        :param mock_process: mock object to mimic the behavior of the psutil.Process functionality
         """
 
         # mock a monitoring process
@@ -720,21 +834,23 @@ class TestGraphAPI(object):
         assert error_raised, "Error must have been raised for unrecognised request UUID."
 
         # test the behaviour when the PID doesn't exist or another OSError is thrown
-        mock_kill.side_effect = OSError("error")
+        mock_process.side_effect = NoSuchProcess("error")
         request = testing.DummyRequest()
         request.matchdict["request_id"] = reqid
         response = GraphAPI(request).stop_graph_pipeline()
-        assert response == {"msg": "Monitoring process has been stopped before this request was executed."}
+        assert response == {"msg": "Monitoring process has been stopped or killed or terminated before this request was executed."}
 
         # test behaviour with existing request UUID and existing PID
         MonitoringProcess.add({"request_id": reqid, "process_id": pid})
         assert MonitoringProcess.exists(reqid)
-        mock_kill.side_effect = None
+        mock_process.side_effect = None
+        mock_process.return_value = Mock()
         request = testing.DummyRequest()
         request.matchdict["request_id"] = reqid
         response = GraphAPI(request).stop_graph_pipeline()
         assert response == {"msg": "Monitoring process has been successfully stopped."}
-        mock_kill.assert_called_with(pid, SIGKILL)  # assert that os.kill was called with termination signal
+        mock_process.return_value.terminate.assert_called_with()
+        mock_process.return_value.wait.assert_called_with(timeout=3)
         assert not MonitoringProcess.exists(reqid), "Request ID must be removed when the process is killed."
 
     @staticmethod
diff --git a/src/service/clmcservice/graphapi/utilities.py b/src/service/clmcservice/graphapi/utilities.py
index 9d8dd6808a5fc16440d65bb128fa6da17fe39f51..df339df1bc8e164e1a45e7262293da7946d138d3 100644
--- a/src/service/clmcservice/graphapi/utilities.py
+++ b/src/service/clmcservice/graphapi/utilities.py
@@ -350,7 +350,7 @@ def delete_temporal_subgraph(graph, subgraph_id):
     return nodes_matched
 
 
-def build_network_graph(graph, switches, links, clusters, ues):
+def build_network_graph(graph, switches, links, clusters, ues, update_existing_links=True):
     """
     A function used to build the network topology in the neo4j graph given the collection of switches, links and clusters.
 
@@ -359,6 +359,7 @@ def build_network_graph(graph, switches, links, clusters, ues):
     :param links: a collection of all switch-to-switch links in the network topology - JSON format, list of objects, each object must have "src-switch", "dst-switch" and "latency" as keys
     :param clusters: a collection of all clusters and the IP address of the service router that they are connected to - mapping between an IP address of a service router and a cluster identifier
     :param ues: a collection of all ues and the IP address of the service router that they are connected to - mapping between an IP address of a servicer router and a ue identifier
+    :param update_existing_links: (defaults to True) a flag to indicate if existing network edges must be updated with the latest latencies
     """
 
     new_switches_count = 0
@@ -391,7 +392,7 @@ def build_network_graph(graph, switches, links, clusters, ues):
 
         # create the link between the two nodes
         edge = find_or_create_edge(graph, "linkedTo", from_node, to_node, latency=latency)
-        if edge["latency"] != latency:
+        if update_existing_links and edge["latency"] != latency:
             log.info("Updating latency for edge {0}, old latency {1}, new latency {2}".format(edge, edge["latency"], latency))
             edge["latency"] = latency  # make sure that the latency is updated if the edge already existed
             graph.push(edge)  # update the relationship in the DB
diff --git a/src/service/clmcservice/graphapi/views.py b/src/service/clmcservice/graphapi/views.py
index e89f32e9376f2b979116f86f8e1f8ca1f21949cd..9232e810ea545259af668de452bcefba99d1088f 100644
--- a/src/service/clmcservice/graphapi/views.py
+++ b/src/service/clmcservice/graphapi/views.py
@@ -34,8 +34,7 @@ from requests import exceptions, get
 from uuid import uuid4
 from json import load, dumps
 from subprocess import Popen
-from os import kill
-from signal import SIGKILL
+from psutil import Process, NoSuchProcess, TimeoutExpired
 from logging import getLogger
 
 
@@ -255,11 +254,32 @@ class GraphAPI(object):
     @view_config(route_name='graph_network_topology', request_method='POST')
     def build_network_topology(self):
         """
-        An API endpoint to build/update the network topology in the neo4j graph.
+        An API endpoint to build the network topology in the neo4j graph, however, without updating the latency of existing edges.
 
         :return: A JSON response with the number of switches, clusters and ues that were built.
         """
 
+        return self._build_network_topology(update_existing_links=False)
+
+    @view_config(route_name='graph_network_topology', request_method='PUT')
+    def build_and_update_network_topology(self):
+        """
+        An API endpoint to build the network topology in the neo4j graph and also update the latency of existing edges.
+
+        :return: A JSON response with the number of switches, clusters and ues that were built.
+        """
+
+        return self._build_network_topology(update_existing_links=True)
+
+    def _build_network_topology(self, update_existing_links):
+        """
+        A utility method used to build the network graph topology with the option of updating existing links.
+
+        :param update_existing_links: a flag to be set to True if existing network links need to be updated
+
+        :return: A dictionary with the number of switches, clusters and ues that were created.
+        """
+
         graph = self.get_graph_reference()
 
         sdn_controller_ip = self.request.registry.settings['sdn_controller_ip']
@@ -307,8 +327,8 @@ class GraphAPI(object):
             ues = {}
 
         # build the network graph and retrieve the number of switch nodes and cluster nodes that were created
-        tmp_switch_count, tmp_clusters_count, tmp_ues_count = build_network_graph(graph, switches, external_links, clusters, ues)
-        switch_count, clusters_count, ues_count = build_network_graph(graph, switches, local_links, clusters, ues)
+        tmp_switch_count, tmp_clusters_count, tmp_ues_count = build_network_graph(graph, switches, external_links, clusters, ues, update_existing_links=update_existing_links)
+        switch_count, clusters_count, ues_count = build_network_graph(graph, switches, local_links, clusters, ues, update_existing_links=update_existing_links)
         switch_count += tmp_switch_count
         clusters_count += tmp_clusters_count
         ues_count += tmp_ues_count
@@ -416,6 +436,32 @@ class GraphAPI(object):
             log.warning("Graph pipeline process for SFC {0} with PID {1} has finished executing unexpectedly with return code {2}".format(sfc, process_pid, process_return_code))
             raise HTTPInternalServerError("An unexpected error occurred while trying to start monitoring graph measurements for service function chain {0}".format(sfc))
 
+    @view_config(route_name='graph_manage_pipeline', request_method='GET')
+    def get_graph_pipeline_status(self):
+        """
+        An API endpoint to get the status of a monitoring graph pipeline script.
+
+        :return: A JSON response with the status of the background process.
+        """
+
+        request_id = self.request.matchdict['request_id']  # get the UUID of the request from the URL
+        process_id = MonitoringProcess.get(request_id)
+
+        if process_id is None:
+            raise HTTPNotFound("A monitoring process with ID {0} couldn't be found.".format(request_id))
+
+        # create a process management class instance
+        try:
+            process_obj = Process(process_id)
+            status = process_obj.status()
+            log.info("Fetching process status with request ID {0} and process ID {1}, status - {2}".format(request_id, process_id, status))
+            response = {"status": status, "msg": "Successfully fetched status of graph pipeline process."}
+        except NoSuchProcess as e:
+            log.warning("Unexpected error occurred when trying to get a process that is registered in the CLMC service, but doesn't exist on the OS - {0}".format(e))
+            response = {"msg": "Monitoring process has been stopped or killed or terminated before this request was executed."}
+
+        return response
+
     @view_config(route_name='graph_manage_pipeline', request_method='DELETE')
     def stop_graph_pipeline(self):
         """
@@ -430,13 +476,19 @@ class GraphAPI(object):
         if process_id is None:
             raise HTTPNotFound("A monitoring process with ID {0} couldn't be found.".format(request_id))
 
+        # create a process management class instance and terminate the process
         try:
-            kill(process_id, SIGKILL)
-            log.info("Successfully stopped process with request ID {0} and process ID {1}".format(request_id, process_id))
+            process_obj = Process(process_id)
+            log.info("Terminating process with request ID {0} and process ID {1}, status before termination - {2}".format(request_id, process_id, process_obj.status()))
+            process_obj.terminate()
+            process_obj.wait(timeout=3)
             response = {"msg": "Monitoring process has been successfully stopped."}
-        except OSError as e:
-            log.warning("Couldn't stop monitoring process with request ID {0} and process ID {1} due to error {2}".format(request_id, process_id, e))
-            response = {"msg": "Monitoring process has been stopped before this request was executed."}
+        except TimeoutExpired as e:
+            log.error("Unexpected error occurred while waiting for a graph pipeline process to terminate - {0}".format(e))
+            raise HTTPInternalServerError("Termination of monitoring process couldn't be completed.")
+        except NoSuchProcess as e:
+            log.warning("Unexpected error occurred when trying to get a process that is registered in the CLMC service, but doesn't exist on the OS - {0}".format(e))
+            response = {"msg": "Monitoring process has been stopped or killed or terminated before this request was executed."}
 
         MonitoringProcess.delete(request_id)
 
diff --git a/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-3.yaml b/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-3.yaml
index 11bcdb2b1413d414ba0cf7e9125453625442e217..b2bbbc28b588516bee49c3485f607a0e8e479382 100644
--- a/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-3.yaml
+++ b/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-3.yaml
@@ -40,7 +40,7 @@ topology_template:
             condition:
               threshold: 5
               granularity: 60
-              aggregation_method: first
+              aggregation_method: spread
               resource_type:
                 flame_sfp: storage
                 flame_sf: storage-users
diff --git a/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-4.yaml b/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-4.yaml
index 25b250c18798f193425960ecc2897649b4343383..b4f8e4a371f42ad628d626ed3f532bb9e7930852 100644
--- a/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-4.yaml
+++ b/src/service/resources/tosca/test-data/clmc-validator/valid/alerts_test_config-4.yaml
@@ -41,7 +41,7 @@ topology_template:
             condition:
               threshold: 5
               granularity: 60
-              aggregation_method: first
+              aggregation_method: stddev
               # resource type missing - optional, so it is valid
               comparison_operator: lte
             action:
diff --git a/src/service/setup.py b/src/service/setup.py
index d1e8ccfd11293fabd1dc17f4eac42e08b6fccea4..10eef05579477ecee93ffeabcf9999fe54128c53 100644
--- a/src/service/setup.py
+++ b/src/service/setup.py
@@ -66,6 +66,7 @@ requires = [
     'tosca-parser==1.1.0',
     'schema==0.6.8',
     'requests==2.21.0',
+    'psutil==5.6.1',
     'pytest==3.8.1'
 ]
 
diff --git a/src/test/VERSION b/src/test/VERSION
index 9790358565b1ca694d3f9a6f7e2f59f2d512b9aa..b842790079f79338cf4a5f7de3efcabd6f25b0b5 100644
--- a/src/test/VERSION
+++ b/src/test/VERSION
@@ -1 +1 @@
-__version__ = "2.0.4"
\ No newline at end of file
+__version__ = "2.1.1"
\ No newline at end of file