blob: 7160dabbc191970f9f2258bc459a9d636e737f77 [file] [log] [blame]
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011-2013 University of California, Los Angeles
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
* Ilya Moiseenko <iliamo@cs.ucla.edu>
*/
#include "annotated-topology-reader.hpp"
#include "ns3/nstime.h"
#include "ns3/log.h"
#include "ns3/assert.h"
#include "ns3/names.h"
#include "ns3/net-device-container.h"
#include "ns3/point-to-point-helper.h"
#include "ns3/point-to-point-net-device.h"
#include "ns3/internet-stack-helper.h"
#include "ns3/ipv4-address-helper.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/drop-tail-queue.h"
#include "ns3/ipv4-interface.h"
#include "ns3/ipv4.h"
#include "ns3/string.h"
#include "ns3/pointer.h"
#include "ns3/uinteger.h"
#include "ns3/ipv4-address.h"
#include "ns3/ndn-l3-protocol.hpp"
#include "ns3/ndn-face.hpp"
#include "ns3/random-variable.h"
#include "ns3/error-model.h"
#include "ns3/constant-position-mobility-model.h"
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <set>
#ifdef NS3_MPI
#include <ns3/mpi-interface.h>
#endif
using namespace std;
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("AnnotatedTopologyReader");
AnnotatedTopologyReader::AnnotatedTopologyReader (const std::string &path, double scale/*=1.0*/)
: m_path (path)
, m_randX (0, 100.0)
, m_randY (0, 100.0)
, m_scale (scale)
, m_requiredPartitions (1)
{
NS_LOG_FUNCTION (this);
SetMobilityModel ("ns3::ConstantPositionMobilityModel");
}
void
AnnotatedTopologyReader::SetBoundingBox (double ulx, double uly, double lrx, double lry)
{
NS_LOG_FUNCTION (this << ulx << uly << lrx << lry);
m_randX = UniformVariable (ulx, lrx);
m_randY = UniformVariable (uly, lry);
}
void
AnnotatedTopologyReader::SetMobilityModel (const std::string &model)
{
NS_LOG_FUNCTION (this << model);
m_mobilityFactory.SetTypeId (model);
}
AnnotatedTopologyReader::~AnnotatedTopologyReader ()
{
NS_LOG_FUNCTION (this);
}
Ptr<Node>
AnnotatedTopologyReader::CreateNode (const std::string name, uint32_t systemId)
{
NS_LOG_FUNCTION (this << name);
m_requiredPartitions = std::max (m_requiredPartitions, systemId + 1);
Ptr<Node> node = CreateObject<Node> (systemId);
Names::Add (m_path, name, node);
m_nodes.Add (node);
return node;
}
Ptr<Node>
AnnotatedTopologyReader::CreateNode (const std::string name, double posX, double posY, uint32_t systemId)
{
NS_LOG_FUNCTION (this << name << posX << posY);
m_requiredPartitions = std::max (m_requiredPartitions, systemId + 1);
Ptr<Node> node = CreateObject<Node> (systemId);
Ptr<MobilityModel> loc = DynamicCast<MobilityModel> (m_mobilityFactory.Create ());
node->AggregateObject (loc);
loc->SetPosition (Vector (posX, posY, 0));
Names::Add (m_path, name, node);
m_nodes.Add (node);
return node;
}
NodeContainer
AnnotatedTopologyReader::GetNodes () const
{
return m_nodes;
}
const std::list<TopologyReader::Link>&
AnnotatedTopologyReader::GetLinks () const
{
return m_linksList;
}
NodeContainer
AnnotatedTopologyReader::Read (void)
{
ifstream topgen;
topgen.open (GetFileName ().c_str ());
if ( !topgen.is_open () || !topgen.good () )
{
NS_FATAL_ERROR ("Cannot open file " << GetFileName () << " for reading");
return m_nodes;
}
while (!topgen.eof ())
{
string line;
getline (topgen, line);
if (line == "router") break;
}
if (topgen.eof ())
{
NS_FATAL_ERROR ("Topology file " << GetFileName () << " does not have \"router\" section");
return m_nodes;
}
while (!topgen.eof ())
{
string line;
getline (topgen,line);
if (line[0] == '#') continue; // comments
if (line=="link") break; // stop reading nodes
istringstream lineBuffer (line);
string name, city;
double latitude = 0, longitude = 0;
uint32_t systemId = 0;
lineBuffer >> name >> city >> latitude >> longitude >> systemId;
if (name.empty ()) continue;
Ptr<Node> node;
if (abs(latitude) > 0.001 && abs(latitude) > 0.001)
node = CreateNode (name, m_scale*longitude, -m_scale*latitude, systemId);
else
{
UniformVariable var (0,200);
node = CreateNode (name, var.GetValue (), var.GetValue (), systemId);
// node = CreateNode (name, systemId);
}
}
map<string, set<string> > processedLinks; // to eliminate duplications
if (topgen.eof ())
{
NS_LOG_ERROR ("Topology file " << GetFileName () << " does not have \"link\" section");
return m_nodes;
}
// SeekToSection ("link");
while (!topgen.eof ())
{
string line;
getline (topgen,line);
if (line == "") continue;
if (line[0] == '#') continue; // comments
// NS_LOG_DEBUG ("Input: [" << line << "]");
istringstream lineBuffer (line);
string from, to, capacity, metric, delay, maxPackets, lossRate;
lineBuffer >> from >> to >> capacity >> metric >> delay >> maxPackets >> lossRate;
if (processedLinks[to].size () != 0 &&
processedLinks[to].find (from) != processedLinks[to].end ())
{
continue; // duplicated link
}
processedLinks[from].insert (to);
Ptr<Node> fromNode = Names::Find<Node> (m_path, from);
NS_ASSERT_MSG (fromNode != 0, from << " node not found");
Ptr<Node> toNode = Names::Find<Node> (m_path, to);
NS_ASSERT_MSG (toNode != 0, to << " node not found");
Link link (fromNode, from, toNode, to);
link.SetAttribute ("DataRate", capacity);
link.SetAttribute ("OSPF", metric);
if (!delay.empty ())
link.SetAttribute ("Delay", delay);
if (!maxPackets.empty ())
link.SetAttribute ("MaxPackets", maxPackets);
// Saran Added lossRate
if (!lossRate.empty ())
link.SetAttribute ("LossRate", lossRate);
AddLink (link);
NS_LOG_DEBUG ("New link " << from << " <==> " << to << " / " << capacity << " with " << metric << " metric (" << delay << ", " << maxPackets << ", " << lossRate << ")");
}
NS_LOG_INFO ("Annotated topology created with " << m_nodes.GetN () << " nodes and " << LinksSize () << " links");
topgen.close ();
ApplySettings ();
return m_nodes;
}
void
AnnotatedTopologyReader::AssignIpv4Addresses (Ipv4Address base)
{
Ipv4AddressHelper address (base, Ipv4Mask ("/24"));
BOOST_FOREACH (const Link &link, m_linksList)
{
address.Assign (NetDeviceContainer (link.GetFromNetDevice (),
link.GetToNetDevice ()));
base = Ipv4Address (base.Get () + 256);
address.SetBase (base, Ipv4Mask ("/24"));
}
}
void
AnnotatedTopologyReader::ApplyOspfMetric ()
{
BOOST_FOREACH (const Link &link, m_linksList)
{
NS_LOG_DEBUG ("OSPF: " << link.GetAttribute ("OSPF"));
uint16_t metric = boost::lexical_cast<uint16_t> (link.GetAttribute ("OSPF"));
{
Ptr<Ipv4> ipv4 = link.GetFromNode ()->GetObject<Ipv4> ();
if (ipv4 != 0)
{
int32_t interfaceId = ipv4->GetInterfaceForDevice (link.GetFromNetDevice ());
NS_ASSERT (interfaceId >= 0);
ipv4->SetMetric (interfaceId,metric);
}
Ptr<ndn::L3Protocol> ndn = link.GetFromNode ()->GetObject<ndn::L3Protocol> ();
if (ndn != 0)
{
Ptr<ndn::Face> face = ndn->GetFaceByNetDevice (link.GetFromNetDevice ());
NS_ASSERT (face != 0);
face->SetMetric (metric);
}
}
{
Ptr<Ipv4> ipv4 = link.GetToNode ()->GetObject<Ipv4> ();
if (ipv4 != 0)
{
int32_t interfaceId = ipv4->GetInterfaceForDevice (link.GetToNetDevice ());
NS_ASSERT (interfaceId >= 0);
ipv4->SetMetric (interfaceId,metric);
}
Ptr<ndn::L3Protocol> ndn = link.GetToNode ()->GetObject<ndn::L3Protocol> ();
if (ndn != 0)
{
Ptr<ndn::Face> face = ndn->GetFaceByNetDevice (link.GetToNetDevice ());
NS_ASSERT (face != 0);
face->SetMetric (metric);
}
}
}
}
void
AnnotatedTopologyReader::ApplySettings ()
{
#ifdef NS3_MPI
if (MpiInterface::IsEnabled () &&
MpiInterface::GetSize () != m_requiredPartitions)
{
std::cerr << "MPI interface is enabled, but number of partitions (" << MpiInterface::GetSize ()
<< ") is not equal to number of partitions in the topology (" << m_requiredPartitions << ")";
exit (-1);
}
#endif
PointToPointHelper p2p;
BOOST_FOREACH (Link &link, m_linksList)
{
// cout << "Link: " << Findlink.GetFromNode () << ", " << link.GetToNode () << endl;
string tmp;
////////////////////////////////////////////////
if (link.GetAttributeFailSafe ("MaxPackets", tmp))
{
NS_LOG_INFO ("MaxPackets = " + link.GetAttribute ("MaxPackets"));
try
{
uint32_t maxPackets = boost::lexical_cast<uint32_t> (link.GetAttribute ("MaxPackets"));
// compatibility mode. Only DropTailQueue is supported
p2p.SetQueue ("ns3::DropTailQueue",
"MaxPackets", UintegerValue (maxPackets));
}
catch (...)
{
typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer;
tokenizer tok (link.GetAttribute ("MaxPackets"));
tokenizer::iterator token = tok.begin ();
p2p.SetQueue (*token);
for (token ++; token != tok.end (); token ++)
{
boost::escaped_list_separator<char> separator ('\\', '=', '\"');
tokenizer attributeTok (*token, separator);
tokenizer::iterator attributeToken = attributeTok.begin ();
string attribute = *attributeToken;
attributeToken++;
if (attributeToken == attributeTok.end ())
{
NS_LOG_ERROR ("Queue attribute [" << *token << "] should be in form <Attribute>=<Value>");
continue;
}
string value = *attributeToken;
p2p.SetQueueAttribute (attribute, StringValue (value));
}
}
}
if (link.GetAttributeFailSafe ("DataRate", tmp))
{
NS_LOG_INFO ("DataRate = " + link.GetAttribute("DataRate"));
p2p.SetDeviceAttribute ("DataRate", StringValue (link.GetAttribute ("DataRate")));
}
if (link.GetAttributeFailSafe ("Delay", tmp))
{
NS_LOG_INFO ("Delay = " + link.GetAttribute("Delay"));
p2p.SetChannelAttribute ("Delay", StringValue (link.GetAttribute ("Delay")));
}
NetDeviceContainer nd = p2p.Install(link.GetFromNode (), link.GetToNode ());
link.SetNetDevices (nd.Get (0), nd.Get (1));
////////////////////////////////////////////////
if (link.GetAttributeFailSafe ("LossRate", tmp))
{
NS_LOG_INFO ("LinkError = " + link.GetAttribute("LossRate"));
typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer;
tokenizer tok (link.GetAttribute("LossRate"));
tokenizer::iterator token = tok.begin ();
ObjectFactory factory (*token);
for (token ++; token != tok.end (); token ++)
{
boost::escaped_list_separator<char> separator ('\\', '=', '\"');
tokenizer attributeTok (*token, separator);
tokenizer::iterator attributeToken = attributeTok.begin ();
string attribute = *attributeToken;
attributeToken++;
if (attributeToken == attributeTok.end ())
{
NS_LOG_ERROR ("ErrorModel attribute [" << *token << "] should be in form <Attribute>=<Value>");
continue;
}
string value = *attributeToken;
factory.Set (attribute, StringValue (value));
}
nd.Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (factory.Create<ErrorModel> ()));
nd.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (factory.Create<ErrorModel> ()));
}
}
}
void
AnnotatedTopologyReader::SaveTopology (const std::string &file)
{
ofstream os (file.c_str (), ios::trunc);
os << "# any empty lines and lines starting with '#' symbol is ignored\n"
<< "\n"
<< "# The file should contain exactly two sections: router and link, each starting with the corresponding keyword\n"
<< "\n"
<< "# router section defines topology nodes and their relative positions (e.g., to use in visualizer)\n"
<< "router\n"
<< "\n"
<< "# each line in this section represents one router and should have the following data\n"
<< "# node comment yPos xPos\n";
for (NodeContainer::Iterator node = m_nodes.Begin ();
node != m_nodes.End ();
node++)
{
std::string name = Names::FindName (*node);
Ptr<MobilityModel> mobility = (*node)->GetObject<MobilityModel> ();
Vector position = mobility->GetPosition ();
os << name << "\t" << "NA" << "\t" << -position.y << "\t" << position.x << "\n";
}
os << "# link section defines point-to-point links between nodes and characteristics of these links\n"
<< "\n"
<< "link\n"
<< "\n"
<< "# Each line should be in the following format (only first two are required, the rest can be omitted)\n"
<< "# srcNode dstNode bandwidth metric delay queue\n"
<< "# bandwidth: link bandwidth\n"
<< "# metric: routing metric\n"
<< "# delay: link delay\n"
<< "# queue: MaxPackets for transmission queue on the link (both directions)\n"
<< "# error: comma-separated list, specifying class for ErrorModel and necessary attributes\n";
for (std::list<Link>::const_iterator link = m_linksList.begin ();
link != m_linksList.end ();
link ++)
{
os << Names::FindName (link->GetFromNode ()) << "\t";
os << Names::FindName (link->GetToNode ()) << "\t";
string tmp;
if (link->GetAttributeFailSafe ("DataRate", tmp))
os << link->GetAttribute("DataRate") << "\t";
else
NS_FATAL_ERROR ("DataRate must be specified for the link");
if (link->GetAttributeFailSafe ("OSPF", tmp))
os << link->GetAttribute("OSPF") << "\t";
else
os << "1\t";
if (link->GetAttributeFailSafe ("Delay", tmp))
{
os << link->GetAttribute("Delay") << "\t";
if (link->GetAttributeFailSafe ("MaxPackets", tmp))
{
os << link->GetAttribute("MaxPackets") << "\t";
if (link->GetAttributeFailSafe ("LossRate", tmp))
{
os << link->GetAttribute ("LossRate") << "\t";
}
}
}
os << "\n";
}
}
template <class Names>
class name_writer {
public:
name_writer(Names _names) : names(_names) {}
template <class VertexOrEdge>
void operator()(std::ostream& out, const VertexOrEdge& v) const {
// out << "[label=\"" << names[v] << "\",style=filled,fillcolor=\"" << colors[v] << "\"]";
out << "[shape=\"circle\",width=0.1,label=\"\",style=filled,fillcolor=\"green\"]";
}
private:
Names names;
};
template <class Names>
inline name_writer<Names>
make_name_writer(Names n) {
return name_writer<Names>(n);
}
void
AnnotatedTopologyReader::SaveGraphviz (const std::string &file)
{
typedef boost::adjacency_list_traits<boost::setS, boost::setS, boost::undirectedS> Traits;
typedef boost::property< boost::vertex_name_t, std::string, boost::property
< boost::vertex_index_t, uint32_t > > nodeProperty;
typedef boost::no_property edgeProperty;
typedef boost::adjacency_list< boost::setS, boost::setS, boost::undirectedS,
nodeProperty, edgeProperty > Graph;
typedef map<string, Traits::vertex_descriptor> node_map_t;
node_map_t graphNodes;
Graph graph;
for (NodeContainer::Iterator node = m_nodes.Begin ();
node != m_nodes.End ();
node++)
{
std::pair<node_map_t::iterator, bool>
retval = graphNodes.insert (make_pair (Names::FindName (*node),
add_vertex (nodeProperty (Names::FindName (*node)), graph)));
// NS_ASSERT (ok == true);
put (boost::vertex_index, graph, retval.first->second, (*node)->GetId ());
}
for (std::list<Link>::const_iterator link = m_linksList.begin ();
link != m_linksList.end ();
link ++)
{
node_map_t::iterator from = graphNodes.find (Names::FindName (link->GetFromNode ()));
node_map_t::iterator to = graphNodes.find (Names::FindName (link->GetToNode ()));
// add_edge (node->second, otherNode->second, m_graph);
boost::add_edge (from->second, to->second, graph);
}
ofstream of (file.c_str ());
boost::property_map<Graph, boost::vertex_name_t>::type names = get (boost::vertex_name, graph);
write_graphviz (of, graph, make_name_writer (names));
}
}