diff --git a/src/service/clmcservice/graphapi/conftest.py b/src/service/clmcservice/graphapi/conftest.py index 105bc29efb011c683a6980ca46f6385d547b5384..3e81cbf30734f3a778d1fca0b6d1c1fdeeb5bced 100644 --- a/src/service/clmcservice/graphapi/conftest.py +++ b/src/service/clmcservice/graphapi/conftest.py @@ -82,6 +82,12 @@ clusters = { "127.0.0.6": "DC6" } +ues = { + "127.0.0.1": "ue1", + "127.0.0.3": "ue3", + "127.0.0.6": "ue6" +} + @pytest.fixture(scope='module', autouse=True) def db_testing_data(): @@ -105,8 +111,8 @@ def db_testing_data(): # create the physical infrastructure subgraph dbs = influx.get_list_database() - switch_count, cluster_count = build_network_graph(graph, switches, links, clusters) - assert switch_count == 6 and cluster_count == 6, "Network graph build failure" + switch_count, cluster_count, ues_count = build_network_graph(graph, switches, links, clusters, ues) + assert switch_count == 6 and cluster_count == 6 and ues_count == 3, "Network graph build failure" # check if exists ( if so, clear ) or create the test DB in influx if test_db_name in dbs: diff --git a/src/service/clmcservice/graphapi/tests.py b/src/service/clmcservice/graphapi/tests.py index 021877ef215899e6a13a1debb66808e6a853fc67..4b8361659b4f2100e2c04dd1cb265eb71b4ec9f7 100644 --- a/src/service/clmcservice/graphapi/tests.py +++ b/src/service/clmcservice/graphapi/tests.py @@ -106,6 +106,9 @@ class TestGraphAPI(object): from_timestamp, to_timestamp, graph_db = db_testing_data + ue_nodes = set([node["name"] for node in graph_db.nodes.match("UserEquipment")]) + assert ue_nodes == set("ue" + str(i) for i in [1, 3, 6]), "UE nodes have not been created" + dc_nodes = set([node["name"] for node in graph_db.nodes.match("Cluster")]) assert dc_nodes == set("DC" + str(i) for i in range(1, 7)), "Compute nodes must have been created by the db_testing_data fixture" @@ -457,7 +460,7 @@ class TestGraphAPI(object): request = testing.DummyRequest() response = GraphAPI(request).delete_network_topology() - assert response == {"deleted_switches_count": 6, "deleted_clusters_count": 6} + assert response == {"deleted_switches_count": 6, "deleted_clusters_count": 6, "deleted_ues_count": 3} @staticmethod def check_exist_relationship(relationships_tuple, graph, uuid): diff --git a/src/service/clmcservice/graphapi/utilities.py b/src/service/clmcservice/graphapi/utilities.py index 292549ef86bece87c69eb6bf030710e845d63f1e..48e3b649e2b263bcb1fbe968485b3da516561d81 100644 --- a/src/service/clmcservice/graphapi/utilities.py +++ b/src/service/clmcservice/graphapi/utilities.py @@ -290,7 +290,7 @@ def delete_temporal_subgraph(graph, subgraph_id): return nodes_matched -def build_network_graph(graph, switches, links, clusters): +def build_network_graph(graph, switches, links, clusters, ues): """ A function used to build the network topology in the neo4j graph given the collection of switches, links and clusters. @@ -298,10 +298,13 @@ def build_network_graph(graph, switches, links, clusters): :param switches: a collection of all switches in the topology - mapping between the DPID of the switch and its IP address :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 """ new_switches_count = 0 new_clusters_count = 0 + new_ues_count = 0 + for link in links: # get the DPID of the source switch source = link["src-switch"] @@ -329,23 +332,41 @@ def build_network_graph(graph, switches, links, clusters): # create the link between the two nodes find_or_create_edge(graph, "linkedTo", from_node, to_node, latency=latency) - # check whether the source service router connects a particular cluster - if source in clusters: - cluster_name = clusters[source] - cluster_node, created = find_or_create_node(graph, "Cluster", return_created=True, name=cluster_name) - if created: - new_clusters_count += 1 - find_or_create_edge(graph, "linkedTo", cluster_node, from_node, latency=0) + # check whether the source service router connects a particular cluster or a particular UE + if create_node_from_mapping(graph, from_node, source, clusters, "Cluster"): + new_clusters_count += 1 + if create_node_from_mapping(graph, from_node, source, ues, "UserEquipment"): + new_ues_count += 1 + + # check whether the destination service router connects a particular cluster or a particular UE + if create_node_from_mapping(graph, to_node, destination, clusters, "Cluster"): + new_clusters_count += 1 + if create_node_from_mapping(graph, to_node, destination, ues, "UserEquipment"): + new_ues_count += 1 + + return new_switches_count, new_clusters_count, new_ues_count + - # check whether the destination service router connects a particular cluster - if destination in clusters: - cluster_name = clusters[destination] - cluster_node, created = find_or_create_node(graph, "Cluster", return_created=True, name=cluster_name) - if created: - new_clusters_count += 1 - find_or_create_edge(graph, "linkedTo", cluster_node, to_node, latency=0) +def create_node_from_mapping(graph, node, node_ip, mapping, new_node_type): + """ + Creates an additional node of a given type if a mapping from a switch node is found. + + :param graph: the neo4j graph database client + :param node: the original node + :param node_ip: the original node's IP address + :param mapping: the mapping object (dictionary from IP address to identifier) + :param new_node_type: the type of the new node to be created - return new_switches_count, new_clusters_count + :return: True if new node was created and False otherwise + """ + + if node_ip in mapping: + new_node_name = mapping[node_ip] + new_node, created = find_or_create_node(graph, new_node_type, return_created=True, name=new_node_name) + find_or_create_edge(graph, "linkedTo", new_node, node, latency=0) + return created + + return False def delete_network_graph(graph): @@ -359,21 +380,28 @@ def delete_network_graph(graph): log.info("Deleting Switch nodes.".format()) subgraph = graph.nodes.match("Switch") - deleted_switches = 0 + deleted_switches = len(subgraph) for node in subgraph: graph.delete(node) - deleted_switches += 1 log.info("Deleted {0} Switch nodes.".format(deleted_switches)) log.info("Deleting Cluster nodes.") subgraph = graph.nodes.match("Cluster") - deleted_clusters = 0 + deleted_clusters = len(subgraph) for node in subgraph: graph.delete(node) - deleted_clusters += 1 log.info("Deleted {0} Cluster nodes.".format(deleted_clusters)) - return deleted_switches, deleted_clusters + log.info("Deleting UserEquipment nodes.") + + subgraph = graph.nodes.match("UserEquipment") + deleted_ues = len(subgraph) + for node in subgraph: + graph.delete(node) + + log.info("Deleted {0} UserEquipment nodes.".format(deleted_clusters)) + + return deleted_switches, deleted_clusters, deleted_ues diff --git a/src/service/clmcservice/graphapi/views.py b/src/service/clmcservice/graphapi/views.py index 21f218c00e914bd4136b5b7f601bcca6b2ce417a..5bb004ceb6980b85b0768ec552c52e2179ebe324 100644 --- a/src/service/clmcservice/graphapi/views.py +++ b/src/service/clmcservice/graphapi/views.py @@ -302,22 +302,33 @@ class GraphAPI(object): raise HTTPNotImplemented("The SDN controller failed to return a valid JSON response when querying for the network topology.") # TODO this is a temporary solution - currently the service router to clusters mapping is read from a file (which must be manually prepared beforehand) - clusters_file = self.request.registry.settings["network_configuration_path"] + clusters_file = self.request.registry.settings["network_clusters_path"] try: with open(clusters_file) as fh: clusters = load(fh) except Exception as e: log.error("Unexpected error: {0}".format(e)) - log.error("No service_router-to-cluster mapping was found while building the network topology.") + log.error("No service-router-to-cluster mapping was found while building the network topology.") clusters = {} + # TODO this is a temporary solution - currently the service router to ues mapping is read from a file (which must be manually prepared beforehand) + 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 ue-to-cluster 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 - tmp_switch_count, tmp_clusters_count = build_network_graph(graph, switches, external_links, clusters) - switch_count, clusters_count = build_network_graph(graph, switches, local_links, clusters) + 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) switch_count += tmp_switch_count clusters_count += tmp_clusters_count + ues_count += tmp_ues_count - return {"new_switches_count": switch_count, "new_clusters_count": clusters_count} + return {"new_switches_count": switch_count, "new_clusters_count": clusters_count, "new_ues_count": ues_count} @view_config(route_name='graph_network_topology', request_method='DELETE') def delete_network_topology(self): @@ -329,6 +340,6 @@ class GraphAPI(object): graph = Graph(host=self.request.registry.settings['neo4j_host'], password=self.request.registry.settings['neo4j_password']) # connect to the neo4j graph db - deleted_switches, deleted_clusters = delete_network_graph(graph) + deleted_switches, deleted_clusters, deleted_ues = delete_network_graph(graph) - return {"deleted_switches_count": deleted_switches, "deleted_clusters_count": deleted_clusters} + return {"deleted_switches_count": deleted_switches, "deleted_clusters_count": deleted_clusters, "deleted_ues_count": deleted_ues} diff --git a/src/service/development.ini b/src/service/development.ini index 1c35584801cc819105628fd140f3436281063b4b..e132f45f6e063f977e0decb8f93ed3bfbde1c25d 100644 --- a/src/service/development.ini +++ b/src/service/development.ini @@ -15,7 +15,9 @@ pyramid.includes = pyramid_debugtoolbar pyramid_exclog exclog.ignore = -network_configuration_path = /opt/clmc/src/service/resources/GraphAPI/network_clusters.json +network_clusters_path = /opt/clmc/src/service/resources/GraphAPI/network_clusters.json +network_ues_path = /opt/clmc/src/service/resources/GraphAPI/network_ues.json + # 10000 Mb/s = 10 Gb/s network_bandwidth = 10000 diff --git a/src/service/production.ini b/src/service/production.ini index 4dda4389de3500217972097d4e9d394ba22582d6..c11a6346ac8553954538356f5e1c261a94bc3a74 100644 --- a/src/service/production.ini +++ b/src/service/production.ini @@ -15,7 +15,9 @@ pyramid.includes = pyramid_exclog exclog.ignore = -network_configuration_path = /opt/clmc/src/service/resources/GraphAPI/network_clusters.json +network_clusters_path = /opt/clmc/src/service/resources/GraphAPI/network_clusters.json +network_ues_path = /opt/clmc/src/service/resources/GraphAPI/network_ues.json + # 10000 Mb/s = 10 Gb/s network_bandwidth = 10000 diff --git a/src/service/resources/GraphAPI/network_ues.json b/src/service/resources/GraphAPI/network_ues.json new file mode 100644 index 0000000000000000000000000000000000000000..40c1c973a03afcf29a8c3b2f42b9fdac85277831 --- /dev/null +++ b/src/service/resources/GraphAPI/network_ues.json @@ -0,0 +1,6 @@ +{ + "172.20.231.3": "ue20", + "172.20.231.22": "ue22", + "172.20.231.7": "ue23", + "172.20.231.19": "ue24" +} \ No newline at end of file