"""Adapter for ns-2.
This module contains the code for converting an FNSS topology object into a Tcl
script to deploy such topology into ns-2.
"""
from warnings import warn
from fnss.units import time_units, capacity_units
from fnss.netconfig.nodeconfig import get_stack, \
get_application_names, \
get_application_properties
__all__ = [
'to_ns2',
'validate_ns2_stacks'
]
# Template text rendered by the template engine
__TEMPLATE = r"""# Code generated by Fast Network Simulator Setup (FNSS)
<%
from fnss.units import time_units, capacity_units
from fnss.netconfig.nodeconfig import get_stack, \
get_application_names, \
get_application_properties
# Convert capacity in Mb
capacity_norm = capacity_units[topology.graph['capacity_unit']] / 1000000.0
# Convert delay it in ms
if set_delays: delay_norm = time_units[topology.graph['delay_unit']]
%>
# Create a simulator object
set ns [new Simulator]
# Create nodes
% for node in topology.nodes():
set n${str(node)} [$ns node]
% endfor
# Create all links
set qtype DropTail
## if topology is undirected, create duplex links, otherwise simplex links
% if topology.is_directed():
% for u, v in topology.edges():
<% delay = "0" if not set_delays else str(topology.adj[u][v]['delay'] * delay_norm) %>\
${"$ns simplex-link $n%s $n%s %sMb %sms $qtype" % (str(u), str(v), str(topology.adj[u][v]['capacity'] * capacity_norm), delay)}
% endfor
% else:
% for u, v in topology.edges():
<% delay = "0" if not set_delays else str(topology.adj[u][v]['delay'] * delay_norm) %>\
${"$ns duplex-link $n%s $n%s %sMb %sms $qtype" % (str(u), str(v), str(topology.adj[u][v]['capacity'] * capacity_norm), delay)}
% endfor
%endif
% if set_weights:
# Set link weights
% for u, v in topology.edges():
${"$ns cost $n%s $n%s %s" % (str(u), str(v), str(topology.adj[u][v]['weight']))}
% if not topology.is_directed():
${"$ns cost $n%s $n%s %s" % (str(v), str(u), str(topology.adj[v][u]['weight']))}
% endif
% endfor
% endif
% if set_buffers:
# Set queue sizes
% for u, v in topology.edges():
${"$ns queue-limit $n%s $n%s %s" % (str(u), str(v), str(topology.adj[u][v]['buffer']))}
% if not topology.is_directed():
${"$ns queue-limit $n%s $n%s %s" % (str(v), str(u), str(topology.adj[v][u]['buffer']))}
% endif
% endfor
% endif
% if deploy_stacks:
# Deploy applications and agents
% for node in topology.nodes():
<%
stack = get_stack(topology, node)
if stack is None:
continue
stack_name, stack_props = stack
stack_class = stack_props['class']
%>\
${"set %s [new %s]" % (str(stack_name), str(stack_class))}
% for prop_name, prop_val in stack_props.items():
<% if prop_name == 'class': continue %>\
${"$%s set %s %s" % (str(stack_name), str(prop_name), str(prop_val))}
% endfor
${"$ns attach-agent $n%s $%s" % (str(node), str(stack_name))}
% for app_name in get_application_names(topology, node):
<%
app_properties = get_application_properties(topology, node, app_name)
app_class = app_properties['class']
%>\
${"set %s [new %s]" % (str(app_name), str(app_class))}
% for prop_name, prop_val in app_properties.items():
<% if prop_name == 'class': continue %>\
${"$%s set %s %s" % (str(app_name), str(prop_name), str(prop_val))}
% endfor
${"$%s attach-agent $%s" % (str(app_name), str(stack_name))}
% endfor
% endfor
% endif
"""
[docs]def validate_ns2_stacks(topology):
"""
Validate whether the stacks and applications of a topology are valid for
ns-2 deployment
Parameters
----------
topology : Topology
The topology object to validate
Returns
-------
valid : bool
*True* if stacks are valid ns-2 stacks, *False* otherwise
"""
for node in topology.nodes():
applications = get_application_names(topology, node)
for name in applications:
if not 'class' in get_application_properties(topology, node, name):
# Each application must have a class attribute to work
return False
stack = get_stack(topology, node)
if stack is None:
# Each node must have a stack if it has an application
if len(applications) > 0: return False
else:
# If there is a stack, it must have a class attribute
if 'class' not in stack[1]: return False
return True
[docs]def to_ns2(topology, path, stacks=True):
"""Convert topology object into an ns-2 Tcl script that deploys that
topology into ns-2.
Parameters
----------
topology : Topology
The topology object to convert
path : str
The path to the output Tcl file
stacks : bool, optional
If True, read the stacks on nodes and write them into the output file.
For this to work, stacks must be formatted in a way understandable by
ns-2. For example, stack and applications must have a 'class' attribute
whose value is the name of the ns-2 class implementing it.
Notes
-----
In order for the function to parse stacks correctly, the input topology
must satisfy the following requirements:
* each stack and each application must have a `class` attribute whose
value is the ns-2 class implementing such stack or application, such as
`Agent/TCP` or `Application/FTP`.
* All names and values of stack and application properties must be valid
properties recognized by the ns-2 application or protocol stack.
"""
try:
from mako.template import Template
except ImportError:
raise ImportError('Cannot import mako.template module. '
'Make sure mako is installed on this machine.')
set_buffers = True
set_delays = True
# if all links are annotated with weights, then set weights
set_weights = all('weight' in topology.adj[u][v]
for u, v in topology.edges())
if not 'capacity_unit' in topology.graph:
raise ValueError('The given topology does not have capacity data.')
if not topology.graph['capacity_unit'] in capacity_units:
raise ValueError('The given topology does not have a valid capacity unit')
if not 'buffer_unit' in topology.graph:
warn('Missing buffer size unit attribute in the topology. '\
'Output file will be generated without buffer assignments.')
set_buffers = False
elif not topology.graph['buffer_unit'] == 'packets':
warn('The buffer size unit of the topology is %s. The only supported '
'unit is packets. Output file will be generated without buffer '
'assignments' % topology.graph['buffer_unit'])
set_buffers = False
if not 'delay_unit' in topology.graph or not topology.graph['delay_unit'] in time_units:
warn('Missing or invalid delay unit attribute in the topology. The '
'output file will be generated with all link delays set to 0.')
set_delays = False
if stacks:
if not validate_ns2_stacks(topology):
warn('Some application stacks cannot be parsed correctly. The '
'output file will be generated without stack assignments.')
stacks = False
if not any('stack' in topology.node[v] for v in topology.nodes()):
stacks = False
template = Template(__TEMPLATE)
variables = {
'topology': topology,
'deploy_stacks': stacks,
'set_delays': set_delays,
'set_buffers': set_buffers,
'set_weights': set_weights
}
with open(path, "w") as out:
out.write(template.render(**variables))