intrig-unicamp / mininet-wifi

Emulator for Software-Defined Wireless Networks
https://mn-wifi.readthedocs.io/
Other
438 stars 239 forks source link

Using Socket Server with plotGraph failed to update the graph #345

Closed jerrylususu closed 2 years ago

jerrylususu commented 3 years ago

I'm trying to use socket to decouple the network/topology definition and the movement of AP/stations. The socket function included in mininet-wifi seems to be a good choice. However, when I added net.plotGraph() to examples/socket_server.py, and try to set position from socket client, the graph won't be updated, and the machine's CPU usage will spike to 100%, until I manually shutdown the socket server.

(Note that without net.plotGraph(), the socket server/client is working fine.)

I tried to investigate this issue, which seems to be related to the thread-unsafe nature of matplotlib library. The socket server is implemented by creating another thread to listen the socket and execute the commands, and when it received a set.[obj].setPosition(coord) request, after some method call, the server will stuck at node.circle.set_radius(), as it is trying to update the graph outside the main thread.

Here is the call chain I found:

net.get_socket_data
node.setPosition
node.updateGraph
node.set_circle_radius()
node.circle.set_radius()

However, from the Youtube video I found, updating graph should be supported with socket. I don't know if this function is broken by some previous changes.

For the record, my environment is the Mininet-WiFi with P4 (pass: wifi) virtual machine, download from the repo's homepage. Git shows the most recent commit in mn_wifi folder is 4e7370c

jerrylususu commented 3 years ago

Just tried with the current version (newest commit, 30cc6ea), the problem still exists.

My modified socket_server.py is as follows.

#!/usr/bin/python

'Setting position of the nodes and enable sockets'

# force to use the local mn_wifi folder version, not the one installed in dist-packages
import sys
sys.path.remove('/usr/local/lib/python3.8/dist-packages/mininet_wifi-2.5-py3.8.egg')
sys.path.append(".")

from mininet.log import setLogLevel, info
from mn_wifi.cli import CLI
from mn_wifi.net import Mininet_wifi

def topology():

    net = Mininet_wifi()

    info("*** Creating nodes\n")
    net.addStation('sta1', mac='00:00:00:00:00:02', ip='10.0.0.1/8',
                   position='30,60,0')
    net.addStation('sta2', mac='00:00:00:00:00:03', ip='10.0.0.2/8',
                   position='70,30,0')
    ap1 = net.addAccessPoint('ap1', ssid='new-ssid', mode='g', channel='1',
                             failMode="standalone", position='50,50,0')
    h1 = net.addHost('h1', ip='10.0.0.3/8')

    net.setPropagationModel(model="logDistance", exp=4.5)

    info("*** Configuring wifi nodes\n")
    net.configureWifiNodes()

    # Try to plot graph
    net.plotGraph(max_x=200, max_y=200)

    info("*** Creating links\n")
    net.addLink(ap1, h1)

    info("*** Starting network\n")
    # net.addNAT(linkTo='ap1').configDefault()
    net.build()
    ap1.start([])

    # set_socket_ip: localhost must be replaced by ip address
    # of the network interface of your system
    # The same must be done with socket_client.py
    info("*** Starting Socket Server\n")
    net.socketServer(ip='127.0.0.1', port=12345)

    info("*** Running CLI\n")
    CLI(net)

    info("*** Stopping network\n")
    net.stop()

if __name__ == '__main__':
    setLogLevel('info')
    topology()

With the following changes to the socket_server.py in example folder:

+# force to use the local mn_wifi folder version, not the one installed in dist-packages +import sys +sys.path.remove('/usr/local/lib/python3.8/dist-packages/mininet_wifi-2.5-py3.8.egg') +sys.path.append(".") + from mininet.log import setLogLevel, info from mn_wifi.cli import CLI from mn_wifi.net import Mininet_wifi @@ -25,11 +30,14 @@ info("*** Configuring wifi nodes\n") net.configureWifiNodes()

And the command I used in client is set.ap1.setPosition(0,0,0), which will receive an empty response from server. image

After the command, the graph is not updated. (ap1 is still at (50,50,0), not (0,0,0)) image

Yet the CPU spikes to 100% usage, with several socket_server threads using all CPU. image

jerrylususu commented 3 years ago

Trying b0368625, which seems to work, at least I was able to reproduce the results shown in the YouTube video. However, the underlying graph mechanism is quite different from current version.

jerrylususu commented 3 years ago

For some mysterious reason, there is a commit saying "we do not need telemetry in socket" (e5d70440). Previous commit (b7cd1ced) still works as expected, with the graph updated after the command from socket client.

jerrylususu commented 3 years ago

After pondering at the commit history for a while, I'm wondering when should I use net.telemetry, and when should I use net.plotGraph. Searching telemetry in the user manual (draft) don't give me any information. Is there an explanation about the differences between net.telemetry and net.plotGraph and under what scenarios I should use one over the other?

From my current understanding, it seems that net.telemetry is implemented by using system commands to dump the network status and update the graph using a while loop (with 0.0001s interval), and net.plotGraph has the internal view and can update on demand.

For the socket examples, using net.telemetry seems to be a better idea.

jerrylususu commented 3 years ago

Back to the newest commit, I found that adding the following lines allows me to achieve what I want.

    nodes = net.stations + net.aps
    net.telemetry(nodes=nodes, data_type='position')

Still, I would appreciate any explanation on the difference between telemetry and plotGraph. Searching net.telemetry on current code base don't return much usage. However, in examples/telemetry.py there is a note: "This uses telemetry() to enable a graph with live statistics".

EDIT: But when I rescale the graph, the scale and center position will be reseted at the next re-draw. Any advice on this?

ramonfontes commented 3 years ago

I tried to investigate this issue, which seems to be related to the thread-unsafe nature of matplotlib library. The socket server is implemented by creating another thread to listen the socket and execute the commands, and when it received a set.[obj].setPosition(coord) request, after some method call, the server will stuck at node.circle.set_radius(), as it is trying to update the graph outside the main thread.

Your findings are correct ;)

Still, I would appreciate any explanation on the difference between telemetry and plotGraph. Searching net.telemetry on current code base don't return much usage. However, in examples/telemetry.py there is a note: "This uses telemetry() to enable a graph with live statistics".

net.telemetry is still in the early stages of implementation and I need to think about how to improve it. In fact, I often start some implementation and expect contributions and discussions like this. Plotting the position of the node was not the main objective, but I also took the opportunity to do so. As you can see at https://github.com/intrig-unicamp/mininet-wifi/blob/master/mn_wifi/telemetry.py#L9, you define a type called RSSI where you can view the RSSI of the nodes from /sys/class/ieee80211/{}/device/net/{}/statistics/{}, but not limited to it. The position is useful in cases like SUMO https://youtu.be/4zp0j-3xEWQ?t=379. For the rest, we will face the problem correctly reported by you.

jerrylususu commented 3 years ago

Thanks for your kind response! For now, I will just use net.telemetry. Still, I'm really happy to see that this bug is confirmed and will be fixed in future versions. Thanks again for your and your community's amazing work!