Skip to content

Commit b4d382a

Browse files
authored
Merge pull request #86 from MortenSchou/parse-topology-zoo
Add parser for topology zoo .gml files
2 parents e20163e + b97c681 commit b4d382a

File tree

12 files changed

+343
-19
lines changed

12 files changed

+343
-19
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ add_flex_bison_dependency(flexer bparser)
1313
find_package(Boost 1.66 COMPONENTS program_options regex filesystem REQUIRED)
1414
include_directories(${Boost_INCLUDE_DIR})
1515

16-
add_library(aalwines ${HEADER_FILES} aalwines/model/builders/JuniperBuilder.cpp aalwines/model/builders/PRexBuilder.cpp aalwines/model/builders/AalWiNesBuilder.cpp aalwines/model/builders/NetworkParsing.cpp
16+
add_library(aalwines ${HEADER_FILES} aalwines/model/builders/JuniperBuilder.cpp aalwines/model/builders/PRexBuilder.cpp
17+
aalwines/model/builders/AalWiNesBuilder.cpp aalwines/model/builders/NetworkParsing.cpp aalwines/model/builders/TopologyBuilder.cpp
1718
aalwines/model/Router.cpp aalwines/model/RoutingTable.cpp aalwines/model/Query.cpp aalwines/model/Network.cpp
1819
aalwines/model/filter.cpp ${BISON_bparser_OUTPUTS} ${FLEX_flexer_OUTPUTS} aalwines/query/QueryBuilder.cpp
1920
aalwines/engine/Moped.cpp aalwines/utils/coordinate.cpp

src/aalwines/model/Network.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,5 +261,33 @@ namespace aalwines {
261261
network.add_null_router();
262262
return network;
263263
}
264+
Network Network::make_network(const std::vector<std::pair<std::string,Coordinate>>& names, const std::vector<std::vector<std::string>>& links) {
265+
Network network;
266+
std::map<std::string, std::string> _interface_map;
267+
for (size_t i = 0; i < names.size(); ++i) {
268+
auto name = names[i].first;
269+
auto coordinate = names[i].second;
270+
auto router = network.add_router(name, coordinate);
271+
network.insert_interface_to("eg" + std::to_string(0), router);
272+
size_t interface_count = 0;
273+
for (const auto& other : links[i]) {
274+
network.insert_interface_to("in" + std::to_string(interface_count), router);
275+
_interface_map.insert({name + other, ("in" + std::to_string(interface_count))});
276+
++interface_count;
277+
}
278+
}
279+
for (size_t i = 0; i < names.size(); ++i) {
280+
auto name = names[i].first;
281+
for (const auto &other : links[i]) {
282+
auto router1 = network.find_router(name);
283+
assert(router1 != nullptr);
284+
auto router2 = network.find_router(other);
285+
if(router2 == nullptr) continue;
286+
router1->find_interface(_interface_map[name + other])->make_pairing(router2->find_interface(_interface_map[other + name]));
287+
}
288+
}
289+
network.add_null_router();
290+
return network;
291+
}
264292

265293
}

src/aalwines/model/Network.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ namespace aalwines {
9696
void concat_network(Interface *link, Network &&nested_network, Interface *nested_ingoing, RoutingTable::label_t post_label);
9797

9898
static Network make_network(const std::vector<std::string>& names, const std::vector<std::vector<std::string>>& links);
99-
99+
static Network make_network(const std::vector<std::pair<std::string,Coordinate>>& names, const std::vector<std::vector<std::string>>& links);
100100
void print_dot(std::ostream& s) const;
101101
void print_simple(std::ostream& s) const;
102102
void print_json(json_stream& json_output) const;

src/aalwines/model/Router.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ namespace aalwines {
142142
[[nodiscard]] std::string latitude() const {return _coordinate ? std::to_string(_coordinate->latitude()) : ""; };
143143
[[nodiscard]] std::string longitude() const {return _coordinate ? std::to_string(_coordinate->longitude()) : ""; };
144144
[[nodiscard]] std::optional<Coordinate> coordinate() const { return _coordinate; }
145+
void set_coordinate(Coordinate coordinate) { _coordinate.emplace(coordinate); }
146+
145147
private:
146148
size_t _index = std::numeric_limits<size_t>::max();
147149
std::vector<std::string> _names;

src/aalwines/model/builders/NetworkParsing.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,39 @@
2929
#include <aalwines/model/builders/JuniperBuilder.h>
3030
#include <aalwines/model/builders/PRexBuilder.h>
3131
#include <aalwines/model/builders/AalWiNesBuilder.h>
32+
#include <aalwines/model/builders/TopologyBuilder.h>
3233
#include <iostream>
3334

3435
namespace aalwines {
3536

3637
Network NetworkParsing::parse(bool no_warnings) {
37-
if(!json_file.empty() && (!prex_routing.empty() || !prex_topo.empty() || !junos_config.empty()))
38-
{
39-
std::cerr << "--input cannot be used with --junos or --topology or --routing." << std::endl;
38+
39+
if(junos_config.empty() && prex_routing.empty() && prex_topo.empty() && json_file.empty() && topo_zoo.empty()) {
40+
std::cerr << "Either an AalWiNes json configuration, a Junos configuration, a P-Rex configuration or a .gml topology must be given." << std::endl;
4041
exit(-1);
4142
}
4243

43-
if(!junos_config.empty() && (!prex_routing.empty() || !prex_topo.empty()))
44-
{
45-
std::cerr << "--junos cannot be used with --topology or --routing." << std::endl;
44+
if(!json_file.empty() && (!prex_routing.empty() || !prex_topo.empty() || !junos_config.empty() || !topo_zoo.empty())) {
45+
std::cerr << "--input cannot be used with --junos, --topology, --routing or --gml." << std::endl;
4646
exit(-1);
4747
}
4848

49-
if(prex_routing.empty() != prex_topo.empty())
50-
{
51-
std::cerr << "Both --topology and --routing have to be non-empty." << std::endl;
49+
if(!junos_config.empty() && (!prex_routing.empty() || !prex_topo.empty() || !topo_zoo.empty())) {
50+
std::cerr << "--junos cannot be used with --topology or --routing or --gml." << std::endl;
51+
exit(-1);
52+
}
53+
54+
if(!topo_zoo.empty() && (!prex_routing.empty() || !prex_topo.empty())) {
55+
std::cerr << "--gml cannot be used with --topology or --routing." << std::endl;
5256
exit(-1);
5357
}
5458

55-
if(junos_config.empty() && prex_routing.empty() && prex_topo.empty() && json_file.empty())
56-
{
57-
std::cerr << "Either a Junos configuration or a P-Rex configuration or an AalWiNes json configuration must be given." << std::endl;
59+
if(prex_routing.empty() != prex_topo.empty()) {
60+
std::cerr << "Both --topology and --routing have to be non-empty." << std::endl;
5861
exit(-1);
5962
}
6063

61-
if(skip_pfe && junos_config.empty())
62-
{
64+
if(skip_pfe && junos_config.empty()) {
6365
std::cerr << "--skip-pfe is only avaliable for --junos configurations." << std::endl;
6466
exit(-1);
6567
}
@@ -68,8 +70,9 @@ namespace aalwines {
6870
std::ostream& warnings = no_warnings ? dummy : std::cerr;
6971

7072
parsing_stopwatch.start();
71-
auto network = junos_config.empty() ? (json_file.empty() ?
73+
auto network = junos_config.empty() ? (json_file.empty() ? (topo_zoo.empty() ?
7274
PRexBuilder::parse(prex_topo, prex_routing, warnings) :
75+
TopologyBuilder::parse(topo_zoo, warnings)) :
7376
AalWiNesBuilder::parse(json_file, warnings)) :
7477
JuniperBuilder::parse(junos_config, warnings, skip_pfe);
7578
parsing_stopwatch.stop();

src/aalwines/model/builders/NetworkParsing.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace aalwines {
5151
"An xml-file defining the topology in the P-Rex format")
5252
("routing", po::value<std::string>(&prex_routing),
5353
"An xml-file defining the routing in the P-Rex format")
54+
("gml", po::value<std::string>(&topo_zoo),"A gml-file defining the topology in the format from topology zoo")
5455
("skip-pfe", po::bool_switch(&skip_pfe),
5556
"Skip \"indirect\" cases of juniper-routing as package-drops (compatability with P-Rex semantics).")
5657
;
@@ -61,7 +62,7 @@ namespace aalwines {
6162
Network parse(bool no_warnings = false);
6263

6364
private:
64-
std::string json_file, junos_config, prex_topo, prex_routing;
65+
std::string json_file, junos_config, prex_topo, prex_routing, topo_zoo;
6566
bool skip_pfe = false;
6667
po::options_description input;
6768
stopwatch parsing_stopwatch{false};
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License as published by
4+
* the Free Software Foundation, either version 3 of the License, or
5+
* (at your option) any later version.
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* GNU General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU General Public License
13+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
*/
15+
16+
/*
17+
* Copyright Dan Kristiansen and Morten K. Schou
18+
*/
19+
20+
/*
21+
* File: TopologyBuilder.cpp
22+
* Author: Dan Kristiansen
23+
*
24+
* Created on 04-06-2020.
25+
*/
26+
27+
#include <fstream>
28+
#include <sstream>
29+
#include "TopologyBuilder.h"
30+
31+
namespace aalwines {
32+
33+
// Stolen from: https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
34+
// trim from start (in place)
35+
static inline void ltrim(std::string &s) {
36+
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
37+
return !std::isspace(ch);
38+
}));
39+
}
40+
// trim from end (in place)
41+
static inline void rtrim(std::string &s) {
42+
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
43+
return !std::isspace(ch);
44+
}).base(), s.end());
45+
}
46+
// trim from both ends (in place)
47+
static inline void trim(std::string &s) {
48+
ltrim(s);
49+
rtrim(s);
50+
}
51+
52+
Network aalwines::TopologyBuilder::parse(const std::string &gml, std::ostream& warnings) {
53+
std::ifstream file(gml);
54+
55+
if(not file){
56+
std::cerr << "Error file not found." << std::endl;
57+
}
58+
59+
std::vector<std::pair<size_t, size_t >> _all_links;
60+
std::vector<std::pair<std::string, Coordinate>> _all_routers;
61+
std::map<const size_t, std::string> _router_map;
62+
std::vector<std::vector<std::string>> _return_links;
63+
64+
std::string str;
65+
while (std::getline(file, str, ' ')) {
66+
if(str =="node") {
67+
size_t id;
68+
std::string router_name;
69+
double latitude = 0;
70+
double longitude = 0;
71+
while (std::getline(file, str)){
72+
trim(str);
73+
auto pos = str.find(' ');
74+
if (pos == str.size()) continue;
75+
std::string key = str.substr(0, pos);
76+
if(key == "id"){
77+
id = std::stoul(str.substr(pos+1));
78+
}
79+
else if(key == "label"){
80+
router_name = str.substr(pos+2, str.size() - pos - 3);
81+
trim(router_name);
82+
//Get_interface dont handle ' ' very well
83+
std::replace(router_name.begin(), router_name.end(), ' ', '_');
84+
router_name.erase(std::remove_if(router_name.begin(), router_name.end(),
85+
[](auto const& c) -> bool { return !std::isalnum(c) && c != '_' && c != '-'; }), router_name.end());
86+
}
87+
else if(key == "Latitude"){
88+
latitude = std::stod(str.substr(pos+1));
89+
}
90+
else if(key == "Longitude"){
91+
longitude = std::stod(str.substr(pos+1));
92+
}
93+
else if(key == "]"){
94+
bool indexer_active = false;
95+
while (std::find_if( _all_routers.begin(), _all_routers.end(),
96+
[&]( std::pair<std::string,Coordinate> &item ) { return item.first == router_name; } )
97+
!= _all_routers.end()) {
98+
if (indexer_active){
99+
if ((int) router_name[router_name.size()-1]++ - 48 > 5) assert(false);
100+
} else {
101+
router_name += "2";
102+
indexer_active = true;
103+
}
104+
}
105+
assert(latitude != 0 or longitude != 0);
106+
_all_routers.emplace_back(router_name, Coordinate{latitude, longitude});
107+
_router_map.insert({id, router_name});
108+
_return_links.emplace_back();
109+
break;
110+
}
111+
}
112+
}
113+
else if(str == "edge"){
114+
size_t source;
115+
size_t target;
116+
while (std::getline(file, str, ' ')) {
117+
if (str == "source") {
118+
file >> source;
119+
} else if (str == "target") {
120+
file >> target;
121+
_all_links.emplace_back(source, target);
122+
break;
123+
}
124+
}
125+
}
126+
}
127+
128+
for(size_t i = 0; i < _all_routers.size(); i++){
129+
for(auto& link : _all_links){
130+
if(link.first == i){
131+
_return_links[i].emplace_back(_router_map[link.second]);
132+
_return_links[link.second].emplace_back(_router_map[link.first]);
133+
}
134+
}
135+
}
136+
return Network::make_network(_all_routers, _return_links);
137+
}
138+
139+
inline json to_json_no_routing(const Router& router) {
140+
auto j = json::object();
141+
j["names"] = router.names();
142+
j["interfaces"] = json::array();
143+
for (const auto& interface : router.interfaces()) {
144+
auto j_i = json::object();
145+
j_i["name"] = interface->get_name();
146+
j_i["routing_table"] = json::object(); // Only topology, so empty routing_table in this mode.
147+
j["interfaces"].push_back(j_i);
148+
}
149+
if (router.coordinate()) {
150+
j["location"] = router.coordinate().value();
151+
}
152+
return j;
153+
}
154+
155+
json TopologyBuilder::json_topology(const Network& network, bool no_routing) {
156+
auto j = json::object();
157+
j["name"] = network.name;
158+
j["routers"] = json::array();
159+
for (const auto& router : network.routers()) {
160+
if (router->is_null()) continue;
161+
if (no_routing) {
162+
j["routers"].push_back(to_json_no_routing(*router));
163+
} else {
164+
j["routers"].push_back(router);
165+
}
166+
}
167+
auto links_j = json::array();
168+
for (const auto& interface : network.all_interfaces()){
169+
if (interface->match() == nullptr) continue; // Not connected
170+
if (interface->source()->is_null() || interface->target()->is_null()) continue; // Skip the NULL router
171+
assert(interface->match()->match() == interface);
172+
if (interface->global_id() > interface->match()->global_id()) continue; // Already covered by bidirectional link the other way.
173+
174+
auto link_j = json::object();
175+
link_j["from_interface"] = interface->get_name();
176+
link_j["from_router"] = interface->source()->name();
177+
link_j["to_interface"] = interface->match()->get_name();
178+
link_j["to_router"] = interface->target()->name();
179+
link_j["bidirectional"] = true;
180+
links_j.push_back(link_j);
181+
}
182+
j["links"] = links_j;
183+
return j;
184+
}
185+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify
3+
* it under the terms of the GNU General Public License as published by
4+
* the Free Software Foundation, either version 3 of the License, or
5+
* (at your option) any later version.
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* GNU General Public License for more details.
11+
*
12+
* You should have received a copy of the GNU General Public License
13+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
14+
*/
15+
16+
/*
17+
* Copyright Dan Kristiansen
18+
*/
19+
20+
/*
21+
* File: TopologyBuilder.h
22+
* Author: Dan Kristiansen
23+
*
24+
* Created on 04-06-2020.
25+
*/
26+
27+
#ifndef AALWINES_TOPOLOGYBUILDER_H
28+
#define AALWINES_TOPOLOGYBUILDER_H
29+
30+
#include <aalwines/model/Network.h>
31+
#include <aalwines/model/builders/AalWiNesBuilder.h>
32+
#include <utility>
33+
34+
namespace aalwines {
35+
36+
class TopologyBuilder {
37+
public:
38+
39+
// Parses a network topology in the gml format used by Topology Zoo.
40+
static Network parse(const std::string& gml, std::ostream& warnings = std::cerr);
41+
42+
// Extracts the topology (assuming all links are bidirectional) from the network into the json format.
43+
// Note: The standard to_json function uses non-empty routing-tables to determine existence of links (and direction).
44+
static json json_topology(const aalwines::Network& network, bool no_routing = true);
45+
46+
};
47+
}
48+
49+
50+
#endif //AALWINES_TOPOLOGYBUILDER_H

src/aalwines/utils/coordinate.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,15 @@ namespace aalwines {
4141
void Coordinate::write_xml_attributes(std::ostream& s) const {
4242
s << " latitude=\"" << _latitude << "\" longitude=\"" << _longitude << "\" ";
4343
}
44+
Coordinate Coordinate::mid_point(const std::vector<Coordinate>& coordinates) {
45+
// Not sure if this is actually a good mid-point, but it suffices for now.
46+
double lat = 0;
47+
double lon = 0;
48+
for (const auto& c : coordinates) {
49+
lat += c.latitude();
50+
lon += c.longitude();
51+
}
52+
return Coordinate(lat/coordinates.size(), lon/coordinates.size());
53+
}
4454
}
4555

0 commit comments

Comments
 (0)