blob: fe039e6042b6200efd6af5c949175f74a084a9da [file] [log] [blame]
from mininet.net import Mininet
from mininet.log import info, error
from mininet.link import Link
class TopoExecutor:
def __init__(self, net: Mininet):
self.net = net
async def get_topo(self):
"""UI Function: Get topology"""
nodes = []
node_ids = set()
links = []
for host in self.net.hosts:
nodes.append(self._node_dict(host))
node_ids.add(host.name)
for switch in self.net.switches:
nodes.append(self._node_dict(switch, switch=True))
for station in getattr(self.net, "stations", []):
if station.name not in node_ids:
nodes.append(self._node_dict(station))
for link in self.net.links:
if obj := self._link_dict(link):
links.append(obj)
return {
"nodes": nodes,
"links": links,
}
async def add_link(self, a, b, link_id, opts):
"""UI Function: Add link"""
link = self.net.addLink(self.net[a], self.net[b], **self._conv_link_opts(opts))
info(f"Added link {link}\n")
return {
"id": link_id,
"mnId": str(link),
**opts,
}
async def del_link(self, a, b, mn_id):
"""UI Function: Delete link"""
link = self._get_link(a, b, mn_id)
if link:
self.net.delLink(link)
return True
error(f"No link found to remove for {mn_id}\n")
return False
async def upd_link(self, a, b, mn_id, opts):
"""UI Function: Update link"""
link = self._get_link(a, b, mn_id)
if link:
params = self._conv_link_opts(opts)
link.intf1.config(**params)
link.intf2.config(**params)
for p in params:
link.intf1.params[p] = params[p]
link.intf2.params[p] = params[p]
return True
info(f"No link to configure for {mn_id}\n")
return False
async def add_node(self, node_id, label):
"""UI Function: Add node (host is added)"""
self.net.addHost(label)
return {
"id": node_id,
"label": label,
}
async def del_node(self, node_id):
"""UI Function: Delete node"""
self.net.delNode(self.net[node_id])
info(f"Removed node {node_id}\n")
return True
async def set_node_pos(self, node_id, x, y):
"""UI Function: Set node position"""
node = self.net[node_id]
if not node or not hasattr(node, "position"):
return False
x, y = x / 10, y / 10
z = node.position[2] if len(node.position) > 2 else 0
node.setPosition(f"{int(x)},{int(y)},{int(z)}")
info(f"Set position of {node_id} to {x},{y}\n")
return True
def _node_dict(self, node, switch=False):
val = {
"id": node.name,
"label": node.name,
}
if switch:
val["isSwitch"] = True
if hasattr(node, "position"):
# Mininet positions are in m, NDN-Play are much smaller
val["x"] = node.position[0] * 10
val["y"] = node.position[1] * 10
if hasattr(node, "params") and "params" in node.params:
p = node.params["params"]
if "color" in p:
val["color"] = p["color"]
return val
def _link_dict(self, link):
if isinstance(link.intf2, str):
if link.intf2 == "wifiAdhoc":
# TODO: visualize adhoc links
pass
return None
obj = {
"mnId": str(link),
"from": link.intf1.node.name,
"to": link.intf2.node.name,
}
if "delay" in link.intf1.params:
d1 = int(link.intf1.params["delay"][:-len("ms")])
d2 = int(link.intf2.params["delay"][:-len("ms")])
obj["latency"] = (d1 + d2) / 2
if "loss" in link.intf1.params:
l1 = link.intf1.params["loss"]
l2 = link.intf2.params["loss"]
obj["loss"] = (l1 + l2) / 2
return obj
def _get_link(self, a, b, mn_id) -> Link | None:
"""Helper: get link between two nodes by name"""
for link in self.net.linksBetween(self.net[a], self.net[b]):
if str(link) == mn_id:
return link
return None
def _conv_link_opts(self, opts: dict):
"""Helper: convert link options"""
params = {}
if "latency" in opts and opts["latency"] is not None and int(opts["latency"]) >= 0:
params["delay"] = str(int(opts["latency"])) + "ms"
if "loss" in opts and opts["loss"] is not None and float(opts["loss"]) >= 0:
params["loss"] = float(opts["loss"])
return params